node-opcua-convert-nodeset-to-javascript 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/.mocharc.yml +10 -0
- package/LICENSE +20 -0
- package/dist/convert_namespace_to_typescript.d.ts +3 -0
- package/dist/convert_namespace_to_typescript.js +158 -0
- package/dist/convert_namespace_to_typescript.js.map +1 -0
- package/dist/convert_to_typescript.d.ts +109 -0
- package/dist/convert_to_typescript.js +879 -0
- package/dist/convert_to_typescript.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/dist/main.d.ts +1 -0
- package/dist/main.js +66 -0
- package/dist/main.js.map +1 -0
- package/dist/official_namespaces.d.ts +23 -0
- package/dist/official_namespaces.js +36 -0
- package/dist/official_namespaces.js.map +1 -0
- package/dist/options.d.ts +4 -0
- package/dist/options.js +3 -0
- package/dist/options.js.map +1 -0
- package/dist/private/cache.d.ts +58 -0
- package/dist/private/cache.js +213 -0
- package/dist/private/cache.js.map +1 -0
- package/dist/private/to_filename.d.ts +1 -0
- package/dist/private/to_filename.js +10 -0
- package/dist/private/to_filename.js.map +1 -0
- package/dist/private/utils.d.ts +24 -0
- package/dist/private/utils.js +261 -0
- package/dist/private/utils.js.map +1 -0
- package/dist/private-stuff.d.ts +4 -0
- package/dist/private-stuff.js +17 -0
- package/dist/private-stuff.js.map +1 -0
- package/dist/walk_through.d.ts +13 -0
- package/dist/walk_through.js +87 -0
- package/dist/walk_through.js.map +1 -0
- package/package.json +58 -0
- package/source/convert_namespace_to_typescript.ts +171 -0
- package/source/convert_to_typescript.ts +1095 -0
- package/source/index.ts +3 -0
- package/source/main.ts +56 -0
- package/source/official_namespaces.ts +35 -0
- package/source/options.ts +4 -0
- package/source/private/cache.ts +228 -0
- package/source/private/to_filename.ts +6 -0
- package/source/private/utils.ts +212 -0
- package/source/private-stuff.ts +6 -0
- package/source/walk_through.ts +73 -0
- package/tsconfig-generated.json +13 -0
|
@@ -0,0 +1,1095 @@
|
|
|
1
|
+
import * as wrap from "wordwrap";
|
|
2
|
+
import { LocalizedText, NodeClass, QualifiedName } from "node-opcua-data-model";
|
|
3
|
+
import { NodeId } from "node-opcua-nodeid";
|
|
4
|
+
import { IBasicSession } from "node-opcua-pseudo-session";
|
|
5
|
+
import { EnumDefinition, ReferenceDescription, StructureDefinition, _enumerationDataChangeTrigger } from "node-opcua-types";
|
|
6
|
+
import { LineFile, lowerFirstLetter } from "node-opcua-utils";
|
|
7
|
+
import { DataType } from "node-opcua-variant";
|
|
8
|
+
import assert from "node-opcua-assert";
|
|
9
|
+
import { ModellingRuleType } from "node-opcua-address-space-base";
|
|
10
|
+
import * as chalk from "chalk";
|
|
11
|
+
import {
|
|
12
|
+
convertNodeIdToDataTypeAsync,
|
|
13
|
+
getBrowseName,
|
|
14
|
+
getIsAbstract,
|
|
15
|
+
getDefinition,
|
|
16
|
+
getDescription,
|
|
17
|
+
getModellingRule,
|
|
18
|
+
getNodeClass,
|
|
19
|
+
getSubtypeNodeId,
|
|
20
|
+
getSubtypeNodeIdIfAny,
|
|
21
|
+
getTypeDefOrBaseType,
|
|
22
|
+
getChildrenOrFolderElements,
|
|
23
|
+
getDataTypeNodeId,
|
|
24
|
+
extractBasicDataType,
|
|
25
|
+
getValueRank
|
|
26
|
+
} from "./private/utils";
|
|
27
|
+
|
|
28
|
+
import { Cache, constructCache, Import, makeTypeNameNew, referenceExtensionObject, RequestedSubSymbol } from "./private/cache";
|
|
29
|
+
import { Options } from "./options";
|
|
30
|
+
import { toFilename } from "./private/to_filename";
|
|
31
|
+
|
|
32
|
+
const wrapText = wrap(0, 50);
|
|
33
|
+
const f2 = (str: string) => str.padEnd(50, "-");
|
|
34
|
+
const f1 = (str: string) => str.padEnd(50, " ");
|
|
35
|
+
const baseExtension = "_Base";
|
|
36
|
+
|
|
37
|
+
export async function convertDataTypeToTypescript(session: IBasicSession, dataTypeId: NodeId): Promise<void> {
|
|
38
|
+
const definition = await getDefinition(session, dataTypeId);
|
|
39
|
+
const browseName = await getBrowseName(session, dataTypeId);
|
|
40
|
+
|
|
41
|
+
const dataTypeTypescriptName = `UA${browseName.name!.toString()}`;
|
|
42
|
+
const f = new LineFile();
|
|
43
|
+
if (definition && definition instanceof StructureDefinition) {
|
|
44
|
+
f.write(`interface ${dataTypeTypescriptName} {`);
|
|
45
|
+
for (const field of definition.fields || []) {
|
|
46
|
+
/** */
|
|
47
|
+
}
|
|
48
|
+
f.write(`}`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// to avoid clashes
|
|
53
|
+
function toJavascritPropertyName(childName: string): string {
|
|
54
|
+
childName = lowerFirstLetter(childName);
|
|
55
|
+
if (childName === "namespaceUri") {
|
|
56
|
+
childName = "$namespaceUri";
|
|
57
|
+
}
|
|
58
|
+
if (childName === "rolePermissions") {
|
|
59
|
+
childName = "$rolePermissions";
|
|
60
|
+
}
|
|
61
|
+
if (childName === "displayName") {
|
|
62
|
+
childName = "$displayName";
|
|
63
|
+
}
|
|
64
|
+
if (childName === "eventNotifier") {
|
|
65
|
+
childName = "$eventNotifier";
|
|
66
|
+
}
|
|
67
|
+
return childName.replace(/</g, "$").replace(/>/g, "$").replace(/ |\./g, "_").replace(/#/g, "_");
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function quotifyIfNecessary(s: string): string {
|
|
71
|
+
if (s.match(/(^[^a-zA-Z])|([^a-zA-Z_0-9])/)) {
|
|
72
|
+
return `"${s}"`;
|
|
73
|
+
}
|
|
74
|
+
if (s === "nodeClass") {
|
|
75
|
+
return `["$nodeClass"]`;
|
|
76
|
+
}
|
|
77
|
+
return s;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async function getCorrepondingJavascriptType2(
|
|
81
|
+
session: IBasicSession,
|
|
82
|
+
nodeId: NodeId,
|
|
83
|
+
dataTypeNodeId: NodeId,
|
|
84
|
+
cache: Cache,
|
|
85
|
+
importCollect?: (t: Import) => void
|
|
86
|
+
): Promise<{ dataType: DataType; jtype: string }> {
|
|
87
|
+
const q = await getCorrepondingJavascriptType(session, dataTypeNodeId, cache, importCollect);
|
|
88
|
+
const valueRank = await getValueRank(session, nodeId);
|
|
89
|
+
return { dataType: q.dataType, jtype: q.jtype + (valueRank >= 1 ? "[]" : "") };
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// eslint-disable-next-line complexity
|
|
93
|
+
async function getCorrepondingJavascriptType(
|
|
94
|
+
session: IBasicSession,
|
|
95
|
+
dataTypeNodeId: NodeId,
|
|
96
|
+
cache: Cache,
|
|
97
|
+
importCollect?: (t: Import) => void
|
|
98
|
+
): Promise<{ dataType: DataType; jtype: string }> {
|
|
99
|
+
const dataType = await convertNodeIdToDataTypeAsync(session, dataTypeNodeId);
|
|
100
|
+
|
|
101
|
+
const referenceBasicType = (name: string): string => {
|
|
102
|
+
const t = { name, namespace: -1, module: "BasicType" };
|
|
103
|
+
importCollect && importCollect(t);
|
|
104
|
+
cache.ensureImported(t);
|
|
105
|
+
return t.name;
|
|
106
|
+
};
|
|
107
|
+
if (dataType === DataType.ExtensionObject) {
|
|
108
|
+
const jtypeImport = await referenceExtensionObject(session, dataTypeNodeId);
|
|
109
|
+
const jtype = jtypeImport.name;
|
|
110
|
+
importCollect && importCollect(jtypeImport);
|
|
111
|
+
return { dataType, jtype: jtype };
|
|
112
|
+
}
|
|
113
|
+
switch (dataType) {
|
|
114
|
+
case DataType.Null:
|
|
115
|
+
return { dataType, jtype: "undefined" };
|
|
116
|
+
case DataType.Boolean:
|
|
117
|
+
return { dataType, jtype: "boolean" };
|
|
118
|
+
case DataType.Byte:
|
|
119
|
+
return { dataType, jtype: referenceBasicType("Byte") };
|
|
120
|
+
case DataType.ByteString:
|
|
121
|
+
return { dataType, jtype: "Buffer" };
|
|
122
|
+
case DataType.DataValue:
|
|
123
|
+
return { dataType, jtype: referenceBasicType("DataValue") };
|
|
124
|
+
case DataType.DateTime:
|
|
125
|
+
return { dataType, jtype: "Date" };
|
|
126
|
+
case DataType.DiagnosticInfo:
|
|
127
|
+
return { dataType, jtype: referenceBasicType("DiagnosticInfo") };
|
|
128
|
+
case DataType.Double:
|
|
129
|
+
return { dataType, jtype: "number" };
|
|
130
|
+
case DataType.Float:
|
|
131
|
+
return { dataType, jtype: "number" };
|
|
132
|
+
case DataType.Guid:
|
|
133
|
+
return { dataType, jtype: referenceBasicType("Guid") };
|
|
134
|
+
case DataType.Int16:
|
|
135
|
+
return { dataType, jtype: referenceBasicType("Int16") };
|
|
136
|
+
case DataType.Int32:
|
|
137
|
+
return { dataType, jtype: referenceBasicType("Int32") };
|
|
138
|
+
case DataType.UInt16:
|
|
139
|
+
return { dataType, jtype: referenceBasicType("UInt16") };
|
|
140
|
+
case DataType.UInt32:
|
|
141
|
+
return { dataType, jtype: referenceBasicType("UInt32") };
|
|
142
|
+
case DataType.UInt64:
|
|
143
|
+
return { dataType, jtype: referenceBasicType("UInt64") };
|
|
144
|
+
case DataType.Int64:
|
|
145
|
+
return { dataType, jtype: referenceBasicType("Int64") };
|
|
146
|
+
case DataType.LocalizedText:
|
|
147
|
+
return { dataType, jtype: referenceBasicType("LocalizedText") };
|
|
148
|
+
case DataType.NodeId:
|
|
149
|
+
return { dataType, jtype: referenceBasicType("NodeId") };
|
|
150
|
+
case DataType.ExpandedNodeId:
|
|
151
|
+
return { dataType, jtype: referenceBasicType("ExpandedNodeId") };
|
|
152
|
+
case DataType.QualifiedName:
|
|
153
|
+
return { dataType, jtype: referenceBasicType("QualifiedName") };
|
|
154
|
+
case DataType.SByte:
|
|
155
|
+
return { dataType, jtype: referenceBasicType("SByte") };
|
|
156
|
+
case DataType.StatusCode:
|
|
157
|
+
return { dataType, jtype: referenceBasicType("StatusCode") };
|
|
158
|
+
case DataType.String:
|
|
159
|
+
return { dataType, jtype: referenceBasicType("UAString") };
|
|
160
|
+
case DataType.Variant:
|
|
161
|
+
return { dataType, jtype: referenceBasicType("Variant") };
|
|
162
|
+
case DataType.XmlElement:
|
|
163
|
+
return { dataType, jtype: referenceBasicType("UAString") };
|
|
164
|
+
default:
|
|
165
|
+
throw new Error("Unsupported " + dataType + " " + DataType[dataType]);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
interface ClassDefinition {
|
|
170
|
+
nodeClass: NodeClass.VariableType | NodeClass.ObjectType;
|
|
171
|
+
browseName: QualifiedName;
|
|
172
|
+
isAbstract: boolean;
|
|
173
|
+
description: LocalizedText;
|
|
174
|
+
//
|
|
175
|
+
superType?: ReferenceDescription;
|
|
176
|
+
baseClassDef?: ClassDefinition | null;
|
|
177
|
+
//
|
|
178
|
+
children: ReferenceDescription[];
|
|
179
|
+
members: ClassMember[];
|
|
180
|
+
//
|
|
181
|
+
interfaceName: Import;
|
|
182
|
+
baseInterfaceName: Import | null;
|
|
183
|
+
// for variables:!
|
|
184
|
+
dataTypeNodeId: NodeId | null;
|
|
185
|
+
dataType: DataType;
|
|
186
|
+
dataTypeName: string;
|
|
187
|
+
dataTypeImport?: Import[];
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export function makeTypeName2(nodeClass: NodeClass, browseName: QualifiedName, suffix?: string): Import {
|
|
191
|
+
assert(browseName);
|
|
192
|
+
if (nodeClass === NodeClass.Method) {
|
|
193
|
+
return { name: "UAMethod", namespace: 0, module: "UAMethod" };
|
|
194
|
+
}
|
|
195
|
+
return makeTypeNameNew(nodeClass, null, browseName, suffix);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export async function extractClassDefinition(session: IBasicSession, nodeId: NodeId, cache: Cache): Promise<ClassDefinition> {
|
|
199
|
+
const extraImports: Import[] = [];
|
|
200
|
+
const _c = (cache as any).classDefCache || {};
|
|
201
|
+
(cache as any).classDefCache = _c;
|
|
202
|
+
|
|
203
|
+
let classDef: ClassDefinition | null = _c[nodeId.toString()];
|
|
204
|
+
if (classDef) {
|
|
205
|
+
return classDef;
|
|
206
|
+
}
|
|
207
|
+
const nodeClass = await getNodeClass(session, nodeId);
|
|
208
|
+
if (nodeClass !== NodeClass.VariableType && nodeClass !== NodeClass.ObjectType) {
|
|
209
|
+
throw new Error("Invalid nodeClass " + NodeClass[nodeClass] + " nodeId " + nodeId.toString());
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const browseName = await getBrowseName(session, nodeId);
|
|
213
|
+
const isAbstract = await getIsAbstract(session, nodeId);
|
|
214
|
+
const superType = (await getSubtypeNodeIdIfAny(session, nodeId)) || undefined;
|
|
215
|
+
|
|
216
|
+
const dataTypeNodeId = await getDataTypeNodeId(session, nodeId);
|
|
217
|
+
let dataTypeName = "";
|
|
218
|
+
let dataType: DataType = DataType.Null;
|
|
219
|
+
let dataTypeImport: Import[] | undefined = undefined;
|
|
220
|
+
if (nodeClass === NodeClass.VariableType) {
|
|
221
|
+
dataType = await extractBasicDataType(session, dataTypeNodeId!);
|
|
222
|
+
const importCollector = (i: Import) => {
|
|
223
|
+
extraImports.push(i);
|
|
224
|
+
cache.ensureImported(i);
|
|
225
|
+
};
|
|
226
|
+
const { jtype } = await getCorrepondingJavascriptType2(session, nodeId, dataTypeNodeId!, cache, importCollector);
|
|
227
|
+
dataTypeName = jtype; // with decoration
|
|
228
|
+
if (!dataTypeNodeId?.isEmpty()) {
|
|
229
|
+
// "DT" + (await getBrowseName(session, dataTypeNodeId!))?.name! || "";
|
|
230
|
+
// const bn = await getBrowseName(session, dataTypeNodeId!);
|
|
231
|
+
dataTypeImport = extraImports; // makeTypeName2(NodeClass.DataType, bn);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const baseClassDef = !superType ? null : await extractClassDefinition(session, superType.nodeId, cache);
|
|
236
|
+
|
|
237
|
+
const description = await getDescription(session, nodeId);
|
|
238
|
+
// const definition = await getDefinition(session, nodeId);
|
|
239
|
+
|
|
240
|
+
const interfaceName: Import = makeTypeName2(nodeClass, browseName);
|
|
241
|
+
|
|
242
|
+
const baseInterfaceName: Import | null = !superType ? null : makeTypeName2(nodeClass, superType.browseName);
|
|
243
|
+
|
|
244
|
+
// extract member
|
|
245
|
+
const children = await getChildrenOrFolderElements(session, nodeId);
|
|
246
|
+
|
|
247
|
+
const members: ClassMember[] = [];
|
|
248
|
+
|
|
249
|
+
classDef = {
|
|
250
|
+
nodeClass,
|
|
251
|
+
browseName,
|
|
252
|
+
isAbstract,
|
|
253
|
+
|
|
254
|
+
dataType,
|
|
255
|
+
dataTypeNodeId,
|
|
256
|
+
dataTypeName,
|
|
257
|
+
dataTypeImport,
|
|
258
|
+
|
|
259
|
+
superType,
|
|
260
|
+
|
|
261
|
+
baseClassDef,
|
|
262
|
+
|
|
263
|
+
description,
|
|
264
|
+
// definition,
|
|
265
|
+
children,
|
|
266
|
+
members,
|
|
267
|
+
|
|
268
|
+
interfaceName,
|
|
269
|
+
baseInterfaceName
|
|
270
|
+
};
|
|
271
|
+
_c[nodeId.toString()] = classDef;
|
|
272
|
+
|
|
273
|
+
for (const child of children) {
|
|
274
|
+
const c = await extractClassMemberDef(session, child.nodeId, classDef, cache);
|
|
275
|
+
classDef.members.push(c);
|
|
276
|
+
}
|
|
277
|
+
return classDef;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
interface ClassMemberBasic {
|
|
281
|
+
name: string;
|
|
282
|
+
childType: Import;
|
|
283
|
+
isOptional: boolean;
|
|
284
|
+
modellingRule: ModellingRuleType | null;
|
|
285
|
+
description: LocalizedText;
|
|
286
|
+
suffix?: string;
|
|
287
|
+
suffixInstantiate?: string;
|
|
288
|
+
chevrons: any;
|
|
289
|
+
typeToReference: Import[];
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
interface ClassMember extends ClassMemberBasic {
|
|
293
|
+
/**
|
|
294
|
+
* the OPCUA name of the class member
|
|
295
|
+
*/
|
|
296
|
+
browseName: QualifiedName;
|
|
297
|
+
nodeClass: NodeClass.Object | NodeClass.Method | NodeClass.Variable;
|
|
298
|
+
/**
|
|
299
|
+
* the Typescript name of the class Member
|
|
300
|
+
*/
|
|
301
|
+
name: string;
|
|
302
|
+
|
|
303
|
+
modellingRule: ModellingRuleType | null;
|
|
304
|
+
isOptional: boolean;
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* class Def
|
|
308
|
+
*/
|
|
309
|
+
classDef: ClassDefinition | null;
|
|
310
|
+
|
|
311
|
+
description: LocalizedText;
|
|
312
|
+
//
|
|
313
|
+
typeDefinition: ReferenceDescription;
|
|
314
|
+
|
|
315
|
+
childType: Import;
|
|
316
|
+
|
|
317
|
+
children: ReferenceDescription[];
|
|
318
|
+
children2: ClassMemberBasic[];
|
|
319
|
+
|
|
320
|
+
// for variables:
|
|
321
|
+
dataTypeNodeId?: NodeId | null;
|
|
322
|
+
dataType?: DataType;
|
|
323
|
+
jtype?: string;
|
|
324
|
+
suffix?: string;
|
|
325
|
+
suffix2?: string;
|
|
326
|
+
suffix3?: string;
|
|
327
|
+
innerClass?: Import | null;
|
|
328
|
+
childBase?: Import | null;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
interface Classified {
|
|
332
|
+
Mandatory: ReferenceDescription[];
|
|
333
|
+
Optional: ReferenceDescription[];
|
|
334
|
+
MandatoryPlaceholder: ReferenceDescription[];
|
|
335
|
+
OptionalPlaceholder: ReferenceDescription[];
|
|
336
|
+
ExposesItsArray: ReferenceDescription[];
|
|
337
|
+
}
|
|
338
|
+
async function classify(session: IBasicSession, refs: ReferenceDescription[]): Promise<Classified> {
|
|
339
|
+
const r: Classified = {
|
|
340
|
+
Mandatory: [],
|
|
341
|
+
Optional: [],
|
|
342
|
+
MandatoryPlaceholder: [],
|
|
343
|
+
OptionalPlaceholder: [],
|
|
344
|
+
ExposesItsArray: []
|
|
345
|
+
};
|
|
346
|
+
|
|
347
|
+
for (const mm of refs) {
|
|
348
|
+
const modellingRule = await getModellingRule(session, mm.nodeId);
|
|
349
|
+
if (modellingRule) {
|
|
350
|
+
r[modellingRule] = r[modellingRule] || [];
|
|
351
|
+
r[modellingRule].push(mm);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
return r;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
async function extractAllMembers(session: IBasicSession, classDef: ClassDefinition, cache: Cache) {
|
|
358
|
+
const m = [...classDef.children];
|
|
359
|
+
let s = classDef;
|
|
360
|
+
while (s.baseClassDef) {
|
|
361
|
+
s = s.baseClassDef;
|
|
362
|
+
// start from top most class
|
|
363
|
+
// do not overwrite most top member definition, so we get mandatory stuff
|
|
364
|
+
for (const mm of s.children) {
|
|
365
|
+
const found = m.findIndex((r) => r.browseName.toString() === mm.browseName.toString());
|
|
366
|
+
if (found < 0) {
|
|
367
|
+
m.push(mm);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
// now separate mandatory and optionals
|
|
372
|
+
const members = await classify(session, m);
|
|
373
|
+
return members;
|
|
374
|
+
}
|
|
375
|
+
export async function findClassMember(
|
|
376
|
+
session: IBasicSession,
|
|
377
|
+
browseName: QualifiedName,
|
|
378
|
+
baseParentDef: ClassDefinition,
|
|
379
|
+
cache: Cache
|
|
380
|
+
): Promise<ClassMember | null> {
|
|
381
|
+
const str = browseName.toString();
|
|
382
|
+
const r = baseParentDef.children.find((a) => a.browseName.toString() === str);
|
|
383
|
+
if (r) {
|
|
384
|
+
const d = await extractClassMemberDef(session, r!.nodeId, baseParentDef, cache);
|
|
385
|
+
return d;
|
|
386
|
+
}
|
|
387
|
+
const baseBaseParentDef =
|
|
388
|
+
baseParentDef.superType && (await extractClassDefinition(session, baseParentDef.superType!.nodeId, cache));
|
|
389
|
+
if (!baseBaseParentDef) {
|
|
390
|
+
return null;
|
|
391
|
+
}
|
|
392
|
+
return await findClassMember(session, browseName, baseBaseParentDef, cache);
|
|
393
|
+
}
|
|
394
|
+
function hasNewMaterial(referenceMembers: ReferenceDescription[], instanceMembers: ReferenceDescription[]) {
|
|
395
|
+
const ref = referenceMembers.map((x) => x.browseName.toString()).sort();
|
|
396
|
+
const instance = instanceMembers.map((x) => x.browseName.toString()).sort();
|
|
397
|
+
for (const n of instance) {
|
|
398
|
+
if (ref.findIndex((x) => x === n) < 0) {
|
|
399
|
+
return true;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
return false;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// we should expose an inner definition if
|
|
406
|
+
// - at least one mandatory in instnace doesn't exist in main.Mandatory
|
|
407
|
+
// - at least one optional in instnace doesn't exist in main.Mandatory
|
|
408
|
+
export function checkIfShouldExposeInnerDefinition(main: Classified, instance: Classified): boolean {
|
|
409
|
+
const hasNewStuff = hasNewMaterial(main.Mandatory, instance.Mandatory) || hasNewMaterial(main.Optional, instance.Optional);
|
|
410
|
+
return hasNewStuff;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
function dump1(a: Classified) {
|
|
414
|
+
console.log(
|
|
415
|
+
"Mandatory = ",
|
|
416
|
+
a.Mandatory.map((x) => x.browseName.toString())
|
|
417
|
+
.sort()
|
|
418
|
+
.join(" ")
|
|
419
|
+
);
|
|
420
|
+
console.log(
|
|
421
|
+
"Optional = ",
|
|
422
|
+
a.Optional.map((x) => x.browseName.toString())
|
|
423
|
+
.sort()
|
|
424
|
+
.join(" ")
|
|
425
|
+
);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
async function _extractLocalMembers(session: IBasicSession, classMember: ClassMember, cache: Cache): Promise<ClassMemberBasic[]> {
|
|
429
|
+
const children2: ClassMemberBasic[] = [];
|
|
430
|
+
for (const child of classMember.children) {
|
|
431
|
+
const nodeId = child.nodeId;
|
|
432
|
+
const browseName = await getBrowseName(session, nodeId);
|
|
433
|
+
const name = toJavascritPropertyName(browseName.name!);
|
|
434
|
+
if (name === "cartesianCoordinates") {
|
|
435
|
+
// debugger;
|
|
436
|
+
}
|
|
437
|
+
const description = await getDescription(session, nodeId);
|
|
438
|
+
const modellingRule = await getModellingRule(session, nodeId);
|
|
439
|
+
const isOptional = modellingRule === "Optional";
|
|
440
|
+
|
|
441
|
+
const typeDefinition = await getTypeDefOrBaseType(session, nodeId);
|
|
442
|
+
|
|
443
|
+
const childInBase: ClassMember | undefined = classMember.classDef?.members.find(
|
|
444
|
+
(a) => a.browseName.toString() === browseName.toString()
|
|
445
|
+
);
|
|
446
|
+
|
|
447
|
+
let childType: Import;
|
|
448
|
+
if (childInBase) {
|
|
449
|
+
childType = childInBase.childType;
|
|
450
|
+
} else {
|
|
451
|
+
const d = typeDefinition.nodeId.isEmpty() ? null : await extractClassDefinition(session, typeDefinition.nodeId, cache);
|
|
452
|
+
childType = d?.interfaceName || { name: "UAMethod", module: "BasicType", namespace: -1 };
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// may be childType is already
|
|
456
|
+
const { suffix, suffixInstantiate, typeToReference, chevrons } = await extractVariableExtra(
|
|
457
|
+
session,
|
|
458
|
+
child.nodeId,
|
|
459
|
+
cache,
|
|
460
|
+
classMember
|
|
461
|
+
);
|
|
462
|
+
|
|
463
|
+
const c: ClassMemberBasic = {
|
|
464
|
+
childType,
|
|
465
|
+
description,
|
|
466
|
+
isOptional,
|
|
467
|
+
modellingRule,
|
|
468
|
+
name,
|
|
469
|
+
suffix,
|
|
470
|
+
suffixInstantiate,
|
|
471
|
+
typeToReference,
|
|
472
|
+
chevrons
|
|
473
|
+
};
|
|
474
|
+
children2.push(c);
|
|
475
|
+
}
|
|
476
|
+
return children2;
|
|
477
|
+
}
|
|
478
|
+
function isUnspecifiedDataType(dataType?: DataType): boolean {
|
|
479
|
+
return dataType === DataType.Null || dataType === DataType.Variant;
|
|
480
|
+
}
|
|
481
|
+
async function extractVariableExtra(session: IBasicSession, nodeId: NodeId, cache: Cache, classMember: ClassMember) {
|
|
482
|
+
const typeToReference: Import[] = [];
|
|
483
|
+
const importCollector = (i: Import) => {
|
|
484
|
+
typeToReference.push(i);
|
|
485
|
+
cache.ensureImported(i);
|
|
486
|
+
};
|
|
487
|
+
const nodeClass = await getNodeClass(session, nodeId);
|
|
488
|
+
if (nodeClass === NodeClass.Variable) {
|
|
489
|
+
const dataTypeNodeId = await getDataTypeNodeId(session, nodeId);
|
|
490
|
+
|
|
491
|
+
const { dataType, jtype } = await getCorrepondingJavascriptType2(session, nodeId, dataTypeNodeId!, cache, importCollector);
|
|
492
|
+
|
|
493
|
+
cache && cache.referenceBasicType("DataType");
|
|
494
|
+
importCollector({ name: "DataType", namespace: -1, module: "BasicType" });
|
|
495
|
+
|
|
496
|
+
const t = isUnspecifiedDataType(classMember.classDef?.dataType);
|
|
497
|
+
const suffix =
|
|
498
|
+
jtype === "undefined" || isUnspecifiedDataType(dataType)
|
|
499
|
+
? `<any, any>`
|
|
500
|
+
: `<${jtype}${t ? `, /*c*/DataType.${DataType[dataType]}` : ""}>`;
|
|
501
|
+
|
|
502
|
+
// const suffix2 = `<T${t ? `, /*a*/DT extends DataType` : ""}>`;
|
|
503
|
+
const suffix3 = `<T${t ? `, /*b*/DT` : ""}>`;
|
|
504
|
+
|
|
505
|
+
const typeDef = await getTypeDefOrBaseType(session, nodeId);
|
|
506
|
+
const typeDefCD = await extractClassDefinition(session, typeDef.nodeId!, cache);
|
|
507
|
+
const chevrons = calculateChevrons(typeDefCD, { dataType });
|
|
508
|
+
const suffix2 = chevrons.chevronsDef;
|
|
509
|
+
|
|
510
|
+
/** Suffix for instantiation
|
|
511
|
+
* if typeDef.dataType is not null, then we need to add a type argument
|
|
512
|
+
*/
|
|
513
|
+
let suffixInstantiate = "";
|
|
514
|
+
if (isUnspecifiedDataType(dataType)) {
|
|
515
|
+
if (!isUnspecifiedDataType(typeDefCD.dataType)) {
|
|
516
|
+
suffixInstantiate = "<any>";
|
|
517
|
+
} else {
|
|
518
|
+
suffixInstantiate = "<any, any>";
|
|
519
|
+
}
|
|
520
|
+
} else {
|
|
521
|
+
if (!isUnspecifiedDataType(typeDefCD.dataType)) {
|
|
522
|
+
suffixInstantiate = `<${jtype}>`;
|
|
523
|
+
} else {
|
|
524
|
+
suffixInstantiate = `<${jtype}, /*z*/DataType.${DataType[dataType]}>`;
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
return { suffixInstantiate, suffix, suffix2, suffix3, dataTypeNodeId, dataType, jtype, typeToReference, chevrons };
|
|
529
|
+
}
|
|
530
|
+
return { suffix: "", typeToReference };
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
interface ClassDefinitionB {
|
|
534
|
+
superType?: {
|
|
535
|
+
nodeId: NodeId;
|
|
536
|
+
};
|
|
537
|
+
interfaceName: Import;
|
|
538
|
+
}
|
|
539
|
+
// eslint-disable-next-line max-statements
|
|
540
|
+
export async function extractClassMemberDef(
|
|
541
|
+
session: IBasicSession,
|
|
542
|
+
nodeId: NodeId,
|
|
543
|
+
parentDef: ClassDefinitionB,
|
|
544
|
+
cache: Cache
|
|
545
|
+
): Promise<ClassMember> {
|
|
546
|
+
const nodeClass = await getNodeClass(session, nodeId);
|
|
547
|
+
const browseName = await getBrowseName(session, nodeId);
|
|
548
|
+
const name = toJavascritPropertyName(browseName.name!);
|
|
549
|
+
|
|
550
|
+
if (nodeClass !== NodeClass.Method && nodeClass !== NodeClass.Object && nodeClass !== NodeClass.Variable) {
|
|
551
|
+
throw new Error("Invalid property " + NodeClass[nodeClass] + " " + browseName?.toString() + " " + nodeId.toString());
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
const description = await getDescription(session, nodeId);
|
|
555
|
+
const children = await getChildrenOrFolderElements(session, nodeId);
|
|
556
|
+
|
|
557
|
+
const modellingRule = await getModellingRule(session, nodeId);
|
|
558
|
+
const isOptional = modellingRule === "Optional";
|
|
559
|
+
|
|
560
|
+
const typeDefinition = await getTypeDefOrBaseType(session, nodeId);
|
|
561
|
+
|
|
562
|
+
if (!typeDefinition.browseName) {
|
|
563
|
+
console.log("cannot find typeDefinition for ", browseName.toString(), "( is the namespace loaded ?)");
|
|
564
|
+
}
|
|
565
|
+
let childType = makeTypeName2(nodeClass, typeDefinition.browseName);
|
|
566
|
+
|
|
567
|
+
const classDef = typeDefinition.nodeId.isEmpty() ? null : await extractClassDefinition(session, typeDefinition.nodeId, cache);
|
|
568
|
+
|
|
569
|
+
let innerClass: Import | null = null;
|
|
570
|
+
let childBase: Import | null = null;
|
|
571
|
+
|
|
572
|
+
let shouldExposeInnerDefinition = false;
|
|
573
|
+
// find member exposed by type definition
|
|
574
|
+
const baseParentDef = parentDef.superType && (await extractClassDefinition(session, parentDef.superType!.nodeId, cache));
|
|
575
|
+
if (classDef && baseParentDef) {
|
|
576
|
+
const chevrons1 = calculateChevrons(classDef, baseParentDef);
|
|
577
|
+
assert(!innerClass);
|
|
578
|
+
|
|
579
|
+
// let's extract the member that are theorically defined in the member
|
|
580
|
+
const membersReference = await extractAllMembers(session, classDef, cache);
|
|
581
|
+
// find member exposed by this member
|
|
582
|
+
const membersInstance: Classified = await classify(session, children);
|
|
583
|
+
|
|
584
|
+
// if (name==="powerup") {
|
|
585
|
+
// dump1(membersReference);
|
|
586
|
+
// dump1(membersInstance);
|
|
587
|
+
// await extractAllMembers(session, classDef, cache);
|
|
588
|
+
// }
|
|
589
|
+
|
|
590
|
+
shouldExposeInnerDefinition = checkIfShouldExposeInnerDefinition(membersReference, membersInstance);
|
|
591
|
+
|
|
592
|
+
const sameMemberInBaseClass = baseParentDef && (await findClassMember(session, browseName, baseParentDef, cache));
|
|
593
|
+
|
|
594
|
+
if (shouldExposeInnerDefinition) {
|
|
595
|
+
if (sameMemberInBaseClass) {
|
|
596
|
+
innerClass = {
|
|
597
|
+
module: parentDef.interfaceName.module,
|
|
598
|
+
name: parentDef.interfaceName.name + "_" + name,
|
|
599
|
+
namespace: nodeId.namespace
|
|
600
|
+
};
|
|
601
|
+
childBase = sameMemberInBaseClass.childType;
|
|
602
|
+
childType = innerClass;
|
|
603
|
+
} else {
|
|
604
|
+
innerClass = {
|
|
605
|
+
module: parentDef.interfaceName.module,
|
|
606
|
+
name: parentDef.interfaceName.name + "_" + name,
|
|
607
|
+
namespace: nodeId.namespace
|
|
608
|
+
};
|
|
609
|
+
childBase = childType;
|
|
610
|
+
childType = innerClass;
|
|
611
|
+
}
|
|
612
|
+
assert(innerClass);
|
|
613
|
+
assert(childBase);
|
|
614
|
+
} else {
|
|
615
|
+
// may not expose definition but we may want to used the augment typescript class defined in the definition
|
|
616
|
+
// we don't need to create an inner class ...
|
|
617
|
+
childType = sameMemberInBaseClass ? sameMemberInBaseClass?.childType : childType;
|
|
618
|
+
assert(!innerClass);
|
|
619
|
+
assert(!childBase);
|
|
620
|
+
}
|
|
621
|
+
} else {
|
|
622
|
+
// the lass member has no HasTypeDefinition reference
|
|
623
|
+
// may be is a method
|
|
624
|
+
assert(!innerClass);
|
|
625
|
+
assert(!childBase);
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
let classMember: ClassMember = {
|
|
629
|
+
name,
|
|
630
|
+
nodeClass,
|
|
631
|
+
browseName,
|
|
632
|
+
modellingRule,
|
|
633
|
+
isOptional,
|
|
634
|
+
classDef,
|
|
635
|
+
description,
|
|
636
|
+
typeDefinition,
|
|
637
|
+
childType,
|
|
638
|
+
suffix2: "",
|
|
639
|
+
suffix: "",
|
|
640
|
+
suffix3: "",
|
|
641
|
+
suffixInstantiate: "",
|
|
642
|
+
children,
|
|
643
|
+
children2: [],
|
|
644
|
+
|
|
645
|
+
typeToReference: [],
|
|
646
|
+
//
|
|
647
|
+
innerClass,
|
|
648
|
+
childBase,
|
|
649
|
+
chevrons: null
|
|
650
|
+
};
|
|
651
|
+
classMember.children2 = await _extractLocalMembers(session, classMember, cache);
|
|
652
|
+
if (nodeClass === NodeClass.Variable) {
|
|
653
|
+
const extra = await extractVariableExtra(session, nodeId, cache, classMember);
|
|
654
|
+
classMember = {
|
|
655
|
+
...classMember,
|
|
656
|
+
...extra
|
|
657
|
+
};
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
return classMember;
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
async function preDumpChildren(session: IBasicSession, padding: string, classDef: ClassDefinition, f: LineFile, cache: Cache) {
|
|
664
|
+
f = f || new LineFile();
|
|
665
|
+
|
|
666
|
+
for (const memberDef of classDef.members) {
|
|
667
|
+
const { innerClass, suffix2, suffix3, nodeClass, childBase, chevrons } = memberDef;
|
|
668
|
+
if (!innerClass || !childBase) {
|
|
669
|
+
continue; // no need to expose inner class
|
|
670
|
+
}
|
|
671
|
+
cache.ensureImported(childBase);
|
|
672
|
+
if (innerClass.name === "UAPubSubDiagnostics_counters" || innerClass.name === "UAProgramStateMachine_currentState") {
|
|
673
|
+
// debugger;
|
|
674
|
+
}
|
|
675
|
+
const baseStuff = getBaseClassWithOmit2(memberDef);
|
|
676
|
+
|
|
677
|
+
//f.write(`export interface ${innerClass.name}${suffix2} extends ${childBase.name}${suffix3} { // ${NodeClass[nodeClass]}`);
|
|
678
|
+
f.write(`export interface ${innerClass.name}${suffix2} extends ${baseStuff} { // ${NodeClass[nodeClass]}`);
|
|
679
|
+
await dumpChildren(session, padding + " ", memberDef.children2, f, cache);
|
|
680
|
+
f.write("}");
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
function dumpChildren(session: IBasicSession, padding: string, children: ClassMemberBasic[], f: LineFile, cache: Cache): void {
|
|
685
|
+
f = f || new LineFile();
|
|
686
|
+
|
|
687
|
+
for (const def of children) {
|
|
688
|
+
const { suffixInstantiate, name, childType, modellingRule, isOptional, description } = def;
|
|
689
|
+
def.typeToReference.forEach((t) => cache.ensureImported(t));
|
|
690
|
+
|
|
691
|
+
if (modellingRule === "MandatoryPlaceholder" || modellingRule === "OptionalPlaceholder") continue;
|
|
692
|
+
cache.ensureImported(childType);
|
|
693
|
+
const adjustedName = toJavascritPropertyName(name);
|
|
694
|
+
if (description.text) {
|
|
695
|
+
f.write(`${padding}/**`);
|
|
696
|
+
f.write(`${padding} * ${name || ""}`);
|
|
697
|
+
f.write(toComment(`${padding} * `, description.text || ""));
|
|
698
|
+
f.write(`${padding} */`);
|
|
699
|
+
}
|
|
700
|
+
f.write(
|
|
701
|
+
`${padding}${quotifyIfNecessary(adjustedName)}${isOptional ? "?" : ""}: ${childType.name}${suffixInstantiate || ""};`
|
|
702
|
+
);
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
// now from other namespace
|
|
706
|
+
const getSubSymbolList = (s: RequestedSubSymbol) => {
|
|
707
|
+
const subSymbolList: string[] = [];
|
|
708
|
+
for (const [subSymbol, count] of Object.entries(s.subSymbols)) {
|
|
709
|
+
subSymbolList.push(subSymbol);
|
|
710
|
+
}
|
|
711
|
+
return subSymbolList;
|
|
712
|
+
};
|
|
713
|
+
export function findUsedImport(namespaceIndex: number, cache: Cache): string[] {
|
|
714
|
+
const usedImport: string[] = [];
|
|
715
|
+
// from standard types
|
|
716
|
+
for (const imp of Object.keys(cache.imports)) {
|
|
717
|
+
const symbolToImport = Object.keys(cache.imports[imp]).filter((f) =>
|
|
718
|
+
Object.prototype.hasOwnProperty.call(cache.requestedBasicTypes, f)
|
|
719
|
+
);
|
|
720
|
+
if (symbolToImport.length == 0) {
|
|
721
|
+
continue;
|
|
722
|
+
}
|
|
723
|
+
usedImport.push(imp);
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
// from namespace
|
|
727
|
+
for (let ns = 0; ns < cache.requestedSymbols.namespace.length; ns++) {
|
|
728
|
+
if (ns === namespaceIndex) {
|
|
729
|
+
continue; // avoid self reference
|
|
730
|
+
}
|
|
731
|
+
const module = cache.namespace[ns].module;
|
|
732
|
+
if (cache.requestedSymbols.namespace[ns] && Object.values(cache.requestedSymbols.namespace[ns]).length > 0) {
|
|
733
|
+
usedImport.push(module);
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
return usedImport;
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
function dumpUsedExport(currentType: string, namespaceIndex: number, cache: Cache, f?: LineFile): string {
|
|
741
|
+
f = f || new LineFile();
|
|
742
|
+
|
|
743
|
+
for (const imp of Object.keys(cache.imports)) {
|
|
744
|
+
const symbolToImport = Object.keys(cache.imports[imp]).filter(
|
|
745
|
+
(f) => f !== currentType && Object.prototype.hasOwnProperty.call(cache.requestedBasicTypes, f)
|
|
746
|
+
);
|
|
747
|
+
if (symbolToImport.length == 0) {
|
|
748
|
+
continue;
|
|
749
|
+
}
|
|
750
|
+
f.write(`import { ${symbolToImport.join(", ")} } from "${imp}"`);
|
|
751
|
+
}
|
|
752
|
+
for (let ns = 0; ns < cache.requestedSymbols.namespace.length; ns++) {
|
|
753
|
+
const n = cache.namespace[ns];
|
|
754
|
+
const sourceFolder = n.sourceFolder;
|
|
755
|
+
const module = n.module;
|
|
756
|
+
if (ns === namespaceIndex) {
|
|
757
|
+
// include individuals stuff
|
|
758
|
+
for (const [symbol, s] of Object.entries(cache.requestedSymbols.namespace[ns].symbols).filter(
|
|
759
|
+
(a) => a[0] !== currentType
|
|
760
|
+
)) {
|
|
761
|
+
const filename = toFilename(symbol);
|
|
762
|
+
const subSymbolList = getSubSymbolList(s);
|
|
763
|
+
f.write(`import { ${subSymbolList.join(", ")} } from "./${filename}"`);
|
|
764
|
+
}
|
|
765
|
+
} else {
|
|
766
|
+
if (cache.requestedSymbols.namespace[ns]) {
|
|
767
|
+
for (const [symbol, s] of Object.entries(cache.requestedSymbols.namespace[ns].symbols)) {
|
|
768
|
+
const subSymbolList = getSubSymbolList(s);
|
|
769
|
+
const filename = toFilename(symbol);
|
|
770
|
+
f.write(`import { ${subSymbolList.join(", ")} } from "${module}/source/${filename}"`);
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
return f.toString();
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
function toComment(prefix: string, description: string) {
|
|
779
|
+
const d = wrapText(description);
|
|
780
|
+
return d
|
|
781
|
+
.split("\n")
|
|
782
|
+
.map((x) => prefix + x)
|
|
783
|
+
.join("\n");
|
|
784
|
+
}
|
|
785
|
+
export type Type = "enum" | "basic" | "structure" | "ua";
|
|
786
|
+
// eslint-disable-next-line max-statements
|
|
787
|
+
export async function _exportDataTypeToTypescript(
|
|
788
|
+
session: IBasicSession,
|
|
789
|
+
nodeId: NodeId,
|
|
790
|
+
cache: Cache,
|
|
791
|
+
f?: LineFile
|
|
792
|
+
): Promise<{ type: Type; content: string; typeName: string }> {
|
|
793
|
+
f = f || new LineFile();
|
|
794
|
+
|
|
795
|
+
const importCollector = (i: Import) => {
|
|
796
|
+
cache.ensureImported(i);
|
|
797
|
+
};
|
|
798
|
+
const nodeClass = NodeClass.DataType;
|
|
799
|
+
const description = await getDescription(session, nodeId);
|
|
800
|
+
const definition = await getDefinition(session, nodeId);
|
|
801
|
+
const browseName = await getBrowseName(session, nodeId);
|
|
802
|
+
const isAbstract = await getIsAbstract(session, nodeId);
|
|
803
|
+
|
|
804
|
+
const interfaceImport: Import = makeTypeNameNew(nodeClass, definition, browseName);
|
|
805
|
+
const interfaceName = interfaceImport.name;
|
|
806
|
+
|
|
807
|
+
const superType = await getSubtypeNodeId(session, nodeId);
|
|
808
|
+
const baseInterfaceImport: Import = makeTypeNameNew(nodeClass, definition, superType.browseName);
|
|
809
|
+
|
|
810
|
+
cache.ensureImported(baseInterfaceImport);
|
|
811
|
+
|
|
812
|
+
const baseInterfaceName = baseInterfaceImport.name;
|
|
813
|
+
// f.write(superType.toString());
|
|
814
|
+
f.write(`/**`);
|
|
815
|
+
if (description.text) {
|
|
816
|
+
f.write(toComment(" * ", description.text || ""));
|
|
817
|
+
f.write(` *`);
|
|
818
|
+
}
|
|
819
|
+
f.write(` * | |${f1(" ")}|`);
|
|
820
|
+
f.write(` * |-----------|${f2("-")}|`);
|
|
821
|
+
f.write(` * | namespace |${f1(cache.namespace[nodeId.namespace].namespaceUri)}|`);
|
|
822
|
+
f.write(` * | nodeClass |${f1(NodeClass[nodeClass])}|`);
|
|
823
|
+
f.write(` * | name |${f1(browseName.toString())}|`);
|
|
824
|
+
f.write(` * | isAbstract|${f1(isAbstract.toString())}|`);
|
|
825
|
+
f.write(` */`);
|
|
826
|
+
|
|
827
|
+
let type: "basic" | "structure" | "enum" | "ua" = "basic";
|
|
828
|
+
if (definition instanceof EnumDefinition) {
|
|
829
|
+
type = "enum";
|
|
830
|
+
f.write(`export enum ${interfaceName} {`);
|
|
831
|
+
for (const field of definition.fields!) {
|
|
832
|
+
if (field.description.text) {
|
|
833
|
+
f.write(` /**`);
|
|
834
|
+
f.write(toComment(" * ", field.description.text || ""));
|
|
835
|
+
f.write(` */`);
|
|
836
|
+
}
|
|
837
|
+
f.write(` ${quotifyIfNecessary(field.name!)} = ${field.value[1]},`);
|
|
838
|
+
}
|
|
839
|
+
f.write(`}`);
|
|
840
|
+
} else if (definition instanceof StructureDefinition) {
|
|
841
|
+
type = "structure";
|
|
842
|
+
if (interfaceName === "DTStructure") {
|
|
843
|
+
f.write(`export interface ${interfaceName} {`);
|
|
844
|
+
} else {
|
|
845
|
+
f.write(`export interface ${interfaceName} extends ${baseInterfaceName} {`);
|
|
846
|
+
}
|
|
847
|
+
for (const field of definition.fields!) {
|
|
848
|
+
const fieldName = toJavascritPropertyName(field.name!);
|
|
849
|
+
// special case ! fieldName=
|
|
850
|
+
if (field.description.text) {
|
|
851
|
+
f.write(`/** ${field.description.text}*/`);
|
|
852
|
+
}
|
|
853
|
+
let ar = "";
|
|
854
|
+
if (field.valueRank >= 1) {
|
|
855
|
+
ar = "[]";
|
|
856
|
+
}
|
|
857
|
+
const { dataType, jtype } = await getCorrepondingJavascriptType(session, field.dataType, cache, importCollector);
|
|
858
|
+
f.write(` ${quotifyIfNecessary(fieldName)}: ${jtype}${ar}; // ${DataType[dataType]} ${field.dataType.toString()}`);
|
|
859
|
+
}
|
|
860
|
+
f.write(`}`);
|
|
861
|
+
} else {
|
|
862
|
+
type = "basic";
|
|
863
|
+
f.write(`// NO DEFINITION`);
|
|
864
|
+
f.write(`export interface ${interfaceName} extends ${baseInterfaceName} {`);
|
|
865
|
+
f.write(`}`);
|
|
866
|
+
// throw new Error("Invalid " + definition?.constructor.name);
|
|
867
|
+
}
|
|
868
|
+
return { type, content: f.toString(), typeName: interfaceName };
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
function calculateChevrons(classDef: ClassDefinition, classDefDerived?: { dataType: DataType }) {
|
|
872
|
+
const { nodeClass, dataType, dataTypeName } = classDef;
|
|
873
|
+
let chevronsDef = "";
|
|
874
|
+
let chevronsUse = "";
|
|
875
|
+
let chevronsExtend = "";
|
|
876
|
+
if (nodeClass !== NodeClass.VariableType) {
|
|
877
|
+
return { chevronsDef, chevronsUse, chevronsExtend };
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
if (!isUnspecifiedDataType(dataType)) {
|
|
881
|
+
chevronsDef = `<T extends ${dataTypeName}/*j*/>`;
|
|
882
|
+
chevronsUse = "<T/*k*/>";
|
|
883
|
+
if (classDefDerived) {
|
|
884
|
+
if (isUnspecifiedDataType(classDefDerived.dataType)) {
|
|
885
|
+
chevronsExtend = `<T, /*i*/DataType.${DataType[dataType]}>`;
|
|
886
|
+
} else {
|
|
887
|
+
chevronsExtend = `<T/*h*/>`;
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
} else {
|
|
891
|
+
// classDef.dataType === DataType.Null
|
|
892
|
+
chevronsDef = `<T, DT extends DataType>`;
|
|
893
|
+
if (classDefDerived) {
|
|
894
|
+
if (isUnspecifiedDataType(classDefDerived.dataType)) {
|
|
895
|
+
chevronsUse = "<T, /*m*/DT>";
|
|
896
|
+
chevronsExtend = `<T/*g*/, DT>`;
|
|
897
|
+
} else {
|
|
898
|
+
chevronsUse = `<T, /*n*/DataType.${DataType[classDefDerived.dataType]}>`;
|
|
899
|
+
chevronsExtend = `<T, /*e*/DataType.${DataType[classDefDerived.dataType]}>`;
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
return { chevronsDef, chevronsUse, chevronsExtend };
|
|
905
|
+
}
|
|
906
|
+
// find the structure member that are already in base structure definition and that are replicated here
|
|
907
|
+
// we will have to use the Omit<Base,"member1" |"member2"> typescript pattern to avoid issues
|
|
908
|
+
function extractMembersRecursively(classDef?: ClassDefinition | null): string[] {
|
|
909
|
+
if (!classDef) {
|
|
910
|
+
return [];
|
|
911
|
+
}
|
|
912
|
+
const m = classDef.members.map((m) => m.name);
|
|
913
|
+
if (classDef.baseClassDef) {
|
|
914
|
+
const m2 = extractMembersRecursively(classDef.baseClassDef);
|
|
915
|
+
return m.concat(m2);
|
|
916
|
+
}
|
|
917
|
+
return m;
|
|
918
|
+
}
|
|
919
|
+
function getBaseClassWithOmit(classDef: ClassDefinition) {
|
|
920
|
+
const { baseInterfaceName, members } = classDef;
|
|
921
|
+
|
|
922
|
+
const allMembers = extractMembersRecursively(classDef.baseClassDef);
|
|
923
|
+
const conflictingMembers = members.filter((m) => allMembers.indexOf(m.name) !== -1);
|
|
924
|
+
//console.log(allMembers.join(" "));
|
|
925
|
+
if (conflictingMembers.length) {
|
|
926
|
+
// console.log("conflictingMembers = ", conflictingMembers.map((a) => a.name).join(" "));
|
|
927
|
+
}
|
|
928
|
+
const chBase = calculateChevrons(classDef.baseClassDef!, classDef);
|
|
929
|
+
|
|
930
|
+
let baseStuff = `${baseInterfaceName?.name}${chBase.chevronsUse}`;
|
|
931
|
+
if (conflictingMembers.length) {
|
|
932
|
+
baseStuff = `Omit<${baseStuff}, ${conflictingMembers.map((a) => `"${a.name}"`).join("|")}>`;
|
|
933
|
+
}
|
|
934
|
+
return baseStuff;
|
|
935
|
+
}
|
|
936
|
+
function getBaseClassWithOmit2(classMember: ClassMember) {
|
|
937
|
+
const members = classMember.children2;
|
|
938
|
+
const allMembers = extractMembersRecursively(classMember.classDef);
|
|
939
|
+
const conflictingMembers = members.filter((m) => allMembers.indexOf(m.name) !== -1);
|
|
940
|
+
//console.log(allMembers.join(" "));
|
|
941
|
+
if (conflictingMembers.length) {
|
|
942
|
+
console.log("conflictingMembers = ", conflictingMembers.map((a) => a.name).join(" "));
|
|
943
|
+
}
|
|
944
|
+
const childBase = classMember.childBase;
|
|
945
|
+
|
|
946
|
+
let baseStuff = `${childBase?.name}${classMember.suffix3}`;
|
|
947
|
+
if (conflictingMembers.length) {
|
|
948
|
+
baseStuff = `Omit<${baseStuff}, ${conflictingMembers.map((a) => `"${a.name}"`).join("|")}>`;
|
|
949
|
+
}
|
|
950
|
+
return baseStuff;
|
|
951
|
+
}
|
|
952
|
+
/**
|
|
953
|
+
* nodeId : a DataType, ReferenceType,AObjectType, VariableType node
|
|
954
|
+
*/
|
|
955
|
+
// eslint-disable-next-line max-statements
|
|
956
|
+
export async function _convertTypeToTypescript(
|
|
957
|
+
session: IBasicSession,
|
|
958
|
+
nodeId: NodeId,
|
|
959
|
+
cache: Cache,
|
|
960
|
+
f?: LineFile
|
|
961
|
+
): Promise<{ type: Type; content: string; typeName: string }> {
|
|
962
|
+
f = f || new LineFile();
|
|
963
|
+
|
|
964
|
+
const nodeClass = await getNodeClass(session, nodeId);
|
|
965
|
+
if (nodeClass === NodeClass.DataType) {
|
|
966
|
+
return await _exportDataTypeToTypescript(session, nodeId, cache, f);
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
const classDef = await extractClassDefinition(session, nodeId, cache);
|
|
970
|
+
// if (classDef.browseName.toString().match(/PubSubDiagnosticsWriterGroupType/)) {
|
|
971
|
+
// debugger;
|
|
972
|
+
// }
|
|
973
|
+
const {
|
|
974
|
+
dataTypeName,
|
|
975
|
+
dataType,
|
|
976
|
+
dataTypeNodeId,
|
|
977
|
+
interfaceName,
|
|
978
|
+
baseInterfaceName,
|
|
979
|
+
browseName,
|
|
980
|
+
description,
|
|
981
|
+
isAbstract,
|
|
982
|
+
members
|
|
983
|
+
} = classDef;
|
|
984
|
+
|
|
985
|
+
await preDumpChildren(session, " ", classDef, f, cache);
|
|
986
|
+
|
|
987
|
+
// f.write(superType.toString());
|
|
988
|
+
f.write(`/**`);
|
|
989
|
+
if (description.text) {
|
|
990
|
+
f.write(toComment(" * ", description.text || ""));
|
|
991
|
+
f.write(` *`);
|
|
992
|
+
}
|
|
993
|
+
f.write(` * | |${f1(" ")}|`);
|
|
994
|
+
f.write(` * |----------------|${f2("-")}|`);
|
|
995
|
+
f.write(` * |namespace |${f1(cache.namespace[nodeId.namespace].namespaceUri)}|`);
|
|
996
|
+
f.write(` * |nodeClass |${f1(NodeClass[nodeClass])}|`);
|
|
997
|
+
f.write(` * |typedDefinition |${f1(browseName.toString() + " " + nodeId.toString())}|`);
|
|
998
|
+
if (nodeClass === NodeClass.VariableType) {
|
|
999
|
+
f.write(` * |dataType |${f1(DataType[dataType])}|`);
|
|
1000
|
+
f.write(` * |dataType Name |${f1(dataTypeName + " " + dataTypeNodeId?.toString())}|`);
|
|
1001
|
+
}
|
|
1002
|
+
f.write(` * |isAbstract |${f1(isAbstract.toString())}|`);
|
|
1003
|
+
f.write(` */`);
|
|
1004
|
+
|
|
1005
|
+
// if (interfaceName.name === "UAOperationLimits") {
|
|
1006
|
+
// debugger;
|
|
1007
|
+
// }
|
|
1008
|
+
cache.ensureImported(baseInterfaceName!);
|
|
1009
|
+
cache.ensureImported({ ...baseInterfaceName!, name: baseInterfaceName!.name + `${baseExtension}` });
|
|
1010
|
+
|
|
1011
|
+
const ch = calculateChevrons(classDef);
|
|
1012
|
+
|
|
1013
|
+
if (nodeClass === NodeClass.VariableType) {
|
|
1014
|
+
if (classDef.dataTypeImport) {
|
|
1015
|
+
console.log(chalk.red(" ----------------> ", classDef.browseName));
|
|
1016
|
+
classDef.dataTypeImport.forEach((a) => {
|
|
1017
|
+
console.log(chalk.red(" ------------------------> ", a.module, a.name, a.namespace));
|
|
1018
|
+
});
|
|
1019
|
+
classDef.dataTypeImport.forEach(cache.ensureImported.bind(cache));
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
cache.referenceBasicType("DataType");
|
|
1023
|
+
const chevrons = calculateChevrons(classDef);
|
|
1024
|
+
const chevronsBase = calculateChevrons(classDef.baseClassDef!, classDef);
|
|
1025
|
+
// Shall we cache.ensureImported({ ...baseInterfaceName!, module: dataTypeName, name: dataTypeName });
|
|
1026
|
+
const classBaseName = `${interfaceName.name}${baseExtension}${chevrons.chevronsDef}`;
|
|
1027
|
+
|
|
1028
|
+
if (baseInterfaceName?.name === "UAVariableT") {
|
|
1029
|
+
f.write(`export interface ${classBaseName} {`);
|
|
1030
|
+
} else {
|
|
1031
|
+
const baseName = `${baseInterfaceName?.name}${baseExtension}${chevronsBase.chevronsExtend}`;
|
|
1032
|
+
f.write(`export interface ${classBaseName} extends ${baseName} {`);
|
|
1033
|
+
}
|
|
1034
|
+
} else {
|
|
1035
|
+
const classBaseName = `${interfaceName.name}${baseExtension}`;
|
|
1036
|
+
if (baseInterfaceName?.name === "UAObject") {
|
|
1037
|
+
f.write(`export interface ${classBaseName} {`);
|
|
1038
|
+
} else {
|
|
1039
|
+
const baseName = `${baseInterfaceName?.name}${baseExtension}`;
|
|
1040
|
+
f.write(`export interface ${classBaseName} extends ${baseName} {`);
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
await dumpChildren(session, " ", members, f, cache);
|
|
1044
|
+
f.write(`}`);
|
|
1045
|
+
|
|
1046
|
+
const baseStuff = getBaseClassWithOmit(classDef);
|
|
1047
|
+
|
|
1048
|
+
if (nodeClass === NodeClass.VariableType) {
|
|
1049
|
+
cache.referenceBasicType("DataType");
|
|
1050
|
+
// if dataType is a extension object we can simpligy by forcing DT
|
|
1051
|
+
const className = `${interfaceName.name}${ch.chevronsDef}`;
|
|
1052
|
+
const c = isUnspecifiedDataType(dataType) ? "<T, DT /*A*/>" : "<T /*B*/>";
|
|
1053
|
+
const classBaseName = `${interfaceName.name}${baseExtension}${c}`;
|
|
1054
|
+
f.write(`export interface ${className} extends ${baseStuff}, ${classBaseName} {`);
|
|
1055
|
+
} else {
|
|
1056
|
+
const className = `${interfaceName.name}`;
|
|
1057
|
+
const classBaseName = `${interfaceName.name}${baseExtension}`;
|
|
1058
|
+
f.write(`export interface ${className} extends ${baseStuff}, ${classBaseName} {`);
|
|
1059
|
+
}
|
|
1060
|
+
f.write(`}`);
|
|
1061
|
+
return { type: "ua", content: f.toString(), typeName: interfaceName.name };
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
export async function convertTypeToTypescript(
|
|
1065
|
+
session: IBasicSession,
|
|
1066
|
+
nodeId: NodeId,
|
|
1067
|
+
options: Options,
|
|
1068
|
+
cache?: Cache,
|
|
1069
|
+
f?: LineFile
|
|
1070
|
+
): Promise<{
|
|
1071
|
+
type: Type;
|
|
1072
|
+
content: string;
|
|
1073
|
+
module: string;
|
|
1074
|
+
folder: string;
|
|
1075
|
+
filename: string;
|
|
1076
|
+
dependencies: string[];
|
|
1077
|
+
}> {
|
|
1078
|
+
f = new LineFile();
|
|
1079
|
+
|
|
1080
|
+
cache = cache || (await constructCache(session, options));
|
|
1081
|
+
|
|
1082
|
+
const { type, content, typeName } = await _convertTypeToTypescript(session, nodeId, cache);
|
|
1083
|
+
|
|
1084
|
+
f.write(`// ----- this file has been automatically generated - do not edit`);
|
|
1085
|
+
f.write(dumpUsedExport(typeName, nodeId.namespace, cache));
|
|
1086
|
+
f.write(content);
|
|
1087
|
+
|
|
1088
|
+
const folder = cache.namespace[nodeId.namespace].sourceFolder;
|
|
1089
|
+
const module = cache.namespace[nodeId.namespace].module;
|
|
1090
|
+
const filename = toFilename(typeName);
|
|
1091
|
+
|
|
1092
|
+
const dependencies = findUsedImport(nodeId.namespace, cache);
|
|
1093
|
+
|
|
1094
|
+
return { type, content: f.toString(), folder, module, filename, dependencies };
|
|
1095
|
+
}
|