node-opcua-service-filter 2.74.0 → 2.76.2
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/check_event_clause.d.ts +17 -0
- package/dist/check_event_clause.js +52 -0
- package/dist/check_event_clause.js.map +1 -0
- package/dist/check_where_clause.d.ts +3 -0
- package/dist/check_where_clause.js +250 -0
- package/dist/check_where_clause.js.map +1 -0
- package/dist/extract_event_field.d.ts +9 -0
- package/dist/extract_event_field.js +53 -0
- package/dist/extract_event_field.js.map +1 -0
- package/dist/filter_context.d.ts +11 -0
- package/dist/filter_context.js +3 -0
- package/dist/filter_context.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -1
- package/dist/make_content_filter.d.ts +22 -0
- package/dist/make_content_filter.js +121 -0
- package/dist/make_content_filter.js.map +1 -0
- package/dist/on_address_space/extract_event_fields.d.ts +10 -0
- package/dist/on_address_space/extract_event_fields.js +18 -0
- package/dist/on_address_space/extract_event_fields.js.map +1 -0
- package/dist/on_address_space/filter_context_on_address_space.d.ts +19 -0
- package/dist/on_address_space/filter_context_on_address_space.js +109 -0
- package/dist/on_address_space/filter_context_on_address_space.js.map +1 -0
- package/dist/on_address_space/index.d.ts +2 -0
- package/dist/on_address_space/index.js +19 -0
- package/dist/on_address_space/index.js.map +1 -0
- package/dist/resolve_operand.d.ts +4 -0
- package/dist/resolve_operand.js +45 -0
- package/dist/resolve_operand.js.map +1 -0
- package/dist/tools_event_filter.d.ts +4 -16
- package/dist/tools_event_filter.js +53 -121
- package/dist/tools_event_filter.js.map +1 -1
- package/package.json +16 -15
- package/source/check_event_clause.ts +54 -0
- package/source/check_where_clause.ts +287 -0
- package/source/extract_event_field.ts +55 -0
- package/source/filter_context.ts +15 -0
- package/source/index.ts +8 -0
- package/source/make_content_filter.ts +132 -0
- package/source/on_address_space/extract_event_fields.ts +24 -0
- package/source/on_address_space/filter_context_on_address_space.ts +134 -0
- package/source/on_address_space/index.ts +3 -0
- package/source/resolve_operand.ts +45 -0
- package/source/tools_event_filter.ts +61 -129
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { NodeClass } from "node-opcua-data-model";
|
|
2
|
+
import { make_warningLog } from "node-opcua-debug";
|
|
3
|
+
import { NodeId } from "node-opcua-nodeid";
|
|
4
|
+
import { constructBrowsePathFromQualifiedName, makeBrowsePath } from "node-opcua-service-translate-browse-path";
|
|
5
|
+
import { StatusCodes } from "node-opcua-status-code";
|
|
6
|
+
import { SimpleAttributeOperand, AttributeOperand, VariableAttributes } from "node-opcua-types";
|
|
7
|
+
import { DataType, Variant } from "node-opcua-variant";
|
|
8
|
+
import { FilterContext } from "./filter_context";
|
|
9
|
+
|
|
10
|
+
const warningLog = make_warningLog("FILTER");
|
|
11
|
+
|
|
12
|
+
// export function readOperand(context: FilterContext, operand: SimpleAttributeOperand): Variant {
|
|
13
|
+
// // navigate to the innerNode specified by the browsePath [ QualifiedName]
|
|
14
|
+
// const browsePath = constructBrowsePathFromQualifiedName({ nodeId: context.eventSource }, operand.browsePath);
|
|
15
|
+
// const targetNode = context.browsePath(browsePath);
|
|
16
|
+
// if (!targetNode) {
|
|
17
|
+
// return new Variant({ dataType: DataType.Null });
|
|
18
|
+
// }
|
|
19
|
+
// return context.readNodeValue(targetNode);
|
|
20
|
+
// }
|
|
21
|
+
|
|
22
|
+
export function resolveOperand(context: FilterContext, operand: SimpleAttributeOperand | AttributeOperand): Variant {
|
|
23
|
+
if (operand instanceof SimpleAttributeOperand) {
|
|
24
|
+
const browsePath = constructBrowsePathFromQualifiedName({ nodeId: context.eventSource }, operand.browsePath);
|
|
25
|
+
|
|
26
|
+
const target: NodeId | null = context.browsePath(browsePath);
|
|
27
|
+
if (!target) {
|
|
28
|
+
return new Variant({ dataType: DataType.Null });
|
|
29
|
+
// return new Variant({ dataType: DataType.StatusCode, value: StatusCodes.BadNodeIdUnknown });
|
|
30
|
+
}
|
|
31
|
+
const nodeClass = context.getNodeClass(target);
|
|
32
|
+
if (nodeClass !== NodeClass.Variable) {
|
|
33
|
+
warningLog("resolveOperand: cannot find variable here but got nodeClass", NodeClass[nodeClass], browsePath.toString());
|
|
34
|
+
return new Variant({ dataType: DataType.StatusCode, value: StatusCodes.BadNodeClassInvalid });
|
|
35
|
+
}
|
|
36
|
+
const value = context.readNodeValue(target);
|
|
37
|
+
return value;
|
|
38
|
+
} else {
|
|
39
|
+
if (!(operand instanceof AttributeOperand)) {
|
|
40
|
+
throw new Error("expecting an AttributeOperand");
|
|
41
|
+
}
|
|
42
|
+
warningLog("AttributeOperand is not yet implemented");
|
|
43
|
+
return new Variant({ dataType: DataType.Null });
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -1,168 +1,100 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @module node-opcua-service-filter
|
|
3
3
|
*/
|
|
4
|
-
// tslint:disable:object-literal-shorthand
|
|
5
|
-
// tslint:disable:only-arrow-functions
|
|
6
|
-
// tslint:disable:max-line-length
|
|
7
|
-
|
|
8
|
-
import { assert } from "node-opcua-assert";
|
|
9
4
|
import { ObjectTypeIds } from "node-opcua-constants";
|
|
10
|
-
import { AttributeIds, stringToQualifiedName } from "node-opcua-data-model";
|
|
11
|
-
import {
|
|
12
|
-
import { makeNodeId, NodeId, resolveNodeId, sameNodeId } from "node-opcua-nodeid";
|
|
13
|
-
import { DataType, Variant } from "node-opcua-variant";
|
|
5
|
+
import { AttributeIds, QualifiedName, stringToQualifiedName } from "node-opcua-data-model";
|
|
6
|
+
import { NodeIdLike, resolveNodeId } from "node-opcua-nodeid";
|
|
14
7
|
|
|
15
|
-
import { EventFilter, SimpleAttributeOperand } from "./imports";
|
|
8
|
+
import { ContentFilter, ContentFilterElement, ContentFilterOptions, EventFilter, FilterOperator, SimpleAttributeOperand } from "./imports";
|
|
16
9
|
|
|
17
|
-
|
|
18
|
-
const doDebug = checkDebugFlag(__filename);
|
|
10
|
+
import { ofType } from "./make_content_filter";
|
|
19
11
|
|
|
20
|
-
|
|
21
|
-
* helper to construct event filters:
|
|
22
|
-
* construct a simple event filter
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
* @example
|
|
26
|
-
*
|
|
27
|
-
* constructEventFilter(["SourceName","Message","ReceiveTime"]);
|
|
28
|
-
*
|
|
29
|
-
* constructEventFilter(["SourceName",{namespaceIndex:2 , "MyData"}]);
|
|
30
|
-
* constructEventFilter(["SourceName","2:MyData" ]);
|
|
31
|
-
*
|
|
32
|
-
* constructEventFilter(["SourceName" ,["EnabledState","EffectiveDisplayName"] ]);
|
|
33
|
-
* constructEventFilter(["SourceName" ,"EnabledState.EffectiveDisplayName" ]);
|
|
34
|
-
*
|
|
35
|
-
*/
|
|
36
|
-
export function constructEventFilter(arrayOfNames: string[] | string, conditionTypes?: NodeId[] | NodeId): EventFilter {
|
|
12
|
+
export function constructSelectClause(arrayOfNames: string | string[]): SimpleAttributeOperand[] {
|
|
37
13
|
if (!Array.isArray(arrayOfNames)) {
|
|
38
|
-
return
|
|
39
|
-
}
|
|
40
|
-
if (conditionTypes && !Array.isArray(conditionTypes)) {
|
|
41
|
-
return constructEventFilter(arrayOfNames, [conditionTypes]);
|
|
42
|
-
}
|
|
43
|
-
// istanbul ignore next
|
|
44
|
-
if (!(arrayOfNames instanceof Array)) {
|
|
45
|
-
throw new Error("internal error");
|
|
14
|
+
return constructSelectClause([arrayOfNames]);
|
|
46
15
|
}
|
|
16
|
+
|
|
47
17
|
// replace "string" element in the form A.B.C into [ "A","B","C"]
|
|
48
|
-
const arrayOfNames2 = arrayOfNames.map((path) =>
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
}
|
|
52
|
-
return path.split(".");
|
|
53
|
-
});
|
|
18
|
+
const arrayOfNames2 = arrayOfNames.map((path) => (typeof path !== "string" ? path : path.split(".")));
|
|
19
|
+
|
|
20
|
+
const arrayOfNames3 = arrayOfNames2.map((path) => (Array.isArray(path) ? path.map(stringToQualifiedName) : path));
|
|
54
21
|
|
|
55
|
-
const arrayOfNames3 = arrayOfNames2.map((path) => {
|
|
56
|
-
if (Array.isArray(path)) {
|
|
57
|
-
return path.map(stringToQualifiedName);
|
|
58
|
-
}
|
|
59
|
-
return path;
|
|
60
|
-
});
|
|
61
22
|
// replace "string" elements in arrayOfName with QualifiedName in namespace 0
|
|
62
|
-
const arrayOfNames4 = arrayOfNames3.map((s) =>
|
|
63
|
-
return typeof s === "string" ? stringToQualifiedName(s) : s;
|
|
64
|
-
});
|
|
23
|
+
const arrayOfNames4 = arrayOfNames3.map((s) => (typeof s === "string" ? stringToQualifiedName(s) : s));
|
|
65
24
|
|
|
66
25
|
// construct browse paths array
|
|
67
|
-
const browsePaths = arrayOfNames4.map((s) =>
|
|
68
|
-
return Array.isArray(s) ? s : [s];
|
|
69
|
-
});
|
|
26
|
+
const browsePaths = arrayOfNames4.map((s) => (Array.isArray(s) ? s : [s]));
|
|
70
27
|
|
|
71
28
|
// Part 4 page 127:
|
|
72
29
|
// In some cases the same BrowsePath will apply to multiple EventTypes. If the Client specifies the BaseEventType
|
|
73
30
|
// in the SimpleAttributeOperand then the Server shall evaluate the BrowsePath without considering the Type.
|
|
74
31
|
|
|
32
|
+
const isBrowsePathForConditionId = (browsePath: QualifiedName[]) =>
|
|
33
|
+
browsePath.length === 1 && browsePath[0].namespaceIndex === 0 && browsePath[0].name === "ConditionId";
|
|
34
|
+
|
|
75
35
|
// [..]
|
|
76
36
|
// The SimpleAttributeOperand structure allows the Client to specify any Attribute, however, the Server is only
|
|
77
37
|
// required to support the Value Attribute for Variable Nodes and the NodeId Attribute for Object Nodes.
|
|
78
38
|
// That said, profiles defined in Part 7 may make support for additional Attributes mandatory.
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
39
|
+
const selectClauses = browsePaths.map((browsePath: QualifiedName[]) => {
|
|
40
|
+
if (isBrowsePathForConditionId(browsePath)) {
|
|
41
|
+
// special case
|
|
42
|
+
//
|
|
43
|
+
// The NodeId of the Condition instance is used as ConditionId. It is not explicitly modelled as a
|
|
44
|
+
// component of the ConditionType. However, it can be requested with the following
|
|
45
|
+
// SimpleAttributeOperand (see Table 10) in the SelectClause of the EventFilter:
|
|
46
|
+
//
|
|
47
|
+
// SimpleAttributeOperand
|
|
48
|
+
// Name Type Description
|
|
49
|
+
// typeId NodeId NodeId of the ConditionType Node
|
|
50
|
+
// browsePath[] QualifiedName empty
|
|
51
|
+
// attributeId IntegerId Id of the NodeId Attribute
|
|
52
|
+
//
|
|
90
53
|
return new SimpleAttributeOperand({
|
|
91
54
|
attributeId: AttributeIds.NodeId,
|
|
92
|
-
browsePath:
|
|
55
|
+
browsePath: null,
|
|
93
56
|
indexRange: undefined, // NumericRange
|
|
94
|
-
typeDefinitionId:
|
|
57
|
+
typeDefinitionId: ObjectTypeIds.ConditionType // i=2782
|
|
58
|
+
});
|
|
59
|
+
} else
|
|
60
|
+
return new SimpleAttributeOperand({
|
|
61
|
+
attributeId: AttributeIds.Value,
|
|
62
|
+
browsePath,
|
|
63
|
+
indexRange: undefined, // NumericRange
|
|
64
|
+
typeDefinitionId: ObjectTypeIds.BaseEventType // i=2041
|
|
95
65
|
});
|
|
96
|
-
});
|
|
97
|
-
selectClauses = selectClauses.concat(extraSelectClauses);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
const filter = new EventFilter({
|
|
101
|
-
selectClauses,
|
|
102
|
-
whereClause: {
|
|
103
|
-
// ContentFilter
|
|
104
|
-
elements: [
|
|
105
|
-
// ContentFilterElement
|
|
106
|
-
// {
|
|
107
|
-
// filterOperator: FilterOperator.IsNull,
|
|
108
|
-
// filterOperands: [ //
|
|
109
|
-
// new ElementOperand({
|
|
110
|
-
// index: 123
|
|
111
|
-
// }),
|
|
112
|
-
// new AttributeOperand({
|
|
113
|
-
// nodeId: "i=10",
|
|
114
|
-
// alias: "someText",
|
|
115
|
-
// browsePath: { //RelativePath
|
|
116
|
-
//
|
|
117
|
-
// },
|
|
118
|
-
// attributeId: AttributeIds.Value
|
|
119
|
-
// })
|
|
120
|
-
// ]
|
|
121
|
-
// }
|
|
122
|
-
]
|
|
123
|
-
}
|
|
124
66
|
});
|
|
125
|
-
return
|
|
67
|
+
return selectClauses;
|
|
126
68
|
}
|
|
127
|
-
|
|
128
69
|
/**
|
|
129
|
-
*
|
|
130
|
-
*
|
|
131
|
-
* @return {String}
|
|
70
|
+
* helper to construct event filters:
|
|
71
|
+
* construct a simple event filter
|
|
132
72
|
*
|
|
133
|
-
*
|
|
73
|
+
* "ConditionId" in the arrayOfNames has a special meaning
|
|
134
74
|
*
|
|
75
|
+
* @example
|
|
135
76
|
*
|
|
136
|
-
|
|
137
|
-
function simpleAttributeOperandToPath(self: SimpleAttributeOperand): string {
|
|
138
|
-
if (!self.browsePath) {
|
|
139
|
-
return "";
|
|
140
|
-
}
|
|
141
|
-
return self.browsePath
|
|
142
|
-
.map((a) => {
|
|
143
|
-
return a.name;
|
|
144
|
-
})
|
|
145
|
-
.join("/");
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* @class SimpleAttributeOperand
|
|
150
|
-
* @method toShortString
|
|
151
|
-
* @return {String}
|
|
77
|
+
* constructEventFilter(["SourceName","Message","ReceiveTime"]);
|
|
152
78
|
*
|
|
153
|
-
*
|
|
79
|
+
* constructEventFilter(["SourceName",{namespaceIndex:2 , "MyData"}]);
|
|
80
|
+
* constructEventFilter(["SourceName","2:MyData" ]);
|
|
154
81
|
*
|
|
82
|
+
* constructEventFilter(["SourceName" ,["EnabledState","EffectiveDisplayName"] ]);
|
|
83
|
+
* constructEventFilter(["SourceName" ,"EnabledState.EffectiveDisplayName" ]);
|
|
155
84
|
*
|
|
156
85
|
*/
|
|
157
|
-
export function
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
):
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
86
|
+
export function constructEventFilter(
|
|
87
|
+
arrayOfNames: string[] | string,
|
|
88
|
+
whereClause?: ContentFilterOptions | ContentFilterElement
|
|
89
|
+
): EventFilter {
|
|
90
|
+
const selectClauses = constructSelectClause(arrayOfNames);
|
|
91
|
+
|
|
92
|
+
if (whereClause instanceof ContentFilterElement) {
|
|
93
|
+
whereClause = new ContentFilter({ elements: [whereClause] });
|
|
165
94
|
}
|
|
166
|
-
|
|
167
|
-
|
|
95
|
+
const filter = new EventFilter({
|
|
96
|
+
selectClauses,
|
|
97
|
+
whereClause
|
|
98
|
+
});
|
|
99
|
+
return filter;
|
|
168
100
|
}
|