node-opcua-client-dynamic-extension-object 2.51.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/LICENSE +20 -0
- package/dist/convert_data_type_definition_to_structuretype_schema.d.ts +12 -0
- package/dist/convert_data_type_definition_to_structuretype_schema.js +284 -0
- package/dist/convert_data_type_definition_to_structuretype_schema.js.map +1 -0
- package/dist/extra_data_type_manager.d.ts +16 -0
- package/dist/extra_data_type_manager.js +76 -0
- package/dist/extra_data_type_manager.js.map +1 -0
- package/dist/get_extension_object_constructor.d.ts +7 -0
- package/dist/get_extension_object_constructor.js +38 -0
- package/dist/get_extension_object_constructor.js.map +1 -0
- package/dist/get_extra_data_type_manager.d.ts +3 -0
- package/dist/get_extra_data_type_manager.js +52 -0
- package/dist/get_extra_data_type_manager.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +24 -0
- package/dist/index.js.map +1 -0
- package/dist/populate_data_type_manager.d.ts +3 -0
- package/dist/populate_data_type_manager.js +26 -0
- package/dist/populate_data_type_manager.js.map +1 -0
- package/dist/private/convert_data_type_definition_to_structuretype_schema.d.ts +12 -0
- package/dist/private/convert_data_type_definition_to_structuretype_schema.js +284 -0
- package/dist/private/convert_data_type_definition_to_structuretype_schema.js.map +1 -0
- package/dist/private/find_encodings.d.ts +4 -0
- package/dist/private/find_encodings.js +56 -0
- package/dist/private/find_encodings.js.map +1 -0
- package/dist/private/populate_data_type_manager_103.d.ts +9 -0
- package/dist/private/populate_data_type_manager_103.js +599 -0
- package/dist/private/populate_data_type_manager_103.js.map +1 -0
- package/dist/private/populate_data_type_manager_104.d.ts +9 -0
- package/dist/private/populate_data_type_manager_104.js +146 -0
- package/dist/private/populate_data_type_manager_104.js.map +1 -0
- package/dist/promote_opaque_structure.d.ts +6 -0
- package/dist/promote_opaque_structure.js +42 -0
- package/dist/promote_opaque_structure.js.map +1 -0
- package/dist/resolve_dynamic_extension_object.d.ts +4 -0
- package/dist/resolve_dynamic_extension_object.js +106 -0
- package/dist/resolve_dynamic_extension_object.js.map +1 -0
- package/package.json +47 -0
- package/source/convert_data_type_definition_to_structuretype_schema.ts +326 -0
- package/source/extra_data_type_manager.ts +89 -0
- package/source/get_extension_object_constructor.ts +28 -0
- package/source/get_extra_data_type_manager.ts +43 -0
- package/source/index.ts +11 -0
- package/source/populate_data_type_manager.ts +14 -0
- package/source/private/find_encodings.ts +44 -0
- package/source/private/populate_data_type_manager_103.ts +715 -0
- package/source/private/populate_data_type_manager_104.ts +153 -0
- package/source/promote_opaque_structure.ts +42 -0
- package/source/resolve_dynamic_extension_object.ts +104 -0
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
import { assert } from "node-opcua-assert";
|
|
2
|
+
import { AttributeIds, BrowseDirection, makeResultMask, NodeClassMask } from "node-opcua-data-model";
|
|
3
|
+
import { DataValue } from "node-opcua-data-value";
|
|
4
|
+
import { make_debugLog } from "node-opcua-debug";
|
|
5
|
+
import { DataTypeFactory, EnumerationDefinitionSchema, FieldCategory, FieldInterfaceOptions, getBuildInType, StructuredTypeSchema, TypeDefinition } from "node-opcua-factory";
|
|
6
|
+
import { NodeId, makeExpandedNodeId, resolveNodeId } from "node-opcua-nodeid";
|
|
7
|
+
import { browseAll, BrowseDescriptionLike, IBasicSession } from "node-opcua-pseudo-session";
|
|
8
|
+
import { StatusCodes } from "node-opcua-status-code";
|
|
9
|
+
import { EnumDefinition, DataTypeDefinition, StructureDefinition, StructureType } from "node-opcua-types";
|
|
10
|
+
//
|
|
11
|
+
import { _findEncodings } from "./private/find_encodings";
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
const debugLog = make_debugLog(__filename);
|
|
15
|
+
|
|
16
|
+
async function findSuperType(session: IBasicSession, dataTypeNodeId: NodeId): Promise<NodeId> {
|
|
17
|
+
const nodeToBrowse3: BrowseDescriptionLike = {
|
|
18
|
+
browseDirection: BrowseDirection.Inverse,
|
|
19
|
+
includeSubtypes: false,
|
|
20
|
+
nodeClassMask: NodeClassMask.DataType,
|
|
21
|
+
nodeId: dataTypeNodeId,
|
|
22
|
+
referenceTypeId: resolveNodeId("HasSubtype"),
|
|
23
|
+
resultMask: makeResultMask("NodeId | ReferenceType | BrowseName | NodeClass")
|
|
24
|
+
};
|
|
25
|
+
const result3 = await browseAll(session, nodeToBrowse3);
|
|
26
|
+
|
|
27
|
+
/* istanbul ignore next */
|
|
28
|
+
if (result3.statusCode !== StatusCodes.Good) {
|
|
29
|
+
throw new Error("Cannot find superType for " + dataTypeNodeId.toString());
|
|
30
|
+
}
|
|
31
|
+
result3.references = result3.references || [];
|
|
32
|
+
|
|
33
|
+
/* istanbul ignore next */
|
|
34
|
+
if (result3.references.length !== 1) {
|
|
35
|
+
console.log(result3.toString());
|
|
36
|
+
throw new Error("Invalid dataType with more than one superType " + dataTypeNodeId.toString());
|
|
37
|
+
}
|
|
38
|
+
return result3.references[0].nodeId;
|
|
39
|
+
}
|
|
40
|
+
async function findDataTypeCategory(
|
|
41
|
+
session: IBasicSession,
|
|
42
|
+
cache: { [key: string]: CacheForFieldResolution },
|
|
43
|
+
dataTypeNodeId: NodeId
|
|
44
|
+
): Promise<FieldCategory> {
|
|
45
|
+
const subTypeNodeId = await findSuperType(session, dataTypeNodeId);
|
|
46
|
+
debugLog("subTypeNodeId of ", dataTypeNodeId.toString(), " is ", subTypeNodeId.toString());
|
|
47
|
+
const key = subTypeNodeId.toString();
|
|
48
|
+
if (cache[key]) {
|
|
49
|
+
return cache[key].category;
|
|
50
|
+
}
|
|
51
|
+
let category: FieldCategory;
|
|
52
|
+
if (subTypeNodeId.namespace === 0 && subTypeNodeId.value <= 29) {
|
|
53
|
+
// well known node ID !
|
|
54
|
+
switch (subTypeNodeId.value) {
|
|
55
|
+
case 22 /* Structure */:
|
|
56
|
+
category = FieldCategory.complex;
|
|
57
|
+
break;
|
|
58
|
+
case 29 /* Enumeration */:
|
|
59
|
+
category = FieldCategory.enumeration;
|
|
60
|
+
break;
|
|
61
|
+
default:
|
|
62
|
+
category = FieldCategory.basic;
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
return category;
|
|
66
|
+
}
|
|
67
|
+
// must drill down ...
|
|
68
|
+
return await findDataTypeCategory(session, cache, subTypeNodeId);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async function findDataTypeBasicType(
|
|
72
|
+
session: IBasicSession,
|
|
73
|
+
cache: { [key: string]: CacheForFieldResolution },
|
|
74
|
+
dataTypeNodeId: NodeId
|
|
75
|
+
): Promise<TypeDefinition> {
|
|
76
|
+
const subTypeNodeId = await findSuperType(session, dataTypeNodeId);
|
|
77
|
+
|
|
78
|
+
debugLog("subTypeNodeId of ", dataTypeNodeId.toString(), " is ", subTypeNodeId.toString());
|
|
79
|
+
|
|
80
|
+
const key = subTypeNodeId.toString();
|
|
81
|
+
if (cache[key]) {
|
|
82
|
+
return cache[key].schema;
|
|
83
|
+
}
|
|
84
|
+
if (subTypeNodeId.namespace === 0 && subTypeNodeId.value < 29) {
|
|
85
|
+
switch (subTypeNodeId.value) {
|
|
86
|
+
case 22: /* Structure */
|
|
87
|
+
case 29 /* Enumeration */:
|
|
88
|
+
throw new Error("Not expecting Structure or Enumeration");
|
|
89
|
+
default:
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
const nameDataValue: DataValue = await session.read({
|
|
93
|
+
attributeId: AttributeIds.BrowseName,
|
|
94
|
+
nodeId: subTypeNodeId
|
|
95
|
+
});
|
|
96
|
+
const name = nameDataValue.value.value.name!;
|
|
97
|
+
return getBuildInType(name);
|
|
98
|
+
}
|
|
99
|
+
// must drill down ...
|
|
100
|
+
return await findDataTypeBasicType(session, cache, subTypeNodeId);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
export interface CacheForFieldResolution {
|
|
105
|
+
fieldTypeName: string;
|
|
106
|
+
schema: TypeDefinition;
|
|
107
|
+
category: FieldCategory;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async function readBrowseName(session: IBasicSession, nodeId: NodeId): Promise<string> {
|
|
111
|
+
const dataValue = await session.read({ nodeId, attributeId: AttributeIds.BrowseName });
|
|
112
|
+
if (dataValue.statusCode !== StatusCodes.Good) {
|
|
113
|
+
const message =
|
|
114
|
+
"cannot extract BrowseName of nodeId = " + nodeId.toString() + " statusCode = " + dataValue.statusCode.toString();
|
|
115
|
+
debugLog(message);
|
|
116
|
+
throw new Error(message);
|
|
117
|
+
}
|
|
118
|
+
return dataValue.value!.value.name;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
async function resolveFieldType(
|
|
122
|
+
session: IBasicSession,
|
|
123
|
+
dataTypeNodeId: NodeId,
|
|
124
|
+
dataTypeFactory: DataTypeFactory,
|
|
125
|
+
cache: { [key: string]: CacheForFieldResolution }
|
|
126
|
+
): Promise<CacheForFieldResolution | null> {
|
|
127
|
+
if (dataTypeNodeId.namespace === 0 && dataTypeNodeId.value === 22) {
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
const key = dataTypeNodeId.toString();
|
|
131
|
+
const v = cache[key];
|
|
132
|
+
if (v) {
|
|
133
|
+
return v;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (dataTypeNodeId.value === 0) {
|
|
137
|
+
const v3: CacheForFieldResolution = {
|
|
138
|
+
category: FieldCategory.basic,
|
|
139
|
+
fieldTypeName: "Variant",
|
|
140
|
+
schema: dataTypeFactory.getSimpleType("Variant")
|
|
141
|
+
};
|
|
142
|
+
cache[key] = v3;
|
|
143
|
+
return v3;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const fieldTypeName = await readBrowseName(session, dataTypeNodeId);
|
|
147
|
+
|
|
148
|
+
let schema: TypeDefinition | undefined;
|
|
149
|
+
let category: FieldCategory = FieldCategory.enumeration;
|
|
150
|
+
|
|
151
|
+
if (dataTypeFactory.hasStructuredType(fieldTypeName!)) {
|
|
152
|
+
schema = dataTypeFactory.getStructuredTypeSchema(fieldTypeName);
|
|
153
|
+
category = FieldCategory.complex;
|
|
154
|
+
} else if (dataTypeFactory.hasSimpleType(fieldTypeName!)) {
|
|
155
|
+
category = FieldCategory.basic;
|
|
156
|
+
schema = dataTypeFactory.getSimpleType(fieldTypeName!);
|
|
157
|
+
} else if (dataTypeFactory.hasEnumeration(fieldTypeName!)) {
|
|
158
|
+
category = FieldCategory.enumeration;
|
|
159
|
+
schema = dataTypeFactory.getEnumeration(fieldTypeName!)!;
|
|
160
|
+
} else {
|
|
161
|
+
debugLog(" type " + fieldTypeName + " has not been seen yet, let resolve it");
|
|
162
|
+
category = await findDataTypeCategory(session, cache, dataTypeNodeId);
|
|
163
|
+
debugLog(" type " + fieldTypeName + " has not been seen yet, let resolve it => (category = ", category, " )");
|
|
164
|
+
|
|
165
|
+
switch (category) {
|
|
166
|
+
case FieldCategory.basic:
|
|
167
|
+
schema = await findDataTypeBasicType(session, cache, dataTypeNodeId);
|
|
168
|
+
/* istanbul ignore next */
|
|
169
|
+
if (!schema) {
|
|
170
|
+
console.log("Cannot find basic type " + fieldTypeName);
|
|
171
|
+
}
|
|
172
|
+
break;
|
|
173
|
+
default:
|
|
174
|
+
case FieldCategory.enumeration:
|
|
175
|
+
case FieldCategory.complex:
|
|
176
|
+
const dataTypeDefinitionDataValue = await session.read({
|
|
177
|
+
attributeId: AttributeIds.DataTypeDefinition,
|
|
178
|
+
nodeId: dataTypeNodeId
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
/* istanbul ignore next */
|
|
182
|
+
if (dataTypeDefinitionDataValue.statusCode !== StatusCodes.Good) {
|
|
183
|
+
throw new Error(" Cannot find dataType Definition ! with nodeId =" + dataTypeNodeId.toString());
|
|
184
|
+
}
|
|
185
|
+
const definition = dataTypeDefinitionDataValue.value.value;
|
|
186
|
+
|
|
187
|
+
if (category === FieldCategory.enumeration) {
|
|
188
|
+
if (definition instanceof EnumDefinition) {
|
|
189
|
+
const e = new EnumerationDefinitionSchema({
|
|
190
|
+
enumValues: definition.fields,
|
|
191
|
+
name: fieldTypeName
|
|
192
|
+
});
|
|
193
|
+
dataTypeFactory.registerEnumeration(e);
|
|
194
|
+
|
|
195
|
+
schema = e;
|
|
196
|
+
}
|
|
197
|
+
} else {
|
|
198
|
+
schema = await convertDataTypeDefinitionToStructureTypeSchema(
|
|
199
|
+
session,
|
|
200
|
+
dataTypeNodeId,
|
|
201
|
+
fieldTypeName,
|
|
202
|
+
definition,
|
|
203
|
+
dataTypeFactory,
|
|
204
|
+
cache
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
// xx const schema1 = dataTypeFactory.getStructuredTypeSchema(fieldTypeName);
|
|
208
|
+
break;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/* istanbul ignore next */
|
|
213
|
+
if (!schema) {
|
|
214
|
+
throw new Error(
|
|
215
|
+
"expecting a schema here fieldTypeName=" + fieldTypeName + " " + dataTypeNodeId.toString() + " category = " + category
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const v2: CacheForFieldResolution = {
|
|
220
|
+
category,
|
|
221
|
+
fieldTypeName,
|
|
222
|
+
schema
|
|
223
|
+
};
|
|
224
|
+
cache[key] = v2;
|
|
225
|
+
return v2;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
async function _setupEncodings(
|
|
229
|
+
session: IBasicSession,
|
|
230
|
+
dataTypeNodeId: NodeId,
|
|
231
|
+
schema: StructuredTypeSchema
|
|
232
|
+
): Promise<StructuredTypeSchema> {
|
|
233
|
+
// read abstract flag
|
|
234
|
+
const isAbstractDV = await session.read({ nodeId: dataTypeNodeId, attributeId: AttributeIds.IsAbstract });
|
|
235
|
+
schema.dataTypeNodeId = dataTypeNodeId;
|
|
236
|
+
schema.id = dataTypeNodeId;
|
|
237
|
+
|
|
238
|
+
if (isAbstractDV.statusCode === StatusCodes.Good && isAbstractDV.value.value === false) {
|
|
239
|
+
const encodings = await _findEncodings(session, dataTypeNodeId);
|
|
240
|
+
schema.encodingDefaultBinary = makeExpandedNodeId(encodings.binaryEncodingNodeId);
|
|
241
|
+
schema.encodingDefaultXml = makeExpandedNodeId(encodings.xmlEncodingNodeId);
|
|
242
|
+
schema.encodingDefaultJson = makeExpandedNodeId(encodings.jsonEncodingNodeId);
|
|
243
|
+
}
|
|
244
|
+
return schema;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
export async function convertDataTypeDefinitionToStructureTypeSchema(
|
|
248
|
+
session: IBasicSession,
|
|
249
|
+
dataTypeNodeId: NodeId,
|
|
250
|
+
name: string,
|
|
251
|
+
definition: DataTypeDefinition,
|
|
252
|
+
dataTypeFactory: DataTypeFactory,
|
|
253
|
+
cache: { [key: string]: CacheForFieldResolution }
|
|
254
|
+
): Promise<StructuredTypeSchema> {
|
|
255
|
+
if (definition instanceof StructureDefinition) {
|
|
256
|
+
const fields: FieldInterfaceOptions[] = [];
|
|
257
|
+
|
|
258
|
+
const isUnion = definition.structureType === StructureType.Union;
|
|
259
|
+
|
|
260
|
+
switch (definition.structureType) {
|
|
261
|
+
case StructureType.Union:
|
|
262
|
+
// xx console.log("Union Found : ", name);
|
|
263
|
+
fields.push({
|
|
264
|
+
fieldType: "UInt32",
|
|
265
|
+
name: "SwitchField"
|
|
266
|
+
});
|
|
267
|
+
break;
|
|
268
|
+
case StructureType.Structure:
|
|
269
|
+
case StructureType.StructureWithOptionalFields:
|
|
270
|
+
break;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
let switchValue = 1;
|
|
274
|
+
let switchBit = 0;
|
|
275
|
+
|
|
276
|
+
const bitFields: { name: string; length?: number }[] | undefined = isUnion ? undefined : [];
|
|
277
|
+
|
|
278
|
+
for (const fieldD of definition.fields!) {
|
|
279
|
+
const rt = (await resolveFieldType(session, fieldD.dataType, dataTypeFactory, cache))!;
|
|
280
|
+
if (!rt) {
|
|
281
|
+
console.log("convertDataTypeDefinitionToStructureTypeSchema cannot handle field", fieldD.name, "in", name);
|
|
282
|
+
continue;
|
|
283
|
+
}
|
|
284
|
+
const { schema, category, fieldTypeName } = rt;
|
|
285
|
+
|
|
286
|
+
const field: FieldInterfaceOptions = {
|
|
287
|
+
fieldType: fieldTypeName!,
|
|
288
|
+
name: fieldD.name!,
|
|
289
|
+
schema
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
if (fieldD.isOptional) {
|
|
293
|
+
field.switchBit = switchBit++;
|
|
294
|
+
bitFields?.push({ name: fieldD.name! + "Specified", length: 1 });
|
|
295
|
+
}
|
|
296
|
+
if (isUnion) {
|
|
297
|
+
field.switchValue = switchValue;
|
|
298
|
+
switchValue += 1;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
assert(fieldD.valueRank === -1 || fieldD.valueRank === 1 || fieldD.valueRank === 0);
|
|
302
|
+
if (fieldD.valueRank === 1) {
|
|
303
|
+
field.isArray = true;
|
|
304
|
+
} else {
|
|
305
|
+
field.isArray = false;
|
|
306
|
+
}
|
|
307
|
+
field.category = category;
|
|
308
|
+
field.schema = schema;
|
|
309
|
+
fields.push(field);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
const a = await resolveFieldType(session, definition.baseDataType, dataTypeFactory, cache);
|
|
313
|
+
const baseType = a ? a.fieldTypeName : "ExtensionObject";
|
|
314
|
+
|
|
315
|
+
const os = new StructuredTypeSchema({
|
|
316
|
+
baseType,
|
|
317
|
+
bitFields,
|
|
318
|
+
fields,
|
|
319
|
+
id: 0,
|
|
320
|
+
name
|
|
321
|
+
});
|
|
322
|
+
const structuredTypeSchema = await _setupEncodings(session, dataTypeNodeId, os);
|
|
323
|
+
return structuredTypeSchema;
|
|
324
|
+
}
|
|
325
|
+
throw new Error("Not Implemented");
|
|
326
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module node-opcua-client-dynamic-extension-object
|
|
3
|
+
*/
|
|
4
|
+
import { format } from "util";
|
|
5
|
+
|
|
6
|
+
import { assert } from "node-opcua-assert";
|
|
7
|
+
import {
|
|
8
|
+
ConstructorFunc,
|
|
9
|
+
DataTypeFactory,
|
|
10
|
+
getStandardDataTypeFactory
|
|
11
|
+
} from "node-opcua-factory";
|
|
12
|
+
import { NodeId } from "node-opcua-nodeid";
|
|
13
|
+
import { AnyConstructorFunc } from "node-opcua-schemas";
|
|
14
|
+
|
|
15
|
+
export class ExtraDataTypeManager {
|
|
16
|
+
public namespaceArray: string[] = [];
|
|
17
|
+
|
|
18
|
+
private readonly dataTypeFactoryMapByNamespace: { [key: number]: DataTypeFactory } = {};
|
|
19
|
+
|
|
20
|
+
constructor() {
|
|
21
|
+
/* */
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
public setNamespaceArray(namespaceArray: string[]): void {
|
|
25
|
+
this.namespaceArray = namespaceArray;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
public hasDataTypeFactory(namespaceIndex: number): boolean {
|
|
29
|
+
return !!Object.prototype.hasOwnProperty.call(this.dataTypeFactoryMapByNamespace,namespaceIndex);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
public registerDataTypeFactory(namespaceIndex: number, dataTypeFactory: DataTypeFactory): void {
|
|
33
|
+
/* istanbul ignore next */
|
|
34
|
+
assert(namespaceIndex !== 0, "registerTypeDictionary cannot be used for namespace 0");
|
|
35
|
+
if (this.hasDataTypeFactory(namespaceIndex)) {
|
|
36
|
+
throw new Error("Dictionary already registered");
|
|
37
|
+
}
|
|
38
|
+
this.dataTypeFactoryMapByNamespace[namespaceIndex] = dataTypeFactory;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
public getDataTypeFactoryForNamespace(namespaceIndex: number): DataTypeFactory {
|
|
42
|
+
assert(namespaceIndex !== 0, "getTypeDictionaryForNamespace cannot be used for namespace 0");
|
|
43
|
+
return this.dataTypeFactoryMapByNamespace[namespaceIndex];
|
|
44
|
+
}
|
|
45
|
+
public getDataTypeFactory(namespaceIndex: number): DataTypeFactory {
|
|
46
|
+
if (namespaceIndex === 0) {
|
|
47
|
+
return getStandardDataTypeFactory();
|
|
48
|
+
}
|
|
49
|
+
return this.dataTypeFactoryMapByNamespace[namespaceIndex];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
public getExtensionObjectConstructorFromDataType(dataTypeNodeId: NodeId): AnyConstructorFunc {
|
|
53
|
+
const dataTypeFactory = this.getDataTypeFactory(dataTypeNodeId.namespace);
|
|
54
|
+
if (!dataTypeFactory) {
|
|
55
|
+
throw new Error("cannot find dataFactory for namespace=" + dataTypeNodeId.namespace);
|
|
56
|
+
}
|
|
57
|
+
// find schema corresponding to dataTypeNodeId in typeDictionary
|
|
58
|
+
const Constructor = dataTypeFactory.findConstructorForDataType(dataTypeNodeId);
|
|
59
|
+
return Constructor;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
public getExtensionObjectConstructorFromBinaryEncoding(binaryEncodingNodeId: NodeId): ConstructorFunc {
|
|
63
|
+
const dataTypeFactory = this.getDataTypeFactoryForNamespace(binaryEncodingNodeId.namespace);
|
|
64
|
+
const Constructor = dataTypeFactory.getConstructor(binaryEncodingNodeId);
|
|
65
|
+
if (!Constructor) {
|
|
66
|
+
throw new Error(
|
|
67
|
+
"getExtensionObjectConstructorFromBinaryEncoding cannot find constructor for binaryEncoding " +
|
|
68
|
+
binaryEncodingNodeId.toString()
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
return Constructor;
|
|
72
|
+
}
|
|
73
|
+
public toString(): string {
|
|
74
|
+
const l: string[] = [];
|
|
75
|
+
function write(...args: [any, ...any[]]) {
|
|
76
|
+
l.push(format.apply(format, args));
|
|
77
|
+
}
|
|
78
|
+
write("ExtraDataTypeManager");
|
|
79
|
+
for (let n = 0; n < this.namespaceArray.length; n++) {
|
|
80
|
+
write("------------- namespace:", this.namespaceArray[n]);
|
|
81
|
+
const dataFactory = this.dataTypeFactoryMapByNamespace[n];
|
|
82
|
+
if (!dataFactory) {
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
write(dataFactory.toString());
|
|
86
|
+
}
|
|
87
|
+
return l.join("\n");
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { QualifiedName, AttributeIds } from "node-opcua-data-model";
|
|
2
|
+
import { NodeId } from "node-opcua-nodeid";
|
|
3
|
+
import { IBasicSession } from "node-opcua-pseudo-session";
|
|
4
|
+
import { AnyConstructorFunc } from "node-opcua-schemas";
|
|
5
|
+
//
|
|
6
|
+
import { getExtraDataTypeManager } from "./get_extra_data_type_manager";
|
|
7
|
+
import { readDataTypeDefinitionAndBuildType } from "./private/populate_data_type_manager_104";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
*
|
|
11
|
+
*/
|
|
12
|
+
export async function getExtensionObjectConstructor(session: IBasicSession, dataTypeNodeId: NodeId): Promise<AnyConstructorFunc> {
|
|
13
|
+
const extraDataTypeManager = await getExtraDataTypeManager(session);
|
|
14
|
+
|
|
15
|
+
const dataTypeFactory = extraDataTypeManager.getDataTypeFactory(dataTypeNodeId.namespace);
|
|
16
|
+
const Constructor = dataTypeFactory.getConstructorForDataType(dataTypeNodeId);
|
|
17
|
+
if (Constructor) {
|
|
18
|
+
return Constructor;
|
|
19
|
+
}
|
|
20
|
+
const dataValue = await session.read({
|
|
21
|
+
nodeId: dataTypeNodeId,
|
|
22
|
+
attributeId: AttributeIds.BrowseName
|
|
23
|
+
});
|
|
24
|
+
const browseName = dataValue.value.value as QualifiedName;
|
|
25
|
+
await readDataTypeDefinitionAndBuildType(session, dataTypeNodeId, browseName.name!, dataTypeFactory, {});
|
|
26
|
+
|
|
27
|
+
return await extraDataTypeManager.getExtensionObjectConstructorFromDataType(dataTypeNodeId);
|
|
28
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { checkDebugFlag, make_debugLog, make_errorLog } from "node-opcua-debug";
|
|
2
|
+
import { DataTypeFactory, getStandardDataTypeFactory } from "node-opcua-factory";
|
|
3
|
+
import { IBasicSession, readNamespaceArray } from "node-opcua-pseudo-session";
|
|
4
|
+
//
|
|
5
|
+
import { ExtraDataTypeManager } from "./extra_data_type_manager";
|
|
6
|
+
import { populateDataTypeManager } from "./populate_data_type_manager";
|
|
7
|
+
|
|
8
|
+
const doDebug = checkDebugFlag(__filename);
|
|
9
|
+
const debugLog = make_debugLog(__filename);
|
|
10
|
+
const errorLog = make_errorLog(__filename);
|
|
11
|
+
|
|
12
|
+
interface IBasicSessionEx extends IBasicSession {
|
|
13
|
+
$$extraDataTypeManager?: ExtraDataTypeManager;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export async function getExtraDataTypeManager(session: IBasicSession): Promise<ExtraDataTypeManager> {
|
|
17
|
+
const sessionPriv: IBasicSessionEx = session as IBasicSessionEx;
|
|
18
|
+
if (!sessionPriv.$$extraDataTypeManager) {
|
|
19
|
+
const dataTypeManager = new ExtraDataTypeManager();
|
|
20
|
+
|
|
21
|
+
const namespaceArray = await readNamespaceArray(sessionPriv);
|
|
22
|
+
// istanbul ignore next
|
|
23
|
+
if (namespaceArray.length === 0) {
|
|
24
|
+
errorLog("namespaceArray is not populated ! Your server must expose a list of namespace ");
|
|
25
|
+
}
|
|
26
|
+
// istanbul ignore next
|
|
27
|
+
if (doDebug) {
|
|
28
|
+
debugLog("Namespace Array = ", namespaceArray.join("\n "));
|
|
29
|
+
}
|
|
30
|
+
sessionPriv.$$extraDataTypeManager = dataTypeManager;
|
|
31
|
+
dataTypeManager.setNamespaceArray(namespaceArray);
|
|
32
|
+
for (let namespaceIndex = 1; namespaceIndex < namespaceArray.length; namespaceIndex++) {
|
|
33
|
+
const dataTypeFactory1 = new DataTypeFactory([getStandardDataTypeFactory()]);
|
|
34
|
+
dataTypeManager.registerDataTypeFactory(namespaceIndex, dataTypeFactory1);
|
|
35
|
+
}
|
|
36
|
+
await populateDataTypeManager(session, dataTypeManager, false);
|
|
37
|
+
}
|
|
38
|
+
// istanbul ignore next
|
|
39
|
+
if (sessionPriv.$$extraDataTypeManager.namespaceArray.length === 0) {
|
|
40
|
+
throw new Error("namespaceArray is not populated ! Your server must expose a list of namespace ");
|
|
41
|
+
}
|
|
42
|
+
return sessionPriv.$$extraDataTypeManager;
|
|
43
|
+
}
|
package/source/index.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module node-opcua-client-dynamic-extension-object
|
|
3
|
+
*/
|
|
4
|
+
export * from "./populate_data_type_manager";
|
|
5
|
+
export * from "./extra_data_type_manager";
|
|
6
|
+
export * from "./resolve_dynamic_extension_object";
|
|
7
|
+
export * from "./promote_opaque_structure";
|
|
8
|
+
export * from "./get_extension_object_constructor";
|
|
9
|
+
export * from "./get_extra_data_type_manager";
|
|
10
|
+
export * from "./resolve_dynamic_extension_object";
|
|
11
|
+
export * from "./convert_data_type_definition_to_structuretype_schema";
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { IBasicSession } from "node-opcua-pseudo-session";
|
|
2
|
+
//
|
|
3
|
+
import { ExtraDataTypeManager } from "./extra_data_type_manager";
|
|
4
|
+
import { populateDataTypeManager103 } from "./private/populate_data_type_manager_103";
|
|
5
|
+
import { populateDataTypeManager104 } from "./private/populate_data_type_manager_104";
|
|
6
|
+
|
|
7
|
+
export async function populateDataTypeManager(session: IBasicSession, dataTypeManager: ExtraDataTypeManager, force: boolean ): Promise<void> {
|
|
8
|
+
// old way for 1.03 and early 1.04 prototype
|
|
9
|
+
await populateDataTypeManager103(session, dataTypeManager);
|
|
10
|
+
// new way for 1.04 and later
|
|
11
|
+
if (force) {
|
|
12
|
+
await populateDataTypeManager104(session, dataTypeManager);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { BrowseDirection, makeNodeClassMask, makeResultMask } from "node-opcua-data-model";
|
|
2
|
+
import { NodeId, resolveNodeId } from "node-opcua-nodeid";
|
|
3
|
+
import { IBasicSession, BrowseDescriptionLike, browseAll } from "node-opcua-pseudo-session";
|
|
4
|
+
import { DataTypeAndEncodingId } from "node-opcua-schemas";
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
export async function _findEncodings(session: IBasicSession, dataTypeNodeId: NodeId): Promise<DataTypeAndEncodingId> {
|
|
8
|
+
const nodeToBrowse: BrowseDescriptionLike = {
|
|
9
|
+
browseDirection: BrowseDirection.Forward,
|
|
10
|
+
includeSubtypes: true,
|
|
11
|
+
nodeClassMask: makeNodeClassMask("Object"),
|
|
12
|
+
nodeId: dataTypeNodeId,
|
|
13
|
+
referenceTypeId: resolveNodeId("HasEncoding"),
|
|
14
|
+
resultMask: makeResultMask("ReferenceType | IsForward | BrowseName | NodeClass | TypeDefinition")
|
|
15
|
+
};
|
|
16
|
+
const result = await browseAll(session, nodeToBrowse);
|
|
17
|
+
const references = result.references || [];
|
|
18
|
+
if (references.length === 0) {
|
|
19
|
+
// xx throw new Error("Cannot find encodings on type " + dataTypeNodeId.toString() + " statusCode " + result.statusCode.toString());
|
|
20
|
+
}
|
|
21
|
+
const encodings: DataTypeAndEncodingId = {
|
|
22
|
+
dataTypeNodeId,
|
|
23
|
+
|
|
24
|
+
binaryEncodingNodeId: NodeId.nullNodeId,
|
|
25
|
+
jsonEncodingNodeId: NodeId.nullNodeId,
|
|
26
|
+
xmlEncodingNodeId: NodeId.nullNodeId
|
|
27
|
+
};
|
|
28
|
+
for (const ref of references) {
|
|
29
|
+
switch (ref.browseName.name) {
|
|
30
|
+
case "Default Binary":
|
|
31
|
+
encodings.binaryEncodingNodeId = ref.nodeId;
|
|
32
|
+
break;
|
|
33
|
+
case "Default XML":
|
|
34
|
+
encodings.xmlEncodingNodeId = ref.nodeId;
|
|
35
|
+
break;
|
|
36
|
+
case "Default JSON":
|
|
37
|
+
encodings.jsonEncodingNodeId = ref.nodeId;
|
|
38
|
+
break;
|
|
39
|
+
default:
|
|
40
|
+
console.log(" ignoring encoding ", ref.browseName.toString());
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return encodings;
|
|
44
|
+
}
|