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.
- package/dist/convert_data_type_definition_to_structuretype_schema.d.ts +4 -3
- package/dist/convert_data_type_definition_to_structuretype_schema.js +96 -66
- package/dist/convert_data_type_definition_to_structuretype_schema.js.map +1 -1
- package/dist/extra_data_type_manager.d.ts +10 -1
- package/dist/extra_data_type_manager.js +96 -2
- package/dist/extra_data_type_manager.js.map +1 -1
- package/dist/get_extension_object_constructor.js +4 -4
- package/dist/get_extension_object_constructor.js.map +1 -1
- package/dist/get_extra_data_type_manager.d.ts +2 -1
- package/dist/get_extra_data_type_manager.js +8 -2
- package/dist/get_extra_data_type_manager.js.map +1 -1
- package/dist/populate_data_type_manager.d.ts +2 -4
- package/dist/populate_data_type_manager.js +88 -81
- package/dist/populate_data_type_manager.js.map +1 -1
- package/dist/private/populate_data_type_manager_103.js +5 -4
- package/dist/private/populate_data_type_manager_103.js.map +1 -1
- package/dist/private/populate_data_type_manager_104.d.ts +1 -2
- package/dist/private/populate_data_type_manager_104.js +41 -18
- package/dist/private/populate_data_type_manager_104.js.map +1 -1
- package/dist/resolve_dynamic_extension_object.d.ts +2 -2
- package/dist/resolve_dynamic_extension_object.js +66 -91
- package/dist/resolve_dynamic_extension_object.js.map +1 -1
- package/package.json +12 -12
- package/source/convert_data_type_definition_to_structuretype_schema.ts +116 -79
- package/source/extra_data_type_manager.ts +113 -4
- package/source/get_extension_object_constructor.ts +5 -4
- package/source/get_extra_data_type_manager.ts +15 -7
- package/source/populate_data_type_manager.ts +99 -89
- package/source/private/populate_data_type_manager_103.ts +6 -4
- package/source/private/populate_data_type_manager_104.ts +67 -28
- 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,
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
|
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
|
|
33
|
+
const serverCapabilities = dataValueServerCapabilities.value?.value as string[] ?? [];
|
|
31
34
|
|
|
32
|
-
|
|
33
|
-
|
|
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
|
-
//
|
|
40
|
-
|
|
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
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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
|
-
//
|
|
90
|
-
//
|
|
91
|
-
const
|
|
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(
|
|
96
|
-
referenceTypeId: "HasSubtype",
|
|
73
|
+
nodeId: resolveNodeId(DataTypeIds.Structure),
|
|
74
|
+
referenceTypeId: resolveNodeId("HasSubtype"),
|
|
97
75
|
resultMask: 63
|
|
98
76
|
});
|
|
99
|
-
|
|
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
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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
|
-
|
|
281
|
+
dataTypeManager: ExtraDataTypeManager
|
|
282
282
|
) {
|
|
283
|
-
assert(
|
|
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
|
-
|
|
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,
|
|
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
|
|
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(
|
|
21
|
-
const debugLog = make_debugLog(
|
|
22
|
-
const warningLog = make_warningLog(
|
|
23
|
-
const doDebug = checkDebugFlag(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
103
|
+
resolvedName,
|
|
98
104
|
dataTypeDefinition,
|
|
99
105
|
null,
|
|
100
|
-
|
|
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,
|
|
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("
|
|
145
|
-
const dependentNamespaces = await readDataTypeDefinitionAndBuildType(
|
|
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
|
|
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
|
-
|
|
205
|
-
|
|
206
|
-
|
|
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
|
}
|