├── .gitignore ├── LICENSE ├── README.md ├── pom.xml └── src ├── main ├── java │ └── com │ │ └── digitalpetri │ │ └── opcua │ │ └── nodeset │ │ ├── UaNodeSet.java │ │ ├── UaNodeSetMerger.java │ │ ├── attributes │ │ ├── DataTypeNodeAttributes.java │ │ ├── MethodNodeAttributes.java │ │ ├── NodeAttributes.java │ │ ├── ObjectNodeAttributes.java │ │ ├── ObjectTypeNodeAttributes.java │ │ ├── ReferenceTypeNodeAttributes.java │ │ ├── VariableNodeAttributes.java │ │ ├── VariableTypeNodeAttributes.java │ │ └── ViewNodeAttributes.java │ │ └── util │ │ └── AttributeUtil.java └── resources │ └── UANodeSet.xsd └── test ├── java └── com │ └── digitalpetri │ └── opcua │ └── nodeset │ └── UaNodeSetTest.java └── resources ├── Opc.Ua.NodeSet2.Part10.xml ├── Opc.Ua.NodeSet2.Part11.xml ├── Opc.Ua.NodeSet2.Part13.xml ├── Opc.Ua.NodeSet2.Part3.xml ├── Opc.Ua.NodeSet2.Part4.xml ├── Opc.Ua.NodeSet2.Part5.xml ├── Opc.Ua.NodeSet2.Part8.xml ├── Opc.Ua.NodeSet2.Part9.xml ├── Opc.Ua.NodeSet2.xml ├── adi └── Opc.Ua.Adi.NodeSet2.xml ├── aml ├── Opc.Ua.AMLBaseTypes.NodeSet2.xml └── Opc.Ua.AMLLibraries.NodeSet2.xml ├── autoid └── Opc.Ua.AutoID.NodeSet2.xml ├── di └── Opc.Ua.Di.NodeSet2.xml ├── mdis └── OPC.MDIS.NodeSet2.xml ├── plc └── Opc.Ua.Plc.NodeSet2.xml └── schema └── Opc.Ua.NodeSet.Schema.Check.xml /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | 3 | # Package Files # 4 | *.jar 5 | *.war 6 | *.ear 7 | 8 | # IntelliJ # 9 | *.iml 10 | .idea/* 11 | 12 | # Maven # 13 | target/* 14 | 15 | # Generated Sources # 16 | src/main/generated/* 17 | 18 | release.properties 19 | 20 | # macOS 21 | .DS_Store 22 | 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | UANodeSet Parser 2 | ================ 3 | 4 | A parser for the OPC UA UANodeSet XML schema. 5 | 6 | ### From Maven 7 | ```xml 8 | 9 |     com.digitalpetri.opcua 10 |     uanodeset-parser 11 |     0.4.0-SNAPSHOT 12 | 13 | ``` 14 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | com.digitalpetri.opcua 6 | uanodeset-parser 7 | 0.5.0-SNAPSHOT 8 | 9 | UANodeSet XML Parser 10 | A parser and node-generator for XML files conforming to the UANodeSet schema. See 11 | http://opcfoundation.org/UA/schemas/1.03/UANodeSet.xsd. 12 | 13 | https://github.com/digitalpetri/opc-ua-nodeset-parser 14 | 15 | 16 | https://github.com/digitalpetri/opc-ua-nodeset-parser 17 | scm:git:git@github.com:digitalpetri/opc-ua-nodeset-parser.git 18 | HEAD 19 | 20 | 21 | 22 | 23 | kevinherron 24 | Kevin Herron 25 | kevinherron@gmail.com 26 | 27 | 28 | 29 | 30 | 31 | Apache License, Version 2.0 32 | http://www.apache.org/licenses/LICENSE-2.0.txt 33 | repo 34 | 35 | 36 | 37 | 38 | UTF-8 39 | UTF-8 40 | 41 | 42 | 43 | 3.0.5 44 | 45 | 46 | 47 | 48 | org.eclipse.milo 49 | sdk-server 50 | 0.5.0 51 | provided 52 | 53 | 54 | 55 | org.glassfish.jaxb 56 | jaxb-runtime 57 | 2.3.2 58 | 59 | 60 | 61 | junit 62 | junit 63 | 4.11 64 | test 65 | 66 | 67 | 68 | org.slf4j 69 | slf4j-simple 70 | 1.7.25 71 | test 72 | 73 | 74 | 75 | 76 | 77 | release 78 | 79 | 80 | 81 | org.apache.maven.plugins 82 | maven-source-plugin 83 | 3.2.0 84 | 85 | 86 | attach-sources 87 | 88 | jar-no-fork 89 | 90 | 91 | 92 | 93 | 94 | org.apache.maven.plugins 95 | maven-javadoc-plugin 96 | 3.1.1 97 | 98 | org.opcfoundation.ua.generated:org.opcfoundation.ua.generated.* 99 | 100 | 101 | 102 | 103 | attach-javadocs 104 | 105 | jar 106 | 107 | 108 | 109 | 110 | 111 | org.apache.maven.plugins 112 | maven-gpg-plugin 113 | 1.6 114 | 115 | 116 | sign-artifacts 117 | verify 118 | 119 | sign 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | org.apache.maven.plugins 133 | maven-compiler-plugin 134 | 3.6.2 135 | 136 | 1.8 137 | 1.8 138 | 139 | 140 | 141 | 142 | 143 | org.codehaus.mojo 144 | jaxb2-maven-plugin 145 | 2.4 146 | 147 | 148 | generate-sources 149 | xjc 150 | 151 | xjc 152 | 153 | 154 | 155 | 156 | 157 | org.opcfoundation.ua.generated 158 | 159 | 160 | ${basedir}/src/main/resources 161 | 162 | 163 | 164 | ${basedir}/target/generated-sources/jaxb2 165 | 166 | 167 | 168 | com.sun.activation 169 | jakarta.activation 170 | 1.2.1 171 | 172 | 173 | org.glassfish.jaxb 174 | jaxb-xjc 175 | 2.3.2 176 | 177 | 178 | 179 | 180 | 181 | org.apache.maven.plugins 182 | maven-release-plugin 183 | 2.5 184 | 185 | true 186 | false 187 | release 188 | deploy 189 | v@{project.version} 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | ossrh 198 | https://oss.sonatype.org/content/repositories/snapshots 199 | 200 | 201 | ossrh 202 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 203 | 204 | 205 | 206 | 207 | -------------------------------------------------------------------------------- /src/main/java/com/digitalpetri/opcua/nodeset/UaNodeSet.java: -------------------------------------------------------------------------------- 1 | package com.digitalpetri.opcua.nodeset; 2 | 3 | import java.io.InputStream; 4 | import java.util.HashMap; 5 | import java.util.List; 6 | import java.util.Map; 7 | import java.util.Optional; 8 | import javax.xml.bind.JAXBContext; 9 | import javax.xml.bind.JAXBException; 10 | import javax.xml.bind.Marshaller; 11 | 12 | import com.digitalpetri.opcua.nodeset.attributes.DataTypeNodeAttributes; 13 | import com.digitalpetri.opcua.nodeset.attributes.MethodNodeAttributes; 14 | import com.digitalpetri.opcua.nodeset.attributes.NodeAttributes; 15 | import com.digitalpetri.opcua.nodeset.attributes.ObjectNodeAttributes; 16 | import com.digitalpetri.opcua.nodeset.attributes.ObjectTypeNodeAttributes; 17 | import com.digitalpetri.opcua.nodeset.attributes.ReferenceTypeNodeAttributes; 18 | import com.digitalpetri.opcua.nodeset.attributes.VariableNodeAttributes; 19 | import com.digitalpetri.opcua.nodeset.attributes.VariableTypeNodeAttributes; 20 | import com.digitalpetri.opcua.nodeset.attributes.ViewNodeAttributes; 21 | import com.digitalpetri.opcua.nodeset.util.AttributeUtil; 22 | import com.google.common.collect.ArrayListMultimap; 23 | import com.google.common.collect.ListMultimap; 24 | import org.eclipse.milo.opcua.stack.core.NamespaceTable; 25 | import org.eclipse.milo.opcua.stack.core.types.builtin.NodeId; 26 | import org.opcfoundation.ua.generated.AliasTable; 27 | import org.opcfoundation.ua.generated.DataTypeDefinition; 28 | import org.opcfoundation.ua.generated.NodeIdAlias; 29 | import org.opcfoundation.ua.generated.ObjectFactory; 30 | import org.opcfoundation.ua.generated.Reference; 31 | import org.opcfoundation.ua.generated.UADataType; 32 | import org.opcfoundation.ua.generated.UAMethod; 33 | import org.opcfoundation.ua.generated.UANodeSet; 34 | import org.opcfoundation.ua.generated.UAObject; 35 | import org.opcfoundation.ua.generated.UAObjectType; 36 | import org.opcfoundation.ua.generated.UAReferenceType; 37 | import org.opcfoundation.ua.generated.UAVariable; 38 | import org.opcfoundation.ua.generated.UAVariableType; 39 | import org.opcfoundation.ua.generated.UAView; 40 | 41 | public class UaNodeSet { 42 | 43 | private ListMultimap combinedReferences; 44 | 45 | private final Map nodes; 46 | private final ListMultimap explicitReferences; 47 | private final ListMultimap implicitReferences; 48 | private final NamespaceTable namespaceTable; 49 | private final Map aliasTable; 50 | private final Map dataTypeDefinitions; 51 | private final Map rawXmlValues; 52 | 53 | public UaNodeSet( 54 | Map nodes, 55 | ListMultimap explicitReferences, 56 | ListMultimap implicitReferences, 57 | NamespaceTable namespaceTable, 58 | Map aliasTable, 59 | Map dataTypeDefinitions, 60 | Map rawXmlValues 61 | ) { 62 | 63 | this.nodes = nodes; 64 | this.explicitReferences = explicitReferences; 65 | this.implicitReferences = implicitReferences; 66 | this.namespaceTable = namespaceTable; 67 | this.aliasTable = aliasTable; 68 | this.dataTypeDefinitions = dataTypeDefinitions; 69 | this.rawXmlValues = rawXmlValues; 70 | } 71 | 72 | UaNodeSet(UANodeSet nodeSet) throws JAXBException { 73 | aliasTable = new HashMap<>(); 74 | namespaceTable = new NamespaceTable(); 75 | explicitReferences = ArrayListMultimap.create(); 76 | implicitReferences = ArrayListMultimap.create(); 77 | nodes = new HashMap<>(); 78 | dataTypeDefinitions = new HashMap<>(); 79 | rawXmlValues = new HashMap<>(); 80 | 81 | JAXBContext jaxbContext = JAXBContext.newInstance(ObjectFactory.class); 82 | Marshaller marshaller = jaxbContext.createMarshaller(); 83 | 84 | // Alias Map 85 | AliasTable aliasTable = nodeSet.getAliases(); 86 | if (aliasTable != null) { 87 | List aliases = aliasTable.getAlias(); 88 | if (aliases != null) { 89 | aliases.forEach(a -> this.aliasTable.put(a.getAlias(), NodeId.parse(a.getValue()))); 90 | } 91 | } 92 | 93 | // Namespace URI List 94 | if (nodeSet.getNamespaceUris() != null) { 95 | List uris = nodeSet.getNamespaceUris().getUri(); 96 | uris.forEach(namespaceTable::addUri); 97 | } 98 | 99 | // Reference Details 100 | nodeSet.getUAObjectOrUAVariableOrUAMethod().forEach(gNode -> { 101 | NodeId sourceNodeId = AttributeUtil.tryParseNodeId(gNode.getNodeId(), this.aliasTable); 102 | 103 | Optional.ofNullable(gNode.getReferences()).ifPresent(gReferences -> gReferences.getReference().forEach( 104 | gReference -> { 105 | org.eclipse.milo.opcua.sdk.core.Reference reference = 106 | referenceFromGenerated(sourceNodeId, gReference); 107 | 108 | explicitReferences.put(sourceNodeId, reference); 109 | 110 | reference.invert(namespaceTable).ifPresent( 111 | inverseReference -> 112 | implicitReferences.put(inverseReference.getSourceNodeId(), inverseReference) 113 | ); 114 | } 115 | )); 116 | }); 117 | 118 | // Node Attributes 119 | nodeSet.getUAObjectOrUAVariableOrUAMethod().forEach(gNode -> { 120 | NodeAttributes attributes = null; 121 | 122 | if (gNode instanceof UADataType) { 123 | UADataType gDataTypeNode = (UADataType) gNode; 124 | 125 | attributes = DataTypeNodeAttributes.fromGenerated(gDataTypeNode); 126 | 127 | DataTypeDefinition definition = gDataTypeNode.getDefinition(); 128 | 129 | if (definition != null) { 130 | dataTypeDefinitions.put(attributes.getNodeId(), definition); 131 | } 132 | } else if (gNode instanceof UAMethod) { 133 | attributes = MethodNodeAttributes.fromGenerated((UAMethod) gNode); 134 | } else if (gNode instanceof UAObject) { 135 | attributes = ObjectNodeAttributes.fromGenerated((UAObject) gNode); 136 | } else if (gNode instanceof UAObjectType) { 137 | attributes = ObjectTypeNodeAttributes.fromGenerated((UAObjectType) gNode); 138 | } else if (gNode instanceof UAReferenceType) { 139 | attributes = ReferenceTypeNodeAttributes.fromGenerated((UAReferenceType) gNode); 140 | } else if (gNode instanceof UAVariable) { 141 | attributes = VariableNodeAttributes.fromGenerated( 142 | (UAVariable) gNode, marshaller, this.aliasTable, rawXmlValues 143 | ); 144 | } else if (gNode instanceof UAVariableType) { 145 | attributes = VariableTypeNodeAttributes.fromGenerated( 146 | (UAVariableType) gNode, marshaller, this.aliasTable, rawXmlValues 147 | ); 148 | } else if (gNode instanceof UAView) { 149 | attributes = ViewNodeAttributes.fromGenerated((UAView) gNode); 150 | } 151 | 152 | if (attributes != null) { 153 | nodes.put(attributes.getNodeId(), attributes); 154 | } 155 | }); 156 | } 157 | 158 | public Map getAliasTable() { 159 | return aliasTable; 160 | } 161 | 162 | public NamespaceTable getNamespaceTable() { 163 | return namespaceTable; 164 | } 165 | 166 | public Map getNodes() { 167 | return nodes; 168 | } 169 | 170 | /** 171 | * Get the {@link Reference}s that were explicitly defined by the NodeSet. 172 | * 173 | * @return the {@link Reference}s that were explicitly defined by the NodeSet. 174 | */ 175 | public ListMultimap getExplicitReferences() { 176 | return explicitReferences; 177 | } 178 | 179 | /** 180 | * Get the {@link Reference}s that were implicitly defined by the NodeSet, i.e. they were derived by inverting an 181 | * explicitly defined reference. 182 | * 183 | * @return the {@link Reference}s that were implicitly defined by the NodeSet 184 | */ 185 | public ListMultimap getImplicitReferences() { 186 | return implicitReferences; 187 | } 188 | 189 | /** 190 | * Get all {@link Reference}s defined by the NodeSet, i.e. both explicit and implicit 191 | * references. 192 | * 193 | * @return all {@link Reference}s defined by the NodeSet. 194 | * @see #getExplicitReferences() 195 | * @see #getImplicitReferences() 196 | */ 197 | public synchronized ListMultimap getCombinedReferences() { 198 | if (combinedReferences == null) { 199 | combinedReferences = ArrayListMultimap.create(); 200 | combinedReferences.putAll(explicitReferences); 201 | combinedReferences.putAll(implicitReferences); 202 | } 203 | 204 | return combinedReferences; 205 | } 206 | 207 | public Map getDataTypeDefinitions() { 208 | return dataTypeDefinitions; 209 | } 210 | 211 | public Map getRawXmlValues() { 212 | return rawXmlValues; 213 | } 214 | 215 | /** 216 | * Merge another {@link UaNodeSet} into this one and return a new {@link UaNodeSet} with the 217 | * contents of both. 218 | * 219 | * @param other the {@link UaNodeSet} to merge. 220 | * @return a new {@link UANodeSet} containing the contents of {@code other} merged into this. 221 | */ 222 | public UaNodeSet merge(UaNodeSet other) { 223 | return UaNodeSetMerger.merge(this, other); 224 | } 225 | 226 | private org.eclipse.milo.opcua.sdk.core.Reference referenceFromGenerated( 227 | NodeId sourceNodeId, 228 | Reference gReference 229 | ) { 230 | 231 | NodeId targetNodeId = AttributeUtil.tryParseNodeId(gReference.getValue(), aliasTable); 232 | NodeId referenceTypeId = AttributeUtil.parseReferenceTypeId(gReference, aliasTable); 233 | boolean isForward = gReference.isIsForward(); 234 | 235 | return new org.eclipse.milo.opcua.sdk.core.Reference( 236 | sourceNodeId, 237 | referenceTypeId, 238 | targetNodeId.expanded(), 239 | isForward 240 | ); 241 | } 242 | 243 | public static UaNodeSet parse(InputStream nodeSetXml) throws JAXBException { 244 | JAXBContext jaxbContext = JAXBContext.newInstance(ObjectFactory.class); 245 | 246 | UANodeSet nodeSet = (UANodeSet) jaxbContext.createUnmarshaller().unmarshal(nodeSetXml); 247 | 248 | return new UaNodeSet(nodeSet); 249 | } 250 | 251 | } 252 | -------------------------------------------------------------------------------- /src/main/java/com/digitalpetri/opcua/nodeset/UaNodeSetMerger.java: -------------------------------------------------------------------------------- 1 | package com.digitalpetri.opcua.nodeset; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | import java.util.Objects; 6 | 7 | import com.digitalpetri.opcua.nodeset.attributes.DataTypeNodeAttributes; 8 | import com.digitalpetri.opcua.nodeset.attributes.MethodNodeAttributes; 9 | import com.digitalpetri.opcua.nodeset.attributes.NodeAttributes; 10 | import com.digitalpetri.opcua.nodeset.attributes.ObjectNodeAttributes; 11 | import com.digitalpetri.opcua.nodeset.attributes.ObjectTypeNodeAttributes; 12 | import com.digitalpetri.opcua.nodeset.attributes.ReferenceTypeNodeAttributes; 13 | import com.digitalpetri.opcua.nodeset.attributes.VariableNodeAttributes; 14 | import com.digitalpetri.opcua.nodeset.attributes.VariableTypeNodeAttributes; 15 | import com.digitalpetri.opcua.nodeset.attributes.ViewNodeAttributes; 16 | import com.google.common.collect.ArrayListMultimap; 17 | import com.google.common.collect.ListMultimap; 18 | import org.eclipse.milo.opcua.sdk.core.Reference; 19 | import org.eclipse.milo.opcua.stack.core.NamespaceTable; 20 | import org.eclipse.milo.opcua.stack.core.serialization.EncodingLimits; 21 | import org.eclipse.milo.opcua.stack.core.serialization.SerializationContext; 22 | import org.eclipse.milo.opcua.stack.core.types.DataTypeManager; 23 | import org.eclipse.milo.opcua.stack.core.types.OpcUaDataTypeManager; 24 | import org.eclipse.milo.opcua.stack.core.types.builtin.ByteString; 25 | import org.eclipse.milo.opcua.stack.core.types.builtin.DataValue; 26 | import org.eclipse.milo.opcua.stack.core.types.builtin.ExpandedNodeId; 27 | import org.eclipse.milo.opcua.stack.core.types.builtin.ExtensionObject; 28 | import org.eclipse.milo.opcua.stack.core.types.builtin.NodeId; 29 | import org.eclipse.milo.opcua.stack.core.types.builtin.QualifiedName; 30 | import org.eclipse.milo.opcua.stack.core.types.builtin.Variant; 31 | import org.eclipse.milo.opcua.stack.core.types.builtin.XmlElement; 32 | import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.UShort; 33 | import org.eclipse.milo.opcua.stack.core.types.structured.Argument; 34 | import org.eclipse.milo.opcua.stack.core.util.ArrayUtil; 35 | import org.opcfoundation.ua.generated.DataTypeDefinition; 36 | import org.opcfoundation.ua.generated.DataTypeField; 37 | import org.slf4j.Logger; 38 | import org.slf4j.LoggerFactory; 39 | 40 | public final class UaNodeSetMerger { 41 | 42 | private static final Logger LOGGER = LoggerFactory.getLogger(UaNodeSetMerger.class); 43 | 44 | private UaNodeSetMerger() {} 45 | 46 | public static UaNodeSet merge(UaNodeSet nodeSet1, UaNodeSet nodeSet2) { 47 | Map nodes = new HashMap<>(nodeSet1.getNodes()); 48 | ListMultimap explicitReferences = 49 | ArrayListMultimap.create(nodeSet1.getExplicitReferences()); 50 | ListMultimap implicitReferences = 51 | ArrayListMultimap.create(nodeSet1.getImplicitReferences()); 52 | NamespaceTable namespaceTable = nodeSet1.getNamespaceTable(); 53 | Map aliasTable = new HashMap<>(nodeSet1.getAliasTable()); 54 | Map dataTypeDefinitions = new HashMap<>(nodeSet1.getDataTypeDefinitions()); 55 | Map rawXmlValues = new HashMap<>(nodeSet1.getRawXmlValues()); 56 | 57 | for (String uri : nodeSet2.getNamespaceTable().toArray()) { 58 | UShort index = namespaceTable.getIndex(uri); 59 | if (index == null) { 60 | namespaceTable.addUri(uri); 61 | } 62 | } 63 | 64 | nodeSet2.getAliasTable().forEach((alias, nodeId) -> { 65 | NodeId newNodeId = reindex( 66 | nodeId, 67 | namespaceTable, 68 | nodeSet2.getNamespaceTable() 69 | ); 70 | 71 | NodeId collision = aliasTable.putIfAbsent(alias, newNodeId); 72 | 73 | if (collision != null && !Objects.equals(collision, newNodeId)) { 74 | String warning = String.format( 75 | "Alias collision: \"%s\". " + 76 | "NodeId in nodeSet1=%s, NodeId in nodeSet2=%s", 77 | alias, collision, newNodeId 78 | ); 79 | System.err.println(warning); 80 | } 81 | }); 82 | 83 | nodeSet2.getNodes().forEach((nodeId, nodeAttributes) -> { 84 | NodeAttributes newNodeAttributes = reindex( 85 | nodeAttributes, 86 | namespaceTable, 87 | nodeSet2.getNamespaceTable() 88 | ); 89 | nodes.put(newNodeAttributes.getNodeId(), newNodeAttributes); 90 | }); 91 | 92 | nodeSet2.getExplicitReferences().forEach((nodeId, reference) -> { 93 | NodeId newNodeId = reindex( 94 | nodeId, 95 | namespaceTable, 96 | nodeSet2.getNamespaceTable() 97 | ); 98 | 99 | Reference newReference = reindex( 100 | reference, 101 | namespaceTable, 102 | nodeSet2.getNamespaceTable() 103 | ); 104 | 105 | explicitReferences.put(newNodeId, newReference); 106 | }); 107 | 108 | nodeSet2.getImplicitReferences().forEach((nodeId, reference) -> { 109 | NodeId newNodeId = reindex( 110 | nodeId, 111 | namespaceTable, 112 | nodeSet2.getNamespaceTable() 113 | ); 114 | 115 | Reference newReference = reindex( 116 | reference, 117 | namespaceTable, 118 | nodeSet2.getNamespaceTable() 119 | ); 120 | 121 | implicitReferences.put(newNodeId, newReference); 122 | }); 123 | 124 | nodeSet2.getDataTypeDefinitions().forEach((nodeId, definition) -> { 125 | NodeId newNodeId = reindex( 126 | nodeId, 127 | namespaceTable, 128 | nodeSet2.getNamespaceTable() 129 | ); 130 | 131 | reindex(definition, aliasTable, namespaceTable, nodeSet2.getNamespaceTable()); 132 | 133 | dataTypeDefinitions.put(newNodeId, definition); 134 | }); 135 | 136 | nodeSet2.getRawXmlValues().forEach((nodeId, value) -> { 137 | NodeId newNodeId = reindex( 138 | nodeId, 139 | namespaceTable, 140 | nodeSet2.getNamespaceTable() 141 | ); 142 | 143 | // TODO reindex value 144 | rawXmlValues.put(newNodeId, value); 145 | }); 146 | 147 | return new UaNodeSet( 148 | nodes, 149 | explicitReferences, 150 | implicitReferences, 151 | namespaceTable, 152 | aliasTable, 153 | dataTypeDefinitions, 154 | rawXmlValues 155 | ); 156 | } 157 | 158 | private static NodeId reindex( 159 | NodeId nodeId, 160 | NamespaceTable currentNamespaceTable, 161 | NamespaceTable originalNamespaceTable 162 | ) { 163 | 164 | String namespaceUri = originalNamespaceTable.getUri(nodeId.getNamespaceIndex()); 165 | 166 | return nodeId.reindex(currentNamespaceTable, namespaceUri); 167 | } 168 | 169 | /** 170 | * Re-index {@code expandedNodeId} from its original namespace index to the corresponding 171 | * index in the server for its namespace URI derived from the original namespace index. 172 | * 173 | * @param expandedNodeId an {@link ExpandedNodeId} from the {@link UaNodeSet}. 174 | * @param currentNamespaceTable the current {@link NamespaceTable}. 175 | * @param originalNamespaceTable the original {@link NamespaceTable}. 176 | * @return a {@link ExpandedNodeId} that has been re-indexed for the current NamespaceTable. 177 | */ 178 | private static ExpandedNodeId reindex( 179 | ExpandedNodeId expandedNodeId, 180 | NamespaceTable currentNamespaceTable, 181 | NamespaceTable originalNamespaceTable 182 | ) { 183 | 184 | if (expandedNodeId.isAbsolute()) { 185 | return expandedNodeId; 186 | } else { 187 | String namespaceUri = originalNamespaceTable.getUri(expandedNodeId.getNamespaceIndex()); 188 | 189 | return expandedNodeId.reindex(currentNamespaceTable, namespaceUri); 190 | } 191 | } 192 | 193 | private static QualifiedName reindex( 194 | QualifiedName browseName, 195 | NamespaceTable currentNamespaceTable, 196 | NamespaceTable originalNamespaceTable 197 | ) { 198 | 199 | String namespaceUri = originalNamespaceTable.getUri(browseName.getNamespaceIndex()); 200 | 201 | return browseName.reindex(currentNamespaceTable, namespaceUri); 202 | } 203 | 204 | private static Reference reindex( 205 | Reference reference, 206 | NamespaceTable currentNamespaceTable, 207 | NamespaceTable originalNamespaceTable 208 | ) { 209 | 210 | String sourceNamespaceUri = originalNamespaceTable.getUri( 211 | reference.getSourceNodeId().getNamespaceIndex() 212 | ); 213 | String referenceNamespaceUri = originalNamespaceTable.getUri( 214 | reference.getReferenceTypeId().getNamespaceIndex() 215 | ); 216 | String targetNamespaceUri = reference.getTargetNodeId().getNamespaceUri(); 217 | 218 | if (targetNamespaceUri == null) { 219 | targetNamespaceUri = originalNamespaceTable.getUri( 220 | reference.getTargetNodeId().getNamespaceIndex() 221 | ); 222 | } 223 | 224 | return reference.reindex(currentNamespaceTable, sourceNamespaceUri, referenceNamespaceUri, targetNamespaceUri); 225 | } 226 | 227 | private static NodeAttributes reindex( 228 | NodeAttributes nodeAttributes, 229 | NamespaceTable currentNamespaceTable, 230 | NamespaceTable originalNamespaceTable 231 | ) { 232 | 233 | if (nodeAttributes instanceof DataTypeNodeAttributes) { 234 | return reindex((DataTypeNodeAttributes) nodeAttributes, currentNamespaceTable, originalNamespaceTable); 235 | } else if (nodeAttributes instanceof MethodNodeAttributes) { 236 | return reindex((MethodNodeAttributes) nodeAttributes, currentNamespaceTable, originalNamespaceTable); 237 | } else if (nodeAttributes instanceof ObjectNodeAttributes) { 238 | return reindex((ObjectNodeAttributes) nodeAttributes, currentNamespaceTable, originalNamespaceTable); 239 | } else if (nodeAttributes instanceof ObjectTypeNodeAttributes) { 240 | return reindex((ObjectTypeNodeAttributes) nodeAttributes, currentNamespaceTable, originalNamespaceTable); 241 | } else if (nodeAttributes instanceof ReferenceTypeNodeAttributes) { 242 | return reindex((ReferenceTypeNodeAttributes) nodeAttributes, currentNamespaceTable, originalNamespaceTable); 243 | } else if (nodeAttributes instanceof VariableNodeAttributes) { 244 | return reindex((VariableNodeAttributes) nodeAttributes, currentNamespaceTable, originalNamespaceTable); 245 | } else if (nodeAttributes instanceof VariableTypeNodeAttributes) { 246 | return reindex((VariableTypeNodeAttributes) nodeAttributes, currentNamespaceTable, originalNamespaceTable); 247 | } else if (nodeAttributes instanceof ViewNodeAttributes) { 248 | return reindex((ViewNodeAttributes) nodeAttributes, currentNamespaceTable, originalNamespaceTable); 249 | } else { 250 | throw new IllegalArgumentException("nodeAttributes: " + nodeAttributes); 251 | } 252 | } 253 | 254 | private static NodeAttributes reindex( 255 | DataTypeNodeAttributes dataTypeNodeAttributes, 256 | NamespaceTable currentNamespaceTable, 257 | NamespaceTable originalNamespaceTable 258 | ) { 259 | 260 | NodeId newNodeId = reindex( 261 | dataTypeNodeAttributes.getNodeId(), 262 | currentNamespaceTable, 263 | originalNamespaceTable 264 | ); 265 | 266 | QualifiedName newBrowseName = reindex( 267 | dataTypeNodeAttributes.getBrowseName(), 268 | currentNamespaceTable, 269 | originalNamespaceTable 270 | ); 271 | 272 | return new DataTypeNodeAttributes( 273 | newNodeId, 274 | newBrowseName, 275 | dataTypeNodeAttributes.getDisplayName(), 276 | dataTypeNodeAttributes.getDescription(), 277 | dataTypeNodeAttributes.getWriteMask(), 278 | dataTypeNodeAttributes.getUserWriteMask(), 279 | dataTypeNodeAttributes.isAbstract() 280 | ); 281 | } 282 | 283 | private static NodeAttributes reindex( 284 | MethodNodeAttributes methodNodeAttributes, 285 | NamespaceTable currentNamespaceTable, 286 | NamespaceTable originalNamespaceTable 287 | ) { 288 | 289 | NodeId newNodeId = reindex( 290 | methodNodeAttributes.getNodeId(), 291 | currentNamespaceTable, 292 | originalNamespaceTable 293 | ); 294 | 295 | QualifiedName newBrowseName = reindex( 296 | methodNodeAttributes.getBrowseName(), 297 | currentNamespaceTable, 298 | originalNamespaceTable 299 | ); 300 | 301 | return new MethodNodeAttributes( 302 | newNodeId, 303 | newBrowseName, 304 | methodNodeAttributes.getDisplayName(), 305 | methodNodeAttributes.getDescription(), 306 | methodNodeAttributes.getWriteMask(), 307 | methodNodeAttributes.getUserWriteMask(), 308 | methodNodeAttributes.isExecutable(), 309 | methodNodeAttributes.isUserExecutable() 310 | ); 311 | } 312 | 313 | private static NodeAttributes reindex( 314 | ObjectNodeAttributes objectNodeAttributes, 315 | NamespaceTable currentNamespaceTable, 316 | NamespaceTable originalNamespaceTable 317 | ) { 318 | 319 | NodeId newNodeId = reindex( 320 | objectNodeAttributes.getNodeId(), 321 | currentNamespaceTable, 322 | originalNamespaceTable 323 | ); 324 | 325 | QualifiedName newBrowseName = reindex( 326 | objectNodeAttributes.getBrowseName(), 327 | currentNamespaceTable, 328 | originalNamespaceTable 329 | ); 330 | 331 | return new ObjectNodeAttributes( 332 | newNodeId, 333 | newBrowseName, 334 | objectNodeAttributes.getDisplayName(), 335 | objectNodeAttributes.getDescription(), 336 | objectNodeAttributes.getWriteMask(), 337 | objectNodeAttributes.getUserWriteMask(), 338 | objectNodeAttributes.getEventNotifier() 339 | ); 340 | } 341 | 342 | private static ObjectTypeNodeAttributes reindex( 343 | ObjectTypeNodeAttributes objectTypeNodeAttributes, 344 | NamespaceTable currentNamespaceTable, 345 | NamespaceTable originalNamespaceTable 346 | ) { 347 | 348 | NodeId newNodeId = reindex( 349 | objectTypeNodeAttributes.getNodeId(), 350 | currentNamespaceTable, 351 | originalNamespaceTable 352 | ); 353 | 354 | QualifiedName newBrowseName = reindex( 355 | objectTypeNodeAttributes.getBrowseName(), 356 | currentNamespaceTable, 357 | originalNamespaceTable 358 | ); 359 | 360 | return new ObjectTypeNodeAttributes( 361 | newNodeId, 362 | newBrowseName, 363 | objectTypeNodeAttributes.getDisplayName(), 364 | objectTypeNodeAttributes.getDescription(), 365 | objectTypeNodeAttributes.getWriteMask(), 366 | objectTypeNodeAttributes.getUserWriteMask(), 367 | objectTypeNodeAttributes.isAbstract() 368 | ); 369 | } 370 | 371 | private static ReferenceTypeNodeAttributes reindex( 372 | ReferenceTypeNodeAttributes referenceTypeNodeAttributes, 373 | NamespaceTable currentNamespaceTable, 374 | NamespaceTable originalNamespaceTable 375 | ) { 376 | 377 | NodeId newNodeId = reindex( 378 | referenceTypeNodeAttributes.getNodeId(), 379 | currentNamespaceTable, 380 | originalNamespaceTable 381 | ); 382 | 383 | QualifiedName newBrowseName = reindex( 384 | referenceTypeNodeAttributes.getBrowseName(), 385 | currentNamespaceTable, 386 | originalNamespaceTable 387 | ); 388 | 389 | return new ReferenceTypeNodeAttributes( 390 | newNodeId, 391 | newBrowseName, 392 | referenceTypeNodeAttributes.getDisplayName(), 393 | referenceTypeNodeAttributes.getDescription(), 394 | referenceTypeNodeAttributes.getWriteMask(), 395 | referenceTypeNodeAttributes.getUserWriteMask(), 396 | referenceTypeNodeAttributes.isAbstract(), 397 | referenceTypeNodeAttributes.isSymmetric(), 398 | referenceTypeNodeAttributes.getInverseName() 399 | ); 400 | } 401 | 402 | private static VariableNodeAttributes reindex( 403 | VariableNodeAttributes variableNodeAttributes, 404 | NamespaceTable currentNamespaceTable, 405 | NamespaceTable originalNamespaceTable 406 | ) { 407 | 408 | NodeId newNodeId = reindex( 409 | variableNodeAttributes.getNodeId(), 410 | currentNamespaceTable, 411 | originalNamespaceTable 412 | ); 413 | 414 | QualifiedName newBrowseName = reindex( 415 | variableNodeAttributes.getBrowseName(), 416 | currentNamespaceTable, 417 | originalNamespaceTable 418 | ); 419 | 420 | NodeId newDataTypeId = reindex( 421 | variableNodeAttributes.getDataType(), 422 | currentNamespaceTable, 423 | originalNamespaceTable 424 | ); 425 | 426 | DataValue newValue = reindex( 427 | variableNodeAttributes.getValue(), 428 | currentNamespaceTable, 429 | originalNamespaceTable 430 | ); 431 | 432 | return new VariableNodeAttributes( 433 | newNodeId, 434 | newBrowseName, 435 | variableNodeAttributes.getDisplayName(), 436 | variableNodeAttributes.getDescription(), 437 | variableNodeAttributes.getWriteMask(), 438 | variableNodeAttributes.getUserWriteMask(), 439 | newValue, 440 | newDataTypeId, 441 | variableNodeAttributes.getValueRank(), 442 | variableNodeAttributes.getArrayDimensions(), 443 | variableNodeAttributes.getAccessLevel(), 444 | variableNodeAttributes.getUserAccessLevel(), 445 | variableNodeAttributes.getMinimumSamplingInterval(), 446 | variableNodeAttributes.isHistorizing() 447 | ); 448 | } 449 | 450 | private static VariableTypeNodeAttributes reindex( 451 | VariableTypeNodeAttributes variableTypeNodeAttributes, 452 | NamespaceTable currentNamespaceTable, 453 | NamespaceTable originalNamespaceTable 454 | ) { 455 | 456 | NodeId newNodeId = reindex( 457 | variableTypeNodeAttributes.getNodeId(), 458 | currentNamespaceTable, 459 | originalNamespaceTable 460 | ); 461 | 462 | QualifiedName newBrowseName = reindex( 463 | variableTypeNodeAttributes.getBrowseName(), 464 | currentNamespaceTable, 465 | originalNamespaceTable 466 | ); 467 | 468 | NodeId newDataTypeId = reindex( 469 | variableTypeNodeAttributes.getDataType(), 470 | currentNamespaceTable, 471 | originalNamespaceTable 472 | ); 473 | 474 | DataValue newValue = reindex( 475 | variableTypeNodeAttributes.getValue(), 476 | currentNamespaceTable, 477 | originalNamespaceTable 478 | ); 479 | 480 | return new VariableTypeNodeAttributes( 481 | newNodeId, 482 | newBrowseName, 483 | variableTypeNodeAttributes.getDisplayName(), 484 | variableTypeNodeAttributes.getDescription(), 485 | variableTypeNodeAttributes.getWriteMask(), 486 | variableTypeNodeAttributes.getUserWriteMask(), 487 | newValue, 488 | newDataTypeId, 489 | variableTypeNodeAttributes.getValueRank(), 490 | variableTypeNodeAttributes.getArrayDimensions(), 491 | variableTypeNodeAttributes.isAbstract() 492 | ); 493 | } 494 | 495 | private static ViewNodeAttributes reindex( 496 | ViewNodeAttributes viewNodeAttributes, 497 | NamespaceTable currentNamespaceTable, 498 | NamespaceTable originalNamespaceTable 499 | ) { 500 | 501 | NodeId newNodeId = reindex( 502 | viewNodeAttributes.getNodeId(), 503 | currentNamespaceTable, 504 | originalNamespaceTable 505 | ); 506 | 507 | QualifiedName newBrowseName = reindex( 508 | viewNodeAttributes.getBrowseName(), 509 | currentNamespaceTable, 510 | originalNamespaceTable 511 | ); 512 | 513 | return new ViewNodeAttributes( 514 | newNodeId, 515 | newBrowseName, 516 | viewNodeAttributes.getDisplayName(), 517 | viewNodeAttributes.getDescription(), 518 | viewNodeAttributes.getWriteMask(), 519 | viewNodeAttributes.getUserWriteMask(), 520 | viewNodeAttributes.isContainsNoLoops(), 521 | viewNodeAttributes.getEventNotifier() 522 | ); 523 | } 524 | 525 | private static void reindex( 526 | DataTypeDefinition definition, 527 | Map aliasTable, 528 | NamespaceTable currentNamespaceTable, 529 | NamespaceTable originalNamespaceTable 530 | ) { 531 | 532 | QualifiedName newName = reindex( 533 | QualifiedName.parse(definition.getName()), 534 | currentNamespaceTable, 535 | originalNamespaceTable 536 | ); 537 | 538 | definition.setName(newName.toParseableString()); 539 | 540 | definition.getField().forEach(field -> 541 | reindex( 542 | field, 543 | aliasTable, 544 | currentNamespaceTable, 545 | originalNamespaceTable 546 | ) 547 | ); 548 | } 549 | 550 | private static void reindex( 551 | DataTypeField field, 552 | Map aliasTable, 553 | NamespaceTable currentNamespaceTable, 554 | NamespaceTable originalNamespaceTable 555 | ) { 556 | 557 | QualifiedName newName = reindex( 558 | QualifiedName.parse(field.getName()), 559 | currentNamespaceTable, 560 | originalNamespaceTable 561 | ); 562 | field.setName(newName.toParseableString()); 563 | 564 | String dataType = field.getDataType(); 565 | if (dataType != null) { 566 | NodeId nodeId = aliasTable.get(dataType); 567 | if (nodeId == null) nodeId = NodeId.parse(dataType); 568 | NodeId newDataType = reindex( 569 | nodeId, 570 | currentNamespaceTable, 571 | originalNamespaceTable 572 | ); 573 | field.setDataType(newDataType.toParseableString()); 574 | } 575 | 576 | DataTypeDefinition innerDefinition = field.getDefinition(); 577 | if (innerDefinition != null) { 578 | reindex(innerDefinition, aliasTable, currentNamespaceTable, originalNamespaceTable); 579 | } 580 | } 581 | 582 | /** 583 | * Re-indexes a {@link DataValue} if necessary. 584 | *

585 | * If {@code value} contains an ExtensionObject the encodingId is re-indexed. Then the struct is decoded and any 586 | * fields that qualify are also re-indexed (e.g. the dataType field in {@link Argument}). 587 | *

588 | * This is verging on major hack because the OPC UA modelling concept is somewhat flawed when it comes to encoding 589 | * embedded values that reference non-absolute namespaces. 590 | * 591 | * @param value the {@link DataValue} to re-index. 592 | * @return a {@link DataValue} that has been re-indexed for the current server. 593 | */ 594 | private static DataValue reindex( 595 | DataValue value, 596 | NamespaceTable currentNamespaceTable, 597 | NamespaceTable originalNamespaceTable 598 | ) { 599 | 600 | try { 601 | if (value == null) return null; 602 | Variant variant = value.getValue(); 603 | if (variant == null) return value; 604 | Object o = variant.getValue(); 605 | if (o == null) return value; 606 | return new DataValue(new Variant(reindexValue(o, currentNamespaceTable, originalNamespaceTable))); 607 | } catch (Throwable t) { 608 | LOGGER.warn("Re-indexing failed: {}", value, t); 609 | return value; 610 | } 611 | } 612 | 613 | private static Object reindexValue( 614 | Object value, 615 | NamespaceTable currentNamespaceTable, 616 | NamespaceTable originalNamespaceTable 617 | ) { 618 | 619 | if (value == null) return null; 620 | 621 | Class clazz = value.getClass(); 622 | 623 | if (clazz.isArray()) { 624 | @SuppressWarnings("rawtypes") 625 | Class componentType = ArrayUtil.getType(value); 626 | 627 | if (componentType != NodeId.class 628 | && componentType != ExpandedNodeId.class 629 | && componentType != QualifiedName.class 630 | && componentType != ExtensionObject.class 631 | ) { 632 | 633 | return value; 634 | } else { 635 | //noinspection unchecked 636 | return ArrayUtil.transformArray( 637 | value, 638 | o -> reindexValue(o, currentNamespaceTable, originalNamespaceTable), 639 | componentType 640 | ); 641 | } 642 | } else { 643 | if (clazz == NodeId.class) { 644 | return reindex((NodeId) value, currentNamespaceTable, originalNamespaceTable); 645 | } else if (clazz == ExpandedNodeId.class) { 646 | return reindex((ExpandedNodeId) value, currentNamespaceTable, originalNamespaceTable); 647 | } else if (clazz == QualifiedName.class) { 648 | return reindex((QualifiedName) value, currentNamespaceTable, originalNamespaceTable); 649 | } else if (clazz == ExtensionObject.class) { 650 | ExtensionObject xo = (ExtensionObject) value; 651 | 652 | if (xo.getBodyType() == ExtensionObject.BodyType.ByteString) { 653 | xo = new ExtensionObject( 654 | (ByteString) xo.getBody(), 655 | reindex(xo.getEncodingId(), currentNamespaceTable, originalNamespaceTable) 656 | ); 657 | } else if (xo.getBodyType() == ExtensionObject.BodyType.XmlElement) { 658 | xo = new ExtensionObject( 659 | (XmlElement) xo.getBody(), 660 | reindex(xo.getEncodingId(), currentNamespaceTable, originalNamespaceTable) 661 | ); 662 | } 663 | 664 | try { 665 | Object struct = xo.decode(SERIALIZATION_CONTEXT); 666 | 667 | if (struct instanceof Argument) { 668 | Argument argument = (Argument) struct; 669 | 670 | return ExtensionObject.encode( 671 | SERIALIZATION_CONTEXT, 672 | new Argument( 673 | argument.getName(), 674 | reindex(argument.getDataType(), currentNamespaceTable, originalNamespaceTable), 675 | argument.getValueRank(), 676 | argument.getArrayDimensions(), 677 | argument.getDescription() 678 | ) 679 | ); 680 | } else { 681 | return xo; 682 | } 683 | } catch (Throwable t) { 684 | LOGGER.warn("Decoding failed: {}", xo, t); 685 | return xo; 686 | } 687 | } else { 688 | return value; 689 | } 690 | } 691 | } 692 | 693 | /** 694 | * A default {@link SerializationContext} that can be used to decode OPC UA built-in types. 695 | */ 696 | private static final SerializationContext SERIALIZATION_CONTEXT = new SerializationContext() { 697 | 698 | private final NamespaceTable namespaceTable = new NamespaceTable(); 699 | 700 | @Override 701 | public EncodingLimits getEncodingLimits() { 702 | return EncodingLimits.DEFAULT; 703 | } 704 | 705 | @Override 706 | public NamespaceTable getNamespaceTable() { 707 | return namespaceTable; 708 | } 709 | 710 | @Override 711 | public DataTypeManager getDataTypeManager() { 712 | return OpcUaDataTypeManager.getInstance(); 713 | } 714 | 715 | }; 716 | 717 | } 718 | -------------------------------------------------------------------------------- /src/main/java/com/digitalpetri/opcua/nodeset/attributes/DataTypeNodeAttributes.java: -------------------------------------------------------------------------------- 1 | package com.digitalpetri.opcua.nodeset.attributes; 2 | 3 | import org.eclipse.milo.opcua.stack.core.types.builtin.LocalizedText; 4 | import org.eclipse.milo.opcua.stack.core.types.builtin.NodeId; 5 | import org.eclipse.milo.opcua.stack.core.types.builtin.QualifiedName; 6 | import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.UInteger; 7 | import org.eclipse.milo.opcua.stack.core.types.enumerated.NodeClass; 8 | import org.opcfoundation.ua.generated.UADataType; 9 | 10 | import static org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.Unsigned.uint; 11 | 12 | public class DataTypeNodeAttributes extends NodeAttributes { 13 | 14 | private final boolean isAbstract; 15 | 16 | public DataTypeNodeAttributes( 17 | NodeId nodeId, 18 | QualifiedName browseName, 19 | LocalizedText displayName, 20 | LocalizedText description, 21 | UInteger writeMask, 22 | UInteger userWriteMask, 23 | boolean isAbstract 24 | ) { 25 | 26 | super(nodeId, NodeClass.DataType, browseName, displayName, description, writeMask, userWriteMask); 27 | 28 | this.isAbstract = isAbstract; 29 | } 30 | 31 | public boolean isAbstract() { 32 | return isAbstract; 33 | } 34 | 35 | @Override 36 | public String toString() { 37 | return "DataTypeNodeAttributes{" + 38 | "isAbstract=" + isAbstract + 39 | "} " + super.toString(); 40 | } 41 | 42 | public static DataTypeNodeAttributes fromGenerated(UADataType gNode) { 43 | NodeId nodeId = NodeId.parse(gNode.getNodeId()); 44 | QualifiedName browseName = QualifiedName.parse(gNode.getBrowseName()); 45 | 46 | LocalizedText displayName = gNode.getDisplayName().stream() 47 | .findFirst() 48 | .map(gLocalizedText -> LocalizedText.english(gLocalizedText.getValue())) 49 | .orElse(LocalizedText.english(browseName.getName())); 50 | 51 | LocalizedText description = gNode.getDescription().stream() 52 | .findFirst() 53 | .map(gLocalizedText -> LocalizedText.english(gLocalizedText.getValue())) 54 | .orElse(LocalizedText.NULL_VALUE); 55 | 56 | UInteger writeMask = uint(gNode.getWriteMask()); 57 | UInteger userWriteMask = uint(gNode.getUserWriteMask()); 58 | 59 | boolean isAbstract = gNode.isIsAbstract(); 60 | 61 | return new DataTypeNodeAttributes( 62 | nodeId, 63 | browseName, 64 | displayName, 65 | description, 66 | writeMask, 67 | userWriteMask, 68 | isAbstract 69 | ); 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/com/digitalpetri/opcua/nodeset/attributes/MethodNodeAttributes.java: -------------------------------------------------------------------------------- 1 | package com.digitalpetri.opcua.nodeset.attributes; 2 | 3 | import org.eclipse.milo.opcua.stack.core.types.builtin.LocalizedText; 4 | import org.eclipse.milo.opcua.stack.core.types.builtin.NodeId; 5 | import org.eclipse.milo.opcua.stack.core.types.builtin.QualifiedName; 6 | import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.UInteger; 7 | import org.eclipse.milo.opcua.stack.core.types.enumerated.NodeClass; 8 | import org.opcfoundation.ua.generated.UAMethod; 9 | 10 | import static org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.Unsigned.uint; 11 | 12 | public class MethodNodeAttributes extends NodeAttributes { 13 | 14 | private final boolean executable; 15 | private final boolean userExecutable; 16 | 17 | public MethodNodeAttributes( 18 | NodeId nodeId, 19 | QualifiedName browseName, 20 | LocalizedText displayName, 21 | LocalizedText description, 22 | UInteger writeMask, 23 | UInteger userWriteMask, 24 | boolean executable, 25 | boolean userExecutable 26 | ) { 27 | 28 | super(nodeId, NodeClass.Method, browseName, displayName, description, writeMask, userWriteMask); 29 | 30 | this.executable = executable; 31 | this.userExecutable = userExecutable; 32 | } 33 | 34 | public boolean isExecutable() { 35 | return executable; 36 | } 37 | 38 | public boolean isUserExecutable() { 39 | return userExecutable; 40 | } 41 | 42 | @Override 43 | public String toString() { 44 | return "MethodNodeAttributes{" + 45 | "executable=" + executable + 46 | ", userExecutable=" + userExecutable + 47 | "} " + super.toString(); 48 | } 49 | 50 | public static MethodNodeAttributes fromGenerated(UAMethod gNode) { 51 | NodeId nodeId = NodeId.parse(gNode.getNodeId()); 52 | QualifiedName browseName = QualifiedName.parse(gNode.getBrowseName()); 53 | 54 | LocalizedText displayName = gNode.getDisplayName().stream() 55 | .findFirst() 56 | .map(gLocalizedText -> LocalizedText.english(gLocalizedText.getValue())) 57 | .orElse(LocalizedText.english(browseName.getName())); 58 | 59 | LocalizedText description = gNode.getDescription().stream() 60 | .findFirst() 61 | .map(gLocalizedText -> LocalizedText.english(gLocalizedText.getValue())) 62 | .orElse(LocalizedText.NULL_VALUE); 63 | 64 | UInteger writeMask = uint(gNode.getWriteMask()); 65 | UInteger userWriteMask = uint(gNode.getUserWriteMask()); 66 | 67 | boolean executable = gNode.isExecutable(); 68 | boolean userExecutable = gNode.isUserExecutable(); 69 | 70 | return new MethodNodeAttributes( 71 | nodeId, 72 | browseName, 73 | displayName, 74 | description, 75 | writeMask, 76 | userWriteMask, 77 | executable, 78 | userExecutable 79 | ); 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/com/digitalpetri/opcua/nodeset/attributes/NodeAttributes.java: -------------------------------------------------------------------------------- 1 | package com.digitalpetri.opcua.nodeset.attributes; 2 | 3 | import org.eclipse.milo.opcua.stack.core.types.builtin.LocalizedText; 4 | import org.eclipse.milo.opcua.stack.core.types.builtin.NodeId; 5 | import org.eclipse.milo.opcua.stack.core.types.builtin.QualifiedName; 6 | import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.UInteger; 7 | import org.eclipse.milo.opcua.stack.core.types.enumerated.NodeClass; 8 | 9 | public abstract class NodeAttributes { 10 | 11 | private final NodeId nodeId; 12 | private final NodeClass nodeClass; 13 | private final QualifiedName browseName; 14 | private final LocalizedText displayName; 15 | private final LocalizedText description; 16 | private final UInteger writeMask; 17 | private final UInteger userWriteMask; 18 | 19 | public NodeAttributes( 20 | NodeId nodeId, 21 | NodeClass nodeClass, 22 | QualifiedName browseName, 23 | LocalizedText displayName, 24 | LocalizedText description, 25 | UInteger writeMask, 26 | UInteger userWriteMask 27 | ) { 28 | 29 | this.nodeId = nodeId; 30 | this.nodeClass = nodeClass; 31 | this.browseName = browseName; 32 | this.displayName = displayName; 33 | this.description = description; 34 | this.writeMask = writeMask; 35 | this.userWriteMask = userWriteMask; 36 | } 37 | 38 | public NodeId getNodeId() { 39 | return nodeId; 40 | } 41 | 42 | public NodeClass getNodeClass() { 43 | return nodeClass; 44 | } 45 | 46 | public QualifiedName getBrowseName() { 47 | return browseName; 48 | } 49 | 50 | public LocalizedText getDisplayName() { 51 | return displayName; 52 | } 53 | 54 | public LocalizedText getDescription() { 55 | return description; 56 | } 57 | 58 | public UInteger getWriteMask() { 59 | return writeMask; 60 | } 61 | 62 | public UInteger getUserWriteMask() { 63 | return userWriteMask; 64 | } 65 | 66 | @Override 67 | public String toString() { 68 | return "NodeAttributes{" + 69 | "nodeId=" + nodeId + 70 | ", nodeClass=" + nodeClass + 71 | ", browseName=" + browseName + 72 | ", displayName=" + displayName + 73 | ", description=" + description + 74 | ", writeMask=" + writeMask + 75 | ", userWriteMask=" + userWriteMask + 76 | '}'; 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/com/digitalpetri/opcua/nodeset/attributes/ObjectNodeAttributes.java: -------------------------------------------------------------------------------- 1 | package com.digitalpetri.opcua.nodeset.attributes; 2 | 3 | import org.eclipse.milo.opcua.stack.core.types.builtin.LocalizedText; 4 | import org.eclipse.milo.opcua.stack.core.types.builtin.NodeId; 5 | import org.eclipse.milo.opcua.stack.core.types.builtin.QualifiedName; 6 | import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.UByte; 7 | import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.UInteger; 8 | import org.eclipse.milo.opcua.stack.core.types.enumerated.NodeClass; 9 | import org.opcfoundation.ua.generated.UAObject; 10 | 11 | import static org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.Unsigned.ubyte; 12 | import static org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.Unsigned.uint; 13 | 14 | public class ObjectNodeAttributes extends NodeAttributes { 15 | 16 | private final UByte eventNotifier; 17 | 18 | public ObjectNodeAttributes( 19 | NodeId nodeId, 20 | QualifiedName browseName, 21 | LocalizedText displayName, 22 | LocalizedText description, 23 | UInteger writeMask, 24 | UInteger userWriteMask, 25 | UByte eventNotifier 26 | ) { 27 | 28 | super(nodeId, NodeClass.Object, browseName, displayName, description, writeMask, userWriteMask); 29 | 30 | this.eventNotifier = eventNotifier; 31 | } 32 | 33 | public UByte getEventNotifier() { 34 | return eventNotifier; 35 | } 36 | 37 | @Override 38 | public String toString() { 39 | return "ObjectNodeAttributes{" + 40 | "eventNotifier=" + eventNotifier + 41 | "} " + super.toString(); 42 | } 43 | 44 | public static ObjectNodeAttributes fromGenerated(UAObject gNode) { 45 | NodeId nodeId = NodeId.parse(gNode.getNodeId()); 46 | QualifiedName browseName = QualifiedName.parse(gNode.getBrowseName()); 47 | 48 | LocalizedText displayName = gNode.getDisplayName().stream() 49 | .findFirst() 50 | .map(gLocalizedText -> LocalizedText.english(gLocalizedText.getValue())) 51 | .orElse(LocalizedText.english(browseName.getName())); 52 | 53 | LocalizedText description = gNode.getDescription().stream() 54 | .findFirst() 55 | .map(gLocalizedText -> LocalizedText.english(gLocalizedText.getValue())) 56 | .orElse(LocalizedText.NULL_VALUE); 57 | 58 | UInteger writeMask = uint(gNode.getWriteMask()); 59 | UInteger userWriteMask = uint(gNode.getUserWriteMask()); 60 | 61 | UByte eventNotifier = ubyte(gNode.getEventNotifier()); 62 | 63 | return new ObjectNodeAttributes( 64 | nodeId, 65 | browseName, 66 | displayName, 67 | description, 68 | writeMask, 69 | userWriteMask, 70 | eventNotifier 71 | ); 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/com/digitalpetri/opcua/nodeset/attributes/ObjectTypeNodeAttributes.java: -------------------------------------------------------------------------------- 1 | package com.digitalpetri.opcua.nodeset.attributes; 2 | 3 | import org.eclipse.milo.opcua.stack.core.types.builtin.LocalizedText; 4 | import org.eclipse.milo.opcua.stack.core.types.builtin.NodeId; 5 | import org.eclipse.milo.opcua.stack.core.types.builtin.QualifiedName; 6 | import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.UInteger; 7 | import org.eclipse.milo.opcua.stack.core.types.enumerated.NodeClass; 8 | import org.opcfoundation.ua.generated.UAObjectType; 9 | 10 | import static org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.Unsigned.uint; 11 | 12 | public class ObjectTypeNodeAttributes extends NodeAttributes { 13 | 14 | private final boolean isAbstract; 15 | 16 | public ObjectTypeNodeAttributes( 17 | NodeId nodeId, 18 | QualifiedName browseName, 19 | LocalizedText displayName, 20 | LocalizedText description, 21 | UInteger writeMask, 22 | UInteger userWriteMask, 23 | boolean isAbstract 24 | ) { 25 | 26 | super(nodeId, NodeClass.ObjectType, browseName, displayName, description, writeMask, userWriteMask); 27 | 28 | this.isAbstract = isAbstract; 29 | } 30 | 31 | public boolean isAbstract() { 32 | return isAbstract; 33 | } 34 | 35 | @Override 36 | public String toString() { 37 | return "ObjectTypeNodeAttributes{" + 38 | "isAbstract=" + isAbstract + 39 | "} " + super.toString(); 40 | } 41 | 42 | public static ObjectTypeNodeAttributes fromGenerated(UAObjectType gNode) { 43 | NodeId nodeId = NodeId.parse(gNode.getNodeId()); 44 | QualifiedName browseName = QualifiedName.parse(gNode.getBrowseName()); 45 | 46 | LocalizedText displayName = gNode.getDisplayName().stream() 47 | .findFirst() 48 | .map(gLocalizedText -> LocalizedText.english(gLocalizedText.getValue())) 49 | .orElse(LocalizedText.english(browseName.getName())); 50 | 51 | LocalizedText description = gNode.getDescription().stream() 52 | .findFirst() 53 | .map(gLocalizedText -> LocalizedText.english(gLocalizedText.getValue())) 54 | .orElse(LocalizedText.NULL_VALUE); 55 | 56 | UInteger writeMask = uint(gNode.getWriteMask()); 57 | UInteger userWriteMask = uint(gNode.getUserWriteMask()); 58 | 59 | boolean isAbstract = gNode.isIsAbstract(); 60 | 61 | return new ObjectTypeNodeAttributes( 62 | nodeId, 63 | browseName, 64 | displayName, 65 | description, 66 | writeMask, 67 | userWriteMask, 68 | isAbstract 69 | ); 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/com/digitalpetri/opcua/nodeset/attributes/ReferenceTypeNodeAttributes.java: -------------------------------------------------------------------------------- 1 | package com.digitalpetri.opcua.nodeset.attributes; 2 | 3 | import org.eclipse.milo.opcua.stack.core.types.builtin.LocalizedText; 4 | import org.eclipse.milo.opcua.stack.core.types.builtin.NodeId; 5 | import org.eclipse.milo.opcua.stack.core.types.builtin.QualifiedName; 6 | import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.UInteger; 7 | import org.eclipse.milo.opcua.stack.core.types.enumerated.NodeClass; 8 | import org.opcfoundation.ua.generated.UAReferenceType; 9 | 10 | import static org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.Unsigned.uint; 11 | 12 | public class ReferenceTypeNodeAttributes extends NodeAttributes { 13 | 14 | private final boolean isAbstract; 15 | private final boolean symmetric; 16 | private final LocalizedText inverseName; 17 | 18 | public ReferenceTypeNodeAttributes( 19 | NodeId nodeId, 20 | QualifiedName browseName, 21 | LocalizedText displayName, 22 | LocalizedText description, 23 | UInteger writeMask, 24 | UInteger userWriteMask, 25 | boolean isAbstract, 26 | boolean symmetric, 27 | LocalizedText inverseName 28 | ) { 29 | 30 | super(nodeId, NodeClass.ReferenceType, browseName, displayName, description, writeMask, userWriteMask); 31 | 32 | this.isAbstract = isAbstract; 33 | this.symmetric = symmetric; 34 | this.inverseName = inverseName; 35 | } 36 | 37 | public boolean isAbstract() { 38 | return isAbstract; 39 | } 40 | 41 | public boolean isSymmetric() { 42 | return symmetric; 43 | } 44 | 45 | public LocalizedText getInverseName() { 46 | return inverseName; 47 | } 48 | 49 | @Override 50 | public String toString() { 51 | return "ReferenceTypeNodeAttributes{" + 52 | "isAbstract=" + isAbstract + 53 | ", symmetric=" + symmetric + 54 | ", inverseName=" + inverseName + 55 | "} " + super.toString(); 56 | } 57 | 58 | public static ReferenceTypeNodeAttributes fromGenerated(UAReferenceType gNode) { 59 | NodeId nodeId = NodeId.parse(gNode.getNodeId()); 60 | QualifiedName browseName = QualifiedName.parse(gNode.getBrowseName()); 61 | 62 | LocalizedText displayName = gNode.getDisplayName().stream() 63 | .findFirst() 64 | .map(gLocalizedText -> LocalizedText.english(gLocalizedText.getValue())) 65 | .orElse(LocalizedText.english(browseName.getName())); 66 | 67 | LocalizedText description = gNode.getDescription().stream() 68 | .findFirst() 69 | .map(gLocalizedText -> LocalizedText.english(gLocalizedText.getValue())) 70 | .orElse(LocalizedText.NULL_VALUE); 71 | 72 | UInteger writeMask = uint(gNode.getWriteMask()); 73 | UInteger userWriteMask = uint(gNode.getUserWriteMask()); 74 | 75 | boolean isAbstract = gNode.isIsAbstract(); 76 | boolean symmetric = gNode.isSymmetric(); 77 | 78 | LocalizedText inverseName = gNode.getInverseName().stream() 79 | .findFirst() 80 | .map(gLocalizedText -> LocalizedText.english(gLocalizedText.getValue())) 81 | .orElse(LocalizedText.NULL_VALUE); 82 | 83 | return new ReferenceTypeNodeAttributes( 84 | nodeId, 85 | browseName, 86 | displayName, 87 | description, 88 | writeMask, 89 | userWriteMask, 90 | isAbstract, 91 | symmetric, 92 | inverseName 93 | ); 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /src/main/java/com/digitalpetri/opcua/nodeset/attributes/VariableNodeAttributes.java: -------------------------------------------------------------------------------- 1 | package com.digitalpetri.opcua.nodeset.attributes; 2 | 3 | import java.util.Arrays; 4 | import java.util.Map; 5 | import javax.xml.bind.Marshaller; 6 | 7 | import com.digitalpetri.opcua.nodeset.util.AttributeUtil; 8 | import org.eclipse.milo.opcua.stack.core.types.builtin.DataValue; 9 | import org.eclipse.milo.opcua.stack.core.types.builtin.LocalizedText; 10 | import org.eclipse.milo.opcua.stack.core.types.builtin.NodeId; 11 | import org.eclipse.milo.opcua.stack.core.types.builtin.QualifiedName; 12 | import org.eclipse.milo.opcua.stack.core.types.builtin.Variant; 13 | import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.UByte; 14 | import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.UInteger; 15 | import org.eclipse.milo.opcua.stack.core.types.enumerated.NodeClass; 16 | import org.opcfoundation.ua.generated.UAVariable; 17 | 18 | import static org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.Unsigned.ubyte; 19 | import static org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.Unsigned.uint; 20 | 21 | public class VariableNodeAttributes extends NodeAttributes { 22 | 23 | private final DataValue value; 24 | private final NodeId dataType; 25 | private final int valueRank; 26 | private final UInteger[] arrayDimensions; 27 | private final UByte accessLevel; 28 | private final UByte userAccessLevel; 29 | private final Double minimumSamplingInterval; 30 | private final boolean historizing; 31 | 32 | public VariableNodeAttributes( 33 | NodeId nodeId, 34 | QualifiedName browseName, 35 | LocalizedText displayName, 36 | LocalizedText description, 37 | UInteger writeMask, 38 | UInteger userWriteMask, 39 | DataValue value, 40 | NodeId dataType, 41 | int valueRank, 42 | UInteger[] arrayDimensions, 43 | UByte accessLevel, 44 | UByte userAccessLevel, 45 | Double minimumSamplingInterval, 46 | boolean historizing 47 | ) { 48 | 49 | super(nodeId, NodeClass.Variable, browseName, displayName, description, writeMask, userWriteMask); 50 | 51 | this.value = value; 52 | this.dataType = dataType; 53 | this.valueRank = valueRank; 54 | this.arrayDimensions = arrayDimensions; 55 | this.accessLevel = accessLevel; 56 | this.userAccessLevel = userAccessLevel; 57 | this.minimumSamplingInterval = minimumSamplingInterval; 58 | this.historizing = historizing; 59 | } 60 | 61 | public DataValue getValue() { 62 | return value; 63 | } 64 | 65 | public NodeId getDataType() { 66 | return dataType; 67 | } 68 | 69 | public int getValueRank() { 70 | return valueRank; 71 | } 72 | 73 | public UInteger[] getArrayDimensions() { 74 | return arrayDimensions; 75 | } 76 | 77 | public UByte getAccessLevel() { 78 | return accessLevel; 79 | } 80 | 81 | public UByte getUserAccessLevel() { 82 | return userAccessLevel; 83 | } 84 | 85 | public Double getMinimumSamplingInterval() { 86 | return minimumSamplingInterval; 87 | } 88 | 89 | public boolean isHistorizing() { 90 | return historizing; 91 | } 92 | 93 | @Override 94 | public String toString() { 95 | return "VariableNodeAttributes{" + 96 | "value=" + value + 97 | ", dataType=" + dataType + 98 | ", valueRank=" + valueRank + 99 | ", arrayDimensions=" + Arrays.toString(arrayDimensions) + 100 | ", accessLevel=" + accessLevel + 101 | ", userAccessLevel=" + userAccessLevel + 102 | ", minimumSamplingInterval=" + minimumSamplingInterval + 103 | ", historizing=" + historizing + 104 | "} " + super.toString(); 105 | } 106 | 107 | public static VariableNodeAttributes fromGenerated( 108 | UAVariable gNode, 109 | Marshaller marshaller, 110 | Map aliasMap, 111 | Map rawXmlValues 112 | ) { 113 | 114 | NodeId nodeId = NodeId.parse(gNode.getNodeId()); 115 | QualifiedName browseName = QualifiedName.parse(gNode.getBrowseName()); 116 | 117 | LocalizedText displayName = gNode.getDisplayName().stream() 118 | .findFirst() 119 | .map(gLocalizedText -> LocalizedText.english(gLocalizedText.getValue())) 120 | .orElse(LocalizedText.english(browseName.getName())); 121 | 122 | LocalizedText description = gNode.getDescription().stream() 123 | .findFirst() 124 | .map(gLocalizedText -> LocalizedText.english(gLocalizedText.getValue())) 125 | .orElse(LocalizedText.NULL_VALUE); 126 | 127 | UInteger writeMask = uint(gNode.getWriteMask()); 128 | UInteger userWriteMask = uint(gNode.getUserWriteMask()); 129 | 130 | DataValue value = value(gNode.getValue(), marshaller, nodeId, rawXmlValues); 131 | NodeId dataType = AttributeUtil.parseDataType(gNode.getDataType(), aliasMap); 132 | int valueRank = gNode.getValueRank(); 133 | UInteger[] arrayDimensions = AttributeUtil.parseArrayDimensions(gNode.getArrayDimensions()); 134 | UByte accessLevel = ubyte(gNode.getAccessLevel()); 135 | UByte userAccessLevel = ubyte(gNode.getUserAccessLevel()); 136 | Double minimumSamplingInterval = gNode.getMinimumSamplingInterval(); 137 | boolean historizing = gNode.isHistorizing(); 138 | 139 | return new VariableNodeAttributes( 140 | nodeId, 141 | browseName, 142 | displayName, 143 | description, 144 | writeMask, 145 | userWriteMask, 146 | value, 147 | dataType, 148 | valueRank, 149 | arrayDimensions, 150 | accessLevel, 151 | userAccessLevel, 152 | minimumSamplingInterval, 153 | historizing 154 | ); 155 | } 156 | 157 | private static DataValue value( 158 | UAVariable.Value gValue, 159 | Marshaller marshaller, 160 | NodeId nodeId, 161 | Map rawXmlValues 162 | ) { 163 | 164 | if (gValue == null || gValue.getAny() == null) { 165 | return new DataValue(Variant.NULL_VALUE); 166 | } 167 | 168 | return AttributeUtil.parseValue(gValue.getAny(), marshaller, nodeId, rawXmlValues); 169 | } 170 | 171 | } 172 | -------------------------------------------------------------------------------- /src/main/java/com/digitalpetri/opcua/nodeset/attributes/VariableTypeNodeAttributes.java: -------------------------------------------------------------------------------- 1 | package com.digitalpetri.opcua.nodeset.attributes; 2 | 3 | import java.util.Arrays; 4 | import java.util.Map; 5 | import java.util.Optional; 6 | import javax.xml.bind.Marshaller; 7 | 8 | import com.digitalpetri.opcua.nodeset.util.AttributeUtil; 9 | import org.eclipse.milo.opcua.stack.core.types.builtin.DataValue; 10 | import org.eclipse.milo.opcua.stack.core.types.builtin.LocalizedText; 11 | import org.eclipse.milo.opcua.stack.core.types.builtin.NodeId; 12 | import org.eclipse.milo.opcua.stack.core.types.builtin.QualifiedName; 13 | import org.eclipse.milo.opcua.stack.core.types.builtin.Variant; 14 | import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.UInteger; 15 | import org.eclipse.milo.opcua.stack.core.types.enumerated.NodeClass; 16 | import org.opcfoundation.ua.generated.UAVariableType; 17 | 18 | import static org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.Unsigned.uint; 19 | 20 | public class VariableTypeNodeAttributes extends NodeAttributes { 21 | 22 | private final DataValue value; 23 | private final NodeId dataType; 24 | private final int valueRank; 25 | private final UInteger[] arrayDimensions; 26 | private final boolean isAbstract; 27 | 28 | public VariableTypeNodeAttributes( 29 | NodeId nodeId, 30 | QualifiedName browseName, 31 | LocalizedText displayName, 32 | LocalizedText description, 33 | UInteger writeMask, 34 | UInteger userWriteMask, 35 | DataValue value, 36 | NodeId dataType, 37 | int valueRank, 38 | UInteger[] arrayDimensions, 39 | boolean isAbstract 40 | ) { 41 | 42 | super(nodeId, NodeClass.VariableType, browseName, displayName, description, writeMask, userWriteMask); 43 | 44 | this.value = value; 45 | this.dataType = dataType; 46 | this.valueRank = valueRank; 47 | this.arrayDimensions = arrayDimensions; 48 | this.isAbstract = isAbstract; 49 | } 50 | 51 | public DataValue getValue() { 52 | return value; 53 | } 54 | 55 | public NodeId getDataType() { 56 | return dataType; 57 | } 58 | 59 | public int getValueRank() { 60 | return valueRank; 61 | } 62 | 63 | public UInteger[] getArrayDimensions() { 64 | return arrayDimensions; 65 | } 66 | 67 | public boolean isAbstract() { 68 | return isAbstract; 69 | } 70 | 71 | @Override 72 | public String toString() { 73 | return "VariableTypeNodeAttributes{" + 74 | "value=" + value + 75 | ", dataType=" + dataType + 76 | ", valueRank=" + valueRank + 77 | ", arrayDimensions=" + Arrays.toString(arrayDimensions) + 78 | ", isAbstract=" + isAbstract + 79 | "} " + super.toString(); 80 | } 81 | 82 | public static VariableTypeNodeAttributes fromGenerated( 83 | UAVariableType gNode, 84 | Marshaller marshaller, 85 | Map aliasMap, Map rawXmlValues) { 86 | 87 | NodeId nodeId = NodeId.parse(gNode.getNodeId()); 88 | QualifiedName browseName = QualifiedName.parse(gNode.getBrowseName()); 89 | 90 | LocalizedText displayName = gNode.getDisplayName().stream() 91 | .findFirst() 92 | .map(gLocalizedText -> LocalizedText.english(gLocalizedText.getValue())) 93 | .orElse(LocalizedText.english(browseName.getName())); 94 | 95 | LocalizedText description = gNode.getDescription().stream() 96 | .findFirst() 97 | .map(gLocalizedText -> LocalizedText.english(gLocalizedText.getValue())) 98 | .orElse(LocalizedText.NULL_VALUE); 99 | 100 | UInteger writeMask = uint(gNode.getWriteMask()); 101 | UInteger userWriteMask = uint(gNode.getUserWriteMask()); 102 | 103 | DataValue value = value(gNode.getValue(), marshaller, nodeId, rawXmlValues) 104 | .orElse(new DataValue(Variant.NULL_VALUE)); 105 | NodeId dataType = AttributeUtil.parseDataType(gNode.getDataType(), aliasMap); 106 | int valueRank = gNode.getValueRank(); 107 | UInteger[] arrayDimensions = AttributeUtil.parseArrayDimensions(gNode.getArrayDimensions()); 108 | boolean isAbstract = gNode.isIsAbstract(); 109 | 110 | return new VariableTypeNodeAttributes( 111 | nodeId, 112 | browseName, 113 | displayName, 114 | description, 115 | writeMask, 116 | userWriteMask, 117 | value, 118 | dataType, 119 | valueRank, 120 | arrayDimensions, 121 | isAbstract 122 | ); 123 | } 124 | 125 | private static Optional value( 126 | UAVariableType.Value gValue, 127 | Marshaller marshaller, 128 | NodeId nodeId, 129 | Map rawXmlValues 130 | ) { 131 | 132 | if (gValue == null) return Optional.empty(); 133 | 134 | return Optional.of(AttributeUtil.parseValue(gValue.getAny(), marshaller, nodeId, rawXmlValues)); 135 | } 136 | 137 | } 138 | -------------------------------------------------------------------------------- /src/main/java/com/digitalpetri/opcua/nodeset/attributes/ViewNodeAttributes.java: -------------------------------------------------------------------------------- 1 | package com.digitalpetri.opcua.nodeset.attributes; 2 | 3 | import org.eclipse.milo.opcua.stack.core.types.builtin.LocalizedText; 4 | import org.eclipse.milo.opcua.stack.core.types.builtin.NodeId; 5 | import org.eclipse.milo.opcua.stack.core.types.builtin.QualifiedName; 6 | import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.UByte; 7 | import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.UInteger; 8 | import org.eclipse.milo.opcua.stack.core.types.enumerated.NodeClass; 9 | import org.opcfoundation.ua.generated.UAView; 10 | 11 | import static org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.Unsigned.ubyte; 12 | import static org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.Unsigned.uint; 13 | 14 | public class ViewNodeAttributes extends NodeAttributes { 15 | 16 | private final boolean containsNoLoops; 17 | private final UByte eventNotifier; 18 | 19 | public ViewNodeAttributes( 20 | NodeId nodeId, 21 | QualifiedName browseName, 22 | LocalizedText displayName, 23 | LocalizedText description, 24 | UInteger writeMask, 25 | UInteger userWriteMask, 26 | boolean containsNoLoops, 27 | UByte eventNotifier 28 | ) { 29 | 30 | super(nodeId, NodeClass.View, browseName, displayName, description, writeMask, userWriteMask); 31 | 32 | this.containsNoLoops = containsNoLoops; 33 | this.eventNotifier = eventNotifier; 34 | } 35 | 36 | public boolean isContainsNoLoops() { 37 | return containsNoLoops; 38 | } 39 | 40 | public UByte getEventNotifier() { 41 | return eventNotifier; 42 | } 43 | 44 | @Override 45 | public String toString() { 46 | return "ViewNodeAttributes{" + 47 | "containsNoLoops=" + containsNoLoops + 48 | ", eventNotifier=" + eventNotifier + 49 | "} " + super.toString(); 50 | } 51 | 52 | public static ViewNodeAttributes fromGenerated(UAView gNode) { 53 | NodeId nodeId = NodeId.parse(gNode.getNodeId()); 54 | QualifiedName browseName = QualifiedName.parse(gNode.getBrowseName()); 55 | 56 | LocalizedText displayName = gNode.getDisplayName().stream() 57 | .findFirst() 58 | .map(gLocalizedText -> LocalizedText.english(gLocalizedText.getValue())) 59 | .orElse(LocalizedText.english(browseName.getName())); 60 | 61 | LocalizedText description = gNode.getDescription().stream() 62 | .findFirst() 63 | .map(gLocalizedText -> LocalizedText.english(gLocalizedText.getValue())) 64 | .orElse(LocalizedText.NULL_VALUE); 65 | 66 | UInteger writeMask = uint(gNode.getWriteMask()); 67 | UInteger userWriteMask = uint(gNode.getUserWriteMask()); 68 | 69 | boolean containsNoLoops = gNode.isContainsNoLoops(); 70 | UByte eventNotifier = ubyte(gNode.getEventNotifier()); 71 | 72 | return new ViewNodeAttributes( 73 | nodeId, 74 | browseName, 75 | displayName, 76 | description, 77 | writeMask, 78 | userWriteMask, 79 | containsNoLoops, 80 | eventNotifier 81 | ); 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /src/main/java/com/digitalpetri/opcua/nodeset/util/AttributeUtil.java: -------------------------------------------------------------------------------- 1 | package com.digitalpetri.opcua.nodeset.util; 2 | 3 | import java.io.StringReader; 4 | import java.io.StringWriter; 5 | import java.util.Arrays; 6 | import java.util.List; 7 | import java.util.Map; 8 | import java.util.Optional; 9 | import javax.xml.bind.JAXBElement; 10 | import javax.xml.bind.JAXBException; 11 | import javax.xml.bind.Marshaller; 12 | import javax.xml.transform.Transformer; 13 | import javax.xml.transform.TransformerException; 14 | import javax.xml.transform.TransformerFactory; 15 | import javax.xml.transform.dom.DOMSource; 16 | import javax.xml.transform.stream.StreamResult; 17 | 18 | import org.eclipse.milo.opcua.stack.core.Identifiers; 19 | import org.eclipse.milo.opcua.stack.core.NamespaceTable; 20 | import org.eclipse.milo.opcua.stack.core.serialization.EncodingLimits; 21 | import org.eclipse.milo.opcua.stack.core.serialization.OpcUaXmlStreamDecoder; 22 | import org.eclipse.milo.opcua.stack.core.serialization.SerializationContext; 23 | import org.eclipse.milo.opcua.stack.core.types.DataTypeManager; 24 | import org.eclipse.milo.opcua.stack.core.types.OpcUaDataTypeManager; 25 | import org.eclipse.milo.opcua.stack.core.types.builtin.DataValue; 26 | import org.eclipse.milo.opcua.stack.core.types.builtin.NodeId; 27 | import org.eclipse.milo.opcua.stack.core.types.builtin.Variant; 28 | import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.UInteger; 29 | import org.opcfoundation.ua.generated.Reference; 30 | import org.slf4j.Logger; 31 | import org.slf4j.LoggerFactory; 32 | import org.w3c.dom.Node; 33 | 34 | import static org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.Unsigned.uint; 35 | 36 | public class AttributeUtil { 37 | 38 | private static final Logger LOGGER = LoggerFactory.getLogger(AttributeUtil.class); 39 | 40 | private static final SerializationContext SERIALIZATION_CONTEXT = new SerializationContext() { 41 | 42 | private final NamespaceTable namespaceTable = new NamespaceTable(); 43 | 44 | @Override 45 | public EncodingLimits getEncodingLimits() { 46 | return EncodingLimits.DEFAULT; 47 | } 48 | 49 | @Override 50 | public NamespaceTable getNamespaceTable() { 51 | return namespaceTable; 52 | } 53 | 54 | @Override 55 | public DataTypeManager getDataTypeManager() { 56 | return OpcUaDataTypeManager.getInstance(); 57 | } 58 | }; 59 | 60 | public static NodeId parseDataType(String dataType, Map aliases) { 61 | return tryParseNodeId(dataType, aliases); 62 | } 63 | 64 | public static NodeId parseReferenceTypeId(Reference gReference, Map aliases) { 65 | String referenceType = gReference.getReferenceType(); 66 | 67 | return tryParseNodeId(referenceType, aliases); 68 | } 69 | 70 | public static NodeId tryParseNodeId(String id, Map aliases) { 71 | return NodeId.parseSafe(id).orElseGet(() -> { 72 | if (aliases.containsKey(id)) { 73 | return aliases.get(id); 74 | } else { 75 | // Ok, last effort... 76 | Optional nodeId = Arrays.stream(Identifiers.class.getFields()) 77 | .filter(field -> field.getName().equals(id)) 78 | .findFirst() 79 | .map(field -> { 80 | try { 81 | return (NodeId) field.get(null); 82 | } catch (Throwable ex) { 83 | throw new RuntimeException("Couldn't get NodeId field: " + id, ex); 84 | } 85 | }); 86 | 87 | return nodeId.orElseThrow(RuntimeException::new); 88 | } 89 | }); 90 | } 91 | 92 | public static DataValue parseValue( 93 | Object value, 94 | Marshaller marshaller, 95 | NodeId nodeId, 96 | Map rawXmlValues 97 | ) { 98 | 99 | StringWriter sw = new StringWriter(); 100 | 101 | if (value instanceof JAXBElement) { 102 | JAXBElement jaxbElement = (JAXBElement) value; 103 | 104 | try { 105 | marshaller.marshal(jaxbElement, sw); 106 | } catch (JAXBException e) { 107 | LOGGER.warn("unable to marshal JAXB element: " + jaxbElement, e); 108 | return new DataValue(Variant.NULL_VALUE); 109 | } 110 | } else if (value instanceof Node) { 111 | Node node = (Node) value; 112 | 113 | try { 114 | Transformer transformer = TransformerFactory.newInstance().newTransformer(); 115 | transformer.setOutputProperty("omit-xml-declaration", "yes"); 116 | transformer.transform(new DOMSource(node), new StreamResult(sw)); 117 | } catch (TransformerException e) { 118 | LOGGER.warn("unable to transform dom node: " + node, e); 119 | return new DataValue(Variant.NULL_VALUE); 120 | } 121 | } 122 | 123 | String xmlString = sw.toString(); 124 | try { 125 | OpcUaXmlStreamDecoder xmlReader = new OpcUaXmlStreamDecoder(SERIALIZATION_CONTEXT); 126 | xmlReader.setInput(new StringReader(xmlString)); 127 | 128 | Object valueObject = xmlReader.readVariantValue(); 129 | 130 | rawXmlValues.put(nodeId, xmlString); 131 | 132 | return new DataValue(new Variant(valueObject)); 133 | } catch (Throwable t) { 134 | LOGGER.warn("unable to parse Value: " + xmlString, t); 135 | return new DataValue(Variant.NULL_VALUE); 136 | } 137 | } 138 | 139 | public static UInteger[] parseArrayDimensions(List list) { 140 | if (list.isEmpty()) { 141 | return new UInteger[0]; 142 | } else { 143 | String[] ss = list.get(0).split(","); 144 | UInteger[] dimensions = new UInteger[ss.length]; 145 | 146 | for (int i = 0; i < ss.length; i++) { 147 | dimensions[i] = uint(Integer.parseInt(ss[i])); 148 | } 149 | 150 | return dimensions; 151 | } 152 | } 153 | 154 | public static class ParsedDataValue { 155 | final String rawXml; 156 | final DataValue value; 157 | 158 | public ParsedDataValue(String rawXml, DataValue value) { 159 | this.rawXml = rawXml; 160 | this.value = value; 161 | } 162 | } 163 | 164 | } 165 | -------------------------------------------------------------------------------- /src/main/resources/UANodeSet.xsd: -------------------------------------------------------------------------------- 1 | 2 | 30 | 31 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | -------------------------------------------------------------------------------- /src/test/java/com/digitalpetri/opcua/nodeset/UaNodeSetTest.java: -------------------------------------------------------------------------------- 1 | package com.digitalpetri.opcua.nodeset; 2 | 3 | import java.io.InputStream; 4 | import javax.xml.bind.JAXBException; 5 | 6 | import org.junit.Test; 7 | 8 | public class UaNodeSetTest { 9 | 10 | @Test 11 | public void testParseNodeSet2() throws Exception { 12 | parse("Opc.Ua.NodeSet2.xml"); 13 | } 14 | 15 | @Test 16 | public void testParseNodeSet2_Part3() throws Exception { 17 | parse("Opc.Ua.NodeSet2.Part3.xml"); 18 | } 19 | 20 | @Test 21 | public void testParseNodeSet2_Part4() throws Exception { 22 | parse("Opc.Ua.NodeSet2.Part4.xml"); 23 | } 24 | 25 | @Test 26 | public void testParseNodeSet2_Part5() throws Exception { 27 | parse("Opc.Ua.NodeSet2.Part5.xml"); 28 | } 29 | 30 | @Test 31 | public void testParseNodeSet2_Part9() throws Exception { 32 | parse("Opc.Ua.NodeSet2.Part9.xml"); 33 | } 34 | 35 | @Test 36 | public void testParseNodeSet2_Part10() throws Exception { 37 | parse("Opc.Ua.NodeSet2.Part10.xml"); 38 | } 39 | 40 | @Test 41 | public void testParseNodeSet2_Part11() throws Exception { 42 | parse("Opc.Ua.NodeSet2.Part11.xml"); 43 | } 44 | 45 | @Test 46 | public void testParseNodeSet2_Part13() throws Exception { 47 | parse("Opc.Ua.NodeSet2.Part13.xml"); 48 | } 49 | 50 | @Test 51 | public void testParseAdiNodeSet() throws Exception { 52 | parse("adi/Opc.Ua.Adi.NodeSet2.xml"); 53 | } 54 | 55 | @Test 56 | public void testParseAMLBaseTypes() throws Exception { 57 | parse("aml/Opc.Ua.AMLBaseTypes.NodeSet2.xml"); 58 | } 59 | 60 | @Test 61 | public void testParseAMLLibraries() throws Exception { 62 | parse("aml/Opc.Ua.AMLLibraries.NodeSet2.xml"); 63 | } 64 | 65 | @Test 66 | public void testParseAutoIdNodeSet() throws Exception { 67 | parse("autoid/Opc.Ua.AutoID.NodeSet2.xml"); 68 | } 69 | 70 | @Test 71 | public void testParseDiNodeSet() throws Exception { 72 | parse("di/Opc.Ua.Di.NodeSet2.xml"); 73 | } 74 | 75 | @Test 76 | public void testParseMdisNodeSet() throws Exception { 77 | parse("mdis/OPC.MDIS.NodeSet2.xml"); 78 | } 79 | 80 | @Test 81 | public void testParsePlcNodeSet() throws Exception { 82 | parse("plc/Opc.Ua.Plc.NodeSet2.xml"); 83 | } 84 | 85 | @Test 86 | public void testParseSchemaCheckNodeSet() throws Exception { 87 | parse("schema/Opc.Ua.NodeSet.Schema.Check.xml"); 88 | } 89 | 90 | private void parse(String nodeSetFilename) throws JAXBException { 91 | InputStream nodeSetXml = getClass().getClassLoader().getResourceAsStream(nodeSetFilename); 92 | 93 | UaNodeSet nodeSet = UaNodeSet.parse(nodeSetXml); 94 | 95 | System.out.println("Parsed " + nodeSetFilename + " and generated " + nodeSet.getNodes().size() + " nodes."); 96 | } 97 | 98 | } 99 | -------------------------------------------------------------------------------- /src/test/resources/Opc.Ua.NodeSet2.Part13.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | i=1 5 | i=2 6 | i=3 7 | i=4 8 | i=5 9 | i=6 10 | i=7 11 | i=8 12 | i=9 13 | i=10 14 | i=11 15 | i=13 16 | i=12 17 | i=15 18 | i=14 19 | i=16 20 | i=17 21 | i=18 22 | i=20 23 | i=21 24 | i=19 25 | i=22 26 | i=26 27 | i=27 28 | i=28 29 | i=47 30 | i=46 31 | i=35 32 | i=36 33 | i=48 34 | i=45 35 | i=40 36 | i=37 37 | i=38 38 | i=39 39 | 40 | 41 | AggregateConfigurationType 42 | 43 | i=11188 44 | i=11189 45 | i=11190 46 | i=11191 47 | i=58 48 | 49 | 50 | 51 | TreatUncertainAsBad 52 | 53 | i=68 54 | i=78 55 | i=11187 56 | 57 | 58 | 59 | PercentDataBad 60 | 61 | i=68 62 | i=78 63 | i=11187 64 | 65 | 66 | 67 | PercentDataGood 68 | 69 | i=68 70 | i=78 71 | i=11187 72 | 73 | 74 | 75 | UseSlopedExtrapolation 76 | 77 | i=68 78 | i=78 79 | i=11187 80 | 81 | 82 | 83 | Interpolative 84 | At the beginning of each interval, retrieve the calculated value from the data points on either side of the requested timestamp. 85 | 86 | i=2340 87 | 88 | 89 | 90 | Average 91 | Retrieve the average value of the data over the interval. 92 | 93 | i=2340 94 | 95 | 96 | 97 | TimeAverage 98 | Retrieve the time weighted average data over the interval using Interpolated Bounding Values. 99 | 100 | i=2340 101 | 102 | 103 | 104 | TimeAverage2 105 | Retrieve the time weighted average data over the interval using Simple Bounding Values. 106 | 107 | i=2340 108 | 109 | 110 | 111 | Total 112 | Retrieve the total (time integral) of the data over the interval using Interpolated Bounding Values. 113 | 114 | i=2340 115 | 116 | 117 | 118 | Total2 119 | Retrieve the total (time integral) of the data over the interval using Simple Bounding Values. 120 | 121 | i=2340 122 | 123 | 124 | 125 | Minimum 126 | Retrieve the minimum raw value in the interval with the timestamp of the start of the interval. 127 | 128 | i=2340 129 | 130 | 131 | 132 | Maximum 133 | Retrieve the maximum raw value in the interval with the timestamp of the start of the interval. 134 | 135 | i=2340 136 | 137 | 138 | 139 | MinimumActualTime 140 | Retrieve the minimum value in the interval and the Timestamp of the minimum value. 141 | 142 | i=2340 143 | 144 | 145 | 146 | MaximumActualTime 147 | Retrieve the maximum value in the interval and the Timestamp of the maximum value. 148 | 149 | i=2340 150 | 151 | 152 | 153 | Range 154 | Retrieve the difference between the minimum and maximum Value over the interval. 155 | 156 | i=2340 157 | 158 | 159 | 160 | Minimum2 161 | Retrieve the minimum value in the interval including the Simple Bounding Values. 162 | 163 | i=2340 164 | 165 | 166 | 167 | Maximum2 168 | Retrieve the maximum value in the interval including the Simple Bounding Values. 169 | 170 | i=2340 171 | 172 | 173 | 174 | MinimumActualTime2 175 | Retrieve the minimum value with the actual timestamp including the Simple Bounding Values. 176 | 177 | i=2340 178 | 179 | 180 | 181 | MaximumActualTime2 182 | Retrieve the maximum value with the actual timestamp including the Simple Bounding Values. 183 | 184 | i=2340 185 | 186 | 187 | 188 | Range2 189 | Retrieve the difference between the Minimum2 and Maximum2 value over the interval. 190 | 191 | i=2340 192 | 193 | 194 | 195 | AnnotationCount 196 | Retrieve the number of Annotations in the interval. 197 | 198 | i=2340 199 | 200 | 201 | 202 | Count 203 | Retrieve the number of raw values over the interval. 204 | 205 | i=2340 206 | 207 | 208 | 209 | DurationInStateZero 210 | Retrieve the time a Boolean or numeric was in a zero state using Simple Bounding Values. 211 | 212 | i=2340 213 | 214 | 215 | 216 | DurationInStateNonZero 217 | Retrieve the time a Boolean or numeric was in a non-zero state using Simple Bounding Values. 218 | 219 | i=2340 220 | 221 | 222 | 223 | NumberOfTransitions 224 | Retrieve the number of changes between zero and non-zero that a Boolean or Numeric value experienced in the interval. 225 | 226 | i=2340 227 | 228 | 229 | 230 | Start 231 | Retrieve the value at the beginning of the interval using Interpolated Bounding Values. 232 | 233 | i=2340 234 | 235 | 236 | 237 | End 238 | Retrieve the value at the end of the interval using Interpolated Bounding Values. 239 | 240 | i=2340 241 | 242 | 243 | 244 | Delta 245 | Retrieve the difference between the Start and End value in the interval. 246 | 247 | i=2340 248 | 249 | 250 | 251 | StartBound 252 | Retrieve the value at the beginning of the interval using Simple Bounding Values. 253 | 254 | i=2340 255 | 256 | 257 | 258 | EndBound 259 | Retrieve the value at the end of the interval using Simple Bounding Values. 260 | 261 | i=2340 262 | 263 | 264 | 265 | DeltaBounds 266 | Retrieve the difference between the StartBound and EndBound value in the interval. 267 | 268 | i=2340 269 | 270 | 271 | 272 | DurationGood 273 | Retrieve the total duration of time in the interval during which the data is good. 274 | 275 | i=2340 276 | 277 | 278 | 279 | DurationBad 280 | Retrieve the total duration of time in the interval during which the data is bad. 281 | 282 | i=2340 283 | 284 | 285 | 286 | PercentGood 287 | Retrieve the percent of data (0 to 100) in the interval which has a good StatusCode. 288 | 289 | i=2340 290 | 291 | 292 | 293 | PercentBad 294 | Retrieve the percent of data (0 to 100) in the interval which has a bad StatusCode. 295 | 296 | i=2340 297 | 298 | 299 | 300 | WorstQuality 301 | Retrieve the worst StatusCode of data in the interval. 302 | 303 | i=2340 304 | 305 | 306 | 307 | WorstQuality2 308 | Retrieve the worst StatusCode of data in the interval including the Simple Bounding Values. 309 | 310 | i=2340 311 | 312 | 313 | 314 | StandardDeviationSample 315 | Retrieve the standard deviation for the interval for a sample of the population (n-1). 316 | 317 | i=2340 318 | 319 | 320 | 321 | StandardDeviationPopulation 322 | Retrieve the standard deviation for the interval for a complete population (n) which includes Simple Bounding Values. 323 | 324 | i=2340 325 | 326 | 327 | 328 | VarianceSample 329 | Retrieve the variance for the interval as calculated by the StandardDeviationSample. 330 | 331 | i=2340 332 | 333 | 334 | 335 | VariancePopulation 336 | Retrieve the variance for the interval as calculated by the StandardDeviationPopulation which includes Simple Bounding Values. 337 | 338 | i=2340 339 | 340 | 341 | -------------------------------------------------------------------------------- /src/test/resources/Opc.Ua.NodeSet2.Part8.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | i=1 5 | i=2 6 | i=3 7 | i=4 8 | i=5 9 | i=6 10 | i=7 11 | i=8 12 | i=9 13 | i=10 14 | i=11 15 | i=13 16 | i=12 17 | i=15 18 | i=14 19 | i=16 20 | i=17 21 | i=18 22 | i=20 23 | i=21 24 | i=19 25 | i=22 26 | i=26 27 | i=27 28 | i=28 29 | i=47 30 | i=46 31 | i=35 32 | i=36 33 | i=48 34 | i=45 35 | i=40 36 | i=37 37 | i=38 38 | i=39 39 | 40 | 41 | DataItemType 42 | A variable that contains live automation data. 43 | 44 | i=2366 45 | i=2367 46 | i=63 47 | 48 | 49 | 50 | Definition 51 | A vendor-specific, human readable string that specifies how the value of this DataItem is calculated. 52 | 53 | i=68 54 | i=80 55 | i=2365 56 | 57 | 58 | 59 | ValuePrecision 60 | The maximum precision that the server can maintain for the item based on restrictions in the target environment. 61 | 62 | i=68 63 | i=80 64 | i=2365 65 | 66 | 67 | 68 | AnalogItemType 69 | 70 | i=2370 71 | i=2369 72 | i=2371 73 | i=2365 74 | 75 | 76 | 77 | InstrumentRange 78 | 79 | i=68 80 | i=80 81 | i=2368 82 | 83 | 84 | 85 | EURange 86 | 87 | i=68 88 | i=78 89 | i=2368 90 | 91 | 92 | 93 | EngineeringUnits 94 | 95 | i=68 96 | i=80 97 | i=2368 98 | 99 | 100 | 101 | DiscreteItemType 102 | 103 | i=2365 104 | 105 | 106 | 107 | TwoStateDiscreteType 108 | 109 | i=2374 110 | i=2375 111 | i=2372 112 | 113 | 114 | 115 | FalseState 116 | 117 | i=68 118 | i=78 119 | i=2373 120 | 121 | 122 | 123 | TrueState 124 | 125 | i=68 126 | i=78 127 | i=2373 128 | 129 | 130 | 131 | MultiStateDiscreteType 132 | 133 | i=2377 134 | i=2372 135 | 136 | 137 | 138 | EnumStrings 139 | 140 | i=68 141 | i=78 142 | i=2376 143 | 144 | 145 | 146 | MultiStateValueDiscreteType 147 | 148 | i=11241 149 | i=11461 150 | i=2372 151 | 152 | 153 | 154 | EnumValues 155 | 156 | i=68 157 | i=78 158 | i=11238 159 | 160 | 161 | 162 | ValueAsText 163 | 164 | i=68 165 | i=78 166 | i=11238 167 | 168 | 169 | 170 | ArrayItemType 171 | 172 | i=12024 173 | i=12025 174 | i=12026 175 | i=12027 176 | i=12028 177 | i=2365 178 | 179 | 180 | 181 | InstrumentRange 182 | 183 | i=68 184 | i=80 185 | i=12021 186 | 187 | 188 | 189 | EURange 190 | 191 | i=68 192 | i=78 193 | i=12021 194 | 195 | 196 | 197 | EngineeringUnits 198 | 199 | i=68 200 | i=78 201 | i=12021 202 | 203 | 204 | 205 | Title 206 | 207 | i=68 208 | i=78 209 | i=12021 210 | 211 | 212 | 213 | AxisScaleType 214 | 215 | i=68 216 | i=78 217 | i=12021 218 | 219 | 220 | 221 | YArrayItemType 222 | 223 | i=12037 224 | i=12021 225 | 226 | 227 | 228 | XAxisDefinition 229 | 230 | i=68 231 | i=78 232 | i=12029 233 | 234 | 235 | 236 | XYArrayItemType 237 | 238 | i=12046 239 | i=12021 240 | 241 | 242 | 243 | XAxisDefinition 244 | 245 | i=68 246 | i=78 247 | i=12038 248 | 249 | 250 | 251 | ImageItemType 252 | 253 | i=12055 254 | i=12056 255 | i=12021 256 | 257 | 258 | 259 | XAxisDefinition 260 | 261 | i=68 262 | i=78 263 | i=12047 264 | 265 | 266 | 267 | YAxisDefinition 268 | 269 | i=68 270 | i=78 271 | i=12047 272 | 273 | 274 | 275 | CubeItemType 276 | 277 | i=12065 278 | i=12066 279 | i=12067 280 | i=12021 281 | 282 | 283 | 284 | XAxisDefinition 285 | 286 | i=68 287 | i=78 288 | i=12057 289 | 290 | 291 | 292 | YAxisDefinition 293 | 294 | i=68 295 | i=78 296 | i=12057 297 | 298 | 299 | 300 | ZAxisDefinition 301 | 302 | i=68 303 | i=78 304 | i=12057 305 | 306 | 307 | 308 | NDimensionArrayItemType 309 | 310 | i=12076 311 | i=12021 312 | 313 | 314 | 315 | AxisDefinition 316 | 317 | i=68 318 | i=78 319 | i=12068 320 | 321 | 322 | 323 | Range 324 | 325 | i=22 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | EUInformation 334 | 335 | i=22 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | AxisScaleEnumeration 346 | 347 | i=12078 348 | i=29 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | EnumStrings 358 | 359 | i=68 360 | i=78 361 | i=12077 362 | 363 | 364 | 365 | 366 | 367 | 368 | Linear 369 | 370 | 371 | 372 | 373 | Log 374 | 375 | 376 | 377 | 378 | Ln 379 | 380 | 381 | 382 | 383 | 384 | ComplexNumberType 385 | 386 | i=22 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | DoubleComplexNumberType 395 | 396 | i=22 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | AxisInformation 405 | 406 | i=22 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | XVType 418 | 419 | i=22 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | Default XML 428 | 429 | i=884 430 | i=8873 431 | i=76 432 | 433 | 434 | 435 | Default XML 436 | 437 | i=887 438 | i=8876 439 | i=76 440 | 441 | 442 | 443 | Default XML 444 | 445 | i=12171 446 | i=12175 447 | i=76 448 | 449 | 450 | 451 | Default XML 452 | 453 | i=12172 454 | i=12178 455 | i=76 456 | 457 | 458 | 459 | Default XML 460 | 461 | i=12079 462 | i=12083 463 | i=76 464 | 465 | 466 | 467 | Default XML 468 | 469 | i=12080 470 | i=12086 471 | i=76 472 | 473 | 474 | 475 | Default Binary 476 | 477 | i=884 478 | i=8238 479 | i=76 480 | 481 | 482 | 483 | Default Binary 484 | 485 | i=887 486 | i=8241 487 | i=76 488 | 489 | 490 | 491 | Default Binary 492 | 493 | i=12171 494 | i=12183 495 | i=76 496 | 497 | 498 | 499 | Default Binary 500 | 501 | i=12172 502 | i=12186 503 | i=76 504 | 505 | 506 | 507 | Default Binary 508 | 509 | i=12079 510 | i=12091 511 | i=76 512 | 513 | 514 | 515 | Default Binary 516 | 517 | i=12080 518 | i=12094 519 | i=76 520 | 521 | 522 | -------------------------------------------------------------------------------- /src/test/resources/aml/Opc.Ua.AMLBaseTypes.NodeSet2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | http://opcfoundation.org/UA/AML/ 5 | 6 | 7 | i=1 8 | i=2 9 | i=3 10 | i=4 11 | i=5 12 | i=6 13 | i=7 14 | i=8 15 | i=9 16 | i=10 17 | i=11 18 | i=13 19 | i=12 20 | i=15 21 | i=14 22 | i=16 23 | i=17 24 | i=18 25 | i=20 26 | i=21 27 | i=19 28 | i=22 29 | i=26 30 | i=27 31 | i=28 32 | i=47 33 | i=46 34 | i=35 35 | i=36 36 | i=48 37 | i=45 38 | i=40 39 | i=37 40 | i=38 41 | i=39 42 | i=290 43 | 44 | 45 | InstanceHierarchies 46 | 47 | i=61 48 | ns=1;i=1005 49 | i=78 50 | 51 | 52 | 53 | InterfaceClassLibs 54 | 55 | i=61 56 | ns=1;i=1005 57 | i=78 58 | 59 | 60 | 61 | RoleClassLibs 62 | 63 | i=61 64 | i=78 65 | ns=1;i=1005 66 | 67 | 68 | 69 | SystemUnitClassLibs 70 | 71 | i=61 72 | i=78 73 | ns=1;i=1005 74 | 75 | 76 | 77 | AutomationMLFiles 78 | 79 | i=61 80 | i=85 81 | 82 | 83 | 84 | AutomationMLInstanceHierarchies 85 | 86 | i=61 87 | i=85 88 | 89 | 90 | 91 | AutomationMLLibraries 92 | The browse entry point when looking for AutomationML libraries in the server address space. 93 | 94 | i=88 95 | i=61 96 | i=85 97 | ns=1;i=5008 98 | ns=1;i=5009 99 | ns=1;i=5010 100 | 101 | 102 | 103 | InterfaceClassLibs 104 | 105 | i=61 106 | ns=1;i=5007 107 | 108 | 109 | 110 | RoleClassLibs 111 | 112 | i=61 113 | ns=1;i=5007 114 | 115 | 116 | 117 | SystemUnitClassLibs 118 | 119 | i=61 120 | ns=1;i=5007 121 | 122 | 123 | 124 | Version 125 | 126 | i=78 127 | i=68 128 | ns=1;i=1006 129 | i=80 130 | 131 | 132 | 133 | ID 134 | 135 | ns=1;i=1001 136 | i=78 137 | i=68 138 | i=80 139 | 140 | 141 | 142 | ID 143 | 144 | i=68 145 | i=80 146 | 147 | 148 | 149 | Version 150 | 151 | i=68 152 | i=80 153 | 154 | 155 | 156 | VariableNodeId 157 | 158 | 159 | i=63 160 | ns=1;i=3002 161 | i=78 162 | 163 | 164 | 165 | VariableName 166 | 167 | 168 | i=63 169 | ns=1;i=3002 170 | i=80 171 | 172 | 173 | 174 | ServerAddress 175 | 176 | 177 | i=63 178 | ns=1;i=3002 179 | i=78 180 | 181 | 182 | 183 | ServerAlias 184 | 185 | 186 | i=63 187 | ns=1;i=3002 188 | i=80 189 | 190 | 191 | 192 | CAEXBasicObjectType 193 | 194 | i=58 195 | ns=1;i=6001 196 | 197 | 198 | 199 | CAEXFileType 200 | 201 | ns=1;i=5001 202 | ns=1;i=5002 203 | ns=1;i=5003 204 | ns=1;i=5004 205 | ns=1;i=1006 206 | 207 | 208 | 209 | CAEXObjectType 210 | 211 | ns=1;i=6002 212 | ns=1;i=1006 213 | 214 | 215 | 216 | AutomationMLBaseInterface 217 | 218 | ns=1;i=1001 219 | 220 | 221 | 222 | AutomationMLBaseRole 223 | 224 | ns=1;i=1001 225 | 226 | 227 | 228 | AutomationMLBaseSystemUnit 229 | 230 | ns=1;i=1001 231 | 232 | 233 | 234 | AMLBaseVariableType 235 | 236 | i=62 237 | ns=1;i=1010 238 | ns=1;i=1011 239 | 240 | 241 | 242 | AMLOpcUaConnectionType 243 | 244 | ns=1;i=3001 245 | ns=1;i=1012 246 | ns=1;i=1013 247 | ns=1;i=1014 248 | ns=1;i=1015 249 | 250 | 251 | 252 | HasAMLRoleReference 253 | 254 | i=32 255 | 256 | IsSupportedRole 257 | 258 | 259 | HasAMLInternalLink 260 | 261 | i=32 262 | 263 | HasAMLInternalLink 264 | 265 | -------------------------------------------------------------------------------- /src/test/resources/plc/Opc.Ua.Plc.NodeSet2.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | http://PLCopen.org/OpcUa/IEC61131-3/ 5 | http://opcfoundation.org/UA/DI/ 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | i=1 15 | i=2 16 | i=3 17 | i=4 18 | i=5 19 | i=6 20 | i=7 21 | i=8 22 | i=9 23 | i=10 24 | i=11 25 | i=13 26 | i=12 27 | i=15 28 | i=14 29 | i=16 30 | i=17 31 | i=18 32 | i=20 33 | i=21 34 | i=19 35 | i=22 36 | i=26 37 | i=27 38 | i=28 39 | i=47 40 | i=46 41 | i=35 42 | i=36 43 | i=48 44 | i=45 45 | i=40 46 | i=37 47 | i=38 48 | i=39 49 | 50 | 51 | HasInputVars 52 | 53 | i=47 54 | 55 | InputVarsOf 56 | 57 | 58 | HasOutputVars 59 | 60 | i=47 61 | 62 | OutputVarsOf 63 | 64 | 65 | HasInOutVars 66 | 67 | i=47 68 | 69 | InOutVarsOf 70 | 71 | 72 | HasLocalVars 73 | 74 | i=47 75 | 76 | LocalVarsOf 77 | 78 | 79 | HasExternalVars 80 | 81 | i=35 82 | 83 | ExternalVarsOf 84 | 85 | 86 | With 87 | 88 | i=32 89 | 90 | Executes 91 | 92 | 93 | CtrlConfigurationType 94 | 95 | ns=1;i=5002 96 | ns=1;i=5004 97 | ns=1;i=5006 98 | ns=1;i=5007 99 | ns=1;i=5008 100 | ns=1;i=5009 101 | ns=1;i=5010 102 | ns=2;i=1002 103 | 104 | 105 | 106 | MethodSet 107 | Flat list of Methods 108 | 109 | ns=1;i=7001 110 | ns=1;i=7002 111 | i=58 112 | i=80 113 | ns=1;i=1001 114 | 115 | 116 | 117 | Start 118 | 119 | i=80 120 | ns=1;i=5002 121 | 122 | 123 | 124 | Stop 125 | 126 | i=80 127 | ns=1;i=5002 128 | 129 | 130 | 131 | Resources 132 | 133 | ns=1;i=5005 134 | ns=2;i=1004 135 | i=78 136 | ns=1;i=1001 137 | 138 | 139 | 140 | SupportedTypes 141 | Folder maintaining the set of (sub-types of) BaseObjectTypes that can be instantiated in the ConfigurableComponent 142 | 143 | i=61 144 | i=78 145 | ns=1;i=5004 146 | 147 | 148 | 149 | GlobalVars 150 | 151 | ns=2;i=1005 152 | i=78 153 | ns=1;i=1001 154 | 155 | 156 | 157 | AccessVars 158 | 159 | ns=2;i=1005 160 | i=78 161 | ns=1;i=1001 162 | 163 | 164 | 165 | ConfigVars 166 | 167 | ns=2;i=1005 168 | i=78 169 | ns=1;i=1001 170 | 171 | 172 | 173 | Configuration 174 | 175 | ns=2;i=1005 176 | i=78 177 | ns=1;i=1001 178 | 179 | 180 | 181 | Diagnostic 182 | 183 | ns=2;i=1005 184 | i=78 185 | ns=1;i=1001 186 | 187 | 188 | 189 | CtrlResourceType 190 | 191 | ns=1;i=5012 192 | ns=1;i=5014 193 | ns=1;i=5016 194 | ns=1;i=5018 195 | ns=1;i=5019 196 | ns=1;i=5020 197 | ns=2;i=1002 198 | 199 | 200 | 201 | MethodSet 202 | Flat list of Methods 203 | 204 | ns=1;i=7003 205 | ns=1;i=7004 206 | i=58 207 | i=80 208 | ns=1;i=1002 209 | 210 | 211 | 212 | Start 213 | 214 | i=80 215 | ns=1;i=5012 216 | 217 | 218 | 219 | Stop 220 | 221 | i=80 222 | ns=1;i=5012 223 | 224 | 225 | 226 | Tasks 227 | 228 | ns=1;i=5015 229 | ns=2;i=1004 230 | i=78 231 | ns=1;i=1002 232 | 233 | 234 | 235 | SupportedTypes 236 | Folder maintaining the set of (sub-types of) BaseObjectTypes that can be instantiated in the ConfigurableComponent 237 | 238 | i=61 239 | i=78 240 | ns=1;i=5014 241 | 242 | 243 | 244 | Programs 245 | 246 | ns=1;i=5017 247 | ns=2;i=1004 248 | i=78 249 | ns=1;i=1002 250 | 251 | 252 | 253 | SupportedTypes 254 | Folder maintaining the set of (sub-types of) BaseObjectTypes that can be instantiated in the ConfigurableComponent 255 | 256 | i=61 257 | i=78 258 | ns=1;i=5016 259 | 260 | 261 | 262 | GlobalVars 263 | 264 | ns=2;i=1005 265 | i=80 266 | ns=1;i=1002 267 | 268 | 269 | 270 | Configuration 271 | 272 | ns=2;i=1005 273 | i=80 274 | ns=1;i=1002 275 | 276 | 277 | 278 | Diagnostic 279 | 280 | ns=2;i=1005 281 | i=80 282 | ns=1;i=1002 283 | 284 | 285 | 286 | CtrlProgramOrganizationUnitType 287 | 288 | ns=1;i=6001 289 | ns=2;i=1003 290 | 291 | 292 | 293 | Body 294 | 295 | i=63 296 | i=80 297 | ns=1;i=1003 298 | 299 | 300 | 301 | CtrlProgramType 302 | 303 | ns=1;i=6002 304 | ns=1;i=1003 305 | 306 | 307 | 308 | Program 309 | 310 | i=63 311 | i=80 312 | ns=1;i=1004 313 | 314 | 315 | 316 | CtrlFunctionBlockType 317 | 318 | ns=1;i=6003 319 | ns=1;i=1003 320 | 321 | 322 | 323 | FunctionBlock 324 | 325 | i=63 326 | i=80 327 | ns=1;i=1005 328 | 329 | 330 | 331 | CtrlTaskType 332 | 333 | ns=1;i=6004 334 | ns=1;i=6005 335 | ns=1;i=6006 336 | i=58 337 | 338 | 339 | 340 | Priority 341 | 342 | i=68 343 | i=78 344 | ns=1;i=1006 345 | 346 | 347 | 348 | Interval 349 | 350 | i=68 351 | i=80 352 | ns=1;i=1006 353 | 354 | 355 | 356 | Single 357 | 358 | i=68 359 | i=80 360 | ns=1;i=1006 361 | 362 | 363 | 364 | SFCType 365 | 366 | i=58 367 | 368 | 369 | -------------------------------------------------------------------------------- /src/test/resources/schema/Opc.Ua.NodeSet.Schema.Check.xml: -------------------------------------------------------------------------------- 1 |  2 | 4 | 5 | i=1 6 | i=2 7 | i=3 8 | i=4 9 | i=5 10 | i=6 11 | i=7 12 | i=8 13 | i=9 14 | i=10 15 | i=11 16 | i=13 17 | i=12 18 | i=15 19 | i=14 20 | i=16 21 | i=17 22 | i=18 23 | i=20 24 | i=21 25 | i=19 26 | i=22 27 | i=26 28 | i=27 29 | i=28 30 | i=47 31 | i=46 32 | i=35 33 | i=36 34 | i=48 35 | i=45 36 | i=40 37 | i=37 38 | i=38 39 | i=39 40 | 41 | 42 | 43 | 44 | 45 | 46 | 0 47 | 48 | 49 | 50 | --------------------------------------------------------------------------------