node-opcua-client-dynamic-extension-object 2.162.0 → 2.163.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/dist/convert_data_type_definition_to_structuretype_schema.d.ts +4 -3
  2. package/dist/convert_data_type_definition_to_structuretype_schema.js +96 -66
  3. package/dist/convert_data_type_definition_to_structuretype_schema.js.map +1 -1
  4. package/dist/extra_data_type_manager.d.ts +10 -1
  5. package/dist/extra_data_type_manager.js +96 -2
  6. package/dist/extra_data_type_manager.js.map +1 -1
  7. package/dist/get_extension_object_constructor.js +4 -4
  8. package/dist/get_extension_object_constructor.js.map +1 -1
  9. package/dist/get_extra_data_type_manager.d.ts +2 -1
  10. package/dist/get_extra_data_type_manager.js +8 -2
  11. package/dist/get_extra_data_type_manager.js.map +1 -1
  12. package/dist/populate_data_type_manager.d.ts +2 -4
  13. package/dist/populate_data_type_manager.js +88 -81
  14. package/dist/populate_data_type_manager.js.map +1 -1
  15. package/dist/private/populate_data_type_manager_103.js +5 -4
  16. package/dist/private/populate_data_type_manager_103.js.map +1 -1
  17. package/dist/private/populate_data_type_manager_104.d.ts +1 -2
  18. package/dist/private/populate_data_type_manager_104.js +41 -18
  19. package/dist/private/populate_data_type_manager_104.js.map +1 -1
  20. package/dist/resolve_dynamic_extension_object.d.ts +2 -2
  21. package/dist/resolve_dynamic_extension_object.js +66 -91
  22. package/dist/resolve_dynamic_extension_object.js.map +1 -1
  23. package/package.json +12 -12
  24. package/source/convert_data_type_definition_to_structuretype_schema.ts +116 -79
  25. package/source/extra_data_type_manager.ts +113 -4
  26. package/source/get_extension_object_constructor.ts +5 -4
  27. package/source/get_extra_data_type_manager.ts +15 -7
  28. package/source/populate_data_type_manager.ts +99 -89
  29. package/source/private/populate_data_type_manager_103.ts +6 -4
  30. package/source/private/populate_data_type_manager_104.ts +67 -28
  31. package/source/resolve_dynamic_extension_object.ts +79 -110
@@ -23,7 +23,7 @@ export interface IBasicSessionAsync2Private extends IBasicSessionAsync2 {
23
23
 
24
24
  $$getSessionForDataTypeExtraction?: () => IBasicSessionAsync2;
25
25
 
26
- on?: (this: IBasicSessionAsync2Private, event: "session_restored", func: () => void)=> void;
26
+ on?: (this: IBasicSessionAsync2Private, event: "session_restored", func: () => void) => void;
27
27
 
28
28
  sessionId?: NodeId;
29
29
 
@@ -38,7 +38,7 @@ export async function invalidateExtraDataTypeManager(session: IBasicSessionAsync
38
38
  }
39
39
  }
40
40
 
41
- export async function extractDataTypeManagerPrivate(session: IBasicSessionAsync2, strategy: DataTypeExtractStrategy): Promise<ExtraDataTypeManager> {
41
+ async function extractDataTypeManagerPrivate(session: IBasicSessionAsync2, strategy: DataTypeExtractStrategy): Promise<ExtraDataTypeManager> {
42
42
  const namespaceArray = await readNamespaceArray(session);
43
43
  // istanbul ignore next
44
44
  if (namespaceArray.length === 0) {
@@ -49,11 +49,13 @@ export async function extractDataTypeManagerPrivate(session: IBasicSessionAsync2
49
49
  debugLog("Namespace Array = ", namespaceArray.join("\n "));
50
50
  }
51
51
  const dataTypeManager = new ExtraDataTypeManager();
52
+ dataTypeManager.setSession(session);
52
53
  dataTypeManager.setNamespaceArray(namespaceArray);
53
54
  for (let namespaceIndex = 1; namespaceIndex < namespaceArray.length; namespaceIndex++) {
54
55
  const dataTypeFactory1 = new DataTypeFactory([getStandardDataTypeFactory()]);
55
56
  dataTypeManager.registerDataTypeFactory(namespaceIndex, dataTypeFactory1);
56
57
  }
58
+
57
59
  await populateDataTypeManager(session, dataTypeManager, strategy);
58
60
  // istanbul ignore next
59
61
  if (dataTypeManager.namespaceArray.length === 0) {
@@ -67,14 +69,19 @@ function getStrategy(session: IBasicSessionAsync2, strategy?: DataTypeExtractStr
67
69
  if (strategy !== undefined) {
68
70
  return strategy;
69
71
  }
70
- const client = (session as any).client;
71
- if (client && client.dataTypeExtractStrategy!== undefined) {
72
+ const client = (session as IBasicSessionAsync2 & { _client: { dataTypeExtractStrategy: DataTypeExtractStrategy } })._client;
73
+ if (client && client.dataTypeExtractStrategy !== undefined) {
72
74
  return client.dataTypeExtractStrategy;
73
75
  }
74
76
  return DataTypeExtractStrategy.Auto;
75
77
  }
76
78
 
77
- function getSessionForDataTypeManagerExtraction(session: IBasicSessionAsync2): IBasicSessionAsync2 {
79
+
80
+ export function hasBoostedSession(session: IBasicSessionAsync2): boolean {
81
+ const _session: IBasicSessionAsync2Private = session as IBasicSessionAsync2Private;
82
+ return !!_session.$$getSessionForDataTypeExtraction || !!(_session as any).session;
83
+ }
84
+ export function getSessionForDataTypeManagerExtraction(session: IBasicSessionAsync2): IBasicSessionAsync2 {
78
85
  const _session: IBasicSessionAsync2Private = session as IBasicSessionAsync2Private;
79
86
  if (_session.$$getSessionForDataTypeExtraction) {
80
87
  return _session.$$getSessionForDataTypeExtraction();
@@ -90,8 +97,8 @@ function followSession(session: IBasicSessionAsync2Private & ICascadingSession):
90
97
  return session;
91
98
  }
92
99
 
93
- export async function getExtraDataTypeManager(session: IBasicSessionAsync2, strategy?: DataTypeExtractStrategy ): Promise<ExtraDataTypeManager> {
94
-
100
+ export async function getExtraDataTypeManager(session: IBasicSessionAsync2, strategy?: DataTypeExtractStrategy): Promise<ExtraDataTypeManager> {
101
+
95
102
  const sessionPriv: IBasicSessionAsync2Private = followSession(session) as IBasicSessionAsync2Private;
96
103
 
97
104
  if (sessionPriv.$$extraDataTypeManager) {
@@ -111,6 +118,7 @@ export async function getExtraDataTypeManager(session: IBasicSessionAsync2, stra
111
118
  (async () => {
112
119
  try {
113
120
  strategy = getStrategy(session, strategy);
121
+
114
122
  const sessionToUse = getSessionForDataTypeManagerExtraction(session);
115
123
 
116
124
  const dataTypeManager = await extractDataTypeManagerPrivate(sessionToUse, strategy);
@@ -2,7 +2,8 @@ import { AttributeIds, BrowseDirection, NodeClassMask, ResultMask } from "node-o
2
2
  import { resolveNodeId } from "node-opcua-nodeid";
3
3
  import {
4
4
  IBasicSessionAsync2,
5
- browseAll
5
+ browseAll,
6
+ readNamespaceArray
6
7
  } from "node-opcua-pseudo-session";
7
8
  import { DataTypeIds, ObjectIds, VariableIds, VariableTypeIds } from "node-opcua-constants";
8
9
  import { DataType } from "node-opcua-variant";
@@ -12,131 +13,106 @@ import { makeBrowsePath } from "node-opcua-service-translate-browse-path";
12
13
  import { ExtraDataTypeManager } from "./extra_data_type_manager";
13
14
  import { populateDataTypeManager103 } from "./private/populate_data_type_manager_103";
14
15
  import { populateDataTypeManager104 } from "./private/populate_data_type_manager_104";
16
+ import { DataTypeFactory } from "node-opcua-factory";
17
+ import { checkDebugFlag, make_debugLog } from "node-opcua-debug";
15
18
 
16
19
 
20
+ const doDebug = checkDebugFlag("populateDataTypeManager");
21
+ const debugLog = make_debugLog("populateDataTypeManager");
22
+
17
23
  const ComplexTypes2017 = "http://opcfoundation.org/UA-Profile/Server/ComplexTypes2017";
18
- /**
19
- * @private
20
- */
24
+
21
25
  export async function serverImplementsDataTypeDefinition(
22
26
  session: IBasicSessionAsync2
23
27
  ): Promise<boolean> {
24
28
 
25
-
26
29
  const dataValueServerCapabilities = await session.read({
27
30
  nodeId: resolveNodeId(VariableIds.Server_ServerCapabilities_ServerProfileArray),
28
31
  attributeId: AttributeIds.Value
29
32
  });
30
- const serverCapabilities = dataValueServerCapabilities.value.value as string[] ?? [];
33
+ const serverCapabilities = dataValueServerCapabilities.value?.value as string[] ?? [];
31
34
 
32
- //read the capabilities
33
- // https://profiles.opcfoundation.org/profile/1725
34
- // and find http://opcfoundation.org/UA-Profile/Server/ComplexTypes2017
35
- if (serverCapabilities.indexOf(ComplexTypes2017) >= 0) {
35
+ if (serverCapabilities.indexOf(ComplexTypes2017) >= 0) {
36
+ doDebug && debugLog("server implements ComplexTypes2017");
36
37
  return true;
37
38
  }
38
39
 
39
- // One way to figure out is to check if the server provides DataTypeDefinition node
40
- // ( see OPCUA 1.04 part 6 -)
41
- // This is the preferred route, as we go along, more and more servers will implement this.
42
- const browseResult1 = await browseAll(session, {
40
+ // Check if any non-deprecated 1.03 dictionary exists
41
+ const browseResult1 = await session.browse({
43
42
  browseDirection: BrowseDirection.Forward,
44
43
  includeSubtypes: true,
45
44
  nodeClassMask: NodeClassMask.Variable,
46
45
  nodeId: resolveNodeId(ObjectIds.OPCBinarySchema_TypeSystem),
47
46
  resultMask: ResultMask.TypeDefinition
48
47
  });
49
-
50
- let count103DataType = 0;
51
- const innerF = async (ref: ReferenceDescription) => {
52
- const td = ref.typeDefinition;
53
- if (!(td.namespace === 0 && td.value === VariableTypeIds.DataTypeDictionaryType)) {
54
- return;
55
- }
56
- // we have a type definition,
57
- // let check if there is a deprecated property
58
- const p = await session.translateBrowsePath(makeBrowsePath(ref.nodeId, "/Deprecated"));
59
- if (!p.statusCode.isGood() || !p.targets || p.targets.length === 0) {
60
- // the dataTypeDictionaryType is not exposing a Deprecated property
61
- count103DataType++;
62
- return;
63
- }
64
- const deprecatedNodeId = p.targets[0].targetId;
65
- // we have a deprecated property => this is a 1.03 server or 1.04
66
- // => we need to check if the server provides DataTypeDefinition
67
- const dataValue = await session.read({ nodeId: deprecatedNodeId, attributeId: AttributeIds.Value });
68
- if (dataValue.statusCode.isGood() && dataValue.value.value === false) {
69
- // this is a 1.03 server
70
- count103DataType++;
71
- return;
72
- }
73
- }
74
48
 
75
- if (false) {
76
- for (let r of (browseResult1.references || [])) {
77
- await innerF(r);
49
+ const references103 = browseResult1.references || [];
50
+ for (const ref of references103) {
51
+ const td = ref.typeDefinition;
52
+ if (td.namespace === 0 && td.value === VariableTypeIds.DataTypeDictionaryType) {
53
+ const p = await session.translateBrowsePath(makeBrowsePath(ref.nodeId, "/Deprecated"));
54
+ if (!p.statusCode.isGood() || !p.targets || p.targets.length === 0) {
55
+ doDebug && debugLog("server implements 1.03 dictionary but no Deprecated node found");
56
+ return false;
57
+ }
58
+ const deprecatedNodeId = p.targets[0].targetId;
59
+ const dataValue = await session.read({ nodeId: deprecatedNodeId, attributeId: AttributeIds.Value });
60
+ if (dataValue.statusCode.isGood() && dataValue.value.value === false) {
61
+ doDebug && debugLog("server implements 1.03 dictionary but Deprecated node is true");
62
+ return false;
63
+ }
78
64
  }
79
- } else {
80
- const promises: Promise<void>[] = (browseResult1.references || []).map((a) => innerF(a));
81
- await Promise.all(promises);
82
- }
83
-
84
- if (count103DataType >= 1) {
85
- // some namespace are old , we cannot assume that all namespace are 1.04
86
- return false;
87
65
  }
88
66
 
89
- // check if server provides DataTypeDefinition => in this case this is the preferred route,
90
- // as we go along, more and more servers will implement this.
91
- const browseResult = await browseAll(session, {
67
+ // Try to find AT LEAST ONE custom DataType and check its DataTypeDefinition
68
+ // We browse the Structure type (22) without recursion first
69
+ const browseResult2 = await session.browse({
92
70
  browseDirection: BrowseDirection.Forward,
93
71
  includeSubtypes: true,
94
72
  nodeClassMask: NodeClassMask.DataType,
95
- nodeId: resolveNodeId(DataType.ExtensionObject),
96
- referenceTypeId: "HasSubtype",
73
+ nodeId: resolveNodeId(DataTypeIds.Structure),
74
+ referenceTypeId: resolveNodeId("HasSubtype"),
97
75
  resultMask: 63
98
76
  });
99
- const browseResult2 = await browseAll(session, {
77
+
78
+ const references = browseResult2.references || [];
79
+ const customDataType = references.find(r => r.nodeId.namespace !== 0);
80
+ if (customDataType) {
81
+ const dv = await session.read({ nodeId: customDataType.nodeId, attributeId: AttributeIds.DataTypeDefinition });
82
+ if (dv.statusCode.isGood()) {
83
+ doDebug && debugLog("server implements 1.04 dictionary with custom DataType");
84
+ return true;
85
+ }
86
+ } else {
87
+ const standardDataType = references.find(r => r.nodeId.namespace == 0);
88
+ if (standardDataType) {
89
+ const dv = await session.read({ nodeId: standardDataType.nodeId, attributeId: AttributeIds.DataTypeDefinition });
90
+ if (dv.statusCode.isGood()) {
91
+ doDebug && debugLog("server implements 1.04 dictionary with standard DataType");
92
+ return true;
93
+ }
94
+ }
95
+ }
96
+
97
+ // If no custom type at first level, check Union too
98
+ const browseResult3 = await session.browse({
100
99
  browseDirection: BrowseDirection.Forward,
101
100
  includeSubtypes: true,
102
101
  nodeClassMask: NodeClassMask.DataType,
103
102
  nodeId: resolveNodeId(DataTypeIds.Union),
104
- referenceTypeId: "HasSubtype",
103
+ referenceTypeId: resolveNodeId("HasSubtype"),
105
104
  resultMask: 63
106
105
  });
107
-
108
- let references: ReferenceDescription[] = [];
109
- if (browseResult && browseResult.references) references = references.concat(browseResult.references);
110
- if (browseResult2 && browseResult2.references) references = references.concat(browseResult2.references);
111
-
112
- if (references.length === 0) return false;
113
-
114
- // DataType Structure from namespace 0 are not interesting and will not provide DataTypeDefinition attribute anyway
115
- // on some servers.
116
- references = references.filter((a, index) => a.nodeId.namespace !== 0);
117
- if (references.length === 0) return false;
118
-
119
- let nodesToRead = references.map((r) => ({
120
- nodeId: r.nodeId,
121
- attributeId: AttributeIds.DataTypeDefinition
122
- }));
123
-
124
- const nodesToRead2 = nodesToRead.map((r) => ({ nodeId: r.nodeId, attributeId: AttributeIds.IsAbstract }));
125
- const abstractFlags: boolean[] = (await session.read(nodesToRead2)).map((d) => d.value.value);
126
-
127
- // also remove the abstract dataStructure => they don't provide valid DataTypeDefinition
128
- nodesToRead = nodesToRead.filter((_nodesToRead, index) => !abstractFlags[index]);
129
- if (nodesToRead.length === 0) return false;
130
-
131
- const dataValues = await session.read(nodesToRead);
132
-
133
- const countOK = dataValues.reduce((prev, a) => prev + (a.statusCode.isGood() ? 1 : 0), 0);
134
- if (countOK === dataValues.length) {
135
- return true;
136
- // await populateDataTypeManager104(session, dataTypeManager);
137
- // return;
106
+ const customDataType2 = (browseResult3.references || []).find(r => r.nodeId.namespace !== 0);
107
+ if (customDataType2) {
108
+ const dv = await session.read({ nodeId: customDataType2.nodeId, attributeId: AttributeIds.DataTypeDefinition });
109
+ if (dv.statusCode.isGood()) {
110
+ doDebug && debugLog("server implements 1.04 dictionary with custom Union");
111
+ return true;
112
+ }
138
113
  }
139
114
 
115
+ doDebug && debugLog("server does not implement 1.04 dictionary or we cannot find any evidence of it");
140
116
  return false;
141
117
  }
142
118
 
@@ -144,7 +120,8 @@ export enum DataTypeExtractStrategy {
144
120
  Auto = 0,
145
121
  Force103 = 1,
146
122
  Force104 = 2,
147
- Both = 3
123
+ Both = 3,
124
+ Lazy = 4
148
125
  };
149
126
 
150
127
  export async function populateDataTypeManager(
@@ -152,21 +129,54 @@ export async function populateDataTypeManager(
152
129
  dataTypeManager: ExtraDataTypeManager,
153
130
  strategy: DataTypeExtractStrategy
154
131
  ): Promise<void> {
132
+ dataTypeManager.setSession(session);
133
+
134
+
135
+ if (strategy === DataTypeExtractStrategy.Lazy) {
136
+ doDebug && debugLog("populateDataTypeManager: Lazy mode");
137
+ const force104 = await serverImplementsDataTypeDefinition(session);
138
+ if (force104) {
139
+ doDebug && debugLog("populateDataTypeManager: Lazy mode - server implements 1.04 dictionary - we will be lazy");
140
+ // we are in lazy mode for 1.0.5+
141
+ // create teh dataTypeFactory for namespace 1
142
+ const namespace = await readNamespaceArray(session);
143
+ for (let i = 1; i < namespace.length; i++) {
144
+ let dataTypeFactory = dataTypeManager.getDataTypeFactory(i);
145
+ if (!dataTypeFactory) {
146
+ dataTypeFactory = new DataTypeFactory([]);
147
+ dataTypeManager.registerDataTypeFactory(i, dataTypeFactory);
148
+ // throw new Error("cannot find dataType Manager for namespace of " + dataTypeNodeId.toString());
149
+ }
150
+ }
151
+ return;
152
+ }
153
+ doDebug && debugLog("populateDataTypeManager: Lazy mode - server does not implement 1.04 dictionary - we will be eager");
154
+ // for old 1.03 servers we must be eager as we don't have a way to lazy load 1.03 dictionaries yet
155
+ await populateDataTypeManager103(session, dataTypeManager);
156
+ await populateDataTypeManager104(session, dataTypeManager);
157
+ return;
158
+ }
155
159
  if (strategy === DataTypeExtractStrategy.Auto) {
160
+ doDebug && debugLog("populateDataTypeManager: Auto mode");
156
161
  const force104 = await serverImplementsDataTypeDefinition(session);
162
+
157
163
  if (force104) {
164
+ doDebug && debugLog("populateDataTypeManager: Auto mode - server implements 1.04 dictionary - we will be 104 eager");
158
165
  await populateDataTypeManager104(session, dataTypeManager);
159
166
  return;
160
167
  }
168
+ doDebug && debugLog("populateDataTypeManager: Auto mode - server does not implement 1.04 dictionary - we will be 103 eager and 104 eager");
161
169
  // old way for 1.03 and early 1.04 prototype
162
170
  await populateDataTypeManager103(session, dataTypeManager);
163
171
  await populateDataTypeManager104(session, dataTypeManager);
164
172
  return;
165
173
  }
166
174
  if (strategy == DataTypeExtractStrategy.Force103 || strategy == DataTypeExtractStrategy.Both) {
175
+ doDebug && debugLog("populateDataTypeManager: Force103 mode - we will be 103 eager");
167
176
  await populateDataTypeManager103(session, dataTypeManager);
168
177
  }
169
178
  if (strategy == DataTypeExtractStrategy.Force104 || strategy == DataTypeExtractStrategy.Both) {
179
+ doDebug && debugLog("populateDataTypeManager: Force104 mode - we will be 104 eager");
170
180
  await populateDataTypeManager104(session, dataTypeManager);
171
181
  }
172
182
  }
@@ -278,9 +278,9 @@ const readIsAbstract = async (session: IBasicSessionAsync, nodeId: NodeId): Prom
278
278
  async function _extractDataTypeDictionaryFromDefinition(
279
279
  session: IBasicSessionAsync2,
280
280
  dataTypeDictionaryNodeId: NodeId,
281
- dataTypeFactory: DataTypeFactory
281
+ dataTypeManager: ExtraDataTypeManager
282
282
  ) {
283
- assert(dataTypeFactory, "expecting a dataTypeFactory");
283
+ assert(dataTypeManager, "expecting a dataTypeManager");
284
284
 
285
285
  const dataTypeDescriptions = await _getDataTypeDescriptions(session, dataTypeDictionaryNodeId);
286
286
  const dataTypeNodeIds = await _enrichWithDescriptionOf(session, dataTypeDescriptions);
@@ -343,6 +343,8 @@ async function _extractDataTypeDictionaryFromDefinition(
343
343
  for (const { className, dataTypeNodeId, dataTypeDefinition, isAbstract } of dataTypeDefinitionsSorted) {
344
344
  promises2.push(
345
345
  (async () => {
346
+
347
+ const dataTypeFactory = dataTypeManager.getDataTypeFactoryForNamespace(dataTypeNodeId.namespace);
346
348
  // istanbul ignore next
347
349
  if (doDebug) {
348
350
  debugLog(chalk.yellow("--------------------------------------- "), className, dataTypeNodeId.toString());
@@ -362,7 +364,7 @@ async function _extractDataTypeDictionaryFromDefinition(
362
364
  className,
363
365
  dataTypeDefinition,
364
366
  dataTypeDescription!, // for encodings
365
- dataTypeFactory,
367
+ dataTypeManager,
366
368
  isAbstract,
367
369
  cache
368
370
  );
@@ -463,7 +465,7 @@ async function _extractDataTypeDictionary(
463
465
  if (!dataTypeFactory2) {
464
466
  throw new Error("cannot find dataTypeFactory for namespace " + dataTypeDictionaryNodeId.namespace);
465
467
  }
466
- await _extractDataTypeDictionaryFromDefinition(session, dataTypeDictionaryNodeId, dataTypeFactory2);
468
+ await _extractDataTypeDictionaryFromDefinition(session, dataTypeDictionaryNodeId, dataTypeManager);
467
469
  } else {
468
470
  const rawSchema = d.rawSchema; // DataValue = await session.read({ nodeId: dataTypeDictionaryNodeId, attributeId: AttributeIds.Value });
469
471
  doDebug &&
@@ -8,32 +8,35 @@ import {
8
8
  ReferenceDescription,
9
9
  BrowseDescriptionOptions,
10
10
  StructureDefinition,
11
- DataTypeDefinition} from "node-opcua-types";
11
+ DataTypeDefinition
12
+ } from "node-opcua-types";
12
13
  //
13
14
  import { ExtraDataTypeManager } from "../extra_data_type_manager";
14
15
  import {
15
16
  ICache,
16
17
  convertDataTypeDefinitionToStructureTypeSchema
17
18
  } from "../convert_data_type_definition_to_structuretype_schema";
19
+ import { hasBoostedSession } from "../get_extra_data_type_manager";
18
20
  import { StatusCodes } from "node-opcua-status-code";
19
21
 
20
- const errorLog = make_errorLog(__filename);
21
- const debugLog = make_debugLog(__filename);
22
- const warningLog = make_warningLog(__filename);
23
- const doDebug = checkDebugFlag(__filename);
22
+ const errorLog = make_errorLog("populateDataTypeManager");
23
+ const debugLog = make_debugLog("populateDataTypeManager");
24
+ const warningLog = make_warningLog("populateDataTypeManager");
25
+ const doDebug = checkDebugFlag("populateDataTypeManager");
24
26
 
25
27
  type DependentNamespaces = Set<number>
26
28
 
27
29
  export async function readDataTypeDefinitionAndBuildType(
28
30
  session: IBasicSessionAsync2,
29
31
  dataTypeNodeId: NodeId,
30
- name: string,
31
- dataTypeFactory: DataTypeFactory,
32
+ name: string | undefined,
33
+ dataTypeManager: ExtraDataTypeManager,
32
34
  cache: ICache
33
35
  ): Promise<DependentNamespaces> {
34
36
  const dependentNamespaces: DependentNamespaces = new Set();
35
37
  try {
36
- if (dataTypeFactory.getStructureInfoForDataType(dataTypeNodeId)) {
38
+
39
+ if (dataTypeManager.getStructureInfoForDataType(dataTypeNodeId)) {
37
40
  return dependentNamespaces;
38
41
  }
39
42
  const [isAbstractDataValue, dataTypeDefinitionDataValue, browseNameDataValue] = await session.read([
@@ -55,6 +58,9 @@ export async function readDataTypeDefinitionAndBuildType(
55
58
  debugLog("Cannot find dataTypeNodeId = ", dataTypeNodeId.toString());
56
59
  return dependentNamespaces;
57
60
  }
61
+
62
+ const resolvedName = name || (browseNameDataValue.value?.value?.name as string) || "Unknown";
63
+
58
64
  /* istanbul ignore next */
59
65
  if (isAbstractDataValue.statusCode.isNotGood()) {
60
66
  errorLog("browseName", browseNameDataValue.value.toString());
@@ -82,7 +88,7 @@ export async function readDataTypeDefinitionAndBuildType(
82
88
 
83
89
  // get dependencies of struct
84
90
  if (dataTypeDefinition instanceof StructureDefinition && dataTypeDefinition.fields) {
85
- for (const field of dataTypeDefinition.fields){
91
+ for (const field of dataTypeDefinition.fields) {
86
92
  const dataTypeNamespace = field.dataType.namespace
87
93
  if (dataTypeNamespace === dataTypeDefinition.defaultEncodingId.namespace) {
88
94
  continue; // not dependent on own namespace
@@ -94,21 +100,23 @@ export async function readDataTypeDefinitionAndBuildType(
94
100
  const schema = await convertDataTypeDefinitionToStructureTypeSchema(
95
101
  session,
96
102
  dataTypeNodeId,
97
- name,
103
+ resolvedName,
98
104
  dataTypeDefinition,
99
105
  null,
100
- dataTypeFactory,
106
+ dataTypeManager,
101
107
  isAbstract,
102
108
  cache
103
109
  );
110
+
111
+ const dataTypeFactory = dataTypeManager.getDataTypeFactoryForNamespace(dataTypeNodeId.namespace);
104
112
  if (isAbstract) {
105
113
  // cannot construct an abstract structure
106
- dataTypeFactory.registerAbstractStructure(dataTypeNodeId, name, schema);
114
+ dataTypeFactory.registerAbstractStructure(dataTypeNodeId, resolvedName, schema);
107
115
  } else {
108
116
  const Constructor = createDynamicObjectConstructorAndRegister(schema, dataTypeFactory);
109
117
  }
110
118
  } catch (err) {
111
- errorLog("Error", (err as Error).message," while processing dataTypeNodeId =", dataTypeNodeId.toString());
119
+ errorLog("Error", (err as Error).message, " while processing dataTypeNodeId =", dataTypeNodeId.toString());
112
120
  }
113
121
  return dependentNamespaces;
114
122
  }
@@ -127,26 +135,38 @@ export async function populateDataTypeManager104(
127
135
  try {
128
136
  if (dataTypeNodeId.namespace === 0) {
129
137
  // already known I guess
138
+ doDebug && debugLog("populateDataTypeManager104: skiping dataType = namespace 0", dataTypeNodeId.toString());
130
139
  return;
131
140
  }
141
+
142
+ // register factory if not already registered
132
143
  let dataTypeFactory = dataTypeManager.getDataTypeFactory(dataTypeNodeId.namespace);
133
144
  if (!dataTypeFactory) {
134
145
  dataTypeFactory = new DataTypeFactory([]);
135
146
  dataTypeManager.registerDataTypeFactory(dataTypeNodeId.namespace, dataTypeFactory);
136
147
  // throw new Error("cannot find dataType Manager for namespace of " + dataTypeNodeId.toString());
137
148
  }
149
+
138
150
  // if not found already
139
151
  if (dataTypeFactory.getStructureInfoForDataType(dataTypeNodeId)) {
140
152
  // already known !
153
+ doDebug && debugLog("populateDataTypeManager104: skiping dataType = already known", dataTypeNodeId.toString());
141
154
  return;
142
155
  }
156
+
143
157
  // extract it formally
144
- doDebug && debugLog(" DataType => ", r.browseName.toString(), dataTypeNodeId.toString());
145
- const dependentNamespaces = await readDataTypeDefinitionAndBuildType(session, dataTypeNodeId, r.browseName.name!, dataTypeFactory, cache);
146
-
158
+ doDebug && debugLog("populateDataTypeManager104: processing dataType = ", r.browseName.toString(), dataTypeNodeId.toString());
159
+ const dependentNamespaces = await readDataTypeDefinitionAndBuildType(
160
+ session,
161
+ dataTypeNodeId,
162
+ r.browseName.name!,
163
+ dataTypeManager,
164
+ cache
165
+ );
166
+
147
167
  // add dependent namespaces to dataFactoriesDependencies
148
168
  let dataFactoryDependencies = dataFactoriesDependencies.get(dataTypeNodeId.namespace);
149
- if (!dataFactoryDependencies){
169
+ if (!dataFactoryDependencies) {
150
170
  // add new dependencies set if not already existing
151
171
  dataFactoryDependencies = new Set([0]); // always dependent on UA node set
152
172
  dataFactoriesDependencies.set(dataTypeNodeId.namespace, dataFactoryDependencies);
@@ -169,11 +189,11 @@ export async function populateDataTypeManager104(
169
189
  await applyOnReferenceRecursively(session, resolveNodeId("Structure"), nodeToBrowse, withDataType);
170
190
 
171
191
  // set factory dependencies
172
- for (const [namespace, dependentNamespaces] of dataFactoriesDependencies){
192
+ for (const [namespace, dependentNamespaces] of dataFactoriesDependencies) {
173
193
 
174
194
  const namespaceDataTypeFactory = dataTypeManager.getDataTypeFactoryForNamespace(namespace);
175
- const dependentTypeFactories = new Set<DataTypeFactory>([getStandardDataTypeFactory()]);
176
-
195
+ const dependentTypeFactories = new Set<DataTypeFactory>([getStandardDataTypeFactory()]);
196
+
177
197
  for (const dependentNamespace of dependentNamespaces) {
178
198
  if (dependentNamespace === 0) continue; // already added above
179
199
  const dependentTypeFactory = dataTypeManager.getDataTypeFactoryForNamespace(dependentNamespace);
@@ -192,23 +212,42 @@ async function applyOnReferenceRecursively(
192
212
  ): Promise<void> {
193
213
 
194
214
 
195
- const oneLevel = async (nodeId: NodeIdLike) => {
215
+ const hasBoosted = hasBoostedSession(session as any);
216
+ const useHeavyParallelization = hasBoosted;
217
+
218
+ debugLog("applyOnReferenceRecursively = useHeavyParallelization", useHeavyParallelization);
196
219
 
220
+ const oneLevel = async (nodeId: NodeIdLike, level: number) => {
221
+
222
+ doDebug && debugLog("applyOnReferenceRecursively = level", level, "nodeId", nodeId.toString());
197
223
  const nodeToBrowse: BrowseDescriptionOptions = {
198
224
  ...browseDescriptionTemplate,
199
225
  nodeId
200
226
  };
201
227
 
202
228
  const browseResult = await browseAll(session, nodeToBrowse);
229
+ if (useHeavyParallelization) {
230
+ // @sterfive/optimized-client (PRO module) we can
231
+ // parallelize and minimize the number of calls to the server to
232
+ // drastically improve performance
233
+ const promises: Promise<void>[] = [];
234
+ for (const ref of browseResult.references || []) {
235
+ promises.push((async () => {
236
+ await action(ref);
237
+ await oneLevel(ref.nodeId, level + 1);
238
+ })());
239
+ }
240
+ await Promise.all(promises);
203
241
 
204
- const promises: Promise<void>[] = [];
205
- for (const ref of browseResult.references || []) {
206
- promises.push((async () => {
242
+ } else {
243
+ // important: we dont parallelize the action on browse reference
244
+ // to avoid overloading browseContinuationToken on client side
245
+ for (const ref of browseResult.references || []) {
207
246
  await action(ref);
208
- await oneLevel(ref.nodeId);
209
- })());
247
+ await oneLevel(ref.nodeId, level + 1);
248
+ }
249
+
210
250
  }
211
- await Promise.all(promises);
212
251
  };
213
- await oneLevel(nodeId);
252
+ await oneLevel(nodeId, 0);
214
253
  }