node-opcua-address-space-for-conformance-testing 2.167.0 → 2.168.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/address_space_for_conformance_testing.d.ts +6 -2
- package/dist/address_space_for_conformance_testing.js +27 -1306
- package/dist/address_space_for_conformance_testing.js.map +1 -1
- package/dist/conformance_testing/access_right_variables.d.ts +5 -0
- package/dist/conformance_testing/access_right_variables.js +45 -0
- package/dist/conformance_testing/access_right_variables.js.map +1 -0
- package/dist/conformance_testing/analog_data_items.d.ts +5 -0
- package/dist/conformance_testing/analog_data_items.js +117 -0
- package/dist/conformance_testing/analog_data_items.js.map +1 -0
- package/dist/conformance_testing/discrete_variables.d.ts +4 -0
- package/dist/conformance_testing/discrete_variables.js +111 -0
- package/dist/conformance_testing/discrete_variables.js.map +1 -0
- package/dist/conformance_testing/enumeration_variable.d.ts +5 -0
- package/dist/conformance_testing/enumeration_variable.js +24 -0
- package/dist/conformance_testing/enumeration_variable.js.map +1 -0
- package/dist/conformance_testing/helpers.d.ts +13 -0
- package/dist/conformance_testing/helpers.js +204 -0
- package/dist/conformance_testing/helpers.js.map +1 -0
- package/dist/conformance_testing/mass_variables.d.ts +5 -0
- package/dist/conformance_testing/mass_variables.js +31 -0
- package/dist/conformance_testing/mass_variables.js.map +1 -0
- package/dist/conformance_testing/node_with_references.d.ts +5 -0
- package/dist/conformance_testing/node_with_references.js +119 -0
- package/dist/conformance_testing/node_with_references.js.map +1 -0
- package/dist/conformance_testing/object_with_methods.d.ts +5 -0
- package/dist/conformance_testing/object_with_methods.js +85 -0
- package/dist/conformance_testing/object_with_methods.js.map +1 -0
- package/dist/conformance_testing/path_and_large_arrays.d.ts +6 -0
- package/dist/conformance_testing/path_and_large_arrays.js +33 -0
- package/dist/conformance_testing/path_and_large_arrays.js.map +1 -0
- package/dist/conformance_testing/sample_view.d.ts +5 -0
- package/dist/conformance_testing/sample_view.js +25 -0
- package/dist/conformance_testing/sample_view.js.map +1 -0
- package/dist/conformance_testing/simulation_variables.d.ts +5 -0
- package/dist/conformance_testing/simulation_variables.js +113 -0
- package/dist/conformance_testing/simulation_variables.js.map +1 -0
- package/dist/conformance_testing/static_variables.d.ts +2 -0
- package/dist/conformance_testing/static_variables.js +70 -0
- package/dist/conformance_testing/static_variables.js.map +1 -0
- package/dist/conformance_testing/trigger_nodes.d.ts +5 -0
- package/dist/conformance_testing/trigger_nodes.js +41 -0
- package/dist/conformance_testing/trigger_nodes.js.map +1 -0
- package/dist/conformance_testing/type_defaults.d.ts +54 -0
- package/dist/conformance_testing/type_defaults.js +96 -0
- package/dist/conformance_testing/type_defaults.js.map +1 -0
- package/package.json +12 -12
- package/src/address_space_for_conformance_testing.ts +34 -1494
- package/src/conformance_testing/access_right_variables.ts +55 -0
- package/src/conformance_testing/analog_data_items.ts +136 -0
- package/src/conformance_testing/discrete_variables.ts +131 -0
- package/src/conformance_testing/enumeration_variable.ts +27 -0
- package/src/conformance_testing/helpers.ts +226 -0
- package/src/conformance_testing/mass_variables.ts +42 -0
- package/src/conformance_testing/node_with_references.ts +130 -0
- package/src/conformance_testing/object_with_methods.ts +93 -0
- package/src/conformance_testing/path_and_large_arrays.ts +35 -0
- package/src/conformance_testing/sample_view.ts +28 -0
- package/src/conformance_testing/simulation_variables.ts +129 -0
- package/src/conformance_testing/static_variables.ts +71 -0
- package/src/conformance_testing/trigger_nodes.ts +49 -0
- package/src/conformance_testing/type_defaults.ts +60 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reference test nodes for CTT conformance testing.
|
|
3
|
+
*/
|
|
4
|
+
import type { Namespace, UAObject } from "node-opcua-address-space";
|
|
5
|
+
|
|
6
|
+
export function addNodeWithReferences(namespace: Namespace, simulation_folder: UAObject): void {
|
|
7
|
+
const parent = simulation_folder;
|
|
8
|
+
const referenceFolder = namespace.addObject({
|
|
9
|
+
browseName: "References",
|
|
10
|
+
nodeId: "s=Demo.CTT.References",
|
|
11
|
+
organizedBy: parent,
|
|
12
|
+
typeDefinition: "FolderType"
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
// --- Has3ForwardReferences1: 3 variable components ---
|
|
16
|
+
{
|
|
17
|
+
const has3ForwardReferences1 = namespace.addObject({
|
|
18
|
+
browseName: "Has3ForwardReferences1",
|
|
19
|
+
nodeId: "s=Demo.CTT.References.Has3ForwardReferences1",
|
|
20
|
+
componentOf: referenceFolder,
|
|
21
|
+
typeDefinition: "FolderType"
|
|
22
|
+
});
|
|
23
|
+
namespace.addVariable({ browseName: "ReferenceNode1", componentOf: has3ForwardReferences1, dataType: "UInt32" });
|
|
24
|
+
namespace.addVariable({ browseName: "ReferenceNode2", componentOf: has3ForwardReferences1, dataType: "UInt32" });
|
|
25
|
+
namespace.addVariable({ browseName: "ReferenceNode3", componentOf: has3ForwardReferences1, dataType: "UInt32" });
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// --- Has3ForwardReferences2: variable + 3 methods + property ---
|
|
29
|
+
{
|
|
30
|
+
const has3ForwardReferences2 = namespace.addObject({
|
|
31
|
+
browseName: "Has3ForwardReferences2",
|
|
32
|
+
nodeId: "s=Demo.CTT.References.Has3ForwardReferences2",
|
|
33
|
+
componentOf: referenceFolder,
|
|
34
|
+
typeDefinition: "FolderType"
|
|
35
|
+
});
|
|
36
|
+
namespace.addVariable({ browseName: "BaseDataVariable", componentOf: has3ForwardReferences2, dataType: "UInt32" });
|
|
37
|
+
namespace.addMethod(has3ForwardReferences2, { browseName: "Method1" });
|
|
38
|
+
namespace.addMethod(has3ForwardReferences2, { browseName: "Method2" });
|
|
39
|
+
namespace.addMethod(has3ForwardReferences2, { browseName: "Method3" });
|
|
40
|
+
namespace.addVariable({ browseName: "Property", propertyOf: has3ForwardReferences2, dataType: "UInt32" });
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// --- Has3ForwardReferences3 + Has3InverseReferences ---
|
|
44
|
+
{
|
|
45
|
+
const has3ForwardReferences3 = namespace.addObject({
|
|
46
|
+
browseName: "Has3ForwardReferences3",
|
|
47
|
+
nodeId: "s=Demo.CTT.References.Has3ForwardReferences3",
|
|
48
|
+
componentOf: referenceFolder,
|
|
49
|
+
typeDefinition: "FolderType"
|
|
50
|
+
});
|
|
51
|
+
const referencedNode1 = namespace.addFolder(has3ForwardReferences3, "ReferencedNode1");
|
|
52
|
+
const referencedNode2 = namespace.addFolder(has3ForwardReferences3, "ReferencedNode2");
|
|
53
|
+
const referencedNode3 = namespace.addFolder(has3ForwardReferences3, "ReferencedNode3");
|
|
54
|
+
|
|
55
|
+
const has3InverseReferences = namespace.addObject({
|
|
56
|
+
browseName: "Has3InverseReferences",
|
|
57
|
+
nodeId: "s=Demo.CTT.References.Has3InverseReferences",
|
|
58
|
+
componentOf: referenceFolder,
|
|
59
|
+
typeDefinition: "FolderType"
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
referencedNode1.addReference({ referenceType: "Organizes", nodeId: has3InverseReferences });
|
|
63
|
+
referencedNode2.addReference({ referenceType: "Organizes", nodeId: has3InverseReferences });
|
|
64
|
+
referencedNode3.addReference({ referenceType: "Organizes", nodeId: has3InverseReferences });
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// --- Has3ForwardReferences4: 3 properties ---
|
|
68
|
+
{
|
|
69
|
+
const has3ForwardReferences4 = namespace.addObject({
|
|
70
|
+
browseName: "Has3ForwardReferences4",
|
|
71
|
+
nodeId: "s=Demo.CTT.References.Has3ForwardReferences4",
|
|
72
|
+
componentOf: referenceFolder,
|
|
73
|
+
typeDefinition: "FolderType"
|
|
74
|
+
});
|
|
75
|
+
namespace.addVariable({ browseName: "ReferenceNode1", propertyOf: has3ForwardReferences4, dataType: "UInt32" });
|
|
76
|
+
namespace.addVariable({ browseName: "ReferenceNode2", propertyOf: has3ForwardReferences4, dataType: "UInt32" });
|
|
77
|
+
namespace.addVariable({ browseName: "ReferenceNode3", propertyOf: has3ForwardReferences4, dataType: "UInt32" });
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// --- Has3ForwardReferences5: folder + property + method ---
|
|
81
|
+
{
|
|
82
|
+
const has3ForwardReferences5 = namespace.addObject({
|
|
83
|
+
browseName: "Has3ForwardReferences5",
|
|
84
|
+
nodeId: "s=Demo.CTT.References.Has3ForwardReferences5",
|
|
85
|
+
componentOf: referenceFolder,
|
|
86
|
+
typeDefinition: "FolderType"
|
|
87
|
+
});
|
|
88
|
+
namespace.addFolder(has3ForwardReferences5, { browseName: "ReferenceNode1" });
|
|
89
|
+
namespace.addVariable({ browseName: "ReferenceNode2", propertyOf: has3ForwardReferences5, dataType: "UInt32" });
|
|
90
|
+
namespace.addMethod(has3ForwardReferences5, { browseName: "ReferenceNode3" });
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// --- HasInverseAndForwardReferences ---
|
|
94
|
+
{
|
|
95
|
+
const hasInverseAndForwardReferences = namespace.addObject({
|
|
96
|
+
browseName: "HasInverseAndForwardReferences",
|
|
97
|
+
nodeId: "s=Demo.CTT.References.HasInverseAndForwardReferences",
|
|
98
|
+
componentOf: referenceFolder,
|
|
99
|
+
typeDefinition: "FolderType"
|
|
100
|
+
});
|
|
101
|
+
const nodeId = namespace.addFolder(hasInverseAndForwardReferences, { browseName: "ReferenceNode1" });
|
|
102
|
+
hasInverseAndForwardReferences.addReference({ nodeId, referenceType: "AlarmGroupMember" });
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// --- HasReferencesWithDifferentParentTypes ---
|
|
106
|
+
namespace.addObject({
|
|
107
|
+
browseName: "HasReferencesWithDifferentParentTypes",
|
|
108
|
+
nodeId: "s=Demo.CTT.References.HasReferencesWithDifferentParentTypes",
|
|
109
|
+
componentOf: referenceFolder,
|
|
110
|
+
typeDefinition: "FolderType"
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
// --- HasReferencesOfAReferenceTypeAndSubType ---
|
|
114
|
+
{
|
|
115
|
+
const hasReferencesOfAReferenceTypeAndSubType = namespace.addObject({
|
|
116
|
+
browseName: "HasReferencesOfAReferenceTypeAndSubType",
|
|
117
|
+
nodeId: "s=Demo.CTT.References.HasReferencesOfAReferenceTypeAndSubType",
|
|
118
|
+
typeDefinition: "FolderType",
|
|
119
|
+
componentOf: referenceFolder
|
|
120
|
+
});
|
|
121
|
+
namespace.addVariable({ browseName: "ReferenceNode1", propertyOf: hasReferencesOfAReferenceTypeAndSubType, dataType: "UInt32" });
|
|
122
|
+
namespace.addVariable({ browseName: "ReferenceNode2", propertyOf: hasReferencesOfAReferenceTypeAndSubType, dataType: "UInt32" });
|
|
123
|
+
namespace.addVariable({ browseName: "ReferenceNode3", propertyOf: hasReferencesOfAReferenceTypeAndSubType, dataType: "UInt32" });
|
|
124
|
+
namespace.addVariable({ browseName: "ReferenceNode4", propertyOf: hasReferencesOfAReferenceTypeAndSubType, dataType: "UInt32" });
|
|
125
|
+
for (let i = 0; i < 4; i++) {
|
|
126
|
+
const a = namespace.addObject({ browseName: `AlarmSuppressionGroup${i}`, componentOf: referenceFolder });
|
|
127
|
+
hasReferencesOfAReferenceTypeAndSubType.addReference({ nodeId: a, referenceType: "HasAlarmSuppressionGroup" });
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Object with methods for conformance testing (MethodNoArgs, MethodIO, MethodI, MethodO).
|
|
3
|
+
*/
|
|
4
|
+
import type { Namespace, UAObject } from "node-opcua-address-space";
|
|
5
|
+
import { assert } from "node-opcua-assert";
|
|
6
|
+
import { makeNodeId } from "node-opcua-nodeid";
|
|
7
|
+
import { StatusCodes } from "node-opcua-status-code";
|
|
8
|
+
import { DataType } from "node-opcua-variant";
|
|
9
|
+
|
|
10
|
+
export function addObjectWithMethod(namespace: Namespace, parentFolder: UAObject) {
|
|
11
|
+
const myObject = namespace.addObject({
|
|
12
|
+
nodeId: "s=ObjectWithMethods",
|
|
13
|
+
organizedBy: parentFolder,
|
|
14
|
+
browseName: "ObjectWithMethods"
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
// MethodNoArgs
|
|
18
|
+
const methodNoArgs = namespace.addMethod(myObject, {
|
|
19
|
+
browseName: "MethodNoArgs",
|
|
20
|
+
nodeId: "s=MethodNoArgs"
|
|
21
|
+
});
|
|
22
|
+
assert(
|
|
23
|
+
makeNodeId("MethodNoArgs", namespace.index)
|
|
24
|
+
.toString()
|
|
25
|
+
.match(/s=MethodNoArgs/)
|
|
26
|
+
);
|
|
27
|
+
assert(methodNoArgs.nodeId.toString().match(/s=MethodNoArgs/));
|
|
28
|
+
|
|
29
|
+
methodNoArgs.bindMethod((_inputArguments, _context, callback) => {
|
|
30
|
+
callback(null, { statusCode: StatusCodes.Good, outputArguments: [] });
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
// MethodIO (input + output)
|
|
34
|
+
const methodIO = namespace.addMethod(myObject, {
|
|
35
|
+
browseName: "MethodIO",
|
|
36
|
+
nodeId: makeNodeId("MethodIO", namespace.index),
|
|
37
|
+
inputArguments: [
|
|
38
|
+
{
|
|
39
|
+
name: "ShutterLag",
|
|
40
|
+
description: { text: "specifies the number of seconds to wait before the picture is taken " },
|
|
41
|
+
dataType: DataType.UInt32
|
|
42
|
+
}
|
|
43
|
+
],
|
|
44
|
+
outputArguments: [
|
|
45
|
+
{
|
|
46
|
+
name: "Result",
|
|
47
|
+
description: { text: "the result" },
|
|
48
|
+
dataType: "Int32"
|
|
49
|
+
}
|
|
50
|
+
]
|
|
51
|
+
});
|
|
52
|
+
methodIO.bindMethod((_inputArguments, _context, callback) => {
|
|
53
|
+
callback(null, {
|
|
54
|
+
statusCode: StatusCodes.Good,
|
|
55
|
+
outputArguments: [{ dataType: DataType.Int32, value: 42 }]
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// MethodI (input only)
|
|
60
|
+
const methodI = namespace.addMethod(myObject, {
|
|
61
|
+
browseName: "MethodI",
|
|
62
|
+
nodeId: "s=MethodI",
|
|
63
|
+
inputArguments: [
|
|
64
|
+
{
|
|
65
|
+
name: "ShutterLag",
|
|
66
|
+
description: { text: "specifies the number of seconds to wait before the picture is taken " },
|
|
67
|
+
dataType: DataType.UInt32
|
|
68
|
+
}
|
|
69
|
+
]
|
|
70
|
+
});
|
|
71
|
+
methodI.bindMethod((_inputArguments, _context, callback) => {
|
|
72
|
+
callback(null, { statusCode: StatusCodes.Good, outputArguments: [] });
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// MethodO (output only)
|
|
76
|
+
const methodO = namespace.addMethod(myObject, {
|
|
77
|
+
browseName: "MethodO",
|
|
78
|
+
nodeId: "s=MethodO",
|
|
79
|
+
outputArguments: [
|
|
80
|
+
{
|
|
81
|
+
name: "Result",
|
|
82
|
+
description: { text: "the result" },
|
|
83
|
+
dataType: "Int32"
|
|
84
|
+
}
|
|
85
|
+
]
|
|
86
|
+
});
|
|
87
|
+
methodO.bindMethod((_inputArguments, _context, callback) => {
|
|
88
|
+
callback(null, {
|
|
89
|
+
statusCode: StatusCodes.Good,
|
|
90
|
+
outputArguments: [{ dataType: DataType.Int32, value: 42 }]
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 10-deep path and very large array variables for conformance testing.
|
|
3
|
+
*/
|
|
4
|
+
import type { Namespace, UAObject } from "node-opcua-address-space";
|
|
5
|
+
|
|
6
|
+
import { addArrayVariable } from "./helpers";
|
|
7
|
+
import { typeAndDefaultValue } from "./type_defaults";
|
|
8
|
+
|
|
9
|
+
export function addPath10Deep(namespace: Namespace, simulation_folder: UAObject) {
|
|
10
|
+
let parent = simulation_folder;
|
|
11
|
+
for (let i = 1; i <= 10; i++) {
|
|
12
|
+
const name = `Path_${i.toString()}Deep`;
|
|
13
|
+
const child = namespace.addObject({
|
|
14
|
+
organizedBy: parent,
|
|
15
|
+
browseName: name,
|
|
16
|
+
description: `A folder at the top of ${i} elements`,
|
|
17
|
+
typeDefinition: "FolderType",
|
|
18
|
+
nodeId: `s=${name}`
|
|
19
|
+
});
|
|
20
|
+
parent = child;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function addVeryLargeArrayVariables(namespace: Namespace, objectsFolder: UAObject): void {
|
|
25
|
+
const scalarStaticLargeArray = namespace.addObject({
|
|
26
|
+
organizedBy: objectsFolder,
|
|
27
|
+
browseName: "Static_Scalar_Large_Array",
|
|
28
|
+
description: "Single dimension, suggested size of 100k-elements per array.",
|
|
29
|
+
nodeId: "s=Static_Scalar_Large_Array"
|
|
30
|
+
});
|
|
31
|
+
for (const e of typeAndDefaultValue) {
|
|
32
|
+
const realType = e.realType || e.type;
|
|
33
|
+
addArrayVariable(namespace, scalarStaticLargeArray, e.type, e.defaultValue, realType, 50 * 1024, "");
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sample views for conformance testing.
|
|
3
|
+
*/
|
|
4
|
+
import type { Namespace } from "node-opcua-address-space";
|
|
5
|
+
|
|
6
|
+
export function addSampleView(namespace: Namespace): void {
|
|
7
|
+
const addressSpace = namespace.addressSpace;
|
|
8
|
+
|
|
9
|
+
const view1 = namespace.addView({
|
|
10
|
+
organizedBy: addressSpace.rootFolder.views,
|
|
11
|
+
browseName: "SampleView",
|
|
12
|
+
nodeId: "s=SampleView"
|
|
13
|
+
});
|
|
14
|
+
view1.addReference({
|
|
15
|
+
nodeId: "i=2256", // Server_Status
|
|
16
|
+
referenceType: "Organizes"
|
|
17
|
+
});
|
|
18
|
+
view1.addReference({
|
|
19
|
+
nodeId: "i=11715", // Server_Namespaces
|
|
20
|
+
referenceType: "Organizes"
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
namespace.addView({
|
|
24
|
+
organizedBy: addressSpace.rootFolder.views,
|
|
25
|
+
browseName: "OtherSampleView",
|
|
26
|
+
nodeId: "s=OtherSampleView"
|
|
27
|
+
});
|
|
28
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simulation (dynamic) variables that change randomly on a timer.
|
|
3
|
+
*/
|
|
4
|
+
import type { Namespace, UAObject, UAVariable } from "node-opcua-address-space";
|
|
5
|
+
import { assert } from "node-opcua-assert";
|
|
6
|
+
import { isValidBoolean, isValidUInt16 } from "node-opcua-basic-types";
|
|
7
|
+
import { DataType, Variant, VariantArrayType } from "node-opcua-variant";
|
|
8
|
+
|
|
9
|
+
import { addVariable, findDataType, getRandomFuncForType } from "./helpers";
|
|
10
|
+
import { typeAndDefaultValue } from "./type_defaults";
|
|
11
|
+
|
|
12
|
+
export function addSimulationVariables(namespace: Namespace, scalarFolder: UAObject): void {
|
|
13
|
+
let values_to_change: { dataType: DataType; randomFunc: () => unknown; variable: UAVariable }[] = [];
|
|
14
|
+
|
|
15
|
+
function add_simulation_variable(parent: UAObject, dataTypeName: string, defaultValue: unknown, realTypeName: string): UAVariable {
|
|
16
|
+
realTypeName = realTypeName || dataTypeName;
|
|
17
|
+
const dataType = findDataType(realTypeName);
|
|
18
|
+
const randomFunc = getRandomFuncForType(dataType);
|
|
19
|
+
|
|
20
|
+
// c8 ignore next
|
|
21
|
+
if (typeof randomFunc !== "function") {
|
|
22
|
+
throw new Error(`a random function must exist for basicType ${dataTypeName}`);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const variable = addVariable(namespace, parent, dataTypeName, realTypeName, defaultValue, -1, null, "");
|
|
26
|
+
values_to_change.push({ dataType, randomFunc, variable });
|
|
27
|
+
return variable;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const simulation = namespace.addObject({
|
|
31
|
+
browseName: "Scalar_Simulation",
|
|
32
|
+
description: "This folder will contain one item per supported data-type.",
|
|
33
|
+
nodeId: "s=Scalar_Simulation",
|
|
34
|
+
organizedBy: scalarFolder
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
for (const e of typeAndDefaultValue) {
|
|
38
|
+
const dataType = e.type;
|
|
39
|
+
const defaultValue = typeof e.defaultValue === "function" ? e.defaultValue() : e.defaultValue;
|
|
40
|
+
const realType = e.realType || dataType;
|
|
41
|
+
add_simulation_variable(simulation, dataType, defaultValue, realType);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Management nodes
|
|
45
|
+
let interval = 2000;
|
|
46
|
+
let enabled = true;
|
|
47
|
+
let timer: NodeJS.Timeout | undefined;
|
|
48
|
+
|
|
49
|
+
function change_randomly() {
|
|
50
|
+
for (const element of values_to_change) {
|
|
51
|
+
const variant = (element.variable as unknown as Record<string, { variant: Variant }>)._backdoor_placeholder.variant;
|
|
52
|
+
variant.value = element.randomFunc();
|
|
53
|
+
element.variable.setValueFromSource(variant);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function delete_Timer() {
|
|
58
|
+
if (timer) {
|
|
59
|
+
try {
|
|
60
|
+
clearInterval(timer);
|
|
61
|
+
} catch (_err) {
|
|
62
|
+
/* c8 ignore next */
|
|
63
|
+
// the leak detector may throw if the timer handle is already disposed
|
|
64
|
+
}
|
|
65
|
+
timer = undefined;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function install_Timer() {
|
|
70
|
+
delete_Timer();
|
|
71
|
+
assert(!timer);
|
|
72
|
+
if (enabled) {
|
|
73
|
+
timer = setInterval(() => {
|
|
74
|
+
change_randomly();
|
|
75
|
+
}, interval);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function tearDown_Timer() {
|
|
80
|
+
delete_Timer();
|
|
81
|
+
values_to_change = [];
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const intervalVariable = namespace.addVariable({
|
|
85
|
+
browseName: "Interval",
|
|
86
|
+
componentOf: simulation,
|
|
87
|
+
dataType: "UInt16",
|
|
88
|
+
description: { locale: "en", text: "The rate (in msec) of change for all Simulated items." },
|
|
89
|
+
nodeId: "s=Scalar_Simulation_Interval",
|
|
90
|
+
value: new Variant({
|
|
91
|
+
arrayType: VariantArrayType.Scalar,
|
|
92
|
+
dataType: DataType.UInt16,
|
|
93
|
+
value: interval
|
|
94
|
+
})
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
intervalVariable.on("value_changed", (dataValue /*,indexRange*/) => {
|
|
98
|
+
const variant = dataValue.value;
|
|
99
|
+
assert(variant instanceof Variant);
|
|
100
|
+
assert(isValidUInt16(variant.value), " value must be valid for dataType");
|
|
101
|
+
interval = variant.value;
|
|
102
|
+
install_Timer();
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
const enabledVariable = namespace.addVariable({
|
|
106
|
+
componentOf: simulation,
|
|
107
|
+
browseName: "Enabled",
|
|
108
|
+
description: { locale: "en", text: "Enabled" },
|
|
109
|
+
nodeId: "s=Scalar_Simulation_Enabled",
|
|
110
|
+
dataType: "Boolean",
|
|
111
|
+
value: new Variant({
|
|
112
|
+
dataType: DataType.Boolean,
|
|
113
|
+
arrayType: VariantArrayType.Scalar,
|
|
114
|
+
value: enabled
|
|
115
|
+
})
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
enabledVariable.on("value_changed", (dataValue /*,indexRange*/) => {
|
|
119
|
+
const variant = dataValue.value;
|
|
120
|
+
assert(variant instanceof Variant);
|
|
121
|
+
assert(isValidBoolean(variant.value), " value must be valid for dataType");
|
|
122
|
+
enabled = variant.value;
|
|
123
|
+
install_Timer();
|
|
124
|
+
});
|
|
125
|
+
install_Timer();
|
|
126
|
+
|
|
127
|
+
const addressSpace = namespace.addressSpace;
|
|
128
|
+
addressSpace.registerShutdownTask(tearDown_Timer);
|
|
129
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Static scalar, array, and multi-dimensional array variables + image loading.
|
|
3
|
+
*/
|
|
4
|
+
import fs from "node:fs";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import type { Namespace, UAObject, UAVariable } from "node-opcua-address-space";
|
|
7
|
+
import { make_errorLog } from "node-opcua-debug";
|
|
8
|
+
import { DataType, Variant } from "node-opcua-variant";
|
|
9
|
+
|
|
10
|
+
import { addArrayVariable, addMultiDimensionalArrayVariable, addScalarVariable } from "./helpers";
|
|
11
|
+
import { typeAndDefaultValue } from "./type_defaults";
|
|
12
|
+
|
|
13
|
+
const errorLog = make_errorLog(__filename);
|
|
14
|
+
|
|
15
|
+
export async function addStaticVariables(namespace: Namespace, scalarFolder: UAObject): Promise<void> {
|
|
16
|
+
const staticScalarFolder = namespace.addObject({
|
|
17
|
+
organizedBy: scalarFolder,
|
|
18
|
+
browseName: "Scalars",
|
|
19
|
+
description: "This folder will contain one item per supported data-type.",
|
|
20
|
+
nodeId: "s=Static_Scalar"
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
// Scalar variables
|
|
24
|
+
for (const e of typeAndDefaultValue) {
|
|
25
|
+
const dataType = e.type;
|
|
26
|
+
const realType = e.realType || dataType;
|
|
27
|
+
const defaultValue = typeof e.defaultValue === "function" ? e.defaultValue() : e.defaultValue;
|
|
28
|
+
addScalarVariable(namespace, staticScalarFolder, dataType, realType, defaultValue, "");
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Load images
|
|
32
|
+
async function setImage(imageType: string, filename: string): Promise<void> {
|
|
33
|
+
const fullPath = path.join(__dirname, "../../data", filename);
|
|
34
|
+
const imageNode = namespace.findNode(`s=Static_Scalar_Image${imageType}`) as UAVariable;
|
|
35
|
+
try {
|
|
36
|
+
const data = await fs.promises.readFile(fullPath);
|
|
37
|
+
imageNode.setValueFromSource(new Variant({ dataType: DataType.ByteString, value: data }));
|
|
38
|
+
} catch (_err) {
|
|
39
|
+
errorLog("cannot load file =", fullPath);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
await setImage("BMP", "image.bmp");
|
|
44
|
+
await setImage("PNG", "tux.png");
|
|
45
|
+
await setImage("GIF", "gif-anime.gif");
|
|
46
|
+
await setImage("JPG", "tiger.jpg");
|
|
47
|
+
|
|
48
|
+
// Array variables
|
|
49
|
+
const staticArraysFolders = namespace.addObject({
|
|
50
|
+
organizedBy: scalarFolder,
|
|
51
|
+
browseName: "Arrays",
|
|
52
|
+
description: "Single dimension, suggested size of 10-elements per array. Unsupported types will be missing from the address-space.",
|
|
53
|
+
nodeId: "s=Static_Array"
|
|
54
|
+
});
|
|
55
|
+
for (const e of typeAndDefaultValue) {
|
|
56
|
+
const realType = e.realType || e.type;
|
|
57
|
+
addArrayVariable(namespace, staticArraysFolders, e.type, e.defaultValue, realType, 10, "");
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Multi-dimensional array variables
|
|
61
|
+
const staticMultiDimensionalArrays = namespace.addObject({
|
|
62
|
+
organizedBy: scalarFolder,
|
|
63
|
+
browseName: "Multi-Dimensional Arrays",
|
|
64
|
+
description: "Single dimension, suggested size of 10-elements per array. Unsupported types will be missing from the address-space.",
|
|
65
|
+
nodeId: "s=Static_MultiDimensional_Array"
|
|
66
|
+
});
|
|
67
|
+
for (const e of typeAndDefaultValue) {
|
|
68
|
+
const realType = e.realType || e.type;
|
|
69
|
+
addMultiDimensionalArrayVariable(namespace, staticMultiDimensionalArrays, e.type, e.defaultValue, realType, 2, 3, "");
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Trigger nodes that raise events on write.
|
|
3
|
+
*/
|
|
4
|
+
import type { Namespace, UAObject } from "node-opcua-address-space";
|
|
5
|
+
import type { NodeIdLike } from "node-opcua-nodeid";
|
|
6
|
+
import { makeNodeId } from "node-opcua-nodeid";
|
|
7
|
+
import { DataType, Variant } from "node-opcua-variant";
|
|
8
|
+
|
|
9
|
+
export function addTriggerNodes(namespace: Namespace, parentFolder: UAObject): void {
|
|
10
|
+
const addressSpace = namespace.addressSpace;
|
|
11
|
+
|
|
12
|
+
const myEvtType =
|
|
13
|
+
namespace.findObjectType("MyEventType") ||
|
|
14
|
+
namespace.addEventType({
|
|
15
|
+
browseName: "MyEventType",
|
|
16
|
+
subtypeOf: "BaseEventType"
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
function _add_trigger_node(parent: UAObject, browseName: string, nodeId: NodeIdLike) {
|
|
20
|
+
const triggerNode = namespace.addVariable({
|
|
21
|
+
browseName,
|
|
22
|
+
nodeId,
|
|
23
|
+
eventSourceOf: parent,
|
|
24
|
+
dataType: "Double",
|
|
25
|
+
typeDefinition: makeNodeId(68)
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
let value = 100.0;
|
|
29
|
+
const getFunc = () => new Variant({ dataType: DataType.Double, value });
|
|
30
|
+
const setFunc = (variant: Variant) => {
|
|
31
|
+
value = variant.value;
|
|
32
|
+
const server = addressSpace.rootFolder.objects.server;
|
|
33
|
+
server.raiseEvent(myEvtType, {
|
|
34
|
+
message: { dataType: DataType.LocalizedText, value: { text: "Hello World" } },
|
|
35
|
+
severity: { dataType: DataType.UInt32, value: 32 }
|
|
36
|
+
});
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
triggerNode.bindVariable({ get: getFunc, set: setFunc });
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const sampleTriggerNode = namespace.addObject({
|
|
43
|
+
browseName: "SampleTriggerNode",
|
|
44
|
+
eventNotifier: 0x1,
|
|
45
|
+
organizedBy: parentFolder
|
|
46
|
+
});
|
|
47
|
+
_add_trigger_node(sampleTriggerNode, "TriggerNode01", "s=TriggerNode01");
|
|
48
|
+
_add_trigger_node(sampleTriggerNode, "TriggerNode02", "s=TriggerNode02");
|
|
49
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default type/value table shared by static, simulation, mass, and large array builders.
|
|
3
|
+
*/
|
|
4
|
+
import { emptyGuid } from "node-opcua-basic-types";
|
|
5
|
+
import * as ec from "node-opcua-basic-types";
|
|
6
|
+
import { LocalizedText, QualifiedName } from "node-opcua-data-model";
|
|
7
|
+
import { coerceNodeId } from "node-opcua-nodeid";
|
|
8
|
+
|
|
9
|
+
export const typeAndDefaultValue = [
|
|
10
|
+
{ type: "Boolean", defaultValue: false },
|
|
11
|
+
{ type: "ByteString", defaultValue: Buffer.from("OPCUA") },
|
|
12
|
+
{ type: "DateTime", defaultValue: ec.getMinOPCUADate() },
|
|
13
|
+
{ type: "Double", defaultValue: 0.0 },
|
|
14
|
+
{ type: "Float", defaultValue: 0.0 },
|
|
15
|
+
{ type: "Guid", defaultValue: emptyGuid },
|
|
16
|
+
{ type: "SByte", defaultValue: 0 },
|
|
17
|
+
{ type: "Int16", defaultValue: 0 },
|
|
18
|
+
{ type: "Int32", defaultValue: 0 },
|
|
19
|
+
{
|
|
20
|
+
type: "NodeId",
|
|
21
|
+
defaultValue() {
|
|
22
|
+
return coerceNodeId(`ns=${3};g=00000000-0000-0000-0000-000000000023`);
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
{ type: "String", defaultValue: "OPCUA" },
|
|
26
|
+
{ type: "Byte", defaultValue: 0 },
|
|
27
|
+
{ type: "UInt16", defaultValue: 0 },
|
|
28
|
+
{ type: "UInt32", defaultValue: 0 },
|
|
29
|
+
{ type: "Duration", realType: "Double", defaultValue: 0.0 },
|
|
30
|
+
{ type: "Number", realType: "UInt16", defaultValue: 0 }, // Number is abstract
|
|
31
|
+
{ type: "Integer", realType: "Int64", defaultValue: 0 }, // because Integer is abstract, we choose Int64
|
|
32
|
+
{ type: "UInteger", realType: "UInt64", defaultValue: 0 },
|
|
33
|
+
{
|
|
34
|
+
type: "UtcTime",
|
|
35
|
+
realType: "DateTime",
|
|
36
|
+
defaultValue() {
|
|
37
|
+
return new Date();
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
{ type: "LocaleId", realType: "String", defaultValue: "" },
|
|
41
|
+
{
|
|
42
|
+
type: "LocalizedText",
|
|
43
|
+
defaultValue() {
|
|
44
|
+
return new LocalizedText({});
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
type: "QualifiedName",
|
|
49
|
+
defaultValue() {
|
|
50
|
+
return new QualifiedName();
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
{ type: "UInt64", defaultValue: [0, 0] },
|
|
54
|
+
{ type: "Int64", defaultValue: [0, 0] },
|
|
55
|
+
{ type: "XmlElement", defaultValue: "<string1>OPCUA</string1>" },
|
|
56
|
+
{ type: "ImageBMP", realType: "ByteString", defaultValue: null },
|
|
57
|
+
{ type: "ImageGIF", realType: "ByteString", defaultValue: null },
|
|
58
|
+
{ type: "ImageJPG", realType: "ByteString", defaultValue: null },
|
|
59
|
+
{ type: "ImagePNG", realType: "ByteString", defaultValue: null }
|
|
60
|
+
];
|