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.
- package/MyModel.NodeSet2.xml +125 -0
- package/dist/addExtensionObjectDataType.d.ts +21 -21
- package/dist/addExtensionObjectDataType.js +130 -130
- package/dist/buildModel.d.ts +16 -0
- package/dist/buildModel.js +91 -0
- package/dist/buildModel.js.map +1 -0
- package/dist/build_model_inner.d.ts +17 -17
- package/dist/build_model_inner.js +40 -40
- package/dist/display.d.ts +2 -0
- package/dist/display.js +96 -0
- package/dist/display.js.map +1 -0
- package/dist/displayNodeElement.d.ts +5 -5
- package/dist/displayNodeElement.js +191 -156
- package/dist/displayNodeElement.js.map +1 -1
- package/dist/dump_state_machine_to_graphviz.d.ts +6 -0
- package/dist/dump_state_machine_to_graphviz.js +120 -0
- package/dist/dump_state_machine_to_graphviz.js.map +1 -0
- package/dist/generate_markdown_doc.d.ts +6 -6
- package/dist/generate_markdown_doc.js +266 -155
- package/dist/generate_markdown_doc.js.map +1 -1
- package/dist/index.d.ts +24 -24
- package/dist/index.js +48 -48
- package/dist/promoteToMandatory.d.ts +8 -8
- package/dist/promoteToMandatory.js +97 -97
- package/dist/setNamespaceMetaData.d.ts +1 -1
- package/dist/setNamespaceMetaData.js +5 -5
- package/dist/symbol.d.ts +1 -1
- package/dist/symbol.js +2 -2
- package/dist/tableHelper.d.ts +9 -9
- package/dist/tableHelper.js +60 -60
- package/dist/to_cvs.d.ts +2 -2
- package/dist/to_cvs.js +11 -11
- package/dist/to_graphivz.d.ts +14 -13
- package/dist/to_graphivz.js +294 -235
- package/dist/to_graphivz.js.map +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/types.js +2 -2
- package/distNodeJS/build_documentation_to_file.d.ts +2 -2
- package/distNodeJS/build_documentation_to_file.js +26 -26
- package/distNodeJS/build_model.d.ts +7 -7
- package/distNodeJS/build_model.js +21 -21
- package/distNodeJS/index.d.ts +5 -5
- package/distNodeJS/index.js +19 -19
- package/distNodeJS/symbol_cvs.d.ts +3 -3
- package/distNodeJS/symbol_cvs.js +63 -63
- package/examples/make_model.ts +4 -3
- package/package.json +19 -19
- package/source/displayNodeElement.ts +165 -112
- package/source/dump_state_machine_to_graphviz.ts +164 -0
- package/source/generate_markdown_doc.ts +217 -82
- package/source/to_graphivz.ts +129 -48
|
@@ -4,7 +4,8 @@ import {
|
|
|
4
4
|
UAObjectType,
|
|
5
5
|
UAVariable,
|
|
6
6
|
resolveReferenceNode,
|
|
7
|
-
resolveReferenceType
|
|
7
|
+
resolveReferenceType,
|
|
8
|
+
UAVariableType
|
|
8
9
|
} from "node-opcua-address-space";
|
|
9
10
|
import { NodeClass } from "node-opcua-data-model";
|
|
10
11
|
import { NodeId, resolveNodeId } from "node-opcua-nodeid";
|
|
@@ -37,142 +38,194 @@ const hasSubtypeNodeId = resolveNodeId("HasSubtype");
|
|
|
37
38
|
export interface DisplayNodeOptions {
|
|
38
39
|
format: "cli" | "markdown";
|
|
39
40
|
}
|
|
41
|
+
function encodeXML(s: string) {
|
|
42
|
+
return s.replace("&", "&").replace("<", "<").replace(">", ">");
|
|
43
|
+
}
|
|
44
|
+
interface Data {
|
|
45
|
+
table: TableHelper;
|
|
46
|
+
node: BaseNode;
|
|
47
|
+
alreadyDumped: Record<string, any>;
|
|
48
|
+
descriptions: Description[];
|
|
49
|
+
}
|
|
50
|
+
interface Description {
|
|
51
|
+
description: string;
|
|
52
|
+
name: string;
|
|
53
|
+
type: string;
|
|
54
|
+
}
|
|
55
|
+
function dumpReference(data: Data, ref: UAReference, filter?: string) {
|
|
56
|
+
resolveReferenceNode(data.node.addressSpace, ref);
|
|
57
|
+
if (!ref.isForward) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
if (NodeId.sameNodeId(ref.referenceType, hasSubtypeNodeId)) {
|
|
61
|
+
return; // ignore forward HasSubtype
|
|
62
|
+
}
|
|
63
|
+
// ignore subtype references
|
|
64
|
+
/* istanbul ignore next */
|
|
65
|
+
if (!ref.node) {
|
|
66
|
+
// tslint:disable-next-line: no-console
|
|
67
|
+
console.log(" Halt ", ref.toString({ addressSpace: data.node.addressSpace }));
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
const dir = ref.isForward ? " " : " ";
|
|
71
|
+
const refNode = ref.node!;
|
|
72
|
+
|
|
73
|
+
const refType = resolveReferenceType(data.node.addressSpace, ref);
|
|
74
|
+
if (filter) {
|
|
75
|
+
if (refType.browseName.name !== filter) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
const key = ref.nodeId.toString() + ref.referenceType.toString();
|
|
80
|
+
if (data.alreadyDumped[key]) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
// xx const r = refNode.findReferencesAsObject("HasModellingRule", true);
|
|
84
|
+
const modelingRule = refNode.modellingRule || ""; // r[0] ? r[0].browseName.toString() : "/";
|
|
85
|
+
|
|
86
|
+
let value = "";
|
|
87
|
+
let dataType = "";
|
|
88
|
+
if (refNode.nodeClass === NodeClass.Variable) {
|
|
89
|
+
const v = refNode as UAVariable;
|
|
90
|
+
|
|
91
|
+
const val = v.readValue().value.value;
|
|
92
|
+
if (v.isExtensionObject()) {
|
|
93
|
+
// don't do anything
|
|
94
|
+
} else if (v.isEnumeration() && val !== null) {
|
|
95
|
+
const enumValue = v.readEnumValue();
|
|
96
|
+
value = enumValue.value + " (" + enumValue.name + ")";
|
|
97
|
+
} else if (val instanceof Date) {
|
|
98
|
+
value = val ? val.toUTCString() : "";
|
|
99
|
+
} else {
|
|
100
|
+
value = val ? val.toString() : "null";
|
|
101
|
+
}
|
|
102
|
+
const actualDataType = DataType[v.readValue().value.dataType];
|
|
103
|
+
const basicDataType = DataType[v.dataTypeObj.basicDataType];
|
|
104
|
+
dataType = v.dataTypeObj.browseName.toString();
|
|
105
|
+
if (basicDataType !== dataType) {
|
|
106
|
+
dataType = dataType + "(" + basicDataType + ")";
|
|
107
|
+
}
|
|
108
|
+
// findBasicDataType(v.dataTypeObj);
|
|
109
|
+
}
|
|
40
110
|
|
|
111
|
+
const row = [
|
|
112
|
+
refType.browseName.toString() + dir + symbol(refNode.nodeClass),
|
|
113
|
+
refNode.nodeId.toString(),
|
|
114
|
+
encodeXML(refNode.browseName.toString()),
|
|
115
|
+
modelingRule,
|
|
116
|
+
(refNode as any).typeDefinitionObj ? (refNode as any).typeDefinitionObj.browseName.toString() : "",
|
|
117
|
+
dataType,
|
|
118
|
+
value
|
|
119
|
+
];
|
|
120
|
+
|
|
121
|
+
data.table.push(row);
|
|
122
|
+
|
|
123
|
+
data.descriptions.push({
|
|
124
|
+
description: refNode.description ? refNode.description.text || "" : "",
|
|
125
|
+
name: refNode.browseName.name!,
|
|
126
|
+
type: dataType
|
|
127
|
+
});
|
|
128
|
+
data.alreadyDumped[key] = 1;
|
|
129
|
+
}
|
|
130
|
+
function dumpReferences(data: Data, _references: UAReference[]) {
|
|
131
|
+
// xx for (const ref of references) {
|
|
132
|
+
// xx dumpReference(ref, "HasSubtype");
|
|
133
|
+
// xx }
|
|
134
|
+
for (const ref of _references) {
|
|
135
|
+
dumpReference(data, ref, "HasTypeDefinition");
|
|
136
|
+
}
|
|
137
|
+
for (const ref of _references) {
|
|
138
|
+
dumpReference(data, ref, "HasEncoding");
|
|
139
|
+
}
|
|
140
|
+
for (const ref of _references) {
|
|
141
|
+
dumpReference(data, ref, "HasComponent");
|
|
142
|
+
}
|
|
143
|
+
for (const ref of _references) {
|
|
144
|
+
dumpReference(data, ref, "HasProperty");
|
|
145
|
+
}
|
|
146
|
+
for (const ref of _references) {
|
|
147
|
+
dumpReference(data, ref, "Organizes");
|
|
148
|
+
}
|
|
149
|
+
for (const ref of _references) {
|
|
150
|
+
console.log(data.node!.addressSpace!.findReferenceType(ref.referenceType)!.toString());
|
|
151
|
+
dumpReference(data, ref, undefined);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function shortDescription(d: string) {
|
|
156
|
+
return d.split(/\.|\n/)[0];
|
|
157
|
+
}
|
|
41
158
|
export function displayNodeElement(node: BaseNode, options?: DisplayNodeOptions): string {
|
|
42
159
|
const head: string[] = ["ReferenceType", "NodeId", "BrowseName", "ModellingRule", "TypeDefinition", "DataType", "Value"];
|
|
43
|
-
const table = new TableHelper(head);
|
|
44
160
|
|
|
45
|
-
|
|
161
|
+
function createTable() {
|
|
162
|
+
const table = new TableHelper(head);
|
|
163
|
+
table.push(["BrowseName: ", { colSpan: 6, content: node.browseName.toString() }]);
|
|
46
164
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
165
|
+
const superType = (node as UAObjectType).subtypeOfObj;
|
|
166
|
+
if (superType) {
|
|
167
|
+
table.push(["Base", superType.browseName.toString(), { colSpan: 6, content: node.browseName.toString() }]);
|
|
168
|
+
}
|
|
51
169
|
|
|
52
|
-
|
|
53
|
-
|
|
170
|
+
if (node.description) {
|
|
171
|
+
table.push(["Description", { colspan: 6, content: shortDescription(node.description.text! || "") }]);
|
|
172
|
+
}
|
|
173
|
+
return table;
|
|
54
174
|
}
|
|
55
175
|
|
|
56
|
-
const
|
|
176
|
+
const table = createTable();
|
|
57
177
|
|
|
58
|
-
const
|
|
178
|
+
const alreadyDumped: Record<string, any> = {};
|
|
59
179
|
|
|
60
|
-
|
|
61
|
-
resolveReferenceNode(node.addressSpace, ref);
|
|
62
|
-
if (!ref.isForward) {
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
65
|
-
if (NodeId.sameNodeId(ref.referenceType, hasSubtypeNodeId)) {
|
|
66
|
-
return; // ignore forward HasSubtype
|
|
67
|
-
}
|
|
68
|
-
// ignore subtype references
|
|
69
|
-
/* istanbul ignore next */
|
|
70
|
-
if (!ref.node) {
|
|
71
|
-
// tslint:disable-next-line: no-console
|
|
72
|
-
console.log(" Halt ", ref.toString({ addressSpace: node.addressSpace }));
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
|
-
const dir = ref.isForward ? " " : " ";
|
|
76
|
-
const refNode = ref.node!;
|
|
77
|
-
|
|
78
|
-
const refType = resolveReferenceType(node.addressSpace, ref);
|
|
79
|
-
if (filter) {
|
|
80
|
-
if (refType.browseName.toString() !== filter) {
|
|
81
|
-
return;
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
if (alreadyDumped[refNode.nodeId.toString()]) {
|
|
85
|
-
return;
|
|
86
|
-
}
|
|
87
|
-
// xx const r = refNode.findReferencesAsObject("HasModellingRule", true);
|
|
88
|
-
const modelingRule = refNode.modellingRule || ""; // r[0] ? r[0].browseName.toString() : "/";
|
|
89
|
-
|
|
90
|
-
let value = "";
|
|
91
|
-
let dataType = "";
|
|
92
|
-
if (refNode.nodeClass === NodeClass.Variable) {
|
|
93
|
-
const v = refNode as UAVariable;
|
|
94
|
-
|
|
95
|
-
const val = v.readValue().value.value;
|
|
96
|
-
if (v.isExtensionObject()) {
|
|
97
|
-
// don't do anything
|
|
98
|
-
} else if (v.isEnumeration() && val !== null) {
|
|
99
|
-
const enumValue = v.readEnumValue();
|
|
100
|
-
value = enumValue.value + " (" + enumValue.name + ")";
|
|
101
|
-
} else if (val instanceof Date) {
|
|
102
|
-
value = val ? val.toUTCString() : "";
|
|
103
|
-
} else {
|
|
104
|
-
value = val ? val.toString() : "null";
|
|
105
|
-
}
|
|
106
|
-
const actualDataType = DataType[v.readValue().value.dataType];
|
|
107
|
-
const basicDataType = DataType[v.dataTypeObj.basicDataType];
|
|
108
|
-
dataType = v.dataTypeObj.browseName.toString();
|
|
109
|
-
if (basicDataType !== dataType) {
|
|
110
|
-
dataType = dataType + "(" + basicDataType + ")";
|
|
111
|
-
}
|
|
112
|
-
// findBasicDataType(v.dataTypeObj);
|
|
113
|
-
}
|
|
180
|
+
const descriptions: Description[] = [];
|
|
114
181
|
|
|
115
|
-
const row = [
|
|
116
|
-
refType.browseName.toString() + dir + symbol(refNode.nodeClass),
|
|
117
|
-
refNode.nodeId.toString(),
|
|
118
|
-
refNode.browseName.toString(),
|
|
119
|
-
modelingRule,
|
|
120
|
-
(refNode as any).typeDefinitionObj ? (refNode as any).typeDefinitionObj.browseName.toString() : "",
|
|
121
|
-
dataType,
|
|
122
|
-
value
|
|
123
|
-
];
|
|
124
|
-
|
|
125
|
-
table.push(row);
|
|
126
|
-
|
|
127
|
-
descriptions.push({
|
|
128
|
-
description: refNode.description ? refNode.description.toString() : "",
|
|
129
|
-
name: refNode.browseName.name!,
|
|
130
|
-
type: dataType
|
|
131
|
-
});
|
|
132
|
-
alreadyDumped[refNode.nodeId.toString()] = 1;
|
|
133
|
-
}
|
|
134
182
|
const references = node.allReferences();
|
|
135
183
|
|
|
136
184
|
const m = {};
|
|
137
185
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
dumpReference(ref, "HasEncoding");
|
|
147
|
-
}
|
|
148
|
-
for (const ref of _references) {
|
|
149
|
-
dumpReference(ref, "HasComponent");
|
|
150
|
-
}
|
|
151
|
-
for (const ref of _references) {
|
|
152
|
-
dumpReference(ref, "HasProperty");
|
|
153
|
-
}
|
|
154
|
-
for (const ref of _references) {
|
|
155
|
-
dumpReference(ref, "Organizes");
|
|
186
|
+
const data = { table, node, alreadyDumped, descriptions };
|
|
187
|
+
dumpReferences(data, references);
|
|
188
|
+
|
|
189
|
+
function toText(table: TableHelper) {
|
|
190
|
+
if (options && options.format === "markdown") {
|
|
191
|
+
return table.toMarkdownTable();
|
|
192
|
+
} else {
|
|
193
|
+
return table.toString();
|
|
156
194
|
}
|
|
157
195
|
}
|
|
158
|
-
|
|
196
|
+
|
|
197
|
+
const str: string[] = [];
|
|
198
|
+
const tables: TableHelper[] = [];
|
|
199
|
+
tables.push(table);
|
|
200
|
+
|
|
201
|
+
if (node.description) {
|
|
202
|
+
str.push(node.description.text! || "");
|
|
203
|
+
str.push("");
|
|
204
|
+
}
|
|
205
|
+
str.push(toText(table));
|
|
206
|
+
|
|
207
|
+
const str2: string[] = [];
|
|
159
208
|
|
|
160
209
|
// add property from base object/variable type
|
|
161
210
|
if (node.nodeClass === NodeClass.ObjectType || node.nodeClass === NodeClass.VariableType) {
|
|
162
211
|
const curNode = node;
|
|
163
212
|
|
|
164
|
-
let subtypeOf = (curNode as UAObjectType).subtypeOfObj;
|
|
213
|
+
let subtypeOf = (curNode as UAObjectType | UAVariableType).subtypeOfObj;
|
|
165
214
|
while (subtypeOf) {
|
|
166
|
-
table
|
|
215
|
+
data.table = createTable();
|
|
216
|
+
data.table.push([subtypeOf.browseName.toString() + ":", "--", "--", "--"]);
|
|
167
217
|
const references2 = subtypeOf.allReferences();
|
|
168
|
-
dumpReferences(references2);
|
|
218
|
+
dumpReferences(data, references2);
|
|
219
|
+
|
|
220
|
+
str.push("<details>");
|
|
221
|
+
str.push("<summary>Base type: " + subtypeOf.browseName.toString() + "</summary>");
|
|
222
|
+
str.push("");
|
|
223
|
+
str.push(toText(data.table));
|
|
224
|
+
str.push("");
|
|
225
|
+
str2.push("</details>");
|
|
226
|
+
|
|
169
227
|
subtypeOf = (subtypeOf as UAObjectType).subtypeOfObj;
|
|
170
228
|
}
|
|
171
229
|
}
|
|
172
|
-
|
|
173
|
-
if (options && options.format === "markdown") {
|
|
174
|
-
return table.toMarkdownTable();
|
|
175
|
-
} else {
|
|
176
|
-
return table.toString();
|
|
177
|
-
}
|
|
230
|
+
return str.join("\n") + str2.join("\n");
|
|
178
231
|
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module node-opcua-address-space
|
|
3
|
+
*/
|
|
4
|
+
// tslint:disable:no-console
|
|
5
|
+
/*
|
|
6
|
+
* class Node {
|
|
7
|
+
* { browseName: "string", nodeId: NodeId}
|
|
8
|
+
* }
|
|
9
|
+
* Transition {
|
|
10
|
+
* browseName: "String"
|
|
11
|
+
* fromState: Node
|
|
12
|
+
* toState: Node
|
|
13
|
+
* }
|
|
14
|
+
* class SateMachineType {
|
|
15
|
+
* initialState: Node
|
|
16
|
+
* states: [ Node,Node, ...]
|
|
17
|
+
* transitions: [Transition]
|
|
18
|
+
* }
|
|
19
|
+
* @param stateMachineType
|
|
20
|
+
*/
|
|
21
|
+
import { BaseNode, UAState, UAStateMachineEx, UATransition, UAObject, UATransitionEx } from "node-opcua-address-space";
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
export function dumpStateMachineToPlantUML(stateMachineType: UAStateMachineEx): string {
|
|
25
|
+
|
|
26
|
+
const lines: string[] = [];
|
|
27
|
+
function w(str: string) {
|
|
28
|
+
lines.push(str);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function s(state: BaseNode) {
|
|
32
|
+
return state? state.nodeId.value.toString() : "";
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function n(state: BaseNode) {
|
|
36
|
+
return state ? state.browseName.name!.toString() : "";
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
//w("@startuml "); // + stateMachineType.browseName.toString() + ".png");
|
|
40
|
+
// initial state if any
|
|
41
|
+
|
|
42
|
+
if (stateMachineType.initialState) {
|
|
43
|
+
w(" [*] --> " + s(stateMachineType.initialState));
|
|
44
|
+
w(" " + s(stateMachineType.initialState) + ":" + n(stateMachineType.initialState));
|
|
45
|
+
} else {
|
|
46
|
+
w("[*] --> [*]");
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function t(transition: BaseNode) {
|
|
50
|
+
let name = n(transition);
|
|
51
|
+
name = name.replace(":", "");
|
|
52
|
+
name = name.replace("To", "\\nTo\\n");
|
|
53
|
+
name = name.replace("Transition", "\\nTransition");
|
|
54
|
+
return name;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
stateMachineType.states && stateMachineType.states.forEach((state: UAObject) => w(" " + s(state) + ": " + n(state)));
|
|
58
|
+
|
|
59
|
+
stateMachineType.transitions && stateMachineType.transitions.forEach((transition: UATransitionEx) =>
|
|
60
|
+
w(" " + s(transition.fromStateNode!) + " --> " + s(transition.toStateNode!) + " : " + t(transition))
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
// w("@enduml");
|
|
64
|
+
return lines.join("\n");
|
|
65
|
+
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/*
|
|
69
|
+
@startuml
|
|
70
|
+
|
|
71
|
+
2930: Unshelved
|
|
72
|
+
|
|
73
|
+
2932: TimedShelved
|
|
74
|
+
|
|
75
|
+
2933: OneShotShelved
|
|
76
|
+
|
|
77
|
+
2930 --> 2932 : "2935\nUnshelvedToTimedShelved"
|
|
78
|
+
|
|
79
|
+
2930 --> 2933 : "2936\nUnshelvedToOneShotShelved"
|
|
80
|
+
|
|
81
|
+
2932 --> 2930 : "2940\nTimedShelvedToUnshelved"
|
|
82
|
+
|
|
83
|
+
2932 --> 2933 : "2942\nTimedShelvedToOneShotShelved"
|
|
84
|
+
|
|
85
|
+
2933 --> 2930 : "2943\nOneShotShelvedToUnshelved"
|
|
86
|
+
|
|
87
|
+
2933 --> 2932 : "2945\nOneShotShelvedToTimedShelved"
|
|
88
|
+
|
|
89
|
+
@enduml
|
|
90
|
+
|
|
91
|
+
*/
|
|
92
|
+
|
|
93
|
+
/*
|
|
94
|
+
digraph finite_state_machine {
|
|
95
|
+
rankdir=LR;
|
|
96
|
+
size="8,5"
|
|
97
|
+
node [shape = doublecircle]; LR_0 LR_3 LR_4 LR_8;
|
|
98
|
+
node [shape = circle];
|
|
99
|
+
LR_0 -> LR_2 [ label = "SS(B)" ];
|
|
100
|
+
LR_0 -> LR_1 [ label = "SS(S)" ];
|
|
101
|
+
LR_1 -> LR_3 [ label = "S($end)" ];
|
|
102
|
+
LR_2 -> LR_6 [ label = "SS(b)" ];
|
|
103
|
+
LR_2 -> LR_5 [ label = "SS(a)" ];
|
|
104
|
+
LR_2 -> LR_4 [ label = "S(A)" ];
|
|
105
|
+
LR_5 -> LR_7 [ label = "S(b)" ];
|
|
106
|
+
LR_5 -> LR_5 [ label = "S(a)" ];
|
|
107
|
+
LR_6 -> LR_6 [ label = "S(b)" ];
|
|
108
|
+
LR_6 -> LR_5 [ label = "S(a)" ];
|
|
109
|
+
LR_7 -> LR_8 [ label = "S(b)" ];
|
|
110
|
+
LR_7 -> LR_5 [ label = "S(a)" ];
|
|
111
|
+
LR_8 -> LR_6 [ label = "S(b)" ];
|
|
112
|
+
LR_8 -> LR_5 [ label = "S(a)" ];
|
|
113
|
+
}
|
|
114
|
+
*/
|
|
115
|
+
export function dumpStateMachineToGraphViz(
|
|
116
|
+
/*StateMachineProxy*/
|
|
117
|
+
stateMachineType: UAStateMachineEx
|
|
118
|
+
): string {
|
|
119
|
+
const lines: string[] = [];
|
|
120
|
+
function w(str: string) {
|
|
121
|
+
lines.push(str)
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function s(state: BaseNode) {
|
|
125
|
+
return state.nodeId.value.toString();
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function n(state: BaseNode) {
|
|
129
|
+
return state.browseName.name!.toString();
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function s_full(state: BaseNode) {
|
|
133
|
+
return s(state) + ' [ label = "' + n(state) + '" ]';
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
w("digraph finite_state_machine {");
|
|
137
|
+
// initial state if any
|
|
138
|
+
|
|
139
|
+
if (stateMachineType.initialState) {
|
|
140
|
+
w("node [ shape = doublecircle];");
|
|
141
|
+
w(" _" + s_full(stateMachineType.initialState!) + " ;");
|
|
142
|
+
}
|
|
143
|
+
w("node [ shape = circle];");
|
|
144
|
+
stateMachineType.states.forEach((state: UAState) => w(" _" + s_full(state)));
|
|
145
|
+
|
|
146
|
+
stateMachineType.transitions.forEach((transition) =>
|
|
147
|
+
w(
|
|
148
|
+
" _" +
|
|
149
|
+
s(transition.fromStateNode!) +
|
|
150
|
+
" -> _" +
|
|
151
|
+
s(transition.toStateNode!) +
|
|
152
|
+
" [ " +
|
|
153
|
+
// " labeltooltip = \"" + i(transition) + "\" " +
|
|
154
|
+
', label = "' +
|
|
155
|
+
n(transition) +
|
|
156
|
+
'" ];'
|
|
157
|
+
)
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
w("}");
|
|
161
|
+
|
|
162
|
+
return lines.join("\n");
|
|
163
|
+
|
|
164
|
+
}
|