node-opcua-client-dynamic-extension-object 2.178.0 → 2.180.0
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 +8 -11
- package/dist/convert_data_type_definition_to_structuretype_schema.js +81 -48
- package/dist/convert_data_type_definition_to_structuretype_schema.js.map +1 -1
- package/dist/convert_structuretype_schema_to_structure_definition.d.ts +1 -1
- package/dist/convert_structuretype_schema_to_structure_definition.js +5 -3
- package/dist/convert_structuretype_schema_to_structure_definition.js.map +1 -1
- package/dist/extra_data_type_manager.d.ts +4 -5
- package/dist/extra_data_type_manager.js +15 -16
- package/dist/extra_data_type_manager.js.map +1 -1
- package/dist/get_extension_object_constructor.d.ts +3 -3
- package/dist/get_extension_object_constructor.js +3 -0
- package/dist/get_extension_object_constructor.js.map +1 -1
- package/dist/get_extra_data_type_manager.d.ts +2 -2
- package/dist/get_extra_data_type_manager.js +8 -8
- package/dist/get_extra_data_type_manager.js.map +1 -1
- package/dist/index.d.ts +5 -6
- package/dist/index.js +20 -21
- package/dist/index.js.map +1 -1
- package/dist/populate_data_type_manager.d.ts +2 -2
- package/dist/populate_data_type_manager.js +10 -10
- package/dist/populate_data_type_manager.js.map +1 -1
- package/dist/private/find_encodings.d.ts +2 -2
- package/dist/private/find_encodings.js.map +1 -1
- package/dist/private/populate_data_type_manager_103.d.ts +4 -4
- package/dist/private/populate_data_type_manager_103.js +97 -53
- package/dist/private/populate_data_type_manager_103.js.map +1 -1
- package/dist/private/populate_data_type_manager_104.d.ts +18 -4
- package/dist/private/populate_data_type_manager_104.js +48 -17
- package/dist/private/populate_data_type_manager_104.js.map +1 -1
- package/dist/promote_opaque_structure.d.ts +2 -2
- package/dist/promote_opaque_structure.js.map +1 -1
- package/dist/promote_opaque_structure_in_notification_data.d.ts +2 -2
- package/dist/promote_opaque_structure_in_notification_data.js +0 -2
- package/dist/promote_opaque_structure_in_notification_data.js.map +1 -1
- package/dist/resolve_dynamic_extension_object.d.ts +4 -4
- package/dist/resolve_dynamic_extension_object.js +15 -12
- package/dist/resolve_dynamic_extension_object.js.map +1 -1
- package/package.json +18 -18
- package/source/convert_data_type_definition_to_structuretype_schema.ts +146 -123
- package/source/convert_structuretype_schema_to_structure_definition.ts +9 -9
- package/source/extra_data_type_manager.ts +24 -22
- package/source/get_extension_object_constructor.ts +12 -7
- package/source/get_extra_data_type_manager.ts +21 -24
- package/source/index.ts +8 -8
- package/source/populate_data_type_manager.ts +17 -25
- package/source/private/find_encodings.ts +2 -2
- package/source/private/populate_data_type_manager_103.ts +166 -105
- package/source/private/populate_data_type_manager_104.ts +102 -45
- package/source/promote_opaque_structure.ts +3 -3
- package/source/promote_opaque_structure_in_notification_data.ts +3 -5
- package/source/resolve_dynamic_extension_object.ts +28 -29
|
@@ -1,32 +1,39 @@
|
|
|
1
|
-
/* eslint-disable max-statements */
|
|
2
|
-
// tslint:disable: no-console
|
|
3
1
|
/**
|
|
4
2
|
* @module node-opcua-client-dynamic-extension-object
|
|
5
3
|
*/
|
|
6
4
|
import chalk from "chalk";
|
|
7
5
|
|
|
8
6
|
import { assert } from "node-opcua-assert";
|
|
9
|
-
import { AttributeIds, makeNodeClassMask, makeResultMask, NodeClassMask, QualifiedName } from "node-opcua-data-model";
|
|
7
|
+
import { AttributeIds, makeNodeClassMask, makeResultMask, NodeClassMask, type QualifiedName } from "node-opcua-data-model";
|
|
8
|
+
import type { DataValue } from "node-opcua-data-value";
|
|
10
9
|
import { checkDebugFlag, make_debugLog, make_errorLog, make_warningLog } from "node-opcua-debug";
|
|
11
|
-
import { ConstructorFuncWithSchema, DataTypeFactory, getStandardDataTypeFactory } from "node-opcua-factory";
|
|
10
|
+
import { type ConstructorFuncWithSchema, DataTypeFactory, getStandardDataTypeFactory } from "node-opcua-factory";
|
|
12
11
|
import { ExpandedNodeId, NodeId, resolveNodeId, sameNodeId } from "node-opcua-nodeid";
|
|
13
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
type BrowseDescriptionLike,
|
|
14
|
+
browseAll,
|
|
15
|
+
type IBasicSessionAsync,
|
|
16
|
+
type IBasicSessionAsync2
|
|
17
|
+
} from "node-opcua-pseudo-session";
|
|
14
18
|
import {
|
|
15
19
|
createDynamicObjectConstructor,
|
|
16
|
-
DataTypeAndEncodingId,
|
|
17
|
-
MapDataTypeAndEncodingIdProvider,
|
|
20
|
+
type DataTypeAndEncodingId,
|
|
21
|
+
type MapDataTypeAndEncodingIdProvider,
|
|
18
22
|
parseBinaryXSD
|
|
19
23
|
} from "node-opcua-schemas";
|
|
20
|
-
import {
|
|
24
|
+
import {
|
|
25
|
+
type BrowseDescriptionOptions,
|
|
26
|
+
BrowseDirection,
|
|
27
|
+
type BrowseResult,
|
|
28
|
+
type ReferenceDescription
|
|
29
|
+
} from "node-opcua-service-browse";
|
|
21
30
|
import { makeBrowsePath } from "node-opcua-service-translate-browse-path";
|
|
22
|
-
import { ReadValueIdOptions, StructureDefinition } from "node-opcua-types";
|
|
23
|
-
|
|
24
|
-
import { ExtraDataTypeManager } from "../extra_data_type_manager";
|
|
31
|
+
import { type ReadValueIdOptions, StructureDefinition } from "node-opcua-types";
|
|
25
32
|
import {
|
|
26
|
-
CacheForFieldResolution,
|
|
33
|
+
type CacheForFieldResolution,
|
|
27
34
|
convertDataTypeDefinitionToStructureTypeSchema
|
|
28
35
|
} from "../convert_data_type_definition_to_structuretype_schema";
|
|
29
|
-
import {
|
|
36
|
+
import type { ExtraDataTypeManager } from "../extra_data_type_manager";
|
|
30
37
|
|
|
31
38
|
const doDebug = checkDebugFlag(__filename);
|
|
32
39
|
const debugLog = make_debugLog(__filename);
|
|
@@ -58,7 +65,8 @@ async function _readDeprecatedFlag(session: IBasicSessionAsync, dataTypeDictiona
|
|
|
58
65
|
/* c8 ignore next */
|
|
59
66
|
if (!a.targets || a.targets.length === 0) {
|
|
60
67
|
// the server is probably version < 1.04.
|
|
61
|
-
|
|
68
|
+
// c8 ignore next
|
|
69
|
+
doDebug && debugLog(`Cannot find Deprecated property for dataTypeDictionary ${dataTypeDictionary.toString()}`);
|
|
62
70
|
return false;
|
|
63
71
|
}
|
|
64
72
|
const deprecatedFlagNodeId = a.targets[0].targetId;
|
|
@@ -103,21 +111,34 @@ async function _getDataTypeDescriptions(
|
|
|
103
111
|
return result2.references.map((r) => ({ nodeId: r.nodeId, browseName: r.browseName }));
|
|
104
112
|
}
|
|
105
113
|
|
|
114
|
+
/**
|
|
115
|
+
* A DataType together with the DataTypeDescription it was reached through.
|
|
116
|
+
*
|
|
117
|
+
* These used to be two arrays paired by index, which only holds while nothing is
|
|
118
|
+
* skipped - and descriptions are skipped, since an abstract type has no DescriptionOf
|
|
119
|
+
* reference. After the first skip every index named a different type in each array, so
|
|
120
|
+
* encodings were written onto the wrong description and the caller's lookup missed. That
|
|
121
|
+
* surfaced as "cannot find dataTypeDescription for ns=..;i=..", caught and logged, with
|
|
122
|
+
* the class silently left unregistered. Keeping the two together makes it unrepresentable.
|
|
123
|
+
*/
|
|
124
|
+
interface IDataTypeAndDescription {
|
|
125
|
+
dataTypeNodeId: NodeId;
|
|
126
|
+
dataTypeDescription: IDataTypeDescription;
|
|
127
|
+
}
|
|
128
|
+
|
|
106
129
|
async function _enrichWithDescriptionOf(
|
|
107
130
|
session: IBasicSessionAsync2,
|
|
108
131
|
dataTypeDescriptions: IDataTypeDescription[]
|
|
109
|
-
): Promise<
|
|
110
|
-
const nodesToBrowse3: BrowseDescriptionOptions[] =
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
}))
|
|
120
|
-
;
|
|
132
|
+
): Promise<IDataTypeAndDescription[]> {
|
|
133
|
+
const nodesToBrowse3: BrowseDescriptionOptions[] = dataTypeDescriptions.map((dataTypeDescription) => ({
|
|
134
|
+
browseDirection: BrowseDirection.Inverse,
|
|
135
|
+
includeSubtypes: false,
|
|
136
|
+
nodeClassMask: makeNodeClassMask("Object"),
|
|
137
|
+
nodeId: dataTypeDescription.nodeId.toString(),
|
|
138
|
+
referenceTypeId: resolveNodeId("HasDescription"),
|
|
139
|
+
// resultMask: makeResultMask("NodeId | ReferenceType | BrowseName | NodeClass | TypeDefinition")
|
|
140
|
+
resultMask: makeResultMask("NodeId")
|
|
141
|
+
}));
|
|
121
142
|
|
|
122
143
|
/* c8 ignore next */
|
|
123
144
|
if (nodesToBrowse3.length === 0) {
|
|
@@ -127,6 +148,8 @@ async function _enrichWithDescriptionOf(
|
|
|
127
148
|
|
|
128
149
|
const binaryEncodings = [];
|
|
129
150
|
const nodesToBrowseDataType: BrowseDescriptionOptions[] = [];
|
|
151
|
+
// parallel to nodesToBrowseDataType, so the pairing survives the skips below
|
|
152
|
+
const browsedDescriptions: IDataTypeDescription[] = [];
|
|
130
153
|
|
|
131
154
|
for (let i = 0; i < results3.length; i++) {
|
|
132
155
|
const result3 = results3[i];
|
|
@@ -153,6 +176,7 @@ async function _enrichWithDescriptionOf(
|
|
|
153
176
|
};
|
|
154
177
|
dataTypeDescription.encodings.binaryEncodingNodeId = binaryEncodingNodeId;
|
|
155
178
|
binaryEncodings.push(binaryEncodingNodeId);
|
|
179
|
+
browsedDescriptions.push(dataTypeDescription);
|
|
156
180
|
nodesToBrowseDataType.push({
|
|
157
181
|
browseDirection: BrowseDirection.Inverse,
|
|
158
182
|
includeSubtypes: false,
|
|
@@ -165,7 +189,7 @@ async function _enrichWithDescriptionOf(
|
|
|
165
189
|
}
|
|
166
190
|
}
|
|
167
191
|
|
|
168
|
-
const
|
|
192
|
+
const pairs: IDataTypeAndDescription[] = [];
|
|
169
193
|
|
|
170
194
|
if (nodesToBrowseDataType.length > 0) {
|
|
171
195
|
const results4 = await browseAll(session, nodesToBrowseDataType);
|
|
@@ -177,16 +201,21 @@ async function _enrichWithDescriptionOf(
|
|
|
177
201
|
if (result4.references.length !== 1) {
|
|
178
202
|
errorLog("What's going on ?", result4.toString(), "result4.references.length = ", result4.references.length);
|
|
179
203
|
}
|
|
204
|
+
// Nothing to pair with, and reading references[0] would throw.
|
|
205
|
+
if (result4.references.length === 0) {
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
180
208
|
|
|
181
|
-
const ref = result4.references
|
|
209
|
+
const ref = result4.references[0];
|
|
182
210
|
const dataTypeNodeId = ref.nodeId;
|
|
183
|
-
|
|
184
|
-
|
|
211
|
+
const dataTypeDescription = browsedDescriptions[i];
|
|
212
|
+
// biome-ignore lint/style/noNonNullAssertion: encodings is always populated for entries that reached nodesToBrowseDataType (see loop above)
|
|
185
213
|
dataTypeDescription.encodings!.dataTypeNodeId = dataTypeNodeId;
|
|
214
|
+
pairs.push({ dataTypeNodeId, dataTypeDescription });
|
|
186
215
|
}
|
|
187
216
|
}
|
|
188
217
|
|
|
189
|
-
const otherEncodingBrowse =
|
|
218
|
+
const otherEncodingBrowse = pairs.map(({ dataTypeNodeId }) => ({
|
|
190
219
|
browseDirection: BrowseDirection.Forward,
|
|
191
220
|
includeSubtypes: false,
|
|
192
221
|
nodeClassMask: NodeClassMask.Object,
|
|
@@ -199,20 +228,22 @@ async function _enrichWithDescriptionOf(
|
|
|
199
228
|
const results5 = await browseAll(session, otherEncodingBrowse);
|
|
200
229
|
for (let i = 0; i < results5.length; i++) {
|
|
201
230
|
const result5 = results5[i];
|
|
202
|
-
const dataTypeDescription =
|
|
231
|
+
const { dataTypeDescription } = pairs[i];
|
|
203
232
|
let encodingCounter = 0;
|
|
233
|
+
// biome-ignore lint/style/noNonNullAssertion: encodings is always populated by the earlier enrichment pass for these dataTypeDescriptions
|
|
234
|
+
const encodings = dataTypeDescription.encodings!;
|
|
204
235
|
for (const ref of result5.references || []) {
|
|
205
236
|
switch (ref.browseName.name) {
|
|
206
237
|
case "Default XML":
|
|
207
|
-
|
|
238
|
+
encodings.xmlEncodingNodeId = ref.nodeId;
|
|
208
239
|
encodingCounter++;
|
|
209
240
|
break;
|
|
210
241
|
case "Default Binary":
|
|
211
|
-
|
|
242
|
+
encodings.binaryEncodingNodeId = ref.nodeId;
|
|
212
243
|
encodingCounter++;
|
|
213
244
|
break;
|
|
214
245
|
case "Default JSON":
|
|
215
|
-
|
|
246
|
+
encodings.jsonEncodingNodeId = ref.nodeId;
|
|
216
247
|
encodingCounter++;
|
|
217
248
|
break;
|
|
218
249
|
default:
|
|
@@ -223,7 +254,7 @@ async function _enrichWithDescriptionOf(
|
|
|
223
254
|
dataTypeDescription.isAbstract = true;
|
|
224
255
|
}
|
|
225
256
|
}
|
|
226
|
-
return
|
|
257
|
+
return pairs;
|
|
227
258
|
}
|
|
228
259
|
|
|
229
260
|
interface IDataTypeDefInfo {
|
|
@@ -231,6 +262,8 @@ interface IDataTypeDefInfo {
|
|
|
231
262
|
dataTypeNodeId: NodeId;
|
|
232
263
|
dataTypeDefinition: StructureDefinition;
|
|
233
264
|
isAbstract: boolean;
|
|
265
|
+
/** the description this type was reached through; carried so nothing has to find it again */
|
|
266
|
+
dataTypeDescription: IDataTypeDescription;
|
|
234
267
|
}
|
|
235
268
|
type DataTypeDefinitions = IDataTypeDefInfo[];
|
|
236
269
|
|
|
@@ -283,19 +316,22 @@ async function _extractDataTypeDictionaryFromDefinition(
|
|
|
283
316
|
assert(dataTypeManager, "expecting a dataTypeManager");
|
|
284
317
|
|
|
285
318
|
const dataTypeDescriptions = await _getDataTypeDescriptions(session, dataTypeDictionaryNodeId);
|
|
286
|
-
const
|
|
319
|
+
const dataTypeAndDescriptions = await _enrichWithDescriptionOf(session, dataTypeDescriptions);
|
|
287
320
|
|
|
288
|
-
// now read DataTypeDefinition attributes of all the
|
|
289
|
-
const nodesToRead: ReadValueIdOptions[] =
|
|
321
|
+
// now read DataTypeDefinition attributes of all the dataTypes, this will only contains concrete structure
|
|
322
|
+
const nodesToRead: ReadValueIdOptions[] = dataTypeAndDescriptions.map(({ dataTypeNodeId }) => ({
|
|
290
323
|
attributeId: AttributeIds.DataTypeDefinition,
|
|
291
|
-
nodeId
|
|
324
|
+
nodeId: dataTypeNodeId
|
|
292
325
|
}));
|
|
293
326
|
|
|
294
327
|
const cache: { [key: string]: CacheForFieldResolution } = {};
|
|
295
328
|
const dataValuesWithDataTypeDefinition = nodesToRead.length > 0 ? await session.read(nodesToRead) : [];
|
|
296
329
|
|
|
297
|
-
//
|
|
298
|
-
//
|
|
330
|
+
// The commented-out assert here compared against dataTypeDescriptions.length and
|
|
331
|
+
// was reported to fail on nodesets such as Euromap. It was right to fail: the two
|
|
332
|
+
// lists are not the same length, because abstract types are skipped. The read is
|
|
333
|
+
// driven by dataTypeAndDescriptions, so this is the invariant that does hold.
|
|
334
|
+
assert(dataValuesWithDataTypeDefinition.length === dataTypeAndDescriptions.length);
|
|
299
335
|
|
|
300
336
|
const dataTypeDefinitions: DataTypeDefinitions = [];
|
|
301
337
|
|
|
@@ -303,31 +339,39 @@ async function _extractDataTypeDictionaryFromDefinition(
|
|
|
303
339
|
|
|
304
340
|
const promise: Promise<void>[] = [];
|
|
305
341
|
for (const dataValue of dataValuesWithDataTypeDefinition) {
|
|
306
|
-
const dataTypeNodeId =
|
|
307
|
-
const dataTypeDescription = dataTypeDescriptions[index];
|
|
308
|
-
|
|
342
|
+
const { dataTypeNodeId, dataTypeDescription } = dataTypeAndDescriptions[index];
|
|
309
343
|
|
|
310
344
|
if (dataValue.statusCode.isGood()) {
|
|
311
345
|
const dataTypeDefinition = dataValue.value.value;
|
|
312
346
|
|
|
313
347
|
if (dataTypeDefinition && dataTypeDefinition instanceof StructureDefinition) {
|
|
314
|
-
|
|
348
|
+
if (!dataTypeDescription.browseName.name) {
|
|
349
|
+
throw new Error(`Unexpected: BrowseName has no name for nodeId ${dataTypeNodeId.toString()}`);
|
|
350
|
+
}
|
|
351
|
+
const className = dataTypeDescription.browseName.name;
|
|
315
352
|
promise.push(
|
|
316
353
|
(async () => {
|
|
317
354
|
const isAbstract = await readIsAbstract(session, dataTypeNodeId);
|
|
318
|
-
dataTypeDefinitions.push({
|
|
355
|
+
dataTypeDefinitions.push({
|
|
356
|
+
className,
|
|
357
|
+
dataTypeNodeId,
|
|
358
|
+
dataTypeDefinition,
|
|
359
|
+
isAbstract,
|
|
360
|
+
dataTypeDescription
|
|
361
|
+
});
|
|
319
362
|
})()
|
|
320
363
|
);
|
|
321
364
|
}
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
365
|
+
} else {
|
|
366
|
+
/* c8 ignore next */
|
|
367
|
+
// c8 ignore next
|
|
368
|
+
doDebug &&
|
|
369
|
+
debugLog(
|
|
370
|
+
"dataTypeNodeId ",
|
|
371
|
+
dataTypeNodeId.toString(),
|
|
372
|
+
" has no DataTypeDescription attribute",
|
|
373
|
+
dataValue.statusCode.toString()
|
|
374
|
+
);
|
|
331
375
|
}
|
|
332
376
|
index++;
|
|
333
377
|
}
|
|
@@ -337,15 +381,14 @@ async function _extractDataTypeDictionaryFromDefinition(
|
|
|
337
381
|
const dataTypeDefinitionsSorted = sortStructure(dataTypeDefinitions);
|
|
338
382
|
// c8 ignore next
|
|
339
383
|
if (doDebug) {
|
|
340
|
-
debugLog("order ", dataTypeDefinitionsSorted.map((a) => a.className
|
|
384
|
+
debugLog("order ", dataTypeDefinitionsSorted.map((a) => `${a.className} ${a.dataTypeNodeId}`).join(" -> "));
|
|
341
385
|
}
|
|
342
386
|
|
|
343
387
|
const promises2: Promise<void>[] = [];
|
|
344
388
|
|
|
345
|
-
for (const { className, dataTypeNodeId, dataTypeDefinition, isAbstract } of dataTypeDefinitionsSorted) {
|
|
389
|
+
for (const { className, dataTypeNodeId, dataTypeDefinition, isAbstract, dataTypeDescription } of dataTypeDefinitionsSorted) {
|
|
346
390
|
promises2.push(
|
|
347
391
|
(async () => {
|
|
348
|
-
|
|
349
392
|
const dataTypeFactory = dataTypeManager.getDataTypeFactoryForNamespace(dataTypeNodeId.namespace);
|
|
350
393
|
// c8 ignore next
|
|
351
394
|
if (doDebug) {
|
|
@@ -356,16 +399,22 @@ async function _extractDataTypeDictionaryFromDefinition(
|
|
|
356
399
|
}
|
|
357
400
|
// now fill typeDictionary
|
|
358
401
|
try {
|
|
359
|
-
|
|
402
|
+
// Previously this searched dataTypeDescriptions for one whose nodeId
|
|
403
|
+
// equalled dataTypeNodeId - comparing a description node against a
|
|
404
|
+
// DataType node, which only ever matched by accident. The description
|
|
405
|
+
// now travels with the type it belongs to.
|
|
360
406
|
if (!dataTypeDefinition) {
|
|
361
|
-
throw new Error(
|
|
407
|
+
throw new Error(`cannot find dataTypeDefinition for ${dataTypeNodeId.toString()}`);
|
|
408
|
+
}
|
|
409
|
+
if (!dataTypeDescription) {
|
|
410
|
+
throw new Error(`cannot find dataTypeDescription for ${dataTypeNodeId.toString()}`);
|
|
362
411
|
}
|
|
363
412
|
const schema = await convertDataTypeDefinitionToStructureTypeSchema(
|
|
364
413
|
session,
|
|
365
414
|
dataTypeNodeId,
|
|
366
415
|
className,
|
|
367
416
|
dataTypeDefinition,
|
|
368
|
-
dataTypeDescription
|
|
417
|
+
dataTypeDescription, // for encodings
|
|
369
418
|
dataTypeManager,
|
|
370
419
|
isAbstract,
|
|
371
420
|
cache
|
|
@@ -393,7 +442,7 @@ async function _extractDataTypeDictionaryFromDefinition(
|
|
|
393
442
|
}
|
|
394
443
|
} catch (err) {
|
|
395
444
|
errorLog("Constructor verification err: ", (<Error>err).message);
|
|
396
|
-
errorLog(
|
|
445
|
+
errorLog(`For this reason class ${className} has not been registered`);
|
|
397
446
|
errorLog(err);
|
|
398
447
|
}
|
|
399
448
|
})()
|
|
@@ -414,7 +463,10 @@ async function _extractNodeIds(
|
|
|
414
463
|
await _enrichWithDescriptionOf(session, dataTypeDescriptions);
|
|
415
464
|
|
|
416
465
|
for (const dataTypeDescription of dataTypeDescriptions) {
|
|
417
|
-
|
|
466
|
+
if (!dataTypeDescription.browseName.name || !dataTypeDescription.encodings) {
|
|
467
|
+
continue;
|
|
468
|
+
}
|
|
469
|
+
map[dataTypeDescription.browseName.name.toString()] = dataTypeDescription.encodings;
|
|
418
470
|
}
|
|
419
471
|
|
|
420
472
|
return {
|
|
@@ -465,7 +517,7 @@ async function _extractDataTypeDictionary(
|
|
|
465
517
|
// dataType definition in store directly in UADataType under the definition attribute
|
|
466
518
|
const dataTypeFactory2 = dataTypeManager.getDataTypeFactory(dataTypeDictionaryNodeId.namespace);
|
|
467
519
|
if (!dataTypeFactory2) {
|
|
468
|
-
throw new Error(
|
|
520
|
+
throw new Error(`cannot find dataTypeFactory for namespace ${dataTypeDictionaryNodeId.namespace}`);
|
|
469
521
|
}
|
|
470
522
|
await _extractDataTypeDictionaryFromDefinition(session, dataTypeDictionaryNodeId, dataTypeManager);
|
|
471
523
|
} else {
|
|
@@ -525,7 +577,7 @@ async function _exploreDataTypeDefinition(
|
|
|
525
577
|
const results2 = await browseAll(session, nodesToBrowse2);
|
|
526
578
|
|
|
527
579
|
const binaryEncodingNodeIds = results2.map((br: BrowseResult) => {
|
|
528
|
-
const defaultBin = br.references
|
|
580
|
+
const defaultBin = (br.references || []).filter((r: ReferenceDescription) => r.browseName.toString() === "Default Binary");
|
|
529
581
|
|
|
530
582
|
/* c8 ignore next */
|
|
531
583
|
if (defaultBin.length < 1) {
|
|
@@ -542,7 +594,10 @@ async function _exploreDataTypeDefinition(
|
|
|
542
594
|
for (let i = 0; i < references.length; i++) {
|
|
543
595
|
const ref = references[i];
|
|
544
596
|
const binaryEncoding = binaryEncodingNodeIds[i];
|
|
545
|
-
const name = ref.browseName
|
|
597
|
+
const name = ref.browseName?.name?.toString();
|
|
598
|
+
if (!name) {
|
|
599
|
+
continue;
|
|
600
|
+
}
|
|
546
601
|
debugLog(" type ", name.padEnd(30, " "), binaryEncoding.toString());
|
|
547
602
|
// let's verify that constructor is operational
|
|
548
603
|
try {
|
|
@@ -558,8 +613,8 @@ async function _exploreDataTypeDefinition(
|
|
|
558
613
|
const testObject = new Constructor();
|
|
559
614
|
debugLog(testObject.toString());
|
|
560
615
|
} catch (err) {
|
|
561
|
-
debugLog(
|
|
562
|
-
debugLog(
|
|
616
|
+
debugLog(` Error cannot construct Extension Object ${name}`);
|
|
617
|
+
debugLog(` ${(<Error>err).message}`);
|
|
563
618
|
}
|
|
564
619
|
}
|
|
565
620
|
}
|
|
@@ -594,7 +649,8 @@ export async function populateDataTypeManager103(
|
|
|
594
649
|
session: IBasicSessionAsync2,
|
|
595
650
|
dataTypeManager: ExtraDataTypeManager
|
|
596
651
|
): Promise<void> {
|
|
597
|
-
|
|
652
|
+
// c8 ignore next
|
|
653
|
+
doDebug && debugLog("in ... populateDataTypeManager");
|
|
598
654
|
|
|
599
655
|
// read namespace array
|
|
600
656
|
const dataValueNamespaceArray = await session.read({
|
|
@@ -606,14 +662,15 @@ export async function populateDataTypeManager103(
|
|
|
606
662
|
|
|
607
663
|
// c8 ignore next
|
|
608
664
|
if (!namespaceArray) {
|
|
609
|
-
|
|
665
|
+
// c8 ignore next
|
|
666
|
+
doDebug && debugLog("session: cannot read Server_NamespaceArray");
|
|
610
667
|
// throw new Error("Cannot get Server_NamespaceArray as a array of string");
|
|
611
668
|
return;
|
|
612
669
|
}
|
|
613
670
|
|
|
614
671
|
// c8 ignore next
|
|
615
672
|
if (doDebug) {
|
|
616
|
-
debugLog("namespaceArray ", namespaceArray.map((a, index) =>
|
|
673
|
+
debugLog("namespaceArray ", namespaceArray.map((a, index) => ` ${index.toString().padEnd(3)}:${a}`).join(" "));
|
|
617
674
|
}
|
|
618
675
|
|
|
619
676
|
if (dataValueNamespaceArray.statusCode.isGood() && namespaceArray && namespaceArray.length > 0) {
|
|
@@ -653,17 +710,18 @@ export async function populateDataTypeManager103(
|
|
|
653
710
|
// c8 ignore next
|
|
654
711
|
if (doDebug) {
|
|
655
712
|
debugLog(result.statusCode.toString());
|
|
656
|
-
debugLog(result.references?.map((r
|
|
713
|
+
debugLog(result.references?.map((r) => r.browseName?.toString()).join(" "));
|
|
657
714
|
}
|
|
658
715
|
|
|
659
716
|
// filter nodes that have the expected namespace Index
|
|
660
717
|
// ( more specifically we want to filter out DataStructure from namespace 0)
|
|
661
718
|
// we also want to keep only object of type DataTypeDictionaryType
|
|
662
|
-
const references = result.references
|
|
719
|
+
const references = (result.references || []).filter(
|
|
663
720
|
(e: ReferenceDescription) => e.nodeId.namespace !== 0 && sameNodeId(e.typeDefinition, dataTypeDictionaryType)
|
|
664
721
|
);
|
|
665
722
|
|
|
666
|
-
|
|
723
|
+
// c8 ignore next
|
|
724
|
+
doDebug && debugLog(`found ${references.length} dictionaries`);
|
|
667
725
|
|
|
668
726
|
async function putInCorrectOrder(): Promise<TypeDictionaryInfo[]> {
|
|
669
727
|
const infos: TypeDictionaryInfo[] = [];
|
|
@@ -673,11 +731,11 @@ export async function populateDataTypeManager103(
|
|
|
673
731
|
const dataTypeDictionaryNodeId = reference.nodeId;
|
|
674
732
|
|
|
675
733
|
const promises: [Promise<boolean>, Promise<DataValue>] = [
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
734
|
+
/* isDictionaryDeprecated: bool = await */ _readDeprecatedFlag(session, dataTypeDictionaryNodeId),
|
|
735
|
+
/* rawSchemaDataValue!: DataValue = await */ session.read({
|
|
736
|
+
attributeId: AttributeIds.Value,
|
|
737
|
+
nodeId: dataTypeDictionaryNodeId
|
|
738
|
+
})
|
|
681
739
|
];
|
|
682
740
|
|
|
683
741
|
const [isDictionaryDeprecated, rawSchemaDataValue] = await Promise.all(promises);
|
|
@@ -721,17 +779,20 @@ export async function populateDataTypeManager103(
|
|
|
721
779
|
if (r) {
|
|
722
780
|
const { xmlns, namespace } = r;
|
|
723
781
|
nsKeyNamespace[xmlns] = namespace;
|
|
724
|
-
|
|
782
|
+
// c8 ignore next
|
|
783
|
+
doDebug && debugLog("xxxx ns= ", xmlns, "=>", namespace);
|
|
725
784
|
}
|
|
726
785
|
}
|
|
727
786
|
info.dependencies = nsKeyNamespace;
|
|
728
|
-
|
|
787
|
+
// c8 ignore next
|
|
788
|
+
doDebug && debugLog("xxx targetNamespace = ", info.targetNamespace);
|
|
729
789
|
innerMap[info.targetNamespace] = info;
|
|
730
790
|
}
|
|
731
791
|
} else {
|
|
732
792
|
// may be 1.04 => the rawSchema is no more needed in new version
|
|
733
793
|
info.targetNamespace = namespaceArray[dataTypeDictionaryNodeId.namespace];
|
|
734
|
-
|
|
794
|
+
// c8 ignore next
|
|
795
|
+
doDebug && debugLog("xxx targetNamespace = ", info.targetNamespace);
|
|
735
796
|
innerMap[info.targetNamespace] = info;
|
|
736
797
|
}
|
|
737
798
|
// assert(info.targetNamespace.length !== 0);
|
|
@@ -741,13 +802,13 @@ export async function populateDataTypeManager103(
|
|
|
741
802
|
|
|
742
803
|
// ----------------------------------
|
|
743
804
|
const orderedList: TypeDictionaryInfo[] = [];
|
|
744
|
-
const visited:
|
|
805
|
+
const visited: Record<string, number> = {};
|
|
745
806
|
function explore(d: TypeDictionaryInfo): void {
|
|
746
807
|
if (visited[d.targetNamespace]) {
|
|
747
808
|
return;
|
|
748
809
|
}
|
|
749
810
|
visited[d.targetNamespace] = 1;
|
|
750
|
-
for (const [
|
|
811
|
+
for (const [_xmlns, namespace] of Object.entries(d.dependencies)) {
|
|
751
812
|
if (!innerMap[namespace] || namespace === d.targetNamespace) {
|
|
752
813
|
continue;
|
|
753
814
|
}
|
|
@@ -759,7 +820,8 @@ export async function populateDataTypeManager103(
|
|
|
759
820
|
explore(d);
|
|
760
821
|
}
|
|
761
822
|
|
|
762
|
-
|
|
823
|
+
// c8 ignore next
|
|
824
|
+
doDebug && debugLog(" Ordered List = ", orderedList.map((a) => a.targetNamespace).join(" "));
|
|
763
825
|
|
|
764
826
|
return orderedList;
|
|
765
827
|
}
|
|
@@ -811,8 +873,8 @@ export async function populateDataTypeManager103(
|
|
|
811
873
|
const set = new Set<DataTypeFactory>();
|
|
812
874
|
set.add(getStandardDataTypeFactory());
|
|
813
875
|
|
|
814
|
-
|
|
815
|
-
|
|
876
|
+
const visited: Set<string> = new Set();
|
|
877
|
+
const explore = (d: TypeDictionaryInfo): void => {
|
|
816
878
|
if (visited.has(d.targetNamespace)) {
|
|
817
879
|
return;
|
|
818
880
|
}
|
|
@@ -825,10 +887,10 @@ export async function populateDataTypeManager103(
|
|
|
825
887
|
if (namespaceUri === d.targetNamespace) {
|
|
826
888
|
continue;
|
|
827
889
|
}
|
|
828
|
-
|
|
829
|
-
if (index
|
|
890
|
+
const index = namespaceArray.indexOf(namespaceUri);
|
|
891
|
+
if (index === -1) continue;
|
|
830
892
|
|
|
831
|
-
|
|
893
|
+
const dd = dataTypeManager.getDataTypeFactory(index);
|
|
832
894
|
set.add(dd);
|
|
833
895
|
|
|
834
896
|
const base = map[namespaceUri];
|
|
@@ -836,7 +898,7 @@ export async function populateDataTypeManager103(
|
|
|
836
898
|
explore(base);
|
|
837
899
|
}
|
|
838
900
|
}
|
|
839
|
-
}
|
|
901
|
+
};
|
|
840
902
|
explore(d);
|
|
841
903
|
const baseDataFactories = Array.from(set);
|
|
842
904
|
const dataTypeFactory = dataTypeManager.getDataTypeFactory(d.dataTypeDictionaryNodeId.namespace);
|
|
@@ -848,7 +910,8 @@ export async function populateDataTypeManager103(
|
|
|
848
910
|
|
|
849
911
|
// now investigate DataTypeDescriptionType
|
|
850
912
|
async function processReferenceOnDataTypeDictionaryType(d: TypeDictionaryInfo): Promise<void> {
|
|
851
|
-
|
|
913
|
+
// c8 ignore next
|
|
914
|
+
doDebug && debugLog(chalk.cyan("processReferenceOnDataTypeDictionaryType on "), d.targetNamespace);
|
|
852
915
|
const ref = d.reference;
|
|
853
916
|
const dataTypeDictionaryNodeId = d.reference.nodeId;
|
|
854
917
|
|
|
@@ -872,8 +935,8 @@ export async function populateDataTypeManager103(
|
|
|
872
935
|
}
|
|
873
936
|
}
|
|
874
937
|
|
|
875
|
-
|
|
876
938
|
// https://medium.com/swlh/dealing-with-multiple-promises-in-javascript-41d6c21f20ff
|
|
939
|
+
// biome-ignore lint/correctness/noConstantCondition: deliberate debug on/off toggle, not dead code
|
|
877
940
|
if (false) {
|
|
878
941
|
// we need to read sequentially the dataTypeDictionaryInfo
|
|
879
942
|
// to esnsure that dependencies are resolved in the correct order
|
|
@@ -897,11 +960,10 @@ export async function populateDataTypeManager103(
|
|
|
897
960
|
s = s.substring(0, s.length - 1);
|
|
898
961
|
}
|
|
899
962
|
return s;
|
|
900
|
-
}
|
|
963
|
+
};
|
|
901
964
|
// check if all dependencies have been processed for this dataTypeDictionary
|
|
902
965
|
const allDependenciesAlreadyProcessed = (typeDictionaryInfo: TypeDictionaryInfo) => {
|
|
903
|
-
|
|
904
|
-
for (const [key, namespace1] of Object.entries(typeDictionaryInfo.dependencies)) {
|
|
966
|
+
for (const [_key, namespace1] of Object.entries(typeDictionaryInfo.dependencies)) {
|
|
905
967
|
// codesys may add quotes around the namespace
|
|
906
968
|
const namespace = unquote(namespace1);
|
|
907
969
|
if (typeDictionaryInfo.targetNamespace === namespace) {
|
|
@@ -913,11 +975,11 @@ export async function populateDataTypeManager103(
|
|
|
913
975
|
}
|
|
914
976
|
}
|
|
915
977
|
return true;
|
|
916
|
-
}
|
|
978
|
+
};
|
|
917
979
|
|
|
918
980
|
const queue: TypeDictionaryInfo[] = [...dataTypeDictionaryInfo];
|
|
919
981
|
|
|
920
|
-
// we load typeDictionary in parallel by processing all
|
|
982
|
+
// we load typeDictionary in parallel by processing all
|
|
921
983
|
// typeDictionary that have all their dependencies resolved
|
|
922
984
|
const extractIndependantTypeInfo = (queue: TypeDictionaryInfo[]): TypeDictionaryInfo[] => {
|
|
923
985
|
const toProcess = queue.splice(0);
|
|
@@ -932,7 +994,7 @@ export async function populateDataTypeManager103(
|
|
|
932
994
|
}
|
|
933
995
|
}
|
|
934
996
|
return result;
|
|
935
|
-
}
|
|
997
|
+
};
|
|
936
998
|
const processFunc = async (typeDictionaryInfo: TypeDictionaryInfo) => {
|
|
937
999
|
try {
|
|
938
1000
|
doDebug && debugLog("processing ", typeDictionaryInfo.targetNamespace);
|
|
@@ -941,9 +1003,8 @@ export async function populateDataTypeManager103(
|
|
|
941
1003
|
errorLog("Error in processReferenceOnDataTypeDictionaryType", err);
|
|
942
1004
|
}
|
|
943
1005
|
alreadyProcessed.add(unquote(typeDictionaryInfo.targetNamespace));
|
|
944
|
-
}
|
|
1006
|
+
};
|
|
945
1007
|
while (queue.length > 0) {
|
|
946
|
-
|
|
947
1008
|
// c8 ignore next
|
|
948
1009
|
if (doDebug) {
|
|
949
1010
|
for (const d of queue) {
|
|
@@ -959,10 +1020,10 @@ export async function populateDataTypeManager103(
|
|
|
959
1020
|
errorLog("Cannot process any more dataTypeDictionary");
|
|
960
1021
|
break;
|
|
961
1022
|
}
|
|
962
|
-
const promises: Promise<void>[] =
|
|
963
|
-
readyToProcess.map((typeDictionaryInfo) => processFunc(typeDictionaryInfo));
|
|
1023
|
+
const promises: Promise<void>[] = readyToProcess.map((typeDictionaryInfo) => processFunc(typeDictionaryInfo));
|
|
964
1024
|
await Promise.all(promises);
|
|
965
1025
|
}
|
|
966
1026
|
}
|
|
967
|
-
|
|
1027
|
+
// c8 ignore next
|
|
1028
|
+
doDebug && debugLog("out ... populateDataTypeManager103");
|
|
968
1029
|
}
|