Complex types fixes (#978)

fix #940 and #975 
- add namespace qualifier to complex type name
- improve handling of known subtypes
- fix Pss detection on .Net 462
- stronger check of valid DataTypeDefinition for servers which expose invalid structure (nodejs)
This commit is contained in:
Martin Regen
2020-05-15 12:54:20 +02:00
committed by GitHub
parent 946f402e87
commit f53f71f8a4
8 changed files with 139 additions and 38 deletions
@@ -110,9 +110,12 @@ namespace Opc.Ua.Client.ComplexTypes
}
// load server types
if (!LoadBaseDataTypes(serverEnumTypes, serverStructTypes))
if (DisableDataTypeDefinition || !LoadBaseDataTypes(serverEnumTypes, serverStructTypes))
{
await LoadDictionaryDataTypes(serverEnumTypes, serverStructTypes, false).ConfigureAwait(false);
if (!DisableDataTypeDictionary)
{
await LoadDictionaryDataTypes(serverEnumTypes, serverStructTypes, false).ConfigureAwait(false);
}
}
}
return GetSystemType(nodeId);
@@ -153,8 +156,12 @@ namespace Opc.Ua.Client.ComplexTypes
serverEnumTypes = serverEnumTypes.Where(rd => rd.NodeId.NamespaceIndex == nameSpaceIndex).ToList();
serverStructTypes = serverStructTypes.Where(rd => rd.NodeId.NamespaceIndex == nameSpaceIndex).ToList();
// load types
if (!LoadBaseDataTypes(serverEnumTypes, serverStructTypes))
if (DisableDataTypeDefinition || !LoadBaseDataTypes(serverEnumTypes, serverStructTypes))
{
if (DisableDataTypeDictionary)
{
return false;
}
return await LoadDictionaryDataTypes(serverEnumTypes, serverStructTypes, false).ConfigureAwait(false);
}
return true;
@@ -193,8 +200,12 @@ namespace Opc.Ua.Client.ComplexTypes
// load server types
IList<INode> serverEnumTypes = LoadDataTypes(DataTypeIds.Enumeration);
IList<INode> serverStructTypes = onlyEnumTypes ? new List<INode>() : LoadDataTypes(DataTypeIds.Structure, true);
if (!LoadBaseDataTypes(serverEnumTypes, serverStructTypes))
if (DisableDataTypeDefinition || !LoadBaseDataTypes(serverEnumTypes, serverStructTypes))
{
if (DisableDataTypeDictionary)
{
return false;
}
return await LoadDictionaryDataTypes(serverEnumTypes, serverStructTypes, true).ConfigureAwait(false);
}
return true;
@@ -219,6 +230,18 @@ namespace Opc.Ua.Client.ComplexTypes
}
#endregion
#region Internal Properties
/// <summary>
/// Disable the use of DataTypeDefinition to create the complex type definition.
/// </summary>
internal bool DisableDataTypeDefinition { get; set; } = false;
/// <summary>
/// Disable the use of DataType Dictinaries to create the complex type definition.
/// </summary>
internal bool DisableDataTypeDictionary { get; set; } = false;
#endregion
#region Private Members
/// <summary>
/// Load listed custom types from dictionaries
@@ -322,7 +345,11 @@ namespace Opc.Ua.Client.ComplexTypes
}
// Use DataTypeDefinition attribute, if available (>=V1.04)
StructureDefinition structureDefinition = GetStructureDefinition(dataTypeNode);
StructureDefinition structureDefinition = null;
if (!DisableDataTypeDefinition)
{
structureDefinition = GetStructureDefinition(dataTypeNode);
}
if (structureDefinition == null)
{
try
@@ -364,7 +391,7 @@ namespace Opc.Ua.Client.ComplexTypes
complexType = AddStructuredType(
complexTypeBuilder,
structureDefinition,
dataTypeNode.BrowseName.Name,
dataTypeNode.BrowseName,
typeId,
binaryEncodingId,
xmlEncodingId
@@ -560,7 +587,7 @@ namespace Opc.Ua.Client.ComplexTypes
newType = AddStructuredType(
complexTypeBuilder,
structureDefinition,
dataTypeNode.BrowseName.Name,
dataTypeNode.BrowseName,
structType.NodeId,
binaryEncodingId,
xmlEncodingId
@@ -635,11 +662,25 @@ namespace Opc.Ua.Client.ComplexTypes
{
return null;
}
// Validate the structure according to Part3, Table 36
foreach (var field in structureDefinition.Fields)
{
// validate if the DataTypeDefinition is correctly
// filled out, some servers don't do it yet...
if (field.BinaryEncodingId.IsNull ||
field.DataType.IsNullNodeId ||
field.TypeId.IsNull)
field.TypeId.IsNull ||
field.Name == null)
{
return null;
}
if (!(field.ValueRank == -1 ||
field.ValueRank >= 1))
{
return null;
}
if (structureDefinition.StructureType == StructureType.Structure &&
field.IsOptional)
{
return null;
}
@@ -678,6 +719,7 @@ namespace Opc.Ua.Client.ComplexTypes
/// <param name="nodeId"></param>
/// <param name="typeId"></param>
/// <param name="encodingId"></param>
/// <param name="dataTypeNode"></param>
/// <returns>true if successful, false otherwise</returns>
private bool BrowseTypeIdsForDictionaryComponent(
NodeId nodeId,
@@ -977,7 +1019,7 @@ namespace Opc.Ua.Client.ComplexTypes
Type newType = null;
if (enumTypeNode != null)
{
string name = enumTypeNode.BrowseName.Name;
QualifiedName name = enumTypeNode.BrowseName;
if (enumTypeNode.DataTypeDefinition != null)
{
// 1. use DataTypeDefinition
@@ -1014,7 +1056,7 @@ namespace Opc.Ua.Client.ComplexTypes
private Type AddStructuredType(
IComplexTypeBuilder complexTypeBuilder,
StructureDefinition structureDefinition,
string typeName,
QualifiedName typeName,
ExpandedNodeId complexTypeId,
ExpandedNodeId binaryEncodingId,
ExpandedNodeId xmlEncodingId
@@ -1183,13 +1225,13 @@ namespace Opc.Ua.Client.ComplexTypes
}
}
}
#endregion
#endregion
#region Private Fields
#region Private Fields
private Session m_session;
private IComplexTypeFactory m_complexTypeBuilderFactory;
private string[] m_supportedEncodings = new string[] { BrowseNames.DefaultBinary, BrowseNames.DefaultXml, BrowseNames.DefaultJson };
private const string kOpcComplexTypesPrefix = "Opc.Ua.ComplexTypes.";
#endregion
#endregion
}
}//namespace
@@ -73,26 +73,26 @@ namespace Opc.Ua.Client.ComplexTypes
/// Create an enum type from an EnumDefinition in an ExtensionObject.
/// Available since OPC UA V1.04 in the DataTypeDefinition attribute.
/// </summary>
Type AddEnumType(string typeName, ExtensionObject typeDefinition);
Type AddEnumType(QualifiedName typeName, ExtensionObject typeDefinition);
/// <summary>
/// Create an enum type from an EnumValue property of a DataType node.
/// Available before OPC UA V1.04.
/// </summary>
Type AddEnumType(string typeName, ExtensionObject[] enumDefinition);
Type AddEnumType(QualifiedName typeName, ExtensionObject[] enumDefinition);
/// <summary>
/// Create an enum type from the EnumString array of a DataType node.
/// Available before OPC UA V1.04.
/// </summary>
Type AddEnumType(string typeName, LocalizedText[] enumDefinition);
Type AddEnumType(QualifiedName typeName, LocalizedText[] enumDefinition);
/// <summary>
/// Create a complex type from a StructureDefinition.
/// Available since OPC UA V1.04 in the DataTypeDefinition attribute.
/// </summary>
IComplexTypeFieldBuilder AddStructuredType(
string name,
QualifiedName name,
StructureDefinition structureDefinition);
}
@@ -14,6 +14,10 @@
<ProjectReference Include="..\Opc.Ua.Client\Opc.Ua.Client.csproj" />
</ItemGroup>
<PropertyGroup Condition="'$(SignAssembly)' == 'true'">
<DefineConstants>$(DefineConstants);SIGNASSEMBLY</DefineConstants>
</PropertyGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net462'">
<Reference Include="System.Reflection" />
</ItemGroup>
@@ -0,0 +1,24 @@
/* Copyright (c) 1996-2019 The OPC Foundation. All rights reserved.
The source code in this file is covered under a dual-license scenario:
- RCL: for OPC Foundation members in good-standing
- GPL V2: everybody else
RCL license terms accompanied with this source code. See http://opcfoundation.org/License/RCL/1.00/
GNU General Public License as published by the Free Software Foundation;
version 2 of the License are accompanied with this source code. See http://opcfoundation.org/License/GPLv2
This source code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
using System.Runtime.CompilerServices;
#if SIGNASSEMBLY
[assembly: InternalsVisibleTo("Opc.Ua.Client.ComplexTypes.Tests, PublicKey = " +
// OPC Foundation Strong Name Public Key
"0024000004800000940000000602000000240000525341310004000001000100d987b12f068b35" +
"80429f3dde01397508880fc7e62621397618456ca1549aeacfbdb90c62adfe918f05ce3677b390" +
"f78357b8745cb6e1334655afce1a9527ac92fc829ff585ea79f007e52ba0f83ead627e3edda40b" +
"ec5ae574128fc9342cb57cb8285aa4e5b589c0ebef3be571b5c8f2ab1067f7c880e8f8882a73c8" +
"0a12a1ef")]
#else
[assembly: InternalsVisibleTo("Opc.Ua.Client.ComplexTypes.Tests")]
#endif
@@ -90,7 +90,7 @@ namespace Opc.Ua.Client.ComplexTypes
/// Create an enum type from an EnumDefinition in an ExtensionObject.
/// Available since OPC UA V1.04 in the DataTypeDefinition attribute.
/// </summary>
public Type AddEnumType(string typeName, ExtensionObject typeDefinition)
public Type AddEnumType(QualifiedName typeName, ExtensionObject typeDefinition)
{
var enumDefinition = typeDefinition.Body as EnumDefinition;
if (enumDefinition == null)
@@ -115,7 +115,7 @@ namespace Opc.Ua.Client.ComplexTypes
/// Create an enum type from an EnumValue property of a DataType node.
/// Available before OPC UA V1.04.
/// </summary>
public Type AddEnumType(string typeName, ExtensionObject[] enumDefinition)
public Type AddEnumType(QualifiedName typeName, ExtensionObject[] enumDefinition)
{
if (enumDefinition == null)
{
@@ -141,7 +141,7 @@ namespace Opc.Ua.Client.ComplexTypes
/// Create an enum type from the EnumString array of a DataType node.
/// Available before OPC UA V1.04.
/// </summary>
public Type AddEnumType(string typeName, LocalizedText[] enumDefinition)
public Type AddEnumType(QualifiedName typeName, LocalizedText[] enumDefinition)
{
if (enumDefinition == null)
{
@@ -169,7 +169,7 @@ namespace Opc.Ua.Client.ComplexTypes
/// Available since OPC UA V1.04 in the DataTypeDefinition attribute.
/// </summary>
public IComplexTypeFieldBuilder AddStructuredType(
string name,
QualifiedName name,
StructureDefinition structureDefinition)
{
if (structureDefinition == null)
@@ -212,9 +212,18 @@ namespace Opc.Ua.Client.ComplexTypes
return moduleName;
}
private string GetFullQualifiedTypeName(string name)
/// <summary>
/// Creates a unique full qualified type name for the assembly.
/// </summary>
/// <param name="browseName">The browse name of the type.</param>
private string GetFullQualifiedTypeName(QualifiedName browseName)
{
return "Opc.Ua.ComplexTypes." + m_moduleName + "." + name;
var result = "Opc.Ua.ComplexTypes." + m_moduleName + ".";
if (browseName.NamespaceIndex > 1)
{
result += browseName.NamespaceIndex + ".";
}
return result + browseName.Name;
}
#endregion
@@ -27,18 +27,7 @@
* http://opcfoundation.org/License/MIT/1.00/
* ======================================================================*/
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using Opc.Ua.Configuration;
namespace Opc.Ua.Client.Controls
{
@@ -72,7 +61,7 @@ namespace Opc.Ua.Client.Controls
private const int ArrayValue = 14;
private const int InputArgument = 15;
private const int OutputArgument = 16;
/// <summary>
/// Returns an image index for the specified method argument.
/// </summary>
@@ -89,6 +78,7 @@ namespace Opc.Ua.Client.Controls
/// <summary>
/// Returns an image index for the specified attribute.
/// </summary>
#pragma warning disable 0162
public static int GetImageIndex(uint attributeId, object value)
{
// Workaround to avoid exception when accessing ImageList
@@ -388,7 +388,7 @@ namespace Opc.Ua
/// Lazy helper to allow runtime to check for Pss support.
/// </summary>
internal static readonly Lazy<bool> IsSupportingRSAPssSign = new Lazy<bool>(() => {
#if NET46 || NET461 || NET47
#if NET46 || NET461 || NET462 || NET47
// The Pss check returns false on .Net4.6/4.7, although it is always supported with certs.
// but not supported with Mono
return !Utils.IsRunningOnMono();
@@ -417,6 +417,6 @@ namespace Opc.Ua
rsa.Dispose();
}
}
#endregion
#endregion
}
}
+33 -1
View File
@@ -514,8 +514,40 @@ namespace Opc.Ua
case DataTypes.Number: { return typeof(Variant); }
case DataTypes.Integer: { return typeof(Variant); }
case DataTypes.UInteger: { return typeof(Variant); }
case DataTypes.UtcTime: { return typeof(DateTime); }
case DataTypes.Enumeration: { return typeof(Int32); }
// subtype of DateTime
case DataTypes.Date:
case DataTypes.UtcTime: goto case DataTypes.DateTime;
// subtype of ByteString
case DataTypes.ApplicationInstanceCertificate:
case DataTypes.AudioDataType:
case DataTypes.ContinuationPoint:
case DataTypes.Image:
case DataTypes.ImageBMP:
case DataTypes.ImageGIF:
case DataTypes.ImageJPG:
case DataTypes.ImagePNG: goto case DataTypes.ByteString;
// subtype of NodeId
case DataTypes.SessionAuthenticationToken: goto case DataTypes.NodeId;
// subtype of Double
case DataTypes.Duration: goto case DataTypes.Double;
// subtype of UInt32
case DataTypes.IntegerId:
case DataTypes.Index:
case DataTypes.VersionTime:
case DataTypes.Counter: goto case DataTypes.UInt32;
// subtype of UInt64
case DataTypes.BitFieldMaskDataType: goto case DataTypes.UInt64;
// subtype of String
case DataTypes.DateString:
case DataTypes.DecimalString:
case DataTypes.DurationString:
case DataTypes.LocaleId:
case DataTypes.NormalizedString:
case DataTypes.NumericRange:
case DataTypes.Time:
case DataTypes.TimeString: goto case DataTypes.String;
}
return factory.GetSystemType(datatypeId);