node-opcua-modeler 2.63.1 → 2.64.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/MyModel.NodeSet2.xml +125 -0
  2. package/dist/addExtensionObjectDataType.d.ts +21 -21
  3. package/dist/addExtensionObjectDataType.js +130 -130
  4. package/dist/buildModel.d.ts +16 -0
  5. package/dist/buildModel.js +91 -0
  6. package/dist/buildModel.js.map +1 -0
  7. package/dist/build_model_inner.d.ts +17 -17
  8. package/dist/build_model_inner.js +40 -40
  9. package/dist/display.d.ts +2 -0
  10. package/dist/display.js +96 -0
  11. package/dist/display.js.map +1 -0
  12. package/dist/displayNodeElement.d.ts +5 -5
  13. package/dist/displayNodeElement.js +191 -156
  14. package/dist/displayNodeElement.js.map +1 -1
  15. package/dist/dump_state_machine_to_graphviz.d.ts +6 -0
  16. package/dist/dump_state_machine_to_graphviz.js +120 -0
  17. package/dist/dump_state_machine_to_graphviz.js.map +1 -0
  18. package/dist/generate_markdown_doc.d.ts +6 -6
  19. package/dist/generate_markdown_doc.js +266 -155
  20. package/dist/generate_markdown_doc.js.map +1 -1
  21. package/dist/index.d.ts +24 -24
  22. package/dist/index.js +48 -48
  23. package/dist/promoteToMandatory.d.ts +8 -8
  24. package/dist/promoteToMandatory.js +97 -97
  25. package/dist/setNamespaceMetaData.d.ts +1 -1
  26. package/dist/setNamespaceMetaData.js +5 -5
  27. package/dist/symbol.d.ts +1 -1
  28. package/dist/symbol.js +2 -2
  29. package/dist/tableHelper.d.ts +9 -9
  30. package/dist/tableHelper.js +60 -60
  31. package/dist/to_cvs.d.ts +2 -2
  32. package/dist/to_cvs.js +11 -11
  33. package/dist/to_graphivz.d.ts +14 -13
  34. package/dist/to_graphivz.js +294 -235
  35. package/dist/to_graphivz.js.map +1 -1
  36. package/dist/types.d.ts +1 -1
  37. package/dist/types.js +2 -2
  38. package/distNodeJS/build_documentation_to_file.d.ts +2 -2
  39. package/distNodeJS/build_documentation_to_file.js +26 -26
  40. package/distNodeJS/build_model.d.ts +7 -7
  41. package/distNodeJS/build_model.js +21 -21
  42. package/distNodeJS/index.d.ts +5 -5
  43. package/distNodeJS/index.js +19 -19
  44. package/distNodeJS/symbol_cvs.d.ts +3 -3
  45. package/distNodeJS/symbol_cvs.js +63 -63
  46. package/examples/make_model.ts +4 -3
  47. package/package.json +19 -19
  48. package/source/displayNodeElement.ts +165 -112
  49. package/source/dump_state_machine_to_graphviz.ts +164 -0
  50. package/source/generate_markdown_doc.ts +217 -82
  51. package/source/to_graphivz.ts +129 -48
@@ -7,19 +7,26 @@ import {
7
7
  UADataType,
8
8
  UAObjectType,
9
9
  UAVariableType,
10
- UAMethod
10
+ UAMethod,
11
+ UAStateMachineEx,
12
+ promoteToStateMachine
11
13
  } from "node-opcua-address-space";
14
+ import { UAStateMachineImpl } from "node-opcua-address-space/src/state_machine/finite_state_machine";
12
15
  import { ReferenceTypeIds } from "node-opcua-constants";
13
16
  import { BrowseDirection, NodeClass } from "node-opcua-data-model";
17
+ import { resolveNodeId } from "node-opcua-nodeid";
14
18
 
19
+ function e(str: string): string {
20
+ return str.replace(/</g, "&lt;").replace(/>/g, "&gt;");
21
+ }
15
22
  function innerText(node: UAObject | UAVariable) {
16
23
  const browseName = node.browseName.name;
17
24
  const typeDefinition = node.typeDefinitionObj?.browseName?.name;
18
25
 
19
26
  if (typeDefinition) {
20
- return `[ label =< <FONT point-size="8" ><I>${typeDefinition}</I></FONT><BR/>${browseName} >]`;
27
+ return `[ label =< <FONT point-size="8" ><I>${typeDefinition}</I></FONT><BR/>${e(browseName!)} >]`;
21
28
  } else {
22
- return `[label =< ${browseName} >]`;
29
+ return `[label =< ${e(browseName!)} >]`;
23
30
  }
24
31
  }
25
32
  function arrowHeadAttribute(reference: UAReference): string {
@@ -27,7 +34,7 @@ function arrowHeadAttribute(reference: UAReference): string {
27
34
  case ReferenceTypeIds.HasTypeDefinition:
28
35
  return "normalnormal";
29
36
  case ReferenceTypeIds.HasComponent:
30
- return "noneteetree";
37
+ return "noneteetee";
31
38
  case ReferenceTypeIds.HasProperty:
32
39
  return "nonetee";
33
40
  case ReferenceTypeIds.HasSubtype:
@@ -45,16 +52,16 @@ function arrowHead(reference: UAReference): string {
45
52
  const regularShapes: Record<string, string> = {
46
53
  ObjectType: '[shape=rectangle, style="filled" fillcolor="#e8edf7;0.75:#d2def0" gradientangle=275]',
47
54
  VariableType: '[shape=rectangle, style="rounded,filled" fillcolor="#e8edf7;0.75:#d2def0" gradientangle=275]',
48
- Object: ' [shape=rectangle, style="rounded,filled" fillcolor="#e8edf7"]',
55
+ Object: ' [shape=rectangle, style="filled" fillcolor="#e8edf7"]',
49
56
  Variable: '[shape=rectangle, style="filled,rounded" fillcolor="#e8edf7"]',
50
- Method: '[shape=circle, style="filled" fillcolor="#e8edf7"]'
57
+ Method: '[shape=oval, style="filled" fillcolor="#e8edf7"]'
51
58
  };
52
59
  const regularShapesOptionals: Record<string, string> = {
53
60
  ObjectType: '[shape=rectangle, style="filled,dashed" fillcolor="#e8edf7;0.75:#d2def0" gradientangle=275]',
54
61
  VariableType: '[shape=rectangle, style="rounded,filled,dashed" fillcolor="#e8edf7;0.75:#d2def0" gradientangle=275]',
55
- Object: ' [shape=rectangle, style="rounded,filled,dashed" fillcolor="#e8edf7"]',
62
+ Object: ' [shape=rectangle, style="filled,dashed" fillcolor="#e8edf7"]',
56
63
  Variable: '[shape=rectangle, style="filled,rounded,dashed" fillcolor="#e8edf7"]',
57
- Method: '[shape=circle, style="filled,dashed" fillcolor="#e8edf7"]'
64
+ Method: '[shape=oval, style="filled,dashed" fillcolor="#e8edf7"]'
58
65
  };
59
66
 
60
67
  interface Options {
@@ -62,12 +69,17 @@ interface Options {
62
69
  }
63
70
 
64
71
  class NodeRegistry {
65
- m: Record<string, BaseNode[]> = {};
72
+ m: Record<string, { name: string; node: BaseNode }[]> = {};
66
73
  invisibleNodes: string[] = [];
67
- add(node: BaseNode) {
74
+ duplicated: { [key: string]: string } = {};
75
+ add(name: string, node: BaseNode) {
76
+ if (this.duplicated[name]) {
77
+ return; //throw new Error("Already included");
78
+ }
79
+ this.duplicated[name] = name;
68
80
  const nodeClass = NodeClass[node.nodeClass];
69
81
  this.m[nodeClass] = this.m[nodeClass] || [];
70
- this.m[nodeClass].push(node);
82
+ this.m[nodeClass].push({ name, node });
71
83
  }
72
84
  addInvisibleNode(name: string) {
73
85
  this.invisibleNodes.push(name);
@@ -80,22 +92,22 @@ function dumpNodeByNodeClass(str: string[], nodeRegistry: NodeRegistry) {
80
92
  }
81
93
  str.push(` ## -> ${className}`);
82
94
 
83
- const mandatoryNodes = listNode.filter((node) => !node.modellingRule || node.modellingRule.match(/Mandatory/));
84
- const optionalNodes = listNode.filter((node) => node.modellingRule && node.modellingRule.match(/Optional/));
95
+ const mandatoryNodes = listNode.filter(({ name, node }) => !node.modellingRule || node.modellingRule.match(/Mandatory/));
96
+ const optionalNodes = listNode.filter(({ name, node }) => node.modellingRule && node.modellingRule.match(/Optional/));
85
97
  if (mandatoryNodes.length > 0) {
86
98
  str.push(` node [];`);
87
99
  const decoration = regularShapes[className] as string;
88
100
  str.push(` node ${decoration};`);
89
- for (const node of mandatoryNodes) {
90
- str.push(` ${node.browseName.name} ${innerText(node as UAVariable | UAObject)}`);
101
+ for (const { name, node } of mandatoryNodes) {
102
+ str.push(` ${name} ${innerText(node as UAVariable | UAObject)}`);
91
103
  }
92
104
  }
93
105
  if (optionalNodes.length > 0) {
94
106
  const decoration2 = regularShapesOptionals[className] as string;
95
107
  str.push(` node [];`);
96
108
  str.push(` node ${decoration2};`);
97
- for (const node of listNode) {
98
- str.push(` ${node.browseName.name} ${innerText(node as UAVariable | UAObject)}`);
109
+ for (const { name, node } of listNode) {
110
+ str.push(` ${name} ${innerText(node as UAVariable | UAObject)}`);
99
111
  }
100
112
  }
101
113
  }
@@ -106,10 +118,22 @@ function dumpNodeByNodeClass(str: string[], nodeRegistry: NodeRegistry) {
106
118
  str.push(` ${nodeRegistry.invisibleNodes.join("\n ")};`);
107
119
  }
108
120
  }
121
+
122
+ import { dumpStateMachineToPlantUML } from "./dump_state_machine_to_graphviz";
123
+
109
124
  export function opcuaToDot(node: UAObjectType | UAVariableType, options?: Options): string {
125
+ if (node.nodeClass === NodeClass.ObjectType) {
126
+ const finitieStateMachineType = node.addressSpace.findObjectType("FiniteStateMachineType");
127
+ if (finitieStateMachineType) {
128
+ if ((node as UAObjectType).isSupertypeOf(finitieStateMachineType)) {
129
+ const stateMachine = node.instantiate({ browseName: "StateMachine" }) as UAObject;
130
+ promoteToStateMachine(stateMachine);
131
+ return dumpStateMachineToPlantUML(stateMachine as UAStateMachineEx);
132
+ }
133
+ }
134
+ }
110
135
  options = options || { naked: false };
111
136
  const nodeRegistry = new NodeRegistry();
112
- nodeRegistry.add(node);
113
137
 
114
138
  const str: string[] = [];
115
139
  const str2: string[] = [];
@@ -120,71 +144,108 @@ export function opcuaToDot(node: UAObjectType | UAVariableType, options?: Option
120
144
  str.push(" node [];");
121
145
  }
122
146
 
147
+ function makeId(p: string, c: string) {
148
+ return `${p}_${c}`.replace(" ", "_").replace(/<|>/g, "_");
149
+ }
150
+ // eslint-disable-next-line max-params
123
151
  // eslint-disable-next-line max-statements
124
152
  function typeMemberAndSubTypeMember(
125
153
  str: string[],
154
+ parentNode: string,
126
155
  node: UAObjectType | UAVariableType | UAMethod | UAVariable | UAObject,
127
156
  parent: UAObjectType | UAVariableType | UAMethod | UAVariable | UAObject | null,
128
157
  offset: number,
129
158
  prefix: string,
130
- joinWithCaller: boolean
159
+ joinWithCaller: boolean,
160
+ visitorMap: Record<string, any>
131
161
  ): [number, string[]] {
132
162
  let innerDepth = 0;
133
163
  const browseName = (parent || node).browseName.name!.toString();
134
164
  const r: string[] = [];
135
165
  const r2: string[] = [];
136
- const references = node.findReferencesEx("Aggregates", BrowseDirection.Forward);
137
- const folderElements = node.findReferencesEx("Organizes", BrowseDirection.Forward);
166
+
167
+ const references = (parent || node).findReferencesEx("Aggregates", BrowseDirection.Forward);
168
+ const folderElements = (parent || node).findReferencesEx("Organizes", BrowseDirection.Forward);
138
169
  const childReferences = [...references, ...folderElements];
170
+ const id = makeId(parentNode, browseName);
171
+ nodeRegistry.add(id, node);
172
+
173
+ function addInvisibleNode(prefix: string, index: number) {
174
+ const breakNode = `${prefix}${index}`;
175
+ r2.push(breakNode);
176
+ nodeRegistry.addInvisibleNode(breakNode);
177
+ return breakNode;
178
+ }
139
179
  for (let i = 0; i < childReferences.length; i++) {
140
180
  const isLast = i === childReferences.length - 1;
141
- innerDepth++;
142
181
  const reference = childReferences[i];
143
182
 
144
183
  const childNode = reference.node! as UAVariable | UAObject | UAMethod;
145
184
  const childName = childNode.browseName.name!.toString();
146
- nodeRegistry.add(childNode);
185
+
186
+ const fullChildName = makeId(id, childName);
187
+ // avoid member duplication
188
+ if (visitorMap[fullChildName]) {
189
+ continue;
190
+ } else {
191
+ visitorMap[fullChildName] = 1;
192
+ }
193
+
194
+ innerDepth++;
195
+
196
+ nodeRegistry.add(fullChildName, childNode);
147
197
  const edgeAttributes = arrowHead(reference);
148
198
 
149
- const breakNode = `${prefix}${i + offset}`;
150
- r2.push(breakNode);
151
- nodeRegistry.addInvisibleNode(breakNode);
152
- const horizontalPart = `{ rank=same ${breakNode} -> ${childName} ${edgeAttributes} }`;
199
+ const breakNode = addInvisibleNode(prefix, i + offset);
200
+ const horizontalPart = `{ rank=same ${breakNode} -> ${fullChildName} ${edgeAttributes} }`;
153
201
  r.push(horizontalPart);
154
202
 
155
203
  // push children on same level
156
- const [depth] = typeMemberAndSubTypeMember(str, childNode, null, 0, `${prefix}${i + offset}_`, false);
157
- for (let d = 0; d < depth; d++) {
158
- offset++;
159
- if (!isLast) {
160
- const breakNode = `${prefix}${i + offset}`;
161
- r2.push(breakNode);
162
- nodeRegistry.addInvisibleNode(breakNode);
204
+ const [depth] = typeMemberAndSubTypeMember(str, id, childNode, null, 0, `${prefix}${i + offset}_`, false, visitorMap);
205
+
206
+ // add invisible nodes
207
+ {
208
+ for (let d = 0; d < depth; d++) {
209
+ offset++;
210
+ if (!isLast) {
211
+ addInvisibleNode(prefix, i + offset);
212
+ }
163
213
  }
214
+ innerDepth += depth;
164
215
  }
165
- innerDepth += depth;
166
216
  }
167
217
 
168
218
  if (node.nodeClass == NodeClass.ObjectType || node.nodeClass === NodeClass.VariableType) {
169
219
  if (node.subtypeOfObj && node.subtypeOfObj.nodeId.namespace === node.nodeId.namespace) {
170
- const [depth, rr2] = typeMemberAndSubTypeMember(str, node.subtypeOfObj, node, r.length, prefix, true);
171
- innerDepth += depth;
220
+ const [depth, rr2] = typeMemberAndSubTypeMember(
221
+ str,
222
+ parentNode,
223
+ node.subtypeOfObj,
224
+ node,
225
+ r.length + offset + 1,
226
+ prefix,
227
+ true,
228
+ visitorMap
229
+ );
230
+ innerDepth += depth + 1;
172
231
  r2.push(...rr2);
173
232
  }
174
233
  }
175
234
  if (r.length) {
176
235
  str.push(...r.map((x) => " " + x));
177
236
  }
237
+
178
238
  if (!joinWithCaller) {
179
239
  if (r2.length) {
180
- str.push(` ${browseName} -> ${r2.join(" -> ")} [arrowhead=none];`);
240
+ str.push(` ${id} -> ${r2.join(" -> ")} [arrowhead=none];`);
181
241
  }
182
242
  }
183
243
 
184
244
  return [innerDepth, r2];
185
245
  }
186
246
 
187
- typeMemberAndSubTypeMember(str2, node, null, 0, "r", false);
247
+ const visitorMap = {};
248
+ typeMemberAndSubTypeMember(str2, "", node, null, 0, "r", false, visitorMap);
188
249
 
189
250
  if (!options.naked) {
190
251
  dumpNodeByNodeClass(str, nodeRegistry);
@@ -206,7 +267,7 @@ export function dumpClassHierachry(
206
267
 
207
268
  const str: string[] = [];
208
269
  const nodeRegistry = new NodeRegistry();
209
- nodeRegistry.add(typeNode);
270
+ nodeRegistry.add(typeNode.browseName.name!, typeNode);
210
271
  str.push("digraph G {");
211
272
  if (!options.naked) {
212
273
  // str.push(" splines=ortho;");
@@ -219,10 +280,10 @@ export function dumpClassHierachry(
219
280
  const references = typeNode.findReferencesEx("HasSubtype", BrowseDirection.Forward);
220
281
  for (let i = 0; i < references.length; i++) {
221
282
  const reference = references[i];
222
- const childNode = reference.node! as UAVariable | UAObject | UAMethod;
283
+ const childNode = reference.node! as UAVariableType | UAObjectType;
223
284
  const nodeClass = NodeClass[childNode.nodeClass];
224
285
  const childName = childNode.browseName.name!.toString();
225
- nodeRegistry.add(typeNode);
286
+ nodeRegistry.add(childName, childNode);
226
287
  const edgeAttributes = arrowHead(reference);
227
288
  str.push(` ${childName} -> ${parentName} ${edgeAttributes};`);
228
289
 
@@ -231,8 +292,27 @@ export function dumpClassHierachry(
231
292
  }
232
293
  }
233
294
  }
234
- /** */
295
+ function dumpBaseTypes(str: string[], typeNode: BaseNode, level: number) {
296
+ const parentName = typeNode.browseName.name!.toString();
297
+ const references = typeNode.findReferencesEx("HasSubtype", BrowseDirection.Inverse);
298
+ for (let i = 0; i < references.length; i++) {
299
+ const reference = references[i];
300
+ const childNode = reference.node! as UAVariableType | UAObjectType;
301
+ const nodeClass = NodeClass[childNode.nodeClass];
302
+ const childName = childNode.browseName.name!.toString();
303
+ nodeRegistry.add(childName, childNode);
304
+ const edgeAttributes = arrowHead(reference);
305
+ str.push(` ${parentName} -> ${childName} ${edgeAttributes};`);
306
+ if (level > 0) {
307
+ dumpBaseTypes(str, childNode, level - 1);
308
+ }
309
+ }
310
+ }
235
311
  const str2: string[] = [];
312
+ if (options.showBaseType) {
313
+ dumpBaseTypes(str2, typeNode, level);
314
+ }
315
+ /** */
236
316
  if (options.showSubType) {
237
317
  dumpSubtypes(str2, typeNode, level);
238
318
  }
@@ -243,9 +323,10 @@ export function dumpClassHierachry(
243
323
  str.push("}");
244
324
  return str.join("\n");
245
325
  }
246
- function graphVizToPlantUml(str: string[]): string {
326
+
327
+ export function graphVizToPlantUml(str: string): string {
247
328
  const ttt = "```";
248
- return `${ttt}plantuml\n@startuml\n${str.join("\n")}\n@enduml\n${ttt}`;
329
+ return `${ttt}plantuml\n@startuml\n${str}\n@enduml\n${ttt}`;
249
330
  }
250
331
  export function dumpTypeDiagram(namespace: any) {
251
332
  const objectTypes = [...namespace._objectTypeIterator()];
@@ -257,14 +338,14 @@ export function dumpTypeDiagram(namespace: any) {
257
338
  const str: string[] = [];
258
339
  for (const type of [...objectTypes, ...variableTypes]) {
259
340
  const d = opcuaToDot(type);
260
- str.push(graphVizToPlantUml([d]));
341
+ str.push(graphVizToPlantUml(d));
261
342
 
262
343
  const d2 = dumpClassHierachry(type);
263
- str.push(graphVizToPlantUml([d2]));
344
+ str.push(graphVizToPlantUml(d2));
264
345
  }
265
346
  for (const dataType of dataTypes) {
266
347
  const d = opcuaToDot(dataType);
267
- str.push(graphVizToPlantUml([d]));
348
+ str.push(graphVizToPlantUml(d));
268
349
  }
269
350
  return str.join("\n");
270
351
  }