diff --git a/SampleApplications/SDK/Opc.Ua.Client.ComplexTypes/ComplexTypeSystem.cs b/SampleApplications/SDK/Opc.Ua.Client.ComplexTypes/ComplexTypeSystem.cs
index 557bc5bbb..6a7735095 100644
--- a/SampleApplications/SDK/Opc.Ua.Client.ComplexTypes/ComplexTypeSystem.cs
+++ b/SampleApplications/SDK/Opc.Ua.Client.ComplexTypes/ComplexTypeSystem.cs
@@ -65,7 +65,7 @@ public ComplexTypeSystem(Session session)
/// and a customized type builder factory
///
public ComplexTypeSystem(
- Session session,
+ Session session,
IComplexTypeFactory complexTypeBuilderFactory)
{
Initialize(session, complexTypeBuilderFactory);
@@ -84,19 +84,87 @@ private void Initialize(
///
/// Load a single custom type with subtypes.
///
- public Task LoadType(NodeId nodeId, bool subTypes)
+ ///
+ /// Type dependencies are not resolved. If the new structure contains an
+ /// unknown type, the load fails. In such a case the caller should proceed
+ /// with a generic load of the whole type system.
+ ///
+ public async Task LoadType(ExpandedNodeId nodeId, bool subTypes = false, bool throwOnError = false)
{
- // FUTURE: Implement loader for a single type. Currently loading all types.
- return Load();
+ try
+ {
+ var subTypeNodes = LoadDataTypes(nodeId, true, true);
+ var subTypeNodesWithoutKnownTypes = RemoveKnownTypes(subTypeNodes);
+
+ if (subTypeNodesWithoutKnownTypes.Count > 0)
+ {
+ IList serverEnumTypes = new List();
+ IList serverStructTypes = serverEnumTypes;
+ var superType = m_session.NodeCache.FindSuperType(nodeId);
+ if (superType == DataTypeIds.Enumeration)
+ {
+ serverEnumTypes = subTypeNodesWithoutKnownTypes;
+ }
+ else
+ {
+ serverStructTypes = subTypeNodesWithoutKnownTypes;
+ }
+
+ // load server types
+ LoadBaseDataTypes(serverEnumTypes, serverStructTypes);
+ await LoadDictionaryDataTypes(serverEnumTypes, serverStructTypes);
+ }
+ return GetSystemType(nodeId);
+ }
+ catch (ServiceResultException sre)
+ {
+ Utils.Trace(sre, "Failed to load the custom type.");
+ if (throwOnError)
+ {
+ throw sre;
+ }
+ return null;
+ }
}
///
/// Load all custom types of a namespace.
///
- public Task LoadTypeDictionary(string nameSpace)
+ ///
+ /// Type dependencies with other namespaces are not resolved.
+ /// If a new structure contains an unknown enum or structured
+ /// type from another namespace, loading such types fails.
+ /// To load such a type the caller must load the whole type system.
+ ///
+ public async Task LoadNamespace(string nameSpace, bool throwOnError = false)
{
- // FUTURE: Implement loader for all custom types of a namespace. Currently loading all types.
- return Load();
+ try
+ {
+ int index = m_session.NamespaceUris.GetIndex(nameSpace);
+ if (index < 0)
+ {
+ throw new ServiceResultException($"Bad argument {nameSpace}. Namespace not found.");
+ }
+ ushort nameSpaceIndex = (ushort)index;
+ var serverEnumTypes = LoadDataTypes(DataTypeIds.Enumeration);
+ var serverStructTypes = LoadDataTypes(DataTypeIds.Structure, true);
+ // filter for namespace
+ serverEnumTypes = serverEnumTypes.Where(rd => rd.NodeId.NamespaceIndex == nameSpaceIndex).ToList();
+ serverStructTypes = serverStructTypes.Where(rd => rd.NodeId.NamespaceIndex == nameSpaceIndex).ToList();
+ // load types
+ LoadBaseDataTypes(serverEnumTypes, serverStructTypes);
+ await LoadDictionaryDataTypes(serverEnumTypes, serverStructTypes);
+ return true;
+ }
+ catch (ServiceResultException sre)
+ {
+ Utils.Trace(sre, $"Failed to load the custom type dictionary.");
+ if (throwOnError)
+ {
+ throw sre;
+ }
+ return false;
+ }
}
///
@@ -115,13 +183,21 @@ public Task LoadTypeDictionary(string nameSpace)
/// - Convert all structured types in the dictionaries to the DataTypeDefinion attribute, if possible.
/// - Create all structured types from the dictionaries using the DataTypeDefinion attribute..
///
- public async Task Load()
+ public async Task Load(bool onlyEnumTypes = false, bool throwOnError = false)
{
try
{
// load server types
- var serverEnumTypes = LoadDataTypes(DataTypeIds.Enumeration);
- var serverStructTypes = LoadDataTypes(DataTypeIds.Structure, true);
+ IList serverEnumTypes = LoadDataTypes(DataTypeIds.Enumeration);
+ IList serverStructTypes;
+ if (onlyEnumTypes)
+ {
+ serverStructTypes = new List();
+ }
+ else
+ {
+ serverStructTypes = LoadDataTypes(DataTypeIds.Structure, true);
+ }
LoadBaseDataTypes(serverEnumTypes, serverStructTypes);
await LoadDictionaryDataTypes(serverEnumTypes, serverStructTypes);
return true;
@@ -129,6 +205,10 @@ public async Task Load()
catch (ServiceResultException sre)
{
Utils.Trace(sre, $"Failed to load the custom type dictionary.");
+ if (throwOnError)
+ {
+ throw sre;
+ }
return false;
}
}
@@ -162,10 +242,11 @@ IList serverStructTypes
// load the binary schema dictionaries from the server
var typeSystem = await m_session.LoadDataTypeSystem();
- // TODO: sort dictionaries with import dependencies to the end of the list
+ // sort dictionaries with import dependencies to the end of the list
+ var sortedTypeSystem = typeSystem.OrderBy(t => t.Value.TypeDictionary.Import?.Count()).ToList();
// create custom types for all dictionaries
- foreach (var dictionaryId in typeSystem)
+ foreach (var dictionaryId in sortedTypeSystem)
{
try
{
@@ -282,7 +363,7 @@ IList serverStructTypes
///
/// Load all custom types with DataTypeDefinition into the type factory.
///
- private void LoadBaseDataTypes(
+ private bool LoadBaseDataTypes(
IList serverEnumTypes,
IList serverStructTypes
)
@@ -350,6 +431,10 @@ IList serverStructTypes
{
Type newType = null;
var dataTypeNode = structType as DataTypeNode;
+ if (dataTypeNode == null)
+ {
+ continue;
+ }
var structureDefinition = dataTypeNode.DataTypeDefinition?.Body as StructureDefinition;
if (structureDefinition != null)
{
@@ -390,6 +475,8 @@ IList serverStructTypes
structTypesToDoList = new List();
}
} while (retryAddStructType);
+
+ return structTypesToDoList.Count == 0;
}
@@ -482,12 +569,26 @@ private INode BrowseForSingleProperty(
/// Load all subTypes and optionally nested subtypes of a type definition.
/// Filter for all subtypes or only subtypesoutside the default namespace.
///
- private IList LoadDataTypes(ExpandedNodeId dataType, bool nestedSubTypes = false, bool filterUATypes = true)
+ private IList LoadDataTypes(
+ ExpandedNodeId dataType,
+ bool nestedSubTypes = false,
+ bool addRootNode = false,
+ bool filterUATypes = true)
{
var result = new List();
var nodesToBrowse = new ExpandedNodeIdCollection();
nodesToBrowse.Add(dataType);
+ if (addRootNode)
+ {
+ var rootNode = m_session.NodeCache.Find(dataType);
+ if (!(rootNode is DataTypeNode))
+ {
+ throw new ServiceResultException("Root Node is not a DataType node.");
+ }
+ result.Add(rootNode);
+ }
+
while (nodesToBrowse.Count > 0)
{
var nextNodesToBrowse = new ExpandedNodeIdCollection();
@@ -635,18 +736,21 @@ DataTypeNode enumTypeNode
{
// browse for EnumFields or EnumStrings property
var property = BrowseForSingleProperty(enumTypeNode.NodeId);
- var enumArray = m_session.ReadValue(
- ExpandedNodeId.ToNodeId(property.NodeId,
- m_session.NamespaceUris));
- if (enumArray.Value is ExtensionObject[])
- {
- // 2. use EnumValues
- newType = complexTypeBuilder.AddEnumType(name, (ExtensionObject[])enumArray.Value);
- }
- else if (enumArray.Value is LocalizedText[])
+ if (property != null)
{
- // 3. use EnumStrings
- newType = complexTypeBuilder.AddEnumType(name, (LocalizedText[])enumArray.Value);
+ var enumArray = m_session.ReadValue(
+ ExpandedNodeId.ToNodeId(property.NodeId,
+ m_session.NamespaceUris));
+ if (enumArray.Value is ExtensionObject[])
+ {
+ // 2. use EnumValues
+ newType = complexTypeBuilder.AddEnumType(name, (ExtensionObject[])enumArray.Value);
+ }
+ else if (enumArray.Value is LocalizedText[])
+ {
+ // 3. use EnumStrings
+ newType = complexTypeBuilder.AddEnumType(name, (LocalizedText[])enumArray.Value);
+ }
}
}
}