node-opcua-server 2.75.0 → 2.76.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/base_server.d.ts +110 -110
- package/dist/base_server.js +473 -473
- package/dist/factory.d.ts +12 -12
- package/dist/factory.js +23 -23
- package/dist/filter/check_where_clause_on_address_space.d.ts +3 -3
- package/dist/filter/check_where_clause_on_address_space.js +22 -22
- package/dist/filter/extract_event_fields.d.ts +10 -10
- package/dist/filter/extract_event_fields.js +17 -17
- package/dist/helper.d.ts +10 -10
- package/dist/helper.js +75 -75
- package/dist/history_server_capabilities.d.ts +35 -35
- package/dist/history_server_capabilities.js +43 -43
- package/dist/i_channel_data.d.ts +13 -13
- package/dist/i_channel_data.js +2 -2
- package/dist/i_register_server_manager.d.ts +16 -16
- package/dist/i_register_server_manager.js +2 -2
- package/dist/i_server_side_publish_engine.d.ts +36 -36
- package/dist/i_server_side_publish_engine.js +49 -49
- package/dist/i_socket_data.d.ts +11 -11
- package/dist/i_socket_data.js +2 -2
- package/dist/index.d.ts +16 -16
- package/dist/index.js +32 -32
- package/dist/monitored_item.d.ts +177 -177
- package/dist/monitored_item.js +1001 -1001
- package/dist/node_sampler.d.ts +3 -3
- package/dist/node_sampler.js +75 -75
- package/dist/opcua_server.d.ts +747 -650
- package/dist/opcua_server.js +2431 -2396
- package/dist/opcua_server.js.map +1 -1
- package/dist/queue.d.ts +11 -11
- package/dist/queue.js +71 -71
- package/dist/register_server_manager.d.ts +96 -96
- package/dist/register_server_manager.js +584 -584
- package/dist/register_server_manager_hidden.d.ts +17 -17
- package/dist/register_server_manager_hidden.js +27 -27
- package/dist/register_server_manager_mdns_only.d.ts +22 -22
- package/dist/register_server_manager_mdns_only.js +55 -55
- package/dist/server_capabilities.d.ts +148 -148
- package/dist/server_capabilities.js +92 -92
- package/dist/server_end_point.d.ts +183 -183
- package/dist/server_end_point.js +817 -817
- package/dist/server_engine.d.ts +317 -317
- package/dist/server_engine.js +1716 -1716
- package/dist/server_publish_engine.d.ts +113 -113
- package/dist/server_publish_engine.js +541 -541
- package/dist/server_publish_engine_for_orphan_subscriptions.d.ts +16 -16
- package/dist/server_publish_engine_for_orphan_subscriptions.js +51 -51
- package/dist/server_session.d.ts +182 -182
- package/dist/server_session.js +739 -739
- package/dist/server_subscription.d.ts +421 -421
- package/dist/server_subscription.js +1346 -1346
- package/dist/sessions_compatible_for_transfer.d.ts +2 -2
- package/dist/sessions_compatible_for_transfer.js +39 -39
- package/dist/user_manager.d.ts +32 -32
- package/dist/user_manager.js +74 -74
- package/dist/user_manager_ua.d.ts +3 -3
- package/dist/user_manager_ua.js +39 -39
- package/dist/validate_filter.d.ts +5 -5
- package/dist/validate_filter.js +60 -60
- package/package.json +47 -49
- package/source/opcua_server.ts +154 -14
package/dist/server_engine.js
CHANGED
|
@@ -1,1717 +1,1717 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
-
});
|
|
10
|
-
};
|
|
11
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.ServerEngine = void 0;
|
|
13
|
-
/**
|
|
14
|
-
* @module node-opcua-server
|
|
15
|
-
*/
|
|
16
|
-
const events_1 = require("events");
|
|
17
|
-
const async = require("async");
|
|
18
|
-
const chalk = require("chalk");
|
|
19
|
-
const node_opcua_assert_1 = require("node-opcua-assert");
|
|
20
|
-
const node_opcua_binary_stream_1 = require("node-opcua-binary-stream");
|
|
21
|
-
const node_opcua_address_space_1 = require("node-opcua-address-space");
|
|
22
|
-
const nodeJS_1 = require("node-opcua-address-space/nodeJS");
|
|
23
|
-
const node_opcua_data_value_1 = require("node-opcua-data-value");
|
|
24
|
-
const node_opcua_common_1 = require("node-opcua-common");
|
|
25
|
-
const node_opcua_data_model_1 = require("node-opcua-data-model");
|
|
26
|
-
const node_opcua_nodeid_1 = require("node-opcua-nodeid");
|
|
27
|
-
const node_opcua_service_read_1 = require("node-opcua-service-read");
|
|
28
|
-
const node_opcua_constants_1 = require("node-opcua-constants");
|
|
29
|
-
const node_opcua_date_time_1 = require("node-opcua-date-time");
|
|
30
|
-
const node_opcua_debug_1 = require("node-opcua-debug");
|
|
31
|
-
const node_opcua_nodesets_1 = require("node-opcua-nodesets");
|
|
32
|
-
const node_opcua_object_registry_1 = require("node-opcua-object-registry");
|
|
33
|
-
const node_opcua_service_call_1 = require("node-opcua-service-call");
|
|
34
|
-
const node_opcua_service_subscription_1 = require("node-opcua-service-subscription");
|
|
35
|
-
const node_opcua_service_endpoints_1 = require("node-opcua-service-endpoints");
|
|
36
|
-
const node_opcua_service_history_1 = require("node-opcua-service-history");
|
|
37
|
-
const node_opcua_status_code_1 = require("node-opcua-status-code");
|
|
38
|
-
const node_opcua_types_1 = require("node-opcua-types");
|
|
39
|
-
const node_opcua_variant_1 = require("node-opcua-variant");
|
|
40
|
-
const history_server_capabilities_1 = require("./history_server_capabilities");
|
|
41
|
-
const monitored_item_1 = require("./monitored_item");
|
|
42
|
-
const server_capabilities_1 = require("./server_capabilities");
|
|
43
|
-
const server_publish_engine_1 = require("./server_publish_engine");
|
|
44
|
-
const server_publish_engine_for_orphan_subscriptions_1 = require("./server_publish_engine_for_orphan_subscriptions");
|
|
45
|
-
const server_session_1 = require("./server_session");
|
|
46
|
-
const server_subscription_1 = require("./server_subscription");
|
|
47
|
-
const sessions_compatible_for_transfer_1 = require("./sessions_compatible_for_transfer");
|
|
48
|
-
const debugLog = (0, node_opcua_debug_1.make_debugLog)(__filename);
|
|
49
|
-
const errorLog = (0, node_opcua_debug_1.make_errorLog)(__filename);
|
|
50
|
-
const warningLog = (0, node_opcua_debug_1.make_warningLog)(__filename);
|
|
51
|
-
const doDebug = (0, node_opcua_debug_1.checkDebugFlag)(__filename);
|
|
52
|
-
function upperCaseFirst(str) {
|
|
53
|
-
return str.slice(0, 1).toUpperCase() + str.slice(1);
|
|
54
|
-
}
|
|
55
|
-
function shutdownAndDisposeAddressSpace() {
|
|
56
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
57
|
-
if (this.addressSpace) {
|
|
58
|
-
yield this.addressSpace.shutdown();
|
|
59
|
-
this.addressSpace.dispose();
|
|
60
|
-
delete this.addressSpace;
|
|
61
|
-
}
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
function setSubscriptionDurable(inputArguments, context, callback) {
|
|
65
|
-
// see https://reference.opcfoundation.org/v104/Core/docs/Part5/9.3/
|
|
66
|
-
// https://reference.opcfoundation.org/v104/Core/docs/Part4/6.8/
|
|
67
|
-
(0, node_opcua_assert_1.assert)(Array.isArray(inputArguments));
|
|
68
|
-
(0, node_opcua_assert_1.assert)(typeof callback === "function");
|
|
69
|
-
(0, node_opcua_assert_1.assert)(Object.prototype.hasOwnProperty.call(context, "session"), " expecting a session id in the context object");
|
|
70
|
-
const session = context.session;
|
|
71
|
-
if (!session) {
|
|
72
|
-
return callback(null, { statusCode: node_opcua_status_code_1.StatusCodes.BadInternalError });
|
|
73
|
-
}
|
|
74
|
-
const subscriptionId = inputArguments[0].value;
|
|
75
|
-
const lifetimeInHours = inputArguments[1].value;
|
|
76
|
-
const subscription = session.getSubscription(subscriptionId);
|
|
77
|
-
if (!subscription) {
|
|
78
|
-
// subscription may belongs to a different session that ours
|
|
79
|
-
if (this.findSubscription(subscriptionId)) {
|
|
80
|
-
// if yes, then access to Subscription data should be denied
|
|
81
|
-
return callback(null, { statusCode: node_opcua_status_code_1.StatusCodes.BadUserAccessDenied });
|
|
82
|
-
}
|
|
83
|
-
return callback(null, { statusCode: node_opcua_status_code_1.StatusCodes.BadSubscriptionIdInvalid });
|
|
84
|
-
}
|
|
85
|
-
if (subscription.monitoredItemCount > 0) {
|
|
86
|
-
// This is returned when a Subscription already contains MonitoredItems.
|
|
87
|
-
return callback(null, { statusCode: node_opcua_status_code_1.StatusCodes.BadInvalidState });
|
|
88
|
-
}
|
|
89
|
-
/**
|
|
90
|
-
* MonitoredItems are used to monitor Variable Values for data changes and event notifier
|
|
91
|
-
* Objects for new Events. Subscriptions are used to combine data changes and events of
|
|
92
|
-
* the assigned MonitoredItems to an optimized stream of network messages. A reliable
|
|
93
|
-
* delivery is ensured as long as the lifetime of the Subscription and the queues in the
|
|
94
|
-
* MonitoredItems are long enough for a network interruption between OPC UA Client and
|
|
95
|
-
* Server. All queues that ensure reliable delivery are normally kept in memory and a
|
|
96
|
-
* Server restart would delete them.
|
|
97
|
-
* There are use cases where OPC UA Clients have no permanent network connection to the
|
|
98
|
-
* OPC UA Server or where reliable delivery of data changes and events is necessary
|
|
99
|
-
* even if the OPC UA Server is restarted or the network connection is interrupted
|
|
100
|
-
* for a longer time.
|
|
101
|
-
* To ensure this reliable delivery, the OPC UA Server must store collected data and
|
|
102
|
-
* events in non-volatile memory until the OPC UA Client has confirmed reception.
|
|
103
|
-
* It is possible that there will be data lost if the Server is not shut down gracefully
|
|
104
|
-
* or in case of power failure. But the OPC UA Server should store the queues frequently
|
|
105
|
-
* even if the Server is not shut down.
|
|
106
|
-
* The Method SetSubscriptionDurable defined in OPC 10000-5 is used to set a Subscription
|
|
107
|
-
* into this durable mode and to allow much longer lifetimes and queue sizes than for normal
|
|
108
|
-
* Subscriptions. The Method shall be called before the MonitoredItems are created in the
|
|
109
|
-
* durable Subscription. The Server shall verify that the Method is called within the
|
|
110
|
-
* Session context of the Session that owns the Subscription.
|
|
111
|
-
*
|
|
112
|
-
* A value of 0 for the parameter lifetimeInHours requests the highest lifetime supported by the Server.
|
|
113
|
-
*/
|
|
114
|
-
const highestLifetimeInHours = 24 * 100;
|
|
115
|
-
const revisedLifetimeInHours = lifetimeInHours === 0 ? highestLifetimeInHours : Math.max(1, Math.min(lifetimeInHours, highestLifetimeInHours));
|
|
116
|
-
// also adjust subscription life time
|
|
117
|
-
const currentLifeTimeInHours = (subscription.lifeTimeCount * subscription.publishingInterval) / (1000 * 60 * 60);
|
|
118
|
-
if (currentLifeTimeInHours < revisedLifetimeInHours) {
|
|
119
|
-
const requestedLifetimeCount = Math.ceil((revisedLifetimeInHours * (1000 * 60 * 60)) / subscription.publishingInterval);
|
|
120
|
-
subscription.modify({
|
|
121
|
-
requestedMaxKeepAliveCount: subscription.maxKeepAliveCount,
|
|
122
|
-
requestedPublishingInterval: subscription.publishingInterval,
|
|
123
|
-
maxNotificationsPerPublish: subscription.maxNotificationsPerPublish,
|
|
124
|
-
priority: subscription.priority,
|
|
125
|
-
requestedLifetimeCount
|
|
126
|
-
});
|
|
127
|
-
}
|
|
128
|
-
const callMethodResult = new node_opcua_service_call_1.CallMethodResult({
|
|
129
|
-
statusCode: node_opcua_status_code_1.StatusCodes.Good,
|
|
130
|
-
outputArguments: [{ dataType: node_opcua_variant_1.DataType.UInt32, arrayType: node_opcua_variant_1.VariantArrayType.Scalar, value: revisedLifetimeInHours }]
|
|
131
|
-
});
|
|
132
|
-
callback(null, callMethodResult);
|
|
133
|
-
}
|
|
134
|
-
// binding methods
|
|
135
|
-
function getMonitoredItemsId(inputArguments, context, callback) {
|
|
136
|
-
(0, node_opcua_assert_1.assert)(Array.isArray(inputArguments));
|
|
137
|
-
(0, node_opcua_assert_1.assert)(typeof callback === "function");
|
|
138
|
-
(0, node_opcua_assert_1.assert)(Object.prototype.hasOwnProperty.call(context, "session"), " expecting a session id in the context object");
|
|
139
|
-
const session = context.session;
|
|
140
|
-
if (!session) {
|
|
141
|
-
return callback(null, { statusCode: node_opcua_status_code_1.StatusCodes.BadInternalError });
|
|
142
|
-
}
|
|
143
|
-
const subscriptionId = inputArguments[0].value;
|
|
144
|
-
const subscription = session.getSubscription(subscriptionId);
|
|
145
|
-
if (!subscription) {
|
|
146
|
-
// subscription may belongs to a different session that ours
|
|
147
|
-
if (this.findSubscription(subscriptionId)) {
|
|
148
|
-
// if yes, then access to Subscription data should be denied
|
|
149
|
-
return callback(null, { statusCode: node_opcua_status_code_1.StatusCodes.BadUserAccessDenied });
|
|
150
|
-
}
|
|
151
|
-
return callback(null, { statusCode: node_opcua_status_code_1.StatusCodes.BadSubscriptionIdInvalid });
|
|
152
|
-
}
|
|
153
|
-
const result = subscription.getMonitoredItems();
|
|
154
|
-
(0, node_opcua_assert_1.assert)(result.statusCode);
|
|
155
|
-
(0, node_opcua_assert_1.assert)(result.serverHandles.length === result.clientHandles.length);
|
|
156
|
-
const callMethodResult = new node_opcua_service_call_1.CallMethodResult({
|
|
157
|
-
statusCode: result.statusCode,
|
|
158
|
-
outputArguments: [
|
|
159
|
-
{ dataType: node_opcua_variant_1.DataType.UInt32, arrayType: node_opcua_variant_1.VariantArrayType.Array, value: result.serverHandles },
|
|
160
|
-
{ dataType: node_opcua_variant_1.DataType.UInt32, arrayType: node_opcua_variant_1.VariantArrayType.Array, value: result.clientHandles }
|
|
161
|
-
]
|
|
162
|
-
});
|
|
163
|
-
callback(null, callMethodResult);
|
|
164
|
-
}
|
|
165
|
-
function __bindVariable(self, nodeId, options) {
|
|
166
|
-
options = options || {};
|
|
167
|
-
const variable = self.addressSpace.findNode(nodeId);
|
|
168
|
-
if (variable && variable.bindVariable) {
|
|
169
|
-
variable.bindVariable(options, true);
|
|
170
|
-
(0, node_opcua_assert_1.assert)(typeof variable.asyncRefresh === "function");
|
|
171
|
-
(0, node_opcua_assert_1.assert)(typeof variable.refreshFunc === "function");
|
|
172
|
-
}
|
|
173
|
-
else {
|
|
174
|
-
warningLog("Warning: cannot bind object with id ", nodeId.toString(), " please check your nodeset.xml file or add this node programmatically");
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
// note OPCUA 1.03 part 4 page 76
|
|
178
|
-
// The Server-assigned identifier for the Subscription (see 7.14 for IntegerId definition). This identifier shall
|
|
179
|
-
// be unique for the entire Server, not just for the Session, in order to allow the Subscription to be transferred
|
|
180
|
-
// to another Session using the TransferSubscriptions service.
|
|
181
|
-
// After Server start-up the generation of subscriptionIds should start from a random IntegerId or continue from
|
|
182
|
-
// the point before the restart.
|
|
183
|
-
let next_subscriptionId = Math.ceil(Math.random() * 1000000);
|
|
184
|
-
function _get_next_subscriptionId() {
|
|
185
|
-
debugLog(" next_subscriptionId = ", next_subscriptionId);
|
|
186
|
-
return next_subscriptionId++;
|
|
187
|
-
}
|
|
188
|
-
/**
|
|
189
|
-
*
|
|
190
|
-
*/
|
|
191
|
-
class ServerEngine extends events_1.EventEmitter {
|
|
192
|
-
constructor(options) {
|
|
193
|
-
super();
|
|
194
|
-
this._globalCounter = { totalMonitoredItemCount: 0 };
|
|
195
|
-
options = options || { applicationUri: "" };
|
|
196
|
-
options.buildInfo = options.buildInfo || {};
|
|
197
|
-
ServerEngine.registry.register(this);
|
|
198
|
-
this._sessions = {};
|
|
199
|
-
this._closedSessions = {};
|
|
200
|
-
this._orphanPublishEngine = undefined; // will be constructed on demand
|
|
201
|
-
this.isAuditing = typeof options.isAuditing === "boolean" ? options.isAuditing : false;
|
|
202
|
-
options.buildInfo.buildDate = options.buildInfo.buildDate || new Date();
|
|
203
|
-
// ---------------------------------------------------- ServerStatusDataType
|
|
204
|
-
this._serverStatus = new node_opcua_common_1.ServerStatusDataType({
|
|
205
|
-
buildInfo: options.buildInfo,
|
|
206
|
-
currentTime: new Date(),
|
|
207
|
-
secondsTillShutdown: 0,
|
|
208
|
-
shutdownReason: { text: "" },
|
|
209
|
-
startTime: new Date(),
|
|
210
|
-
state: node_opcua_common_1.ServerState.NoConfiguration
|
|
211
|
-
});
|
|
212
|
-
// --------------------------------------------------- ServerCapabilities
|
|
213
|
-
options.serverCapabilities = options.serverCapabilities || {};
|
|
214
|
-
// https://profiles.opcfoundation.org/profile
|
|
215
|
-
options.serverCapabilities.serverProfileArray = options.serverCapabilities.serverProfileArray || [
|
|
216
|
-
"http://opcfoundation.org/UA-Profile/Server/Standard",
|
|
217
|
-
"http://opcfoundation.org/UA-Profile/Server/DataAccess",
|
|
218
|
-
"http://opcfoundation.org/UA-Profile/Server/Events",
|
|
219
|
-
"http://opcfoundation.org/UA-Profile/Client/HistoricalAccess",
|
|
220
|
-
"http://opcfoundation.org/UA-Profile/Server/Methods",
|
|
221
|
-
"http://opcfoundation.org/UA-Profile/Server/StandardEventSubscription",
|
|
222
|
-
"http://opcfoundation.org/UA-Profile/Transport/uatcp-uasc-uabinary",
|
|
223
|
-
"http://opcfoundation.org/UA-Profile/Server/FileAccess",
|
|
224
|
-
"http://opcfoundation.org/UA-Profile/Server/StateMachine"
|
|
225
|
-
// "http://opcfoundation.org/UA-Profile/Transport/wss-uajson",
|
|
226
|
-
// "http://opcfoundation.org/UA-Profile/Transport/wss-uasc-uabinary"
|
|
227
|
-
// "http://opcfoundation.org/UA-Profile/Server/DurableSubscription"
|
|
228
|
-
// "http://opcfoundation.org/UA-Profile/Server/ReverseConnect",
|
|
229
|
-
// "http://opcfoundation.org/UAProfile/Server/NodeManagement",
|
|
230
|
-
// "Embedded UA Server Profile",
|
|
231
|
-
// "Micro Embedded Device Server Profile",
|
|
232
|
-
// "Nano Embedded Device Server Profile"
|
|
233
|
-
];
|
|
234
|
-
options.serverCapabilities.localeIdArray = options.serverCapabilities.localeIdArray || ["en-EN", "fr-FR"];
|
|
235
|
-
this.serverCapabilities = new server_capabilities_1.ServerCapabilities(options.serverCapabilities);
|
|
236
|
-
// to do when spec is clear about what goes here!
|
|
237
|
-
// spec 1.04 says (in Part 4 7.33 SignedSoftwareCertificate
|
|
238
|
-
// Note: Details on SoftwareCertificates need to be defined in a future version.
|
|
239
|
-
this.serverCapabilities.softwareCertificates = [
|
|
240
|
-
// new SignedSoftwareCertificate({})
|
|
241
|
-
];
|
|
242
|
-
// make sure minSupportedSampleRate matches MonitoredItem.minimumSamplingInterval
|
|
243
|
-
this.serverCapabilities.__defineGetter__("minSupportedSampleRate", () => {
|
|
244
|
-
return monitored_item_1.MonitoredItem.minimumSamplingInterval;
|
|
245
|
-
});
|
|
246
|
-
this.historyServerCapabilities = new history_server_capabilities_1.HistoryServerCapabilities(options.historyServerCapabilities);
|
|
247
|
-
// --------------------------------------------------- serverDiagnosticsSummary extension Object
|
|
248
|
-
this.serverDiagnosticsSummary = new node_opcua_common_1.ServerDiagnosticsSummaryDataType();
|
|
249
|
-
(0, node_opcua_assert_1.assert)(Object.prototype.hasOwnProperty.call(this.serverDiagnosticsSummary, "currentSessionCount"));
|
|
250
|
-
// note spelling is different for serverDiagnosticsSummary.currentSubscriptionCount
|
|
251
|
-
// and sessionDiagnostics.currentSubscriptionsCount ( with an s)
|
|
252
|
-
(0, node_opcua_assert_1.assert)(Object.prototype.hasOwnProperty.call(this.serverDiagnosticsSummary, "currentSubscriptionCount"));
|
|
253
|
-
this.serverDiagnosticsSummary.__defineGetter__("currentSubscriptionCount", () => {
|
|
254
|
-
// currentSubscriptionCount returns the total number of subscriptions
|
|
255
|
-
// that are currently active on all sessions
|
|
256
|
-
let counter = 0;
|
|
257
|
-
Object.values(this._sessions).forEach((session) => {
|
|
258
|
-
counter += session.currentSubscriptionCount;
|
|
259
|
-
});
|
|
260
|
-
// we also need to add the orphan subscriptions
|
|
261
|
-
counter += this._orphanPublishEngine ? this._orphanPublishEngine.subscriptions.length : 0;
|
|
262
|
-
return counter;
|
|
263
|
-
});
|
|
264
|
-
this._internalState = "creating";
|
|
265
|
-
this.setServerState(node_opcua_common_1.ServerState.NoConfiguration);
|
|
266
|
-
this.addressSpace = null;
|
|
267
|
-
this._shutdownTasks = [];
|
|
268
|
-
this._applicationUri = "";
|
|
269
|
-
if (typeof options.applicationUri === "function") {
|
|
270
|
-
this.__defineGetter__("_applicationUri", options.applicationUri);
|
|
271
|
-
}
|
|
272
|
-
else {
|
|
273
|
-
this._applicationUri = options.applicationUri || "<unset _applicationUri>";
|
|
274
|
-
}
|
|
275
|
-
options.serverDiagnosticsEnabled = Object.prototype.hasOwnProperty.call(options, "serverDiagnosticsEnable")
|
|
276
|
-
? options.serverDiagnosticsEnabled
|
|
277
|
-
: true;
|
|
278
|
-
this.serverDiagnosticsEnabled = options.serverDiagnosticsEnabled;
|
|
279
|
-
}
|
|
280
|
-
isStarted() {
|
|
281
|
-
return !!this._serverStatus;
|
|
282
|
-
}
|
|
283
|
-
dispose() {
|
|
284
|
-
this.addressSpace = null;
|
|
285
|
-
(0, node_opcua_assert_1.assert)(Object.keys(this._sessions).length === 0, "ServerEngine#_sessions not empty");
|
|
286
|
-
this._sessions = {};
|
|
287
|
-
// todo fix me
|
|
288
|
-
this._closedSessions = {};
|
|
289
|
-
(0, node_opcua_assert_1.assert)(Object.keys(this._closedSessions).length === 0, "ServerEngine#_closedSessions not empty");
|
|
290
|
-
this._closedSessions = {};
|
|
291
|
-
if (this._orphanPublishEngine) {
|
|
292
|
-
this._orphanPublishEngine.dispose();
|
|
293
|
-
this._orphanPublishEngine = undefined;
|
|
294
|
-
}
|
|
295
|
-
this._shutdownTasks = [];
|
|
296
|
-
this._serverStatus = null;
|
|
297
|
-
this._internalState = "disposed";
|
|
298
|
-
this.removeAllListeners();
|
|
299
|
-
ServerEngine.registry.unregister(this);
|
|
300
|
-
}
|
|
301
|
-
get startTime() {
|
|
302
|
-
return this._serverStatus.startTime;
|
|
303
|
-
}
|
|
304
|
-
get currentTime() {
|
|
305
|
-
return this._serverStatus.currentTime;
|
|
306
|
-
}
|
|
307
|
-
get buildInfo() {
|
|
308
|
-
return this._serverStatus.buildInfo;
|
|
309
|
-
}
|
|
310
|
-
/**
|
|
311
|
-
* register a function that will be called when the server will perform its shut down.
|
|
312
|
-
* @method registerShutdownTask
|
|
313
|
-
*/
|
|
314
|
-
registerShutdownTask(task) {
|
|
315
|
-
(0, node_opcua_assert_1.assert)(typeof task === "function");
|
|
316
|
-
this._shutdownTasks.push(task);
|
|
317
|
-
}
|
|
318
|
-
/**
|
|
319
|
-
* @method shutdown
|
|
320
|
-
*/
|
|
321
|
-
shutdown() {
|
|
322
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
323
|
-
debugLog("ServerEngine#shutdown");
|
|
324
|
-
this._internalState = "shutdown";
|
|
325
|
-
this.setServerState(node_opcua_common_1.ServerState.Shutdown);
|
|
326
|
-
// delete any existing sessions
|
|
327
|
-
const tokens = Object.keys(this._sessions).map((key) => {
|
|
328
|
-
const session = this._sessions[key];
|
|
329
|
-
return session.authenticationToken;
|
|
330
|
-
});
|
|
331
|
-
// delete and close any orphan subscriptions
|
|
332
|
-
if (this._orphanPublishEngine) {
|
|
333
|
-
this._orphanPublishEngine.shutdown();
|
|
334
|
-
}
|
|
335
|
-
for (const token of tokens) {
|
|
336
|
-
this.closeSession(token, true, "Terminated");
|
|
337
|
-
}
|
|
338
|
-
// all sessions must have been terminated
|
|
339
|
-
(0, node_opcua_assert_1.assert)(this.currentSessionCount === 0);
|
|
340
|
-
// all subscriptions must have been terminated
|
|
341
|
-
(0, node_opcua_assert_1.assert)(this.currentSubscriptionCount === 0, "all subscriptions must have been terminated");
|
|
342
|
-
this._shutdownTasks.push(shutdownAndDisposeAddressSpace);
|
|
343
|
-
// perform registerShutdownTask
|
|
344
|
-
for (const task of this._shutdownTasks) {
|
|
345
|
-
yield task.call(this);
|
|
346
|
-
}
|
|
347
|
-
this.dispose();
|
|
348
|
-
});
|
|
349
|
-
}
|
|
350
|
-
/**
|
|
351
|
-
* the number of active sessions
|
|
352
|
-
*/
|
|
353
|
-
get currentSessionCount() {
|
|
354
|
-
return this.serverDiagnosticsSummary.currentSessionCount;
|
|
355
|
-
}
|
|
356
|
-
/**
|
|
357
|
-
* the cumulated number of sessions that have been opened since this object exists
|
|
358
|
-
*/
|
|
359
|
-
get cumulatedSessionCount() {
|
|
360
|
-
return this.serverDiagnosticsSummary.cumulatedSessionCount;
|
|
361
|
-
}
|
|
362
|
-
/**
|
|
363
|
-
* the number of active subscriptions.
|
|
364
|
-
*/
|
|
365
|
-
get currentSubscriptionCount() {
|
|
366
|
-
return this.serverDiagnosticsSummary.currentSubscriptionCount;
|
|
367
|
-
}
|
|
368
|
-
/**
|
|
369
|
-
* the cumulated number of subscriptions that have been created since this object exists
|
|
370
|
-
*/
|
|
371
|
-
get cumulatedSubscriptionCount() {
|
|
372
|
-
return this.serverDiagnosticsSummary.cumulatedSubscriptionCount;
|
|
373
|
-
}
|
|
374
|
-
get rejectedSessionCount() {
|
|
375
|
-
return this.serverDiagnosticsSummary.rejectedSessionCount;
|
|
376
|
-
}
|
|
377
|
-
get rejectedRequestsCount() {
|
|
378
|
-
return this.serverDiagnosticsSummary.rejectedRequestsCount;
|
|
379
|
-
}
|
|
380
|
-
get sessionAbortCount() {
|
|
381
|
-
return this.serverDiagnosticsSummary.sessionAbortCount;
|
|
382
|
-
}
|
|
383
|
-
get sessionTimeoutCount() {
|
|
384
|
-
return this.serverDiagnosticsSummary.sessionTimeoutCount;
|
|
385
|
-
}
|
|
386
|
-
get publishingIntervalCount() {
|
|
387
|
-
return this.serverDiagnosticsSummary.publishingIntervalCount;
|
|
388
|
-
}
|
|
389
|
-
incrementSessionTimeoutCount() {
|
|
390
|
-
if (this.serverDiagnosticsSummary && this.serverDiagnosticsEnabled) {
|
|
391
|
-
// The requests include all Services defined in Part 4 of the OPC UA Specification, also requests to create sessions. This number includes the securityRejectedRequestsCount.
|
|
392
|
-
this.serverDiagnosticsSummary.sessionTimeoutCount += 1;
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
incrementSessionAbortCount() {
|
|
396
|
-
if (this.serverDiagnosticsSummary && this.serverDiagnosticsEnabled) {
|
|
397
|
-
// The requests include all Services defined in Part 4 of the OPC UA Specification, also requests to create sessions. This number includes the securityRejectedRequestsCount.
|
|
398
|
-
this.serverDiagnosticsSummary.sessionAbortCount += 1;
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
incrementRejectedRequestsCount() {
|
|
402
|
-
if (this.serverDiagnosticsSummary && this.serverDiagnosticsEnabled) {
|
|
403
|
-
// The requests include all Services defined in Part 4 of the OPC UA Specification, also requests to create sessions. This number includes the securityRejectedRequestsCount.
|
|
404
|
-
this.serverDiagnosticsSummary.rejectedRequestsCount += 1;
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
/**
|
|
408
|
-
* increment rejected session count (also increment rejected requests count)
|
|
409
|
-
*/
|
|
410
|
-
incrementRejectedSessionCount() {
|
|
411
|
-
if (this.serverDiagnosticsSummary && this.serverDiagnosticsEnabled) {
|
|
412
|
-
// The requests include all Services defined in Part 4 of the OPC UA Specification, also requests to create sessions. This number includes the securityRejectedRequestsCount.
|
|
413
|
-
this.serverDiagnosticsSummary.rejectedSessionCount += 1;
|
|
414
|
-
}
|
|
415
|
-
this.incrementRejectedRequestsCount();
|
|
416
|
-
}
|
|
417
|
-
incrementSecurityRejectedRequestsCount() {
|
|
418
|
-
if (this.serverDiagnosticsSummary && this.serverDiagnosticsEnabled) {
|
|
419
|
-
// The requests include all Services defined in Part 4 of the OPC UA Specification, also requests to create sessions. This number includes the securityRejectedRequestsCount.
|
|
420
|
-
this.serverDiagnosticsSummary.securityRejectedRequestsCount += 1;
|
|
421
|
-
}
|
|
422
|
-
this.incrementRejectedRequestsCount();
|
|
423
|
-
}
|
|
424
|
-
/**
|
|
425
|
-
* increment rejected session count (also increment rejected requests count)
|
|
426
|
-
*/
|
|
427
|
-
incrementSecurityRejectedSessionCount() {
|
|
428
|
-
if (this.serverDiagnosticsSummary && this.serverDiagnosticsEnabled) {
|
|
429
|
-
// The requests include all Services defined in Part 4 of the OPC UA Specification, also requests to create sessions. This number includes the securityRejectedRequestsCount.
|
|
430
|
-
this.serverDiagnosticsSummary.securityRejectedSessionCount += 1;
|
|
431
|
-
}
|
|
432
|
-
this.incrementSecurityRejectedRequestsCount();
|
|
433
|
-
}
|
|
434
|
-
setShutdownTime(date) {
|
|
435
|
-
this._expectedShutdownTime = date;
|
|
436
|
-
}
|
|
437
|
-
setShutdownReason(reason) {
|
|
438
|
-
var _a;
|
|
439
|
-
(_a = this.addressSpace) === null || _a === void 0 ? void 0 : _a.rootFolder.objects.server.serverStatus.shutdownReason.setValueFromSource({
|
|
440
|
-
dataType: node_opcua_variant_1.DataType.LocalizedText,
|
|
441
|
-
value: (0, node_opcua_data_model_1.coerceLocalizedText)(reason)
|
|
442
|
-
});
|
|
443
|
-
}
|
|
444
|
-
/**
|
|
445
|
-
* @method secondsTillShutdown
|
|
446
|
-
* @return the approximate number of seconds until the server will be shut down. The
|
|
447
|
-
* value is only relevant once the state changes into SHUTDOWN.
|
|
448
|
-
*/
|
|
449
|
-
secondsTillShutdown() {
|
|
450
|
-
if (!this._expectedShutdownTime) {
|
|
451
|
-
return 0;
|
|
452
|
-
}
|
|
453
|
-
// ToDo: implement a correct solution here
|
|
454
|
-
const now = Date.now();
|
|
455
|
-
return Math.max(0, Math.ceil((this._expectedShutdownTime.getTime() - now) / 1000));
|
|
456
|
-
}
|
|
457
|
-
/**
|
|
458
|
-
* the name of the server
|
|
459
|
-
*/
|
|
460
|
-
get serverName() {
|
|
461
|
-
return this._serverStatus.buildInfo.productName;
|
|
462
|
-
}
|
|
463
|
-
/**
|
|
464
|
-
* the server urn
|
|
465
|
-
*/
|
|
466
|
-
get serverNameUrn() {
|
|
467
|
-
return this._applicationUri;
|
|
468
|
-
}
|
|
469
|
-
/**
|
|
470
|
-
* the urn of the server namespace
|
|
471
|
-
*/
|
|
472
|
-
get serverNamespaceUrn() {
|
|
473
|
-
return this._applicationUri; // "urn:" + engine.serverName;
|
|
474
|
-
}
|
|
475
|
-
get serverStatus() {
|
|
476
|
-
return this._serverStatus;
|
|
477
|
-
}
|
|
478
|
-
setServerState(serverState) {
|
|
479
|
-
var _a, _b, _c, _d, _e, _f;
|
|
480
|
-
(0, node_opcua_assert_1.assert)(serverState !== null && serverState !== undefined);
|
|
481
|
-
(_f = (_e = (_d = (_c = (_b = (_a = this.addressSpace) === null || _a === void 0 ? void 0 : _a.rootFolder) === null || _b === void 0 ? void 0 : _b.objects) === null || _c === void 0 ? void 0 : _c.server) === null || _d === void 0 ? void 0 : _d.serverStatus) === null || _e === void 0 ? void 0 : _e.state) === null || _f === void 0 ? void 0 : _f.setValueFromSource({
|
|
482
|
-
dataType: node_opcua_variant_1.DataType.Int32,
|
|
483
|
-
value: serverState
|
|
484
|
-
});
|
|
485
|
-
}
|
|
486
|
-
getServerDiagnosticsEnabledFlag() {
|
|
487
|
-
const server = this.addressSpace.rootFolder.objects.server;
|
|
488
|
-
const serverDiagnostics = server.getComponentByName("ServerDiagnostics");
|
|
489
|
-
if (!serverDiagnostics) {
|
|
490
|
-
return false;
|
|
491
|
-
}
|
|
492
|
-
return serverDiagnostics.readValue().value.value;
|
|
493
|
-
}
|
|
494
|
-
/**
|
|
495
|
-
* @method initialize
|
|
496
|
-
* @async
|
|
497
|
-
*
|
|
498
|
-
* @param options {Object}
|
|
499
|
-
* @param options.nodeset_filename {String} - [option](default : 'mini.Node.Set2.xml' )
|
|
500
|
-
* @param callback
|
|
501
|
-
*/
|
|
502
|
-
initialize(options, callback) {
|
|
503
|
-
(0, node_opcua_assert_1.assert)(!this.addressSpace); // check that 'initialize' has not been already called
|
|
504
|
-
this._internalState = "initializing";
|
|
505
|
-
options = options || {};
|
|
506
|
-
(0, node_opcua_assert_1.assert)(typeof callback === "function");
|
|
507
|
-
options.nodeset_filename = options.nodeset_filename || node_opcua_nodesets_1.nodesets.standard;
|
|
508
|
-
const startTime = new Date();
|
|
509
|
-
debugLog("Loading ", options.nodeset_filename, "...");
|
|
510
|
-
this.addressSpace = node_opcua_address_space_1.AddressSpace.create();
|
|
511
|
-
// register namespace 1 (our namespace);
|
|
512
|
-
const serverNamespace = this.addressSpace.registerNamespace(this.serverNamespaceUrn);
|
|
513
|
-
(0, node_opcua_assert_1.assert)(serverNamespace.index === 1);
|
|
514
|
-
// eslint-disable-next-line max-statements
|
|
515
|
-
(0, nodeJS_1.generateAddressSpace)(this.addressSpace, options.nodeset_filename, () => {
|
|
516
|
-
/* istanbul ignore next */
|
|
517
|
-
if (!this.addressSpace) {
|
|
518
|
-
throw new Error("Internal error");
|
|
519
|
-
}
|
|
520
|
-
const addressSpace = this.addressSpace;
|
|
521
|
-
const endTime = new Date();
|
|
522
|
-
debugLog("Loading ", options.nodeset_filename, " done : ", endTime.getTime() - startTime.getTime(), " ms");
|
|
523
|
-
const bindVariableIfPresent = (nodeId, opts) => {
|
|
524
|
-
(0, node_opcua_assert_1.assert)(nodeId instanceof node_opcua_nodeid_1.NodeId);
|
|
525
|
-
(0, node_opcua_assert_1.assert)(!nodeId.isEmpty());
|
|
526
|
-
const obj = addressSpace.findNode(nodeId);
|
|
527
|
-
if (obj) {
|
|
528
|
-
__bindVariable(this, nodeId, opts);
|
|
529
|
-
}
|
|
530
|
-
return obj;
|
|
531
|
-
};
|
|
532
|
-
// -------------------------------------------- install default get/put handler
|
|
533
|
-
const server_NamespaceArray_Id = (0, node_opcua_nodeid_1.makeNodeId)(node_opcua_constants_1.VariableIds.Server_NamespaceArray); // ns=0;i=2255
|
|
534
|
-
bindVariableIfPresent(server_NamespaceArray_Id, {
|
|
535
|
-
get() {
|
|
536
|
-
return new node_opcua_variant_1.Variant({
|
|
537
|
-
arrayType: node_opcua_variant_1.VariantArrayType.Array,
|
|
538
|
-
dataType: node_opcua_variant_1.DataType.String,
|
|
539
|
-
value: addressSpace.getNamespaceArray().map((x) => x.namespaceUri)
|
|
540
|
-
});
|
|
541
|
-
},
|
|
542
|
-
set: null // read only
|
|
543
|
-
});
|
|
544
|
-
const server_NameUrn_var = new node_opcua_variant_1.Variant({
|
|
545
|
-
arrayType: node_opcua_variant_1.VariantArrayType.Array,
|
|
546
|
-
dataType: node_opcua_variant_1.DataType.String,
|
|
547
|
-
value: [
|
|
548
|
-
this.serverNameUrn // this is us !
|
|
549
|
-
]
|
|
550
|
-
});
|
|
551
|
-
const server_ServerArray_Id = (0, node_opcua_nodeid_1.makeNodeId)(node_opcua_constants_1.VariableIds.Server_ServerArray); // ns=0;i=2254
|
|
552
|
-
bindVariableIfPresent(server_ServerArray_Id, {
|
|
553
|
-
get() {
|
|
554
|
-
return server_NameUrn_var;
|
|
555
|
-
},
|
|
556
|
-
set: null // read only
|
|
557
|
-
});
|
|
558
|
-
// fix DefaultUserRolePermissions and DefaultUserRolePermissions
|
|
559
|
-
// of namespaces
|
|
560
|
-
const namespaces = (0, node_opcua_nodeid_1.makeNodeId)(node_opcua_constants_1.ObjectIds.Server_Namespaces);
|
|
561
|
-
const namespacesNode = addressSpace.findNode(namespaces);
|
|
562
|
-
if (namespacesNode) {
|
|
563
|
-
for (const ns of namespacesNode.getComponents()) {
|
|
564
|
-
const defaultUserRolePermissions = ns.getChildByName("DefaultUserRolePermissions");
|
|
565
|
-
if (defaultUserRolePermissions) {
|
|
566
|
-
defaultUserRolePermissions.setValueFromSource({ dataType: node_opcua_variant_1.DataType.Null });
|
|
567
|
-
}
|
|
568
|
-
const defaultRolePermissions = ns.getChildByName("DefaultRolePermissions");
|
|
569
|
-
if (defaultRolePermissions) {
|
|
570
|
-
defaultRolePermissions.setValueFromSource({ dataType: node_opcua_variant_1.DataType.Null });
|
|
571
|
-
}
|
|
572
|
-
}
|
|
573
|
-
}
|
|
574
|
-
const bindStandardScalar = (id, dataType, func, setter_func) => {
|
|
575
|
-
(0, node_opcua_assert_1.assert)(typeof id === "number", "expecting id to be a number");
|
|
576
|
-
(0, node_opcua_assert_1.assert)(typeof func === "function");
|
|
577
|
-
(0, node_opcua_assert_1.assert)(typeof setter_func === "function" || !setter_func);
|
|
578
|
-
(0, node_opcua_assert_1.assert)(dataType !== null); // check invalid dataType
|
|
579
|
-
let setter_func2 = null;
|
|
580
|
-
if (setter_func) {
|
|
581
|
-
setter_func2 = (variant) => {
|
|
582
|
-
const variable2 = !!variant.value;
|
|
583
|
-
setter_func(variable2);
|
|
584
|
-
return node_opcua_status_code_1.StatusCodes.Good;
|
|
585
|
-
};
|
|
586
|
-
}
|
|
587
|
-
const nodeId = (0, node_opcua_nodeid_1.makeNodeId)(id);
|
|
588
|
-
// make sur the provided function returns a valid value for the variant type
|
|
589
|
-
// This test may not be exhaustive but it will detect obvious mistakes.
|
|
590
|
-
/* istanbul ignore next */
|
|
591
|
-
if (!(0, node_opcua_variant_1.isValidVariant)(node_opcua_variant_1.VariantArrayType.Scalar, dataType, func())) {
|
|
592
|
-
errorLog("func", func());
|
|
593
|
-
throw new Error("bindStandardScalar : func doesn't provide an value of type " + node_opcua_variant_1.DataType[dataType]);
|
|
594
|
-
}
|
|
595
|
-
return bindVariableIfPresent(nodeId, {
|
|
596
|
-
get() {
|
|
597
|
-
return new node_opcua_variant_1.Variant({
|
|
598
|
-
arrayType: node_opcua_variant_1.VariantArrayType.Scalar,
|
|
599
|
-
dataType,
|
|
600
|
-
value: func()
|
|
601
|
-
});
|
|
602
|
-
},
|
|
603
|
-
set: setter_func2
|
|
604
|
-
});
|
|
605
|
-
};
|
|
606
|
-
const bindStandardArray = (id, variantDataType, dataType, func) => {
|
|
607
|
-
(0, node_opcua_assert_1.assert)(typeof func === "function");
|
|
608
|
-
(0, node_opcua_assert_1.assert)(variantDataType !== null); // check invalid dataType
|
|
609
|
-
const nodeId = (0, node_opcua_nodeid_1.makeNodeId)(id);
|
|
610
|
-
// make sur the provided function returns a valid value for the variant type
|
|
611
|
-
// This test may not be exhaustive but it will detect obvious mistakes.
|
|
612
|
-
(0, node_opcua_assert_1.assert)((0, node_opcua_variant_1.isValidVariant)(node_opcua_variant_1.VariantArrayType.Array, variantDataType, func()));
|
|
613
|
-
bindVariableIfPresent(nodeId, {
|
|
614
|
-
get() {
|
|
615
|
-
const value = func();
|
|
616
|
-
(0, node_opcua_assert_1.assert)(Array.isArray(value));
|
|
617
|
-
return new node_opcua_variant_1.Variant({
|
|
618
|
-
arrayType: node_opcua_variant_1.VariantArrayType.Array,
|
|
619
|
-
dataType: variantDataType,
|
|
620
|
-
value
|
|
621
|
-
});
|
|
622
|
-
},
|
|
623
|
-
set: null // read only
|
|
624
|
-
});
|
|
625
|
-
};
|
|
626
|
-
bindStandardScalar(node_opcua_constants_1.VariableIds.Server_EstimatedReturnTime, node_opcua_variant_1.DataType.DateTime, () => node_opcua_date_time_1.minOPCUADate);
|
|
627
|
-
// TimeZoneDataType
|
|
628
|
-
const timeZoneDataType = addressSpace.findDataType((0, node_opcua_nodeid_1.resolveNodeId)(node_opcua_constants_1.DataTypeIds.TimeZoneDataType));
|
|
629
|
-
const timeZone = new node_opcua_types_1.TimeZoneDataType({
|
|
630
|
-
daylightSavingInOffset: /* boolean*/ false,
|
|
631
|
-
offset: /* int16 */ 0
|
|
632
|
-
});
|
|
633
|
-
bindStandardScalar(node_opcua_constants_1.VariableIds.Server_LocalTime, node_opcua_variant_1.DataType.ExtensionObject, () => {
|
|
634
|
-
return timeZone;
|
|
635
|
-
});
|
|
636
|
-
bindStandardScalar(node_opcua_constants_1.VariableIds.Server_ServiceLevel, node_opcua_variant_1.DataType.Byte, () => {
|
|
637
|
-
return 255;
|
|
638
|
-
});
|
|
639
|
-
bindStandardScalar(node_opcua_constants_1.VariableIds.Server_Auditing, node_opcua_variant_1.DataType.Boolean, () => {
|
|
640
|
-
return this.isAuditing;
|
|
641
|
-
});
|
|
642
|
-
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
643
|
-
const engine = this;
|
|
644
|
-
const makeNotReadableIfEnabledFlagIsFalse = (variable) => {
|
|
645
|
-
const originalIsReadable = variable.isReadable;
|
|
646
|
-
variable.isUserReadable = checkReadableFlag;
|
|
647
|
-
function checkReadableFlag(context) {
|
|
648
|
-
const isEnabled = engine.serverDiagnosticsEnabled;
|
|
649
|
-
return originalIsReadable.call(this, context) && isEnabled;
|
|
650
|
-
}
|
|
651
|
-
for (const c of variable.getAggregates()) {
|
|
652
|
-
if (c.nodeClass === node_opcua_data_model_1.NodeClass.Variable) {
|
|
653
|
-
makeNotReadableIfEnabledFlagIsFalse(c);
|
|
654
|
-
}
|
|
655
|
-
}
|
|
656
|
-
};
|
|
657
|
-
const bindServerDiagnostics = () => {
|
|
658
|
-
bindStandardScalar(node_opcua_constants_1.VariableIds.Server_ServerDiagnostics_EnabledFlag, node_opcua_variant_1.DataType.Boolean, () => {
|
|
659
|
-
return this.serverDiagnosticsEnabled;
|
|
660
|
-
}, (newFlag) => {
|
|
661
|
-
this.serverDiagnosticsEnabled = newFlag;
|
|
662
|
-
});
|
|
663
|
-
const nodeId = (0, node_opcua_nodeid_1.makeNodeId)(node_opcua_constants_1.VariableIds.Server_ServerDiagnostics_ServerDiagnosticsSummary);
|
|
664
|
-
const serverDiagnosticsSummaryNode = addressSpace.findNode(nodeId);
|
|
665
|
-
if (serverDiagnosticsSummaryNode) {
|
|
666
|
-
serverDiagnosticsSummaryNode.bindExtensionObject(this.serverDiagnosticsSummary);
|
|
667
|
-
this.serverDiagnosticsSummary = serverDiagnosticsSummaryNode.$extensionObject;
|
|
668
|
-
makeNotReadableIfEnabledFlagIsFalse(serverDiagnosticsSummaryNode);
|
|
669
|
-
}
|
|
670
|
-
};
|
|
671
|
-
const bindServerStatus = () => {
|
|
672
|
-
const serverStatusNode = addressSpace.findNode((0, node_opcua_nodeid_1.makeNodeId)(node_opcua_constants_1.VariableIds.Server_ServerStatus));
|
|
673
|
-
if (!serverStatusNode) {
|
|
674
|
-
return;
|
|
675
|
-
}
|
|
676
|
-
if (serverStatusNode) {
|
|
677
|
-
serverStatusNode.bindExtensionObject(this._serverStatus);
|
|
678
|
-
serverStatusNode.minimumSamplingInterval = 1000;
|
|
679
|
-
}
|
|
680
|
-
const currentTimeNode = addressSpace.findNode((0, node_opcua_nodeid_1.makeNodeId)(node_opcua_constants_1.VariableIds.Server_ServerStatus_CurrentTime));
|
|
681
|
-
if (currentTimeNode) {
|
|
682
|
-
currentTimeNode.minimumSamplingInterval = 1000;
|
|
683
|
-
}
|
|
684
|
-
const secondsTillShutdown = addressSpace.findNode((0, node_opcua_nodeid_1.makeNodeId)(node_opcua_constants_1.VariableIds.Server_ServerStatus_SecondsTillShutdown));
|
|
685
|
-
if (secondsTillShutdown) {
|
|
686
|
-
secondsTillShutdown.minimumSamplingInterval = 1000;
|
|
687
|
-
}
|
|
688
|
-
(0, node_opcua_assert_1.assert)(serverStatusNode.$extensionObject);
|
|
689
|
-
serverStatusNode.$extensionObject = new Proxy(serverStatusNode.$extensionObject, {
|
|
690
|
-
get(target, prop) {
|
|
691
|
-
if (prop === "currentTime") {
|
|
692
|
-
serverStatusNode.currentTime.touchValue();
|
|
693
|
-
return new Date();
|
|
694
|
-
}
|
|
695
|
-
else if (prop === "secondsTillShutdown") {
|
|
696
|
-
serverStatusNode.secondsTillShutdown.touchValue();
|
|
697
|
-
return engine.secondsTillShutdown();
|
|
698
|
-
}
|
|
699
|
-
return target[prop];
|
|
700
|
-
}
|
|
701
|
-
});
|
|
702
|
-
this._serverStatus = serverStatusNode.$extensionObject;
|
|
703
|
-
};
|
|
704
|
-
const bindServerCapabilities = () => {
|
|
705
|
-
bindStandardArray(node_opcua_constants_1.VariableIds.Server_ServerCapabilities_ServerProfileArray, node_opcua_variant_1.DataType.String, node_opcua_variant_1.DataType.String, () => {
|
|
706
|
-
return this.serverCapabilities.serverProfileArray;
|
|
707
|
-
});
|
|
708
|
-
bindStandardArray(node_opcua_constants_1.VariableIds.Server_ServerCapabilities_LocaleIdArray, node_opcua_variant_1.DataType.String, "LocaleId", () => {
|
|
709
|
-
return this.serverCapabilities.localeIdArray;
|
|
710
|
-
});
|
|
711
|
-
bindStandardScalar(node_opcua_constants_1.VariableIds.Server_ServerCapabilities_MinSupportedSampleRate, node_opcua_variant_1.DataType.Double, () => {
|
|
712
|
-
return this.serverCapabilities.minSupportedSampleRate;
|
|
713
|
-
});
|
|
714
|
-
bindStandardScalar(node_opcua_constants_1.VariableIds.Server_ServerCapabilities_MaxBrowseContinuationPoints, node_opcua_variant_1.DataType.UInt16, () => {
|
|
715
|
-
return this.serverCapabilities.maxBrowseContinuationPoints;
|
|
716
|
-
});
|
|
717
|
-
bindStandardScalar(node_opcua_constants_1.VariableIds.Server_ServerCapabilities_MaxQueryContinuationPoints, node_opcua_variant_1.DataType.UInt16, () => {
|
|
718
|
-
return this.serverCapabilities.maxQueryContinuationPoints;
|
|
719
|
-
});
|
|
720
|
-
bindStandardScalar(node_opcua_constants_1.VariableIds.Server_ServerCapabilities_MaxHistoryContinuationPoints, node_opcua_variant_1.DataType.UInt16, () => {
|
|
721
|
-
return this.serverCapabilities.maxHistoryContinuationPoints;
|
|
722
|
-
});
|
|
723
|
-
// new in 1.05
|
|
724
|
-
bindStandardScalar(node_opcua_constants_1.VariableIds.Server_ServerCapabilities_MaxSessions, node_opcua_variant_1.DataType.UInt32, () => {
|
|
725
|
-
return this.serverCapabilities.maxSessions;
|
|
726
|
-
});
|
|
727
|
-
bindStandardScalar(node_opcua_constants_1.VariableIds.Server_ServerCapabilities_MaxSubscriptions, node_opcua_variant_1.DataType.UInt32, () => {
|
|
728
|
-
return this.serverCapabilities.maxSubscriptions;
|
|
729
|
-
});
|
|
730
|
-
bindStandardScalar(node_opcua_constants_1.VariableIds.Server_ServerCapabilities_MaxMonitoredItems, node_opcua_variant_1.DataType.UInt32, () => {
|
|
731
|
-
return this.serverCapabilities.maxMonitoredItems;
|
|
732
|
-
});
|
|
733
|
-
bindStandardScalar(node_opcua_constants_1.VariableIds.Server_ServerCapabilities_MaxSubscriptionsPerSession, node_opcua_variant_1.DataType.UInt32, () => {
|
|
734
|
-
return this.serverCapabilities.maxSubscriptionsPerSession;
|
|
735
|
-
});
|
|
736
|
-
bindStandardScalar(node_opcua_constants_1.VariableIds.Server_ServerCapabilities_MaxSelectClauseParameters, node_opcua_variant_1.DataType.UInt32, () => {
|
|
737
|
-
return this.serverCapabilities.maxSelectClauseParameters;
|
|
738
|
-
});
|
|
739
|
-
bindStandardScalar(node_opcua_constants_1.VariableIds.Server_ServerCapabilities_MaxWhereClauseParameters, node_opcua_variant_1.DataType.UInt32, () => {
|
|
740
|
-
return this.serverCapabilities.maxWhereClauseParameters;
|
|
741
|
-
});
|
|
742
|
-
//bindStandardArray(VariableIds.Server_ServerCapabilities_ConformanceUnits, DataType.QualifiedName, () => {
|
|
743
|
-
// return this.serverCapabilities.conformanceUnits;
|
|
744
|
-
//});
|
|
745
|
-
bindStandardScalar(node_opcua_constants_1.VariableIds.Server_ServerCapabilities_MaxMonitoredItemsPerSubscription, node_opcua_variant_1.DataType.UInt32, () => {
|
|
746
|
-
return this.serverCapabilities.maxMonitoredItemsPerSubscription;
|
|
747
|
-
});
|
|
748
|
-
// added by DI : Server-specific period of time in milliseconds until the Server will revoke a lock.
|
|
749
|
-
// TODO bindStandardScalar(VariableIds.Server_ServerCapabilities_MaxInactiveLockTime,
|
|
750
|
-
// TODO DataType.UInt16, function () {
|
|
751
|
-
// TODO return self.serverCapabilities.maxInactiveLockTime;
|
|
752
|
-
// TODO });
|
|
753
|
-
bindStandardArray(node_opcua_constants_1.VariableIds.Server_ServerCapabilities_SoftwareCertificates, node_opcua_variant_1.DataType.ExtensionObject, "SoftwareCertificates", () => {
|
|
754
|
-
return this.serverCapabilities.softwareCertificates;
|
|
755
|
-
});
|
|
756
|
-
bindStandardScalar(node_opcua_constants_1.VariableIds.Server_ServerCapabilities_MaxArrayLength, node_opcua_variant_1.DataType.UInt32, () => {
|
|
757
|
-
return Math.min(this.serverCapabilities.maxArrayLength, node_opcua_variant_1.Variant.maxArrayLength);
|
|
758
|
-
});
|
|
759
|
-
bindStandardScalar(node_opcua_constants_1.VariableIds.Server_ServerCapabilities_MaxStringLength, node_opcua_variant_1.DataType.UInt32, () => {
|
|
760
|
-
return Math.min(this.serverCapabilities.maxStringLength, node_opcua_binary_stream_1.BinaryStream.maxStringLength);
|
|
761
|
-
});
|
|
762
|
-
bindStandardScalar(node_opcua_constants_1.VariableIds.Server_ServerCapabilities_MaxByteStringLength, node_opcua_variant_1.DataType.UInt32, () => {
|
|
763
|
-
return Math.min(this.serverCapabilities.maxByteStringLength, node_opcua_binary_stream_1.BinaryStream.maxByteStringLength);
|
|
764
|
-
});
|
|
765
|
-
const bindOperationLimits = (operationLimits) => {
|
|
766
|
-
(0, node_opcua_assert_1.assert)(operationLimits !== null && typeof operationLimits === "object");
|
|
767
|
-
const keys = Object.keys(operationLimits);
|
|
768
|
-
keys.forEach((key) => {
|
|
769
|
-
const uid = "Server_ServerCapabilities_OperationLimits_" + upperCaseFirst(key);
|
|
770
|
-
const nodeId = (0, node_opcua_nodeid_1.makeNodeId)(node_opcua_constants_1.VariableIds[uid]);
|
|
771
|
-
(0, node_opcua_assert_1.assert)(!nodeId.isEmpty());
|
|
772
|
-
bindStandardScalar(node_opcua_constants_1.VariableIds[uid], node_opcua_variant_1.DataType.UInt32, () => {
|
|
773
|
-
return operationLimits[key];
|
|
774
|
-
});
|
|
775
|
-
});
|
|
776
|
-
};
|
|
777
|
-
bindOperationLimits(this.serverCapabilities.operationLimits);
|
|
778
|
-
// i=2399 [ProgramStateMachineType_ProgramDiagnostics];
|
|
779
|
-
function fix_ProgramStateMachineType_ProgramDiagnostics() {
|
|
780
|
-
const nodeId = (0, node_opcua_nodeid_1.coerceNodeId)("i=2399"); // ProgramStateMachineType_ProgramDiagnostics
|
|
781
|
-
const variable = addressSpace.findNode(nodeId);
|
|
782
|
-
if (variable) {
|
|
783
|
-
variable.$extensionObject = new node_opcua_types_1.ProgramDiagnosticDataType({});
|
|
784
|
-
// variable.setValueFromSource({
|
|
785
|
-
// dataType: DataType.ExtensionObject,
|
|
786
|
-
// // value: new ProgramDiagnostic2DataType()
|
|
787
|
-
// value: new ProgramDiagnosticDataType({})
|
|
788
|
-
// });
|
|
789
|
-
}
|
|
790
|
-
}
|
|
791
|
-
fix_ProgramStateMachineType_ProgramDiagnostics();
|
|
792
|
-
};
|
|
793
|
-
const bindHistoryServerCapabilities = () => {
|
|
794
|
-
bindStandardScalar(node_opcua_constants_1.VariableIds.HistoryServerCapabilities_MaxReturnDataValues, node_opcua_variant_1.DataType.UInt32, () => {
|
|
795
|
-
return this.historyServerCapabilities.maxReturnDataValues;
|
|
796
|
-
});
|
|
797
|
-
bindStandardScalar(node_opcua_constants_1.VariableIds.HistoryServerCapabilities_MaxReturnEventValues, node_opcua_variant_1.DataType.UInt32, () => {
|
|
798
|
-
return this.historyServerCapabilities.maxReturnEventValues;
|
|
799
|
-
});
|
|
800
|
-
bindStandardScalar(node_opcua_constants_1.VariableIds.HistoryServerCapabilities_AccessHistoryDataCapability, node_opcua_variant_1.DataType.Boolean, () => {
|
|
801
|
-
return this.historyServerCapabilities.accessHistoryDataCapability;
|
|
802
|
-
});
|
|
803
|
-
bindStandardScalar(node_opcua_constants_1.VariableIds.HistoryServerCapabilities_AccessHistoryEventsCapability, node_opcua_variant_1.DataType.Boolean, () => {
|
|
804
|
-
return this.historyServerCapabilities.accessHistoryEventsCapability;
|
|
805
|
-
});
|
|
806
|
-
bindStandardScalar(node_opcua_constants_1.VariableIds.HistoryServerCapabilities_InsertDataCapability, node_opcua_variant_1.DataType.Boolean, () => {
|
|
807
|
-
return this.historyServerCapabilities.insertDataCapability;
|
|
808
|
-
});
|
|
809
|
-
bindStandardScalar(node_opcua_constants_1.VariableIds.HistoryServerCapabilities_ReplaceDataCapability, node_opcua_variant_1.DataType.Boolean, () => {
|
|
810
|
-
return this.historyServerCapabilities.replaceDataCapability;
|
|
811
|
-
});
|
|
812
|
-
bindStandardScalar(node_opcua_constants_1.VariableIds.HistoryServerCapabilities_UpdateDataCapability, node_opcua_variant_1.DataType.Boolean, () => {
|
|
813
|
-
return this.historyServerCapabilities.updateDataCapability;
|
|
814
|
-
});
|
|
815
|
-
bindStandardScalar(node_opcua_constants_1.VariableIds.HistoryServerCapabilities_InsertEventCapability, node_opcua_variant_1.DataType.Boolean, () => {
|
|
816
|
-
return this.historyServerCapabilities.insertEventCapability;
|
|
817
|
-
});
|
|
818
|
-
bindStandardScalar(node_opcua_constants_1.VariableIds.HistoryServerCapabilities_ReplaceEventCapability, node_opcua_variant_1.DataType.Boolean, () => {
|
|
819
|
-
return this.historyServerCapabilities.replaceEventCapability;
|
|
820
|
-
});
|
|
821
|
-
bindStandardScalar(node_opcua_constants_1.VariableIds.HistoryServerCapabilities_UpdateEventCapability, node_opcua_variant_1.DataType.Boolean, () => {
|
|
822
|
-
return this.historyServerCapabilities.updateEventCapability;
|
|
823
|
-
});
|
|
824
|
-
bindStandardScalar(node_opcua_constants_1.VariableIds.HistoryServerCapabilities_DeleteEventCapability, node_opcua_variant_1.DataType.Boolean, () => {
|
|
825
|
-
return this.historyServerCapabilities.deleteEventCapability;
|
|
826
|
-
});
|
|
827
|
-
bindStandardScalar(node_opcua_constants_1.VariableIds.HistoryServerCapabilities_DeleteRawCapability, node_opcua_variant_1.DataType.Boolean, () => {
|
|
828
|
-
return this.historyServerCapabilities.deleteRawCapability;
|
|
829
|
-
});
|
|
830
|
-
bindStandardScalar(node_opcua_constants_1.VariableIds.HistoryServerCapabilities_DeleteAtTimeCapability, node_opcua_variant_1.DataType.Boolean, () => {
|
|
831
|
-
return this.historyServerCapabilities.deleteAtTimeCapability;
|
|
832
|
-
});
|
|
833
|
-
bindStandardScalar(node_opcua_constants_1.VariableIds.HistoryServerCapabilities_InsertAnnotationCapability, node_opcua_variant_1.DataType.Boolean, () => {
|
|
834
|
-
return this.historyServerCapabilities.insertAnnotationCapability;
|
|
835
|
-
});
|
|
836
|
-
};
|
|
837
|
-
bindServerDiagnostics();
|
|
838
|
-
bindServerStatus();
|
|
839
|
-
bindServerCapabilities();
|
|
840
|
-
bindHistoryServerCapabilities();
|
|
841
|
-
const bindExtraStuff = () => {
|
|
842
|
-
// mainly for compliance
|
|
843
|
-
/*
|
|
844
|
-
// The version number for the data type description. i=104
|
|
845
|
-
bindStandardScalar(VariableIds.DataTypeDescriptionType_DataTypeVersion, DataType.String, () => {
|
|
846
|
-
return "0";
|
|
847
|
-
});
|
|
848
|
-
|
|
849
|
-
const namingRuleDataTypeNode = addressSpace.findDataType(resolveNodeId(DataTypeIds.NamingRuleType))! as UADataType;
|
|
850
|
-
|
|
851
|
-
if (namingRuleDataTypeNode) {
|
|
852
|
-
const namingRuleType = (namingRuleDataTypeNode as any)._getEnumerationInfo().nameIndex; // getEnumeration("NamingRuleType");
|
|
853
|
-
if (!namingRuleType) {
|
|
854
|
-
throw new Error("Cannot find Enumeration definition for NamingRuleType");
|
|
855
|
-
}
|
|
856
|
-
// i=111
|
|
857
|
-
bindStandardScalar(VariableIds.ModellingRuleType_NamingRule, DataType.Int32, () => {
|
|
858
|
-
return 0;
|
|
859
|
-
});
|
|
860
|
-
|
|
861
|
-
// i=112
|
|
862
|
-
bindStandardScalar(VariableIds.ModellingRule_Mandatory_NamingRule, DataType.Int32, () => {
|
|
863
|
-
return namingRuleType.Mandatory ? namingRuleType.Mandatory.value : 0;
|
|
864
|
-
});
|
|
865
|
-
|
|
866
|
-
// i=113
|
|
867
|
-
bindStandardScalar(VariableIds.ModellingRule_Optional_NamingRule, DataType.Int32, () => {
|
|
868
|
-
return namingRuleType.Optional ? namingRuleType.Optional.value : 0;
|
|
869
|
-
});
|
|
870
|
-
// i=114
|
|
871
|
-
bindStandardScalar(VariableIds.ModellingRule_ExposesItsArray_NamingRule, DataType.Int32, () => {
|
|
872
|
-
return namingRuleType.ExposesItsArray ? namingRuleType.ExposesItsArray.value : 0;
|
|
873
|
-
});
|
|
874
|
-
bindStandardScalar(VariableIds.ModellingRule_MandatoryPlaceholder_NamingRule, DataType.Int32, () => {
|
|
875
|
-
return namingRuleType.MandatoryPlaceholder ? namingRuleType.MandatoryPlaceholder.value : 0;
|
|
876
|
-
});
|
|
877
|
-
}
|
|
878
|
-
*/
|
|
879
|
-
};
|
|
880
|
-
bindExtraStuff();
|
|
881
|
-
this.__internal_bindMethod((0, node_opcua_nodeid_1.makeNodeId)(node_opcua_constants_1.MethodIds.Server_GetMonitoredItems), getMonitoredItemsId.bind(this));
|
|
882
|
-
this.__internal_bindMethod((0, node_opcua_nodeid_1.makeNodeId)(node_opcua_constants_1.MethodIds.Server_SetSubscriptionDurable), setSubscriptionDurable.bind(this));
|
|
883
|
-
// fix getMonitoredItems.outputArguments arrayDimensions
|
|
884
|
-
const fixGetMonitoredItemArgs = () => {
|
|
885
|
-
var _a;
|
|
886
|
-
const objects = (_a = this.addressSpace.rootFolder) === null || _a === void 0 ? void 0 : _a.objects;
|
|
887
|
-
if (!objects || !objects.server || !objects.server.getMonitoredItems) {
|
|
888
|
-
return;
|
|
889
|
-
}
|
|
890
|
-
const outputArguments = objects.server.getMonitoredItems.outputArguments;
|
|
891
|
-
const dataValue = outputArguments.readValue();
|
|
892
|
-
(0, node_opcua_assert_1.assert)(dataValue.value.value[0].arrayDimensions.length === 1 && dataValue.value.value[0].arrayDimensions[0] === 0);
|
|
893
|
-
(0, node_opcua_assert_1.assert)(dataValue.value.value[1].arrayDimensions.length === 1 && dataValue.value.value[1].arrayDimensions[0] === 0);
|
|
894
|
-
};
|
|
895
|
-
fixGetMonitoredItemArgs();
|
|
896
|
-
const prepareServerDiagnostics = () => {
|
|
897
|
-
const addressSpace1 = this.addressSpace;
|
|
898
|
-
if (!addressSpace1.rootFolder.objects) {
|
|
899
|
-
return;
|
|
900
|
-
}
|
|
901
|
-
const server = addressSpace1.rootFolder.objects.server;
|
|
902
|
-
if (!server) {
|
|
903
|
-
return;
|
|
904
|
-
}
|
|
905
|
-
// create SessionsDiagnosticsSummary
|
|
906
|
-
const serverDiagnosticsNode = server.getComponentByName("ServerDiagnostics");
|
|
907
|
-
if (!serverDiagnosticsNode) {
|
|
908
|
-
return;
|
|
909
|
-
}
|
|
910
|
-
if (true) {
|
|
911
|
-
// set serverDiagnosticsNode enabledFlag writeable for admin user only
|
|
912
|
-
// TO DO ...
|
|
913
|
-
serverDiagnosticsNode.enabledFlag.userAccessLevel = (0, node_opcua_data_model_1.makeAccessLevelFlag)("CurrentRead");
|
|
914
|
-
serverDiagnosticsNode.enabledFlag.accessLevel = (0, node_opcua_data_model_1.makeAccessLevelFlag)("CurrentRead");
|
|
915
|
-
}
|
|
916
|
-
// A Server may not expose the SamplingIntervalDiagnosticsArray if it does not use fixed sampling rates.
|
|
917
|
-
// because we are not using fixed sampling rate, we need to remove the optional SamplingIntervalDiagnosticsArray
|
|
918
|
-
// component
|
|
919
|
-
const samplingIntervalDiagnosticsArray = serverDiagnosticsNode.getComponentByName("SamplingIntervalDiagnosticsArray");
|
|
920
|
-
if (samplingIntervalDiagnosticsArray) {
|
|
921
|
-
addressSpace.deleteNode(samplingIntervalDiagnosticsArray);
|
|
922
|
-
const s = serverDiagnosticsNode.getComponents();
|
|
923
|
-
}
|
|
924
|
-
const subscriptionDiagnosticsArrayNode = serverDiagnosticsNode.getComponentByName("SubscriptionDiagnosticsArray");
|
|
925
|
-
(0, node_opcua_assert_1.assert)(subscriptionDiagnosticsArrayNode.nodeClass === node_opcua_data_model_1.NodeClass.Variable);
|
|
926
|
-
(0, node_opcua_address_space_1.bindExtObjArrayNode)(subscriptionDiagnosticsArrayNode, "SubscriptionDiagnosticsType", "subscriptionId");
|
|
927
|
-
makeNotReadableIfEnabledFlagIsFalse(subscriptionDiagnosticsArrayNode);
|
|
928
|
-
const sessionsDiagnosticsSummary = serverDiagnosticsNode.getComponentByName("SessionsDiagnosticsSummary");
|
|
929
|
-
const sessionDiagnosticsArray = sessionsDiagnosticsSummary.getComponentByName("SessionDiagnosticsArray");
|
|
930
|
-
(0, node_opcua_assert_1.assert)(sessionDiagnosticsArray.nodeClass === node_opcua_data_model_1.NodeClass.Variable);
|
|
931
|
-
(0, node_opcua_address_space_1.bindExtObjArrayNode)(sessionDiagnosticsArray, "SessionDiagnosticsVariableType", "sessionId");
|
|
932
|
-
const varType = addressSpace.findVariableType("SessionSecurityDiagnosticsType");
|
|
933
|
-
if (!varType) {
|
|
934
|
-
debugLog("Warning cannot find SessionSecurityDiagnosticsType variable Type");
|
|
935
|
-
}
|
|
936
|
-
else {
|
|
937
|
-
const sessionSecurityDiagnosticsArray = sessionsDiagnosticsSummary.getComponentByName("SessionSecurityDiagnosticsArray");
|
|
938
|
-
(0, node_opcua_assert_1.assert)(sessionSecurityDiagnosticsArray.nodeClass === node_opcua_data_model_1.NodeClass.Variable);
|
|
939
|
-
(0, node_opcua_address_space_1.bindExtObjArrayNode)(sessionSecurityDiagnosticsArray, "SessionSecurityDiagnosticsType", "sessionId");
|
|
940
|
-
(0, node_opcua_address_space_1.ensureObjectIsSecure)(sessionSecurityDiagnosticsArray);
|
|
941
|
-
}
|
|
942
|
-
};
|
|
943
|
-
prepareServerDiagnostics();
|
|
944
|
-
this._internalState = "initialized";
|
|
945
|
-
this.setServerState(node_opcua_common_1.ServerState.Running);
|
|
946
|
-
setImmediate(() => callback());
|
|
947
|
-
});
|
|
948
|
-
}
|
|
949
|
-
/**
|
|
950
|
-
*
|
|
951
|
-
* @method browseSingleNode
|
|
952
|
-
* @param nodeId {NodeId|String} : the nodeid of the element to browse
|
|
953
|
-
* @param browseDescription
|
|
954
|
-
* @param browseDescription.browseDirection {BrowseDirection} :
|
|
955
|
-
* @param browseDescription.referenceTypeId {String|NodeId}
|
|
956
|
-
* @param [context]
|
|
957
|
-
* @return the browse result
|
|
958
|
-
*/
|
|
959
|
-
browseSingleNode(nodeId, browseDescription, context) {
|
|
960
|
-
const addressSpace = this.addressSpace;
|
|
961
|
-
return addressSpace.browseSingleNode(nodeId, browseDescription, context);
|
|
962
|
-
}
|
|
963
|
-
browseWithAutomaticExpansion(nodesToBrowse, context) {
|
|
964
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
965
|
-
// do expansion first
|
|
966
|
-
for (const browseDescription of nodesToBrowse) {
|
|
967
|
-
const nodeId = (0, node_opcua_nodeid_1.resolveNodeId)(browseDescription.nodeId);
|
|
968
|
-
const node = this.addressSpace.findNode(nodeId);
|
|
969
|
-
if (node) {
|
|
970
|
-
if (node.onFirstBrowseAction) {
|
|
971
|
-
try {
|
|
972
|
-
yield node.onFirstBrowseAction();
|
|
973
|
-
node.onFirstBrowseAction = undefined;
|
|
974
|
-
}
|
|
975
|
-
catch (err) {
|
|
976
|
-
if (err instanceof Error) {
|
|
977
|
-
errorLog("onFirstBrowseAction method has failed", err.message);
|
|
978
|
-
}
|
|
979
|
-
errorLog(err);
|
|
980
|
-
}
|
|
981
|
-
(0, node_opcua_assert_1.assert)(node.onFirstBrowseAction === undefined, "expansion can only be made once");
|
|
982
|
-
}
|
|
983
|
-
}
|
|
984
|
-
}
|
|
985
|
-
return this.browse(nodesToBrowse, context);
|
|
986
|
-
});
|
|
987
|
-
}
|
|
988
|
-
/**
|
|
989
|
-
*
|
|
990
|
-
*/
|
|
991
|
-
browse(nodesToBrowse, context) {
|
|
992
|
-
const results = [];
|
|
993
|
-
for (const browseDescription of nodesToBrowse) {
|
|
994
|
-
const nodeId = (0, node_opcua_nodeid_1.resolveNodeId)(browseDescription.nodeId);
|
|
995
|
-
const r = this.browseSingleNode(nodeId, browseDescription, context);
|
|
996
|
-
results.push(r);
|
|
997
|
-
}
|
|
998
|
-
return results;
|
|
999
|
-
}
|
|
1000
|
-
/**
|
|
1001
|
-
*
|
|
1002
|
-
* @method readSingleNode
|
|
1003
|
-
* @param context
|
|
1004
|
-
* @param nodeId
|
|
1005
|
-
* @param attributeId
|
|
1006
|
-
* @param [timestampsToReturn=TimestampsToReturn.Neither]
|
|
1007
|
-
* @return DataValue
|
|
1008
|
-
*/
|
|
1009
|
-
readSingleNode(context, nodeId, attributeId, timestampsToReturn) {
|
|
1010
|
-
context.currentTime = (0, node_opcua_date_time_1.getCurrentClock)();
|
|
1011
|
-
return this._readSingleNode(context, new node_opcua_types_1.ReadValueId({
|
|
1012
|
-
attributeId,
|
|
1013
|
-
nodeId: (0, node_opcua_nodeid_1.resolveNodeId)(nodeId)
|
|
1014
|
-
}), timestampsToReturn);
|
|
1015
|
-
}
|
|
1016
|
-
/**
|
|
1017
|
-
*
|
|
1018
|
-
*
|
|
1019
|
-
* Maximum age of the value to be read in milliseconds. The age of the value is based on the difference between
|
|
1020
|
-
* the ServerTimestamp and the time when the Server starts processing the request. For example if the Client
|
|
1021
|
-
* specifies a maxAge of 500 milliseconds and it takes 100 milliseconds until the Server starts processing
|
|
1022
|
-
* the request, the age of the returned value could be 600 milliseconds prior to the time it was requested.
|
|
1023
|
-
* If the Server has one or more values of an Attribute that are within the maximum age, it can return any one
|
|
1024
|
-
* of the values or it can read a new value from the data source. The number of values of an Attribute that
|
|
1025
|
-
* a Server has depends on the number of MonitoredItems that are defined for the Attribute. In any case,
|
|
1026
|
-
* the Client can make no assumption about which copy of the data will be returned.
|
|
1027
|
-
* If the Server does not have a value that is within the maximum age, it shall attempt to read a new value
|
|
1028
|
-
* from the data source.
|
|
1029
|
-
* If the Server cannot meet the requested maxAge, it returns its 'best effort' value rather than rejecting the
|
|
1030
|
-
* request.
|
|
1031
|
-
* This may occur when the time it takes the Server to process and return the new data value after it has been
|
|
1032
|
-
* accessed is greater than the specified maximum age.
|
|
1033
|
-
* If maxAge is set to 0, the Server shall attempt to read a new value from the data source.
|
|
1034
|
-
* If maxAge is set to the max Int32 value or greater, the Server shall attempt to get a cached value.
|
|
1035
|
-
* Negative values are invalid for maxAge.
|
|
1036
|
-
*
|
|
1037
|
-
* @return an array of DataValue
|
|
1038
|
-
*/
|
|
1039
|
-
read(context, readRequest) {
|
|
1040
|
-
(0, node_opcua_assert_1.assert)(context instanceof node_opcua_address_space_1.SessionContext);
|
|
1041
|
-
(0, node_opcua_assert_1.assert)(readRequest instanceof node_opcua_service_read_1.ReadRequest);
|
|
1042
|
-
(0, node_opcua_assert_1.assert)(readRequest.maxAge >= 0);
|
|
1043
|
-
const timestampsToReturn = readRequest.timestampsToReturn;
|
|
1044
|
-
const nodesToRead = readRequest.nodesToRead || [];
|
|
1045
|
-
(0, node_opcua_assert_1.assert)(Array.isArray(nodesToRead));
|
|
1046
|
-
context.currentTime = (0, node_opcua_date_time_1.getCurrentClock)();
|
|
1047
|
-
const dataValues = [];
|
|
1048
|
-
for (const readValueId of nodesToRead) {
|
|
1049
|
-
const dataValue = this._readSingleNode(context, readValueId, timestampsToReturn);
|
|
1050
|
-
if (timestampsToReturn === node_opcua_service_read_1.TimestampsToReturn.Server) {
|
|
1051
|
-
dataValue.sourceTimestamp = null;
|
|
1052
|
-
dataValue.sourcePicoseconds = 0;
|
|
1053
|
-
}
|
|
1054
|
-
if ((timestampsToReturn === node_opcua_service_read_1.TimestampsToReturn.Both || timestampsToReturn === node_opcua_service_read_1.TimestampsToReturn.Server) &&
|
|
1055
|
-
(!dataValue.serverTimestamp || dataValue.serverTimestamp.getTime() === node_opcua_date_time_1.minOPCUADate.getTime())) {
|
|
1056
|
-
dataValue.serverTimestamp = context.currentTime.timestamp;
|
|
1057
|
-
dataValue.sourcePicoseconds = 0; // context.currentTime.picosecond // do we really need picosecond here ? this would inflate binary data
|
|
1058
|
-
}
|
|
1059
|
-
dataValues.push(dataValue);
|
|
1060
|
-
}
|
|
1061
|
-
return dataValues;
|
|
1062
|
-
}
|
|
1063
|
-
/**
|
|
1064
|
-
*
|
|
1065
|
-
* @method writeSingleNode
|
|
1066
|
-
* @param context
|
|
1067
|
-
* @param writeValue
|
|
1068
|
-
* @param callback
|
|
1069
|
-
* @param callback.err
|
|
1070
|
-
* @param callback.statusCode
|
|
1071
|
-
* @async
|
|
1072
|
-
*/
|
|
1073
|
-
writeSingleNode(context, writeValue, callback) {
|
|
1074
|
-
(0, node_opcua_assert_1.assert)(context instanceof node_opcua_address_space_1.SessionContext);
|
|
1075
|
-
(0, node_opcua_assert_1.assert)(typeof callback === "function");
|
|
1076
|
-
(0, node_opcua_assert_1.assert)(writeValue.schema.name === "WriteValue");
|
|
1077
|
-
(0, node_opcua_assert_1.assert)(writeValue.value instanceof node_opcua_data_value_1.DataValue);
|
|
1078
|
-
if (writeValue.value.value === null) {
|
|
1079
|
-
return callback(null, node_opcua_status_code_1.StatusCodes.BadTypeMismatch);
|
|
1080
|
-
}
|
|
1081
|
-
(0, node_opcua_assert_1.assert)(writeValue.value.value instanceof node_opcua_variant_1.Variant);
|
|
1082
|
-
const nodeId = writeValue.nodeId;
|
|
1083
|
-
const obj = this.__findNode(nodeId);
|
|
1084
|
-
if (!obj) {
|
|
1085
|
-
return callback(null, node_opcua_status_code_1.StatusCodes.BadNodeIdUnknown);
|
|
1086
|
-
}
|
|
1087
|
-
else {
|
|
1088
|
-
obj.writeAttribute(context, writeValue, callback);
|
|
1089
|
-
}
|
|
1090
|
-
}
|
|
1091
|
-
/**
|
|
1092
|
-
* write a collection of nodes
|
|
1093
|
-
* @method write
|
|
1094
|
-
* @param context
|
|
1095
|
-
* @param nodesToWrite
|
|
1096
|
-
* @param callback
|
|
1097
|
-
* @param callback.err
|
|
1098
|
-
* @param callback.results
|
|
1099
|
-
* @async
|
|
1100
|
-
*/
|
|
1101
|
-
write(context, nodesToWrite, callback) {
|
|
1102
|
-
(0, node_opcua_assert_1.assert)(context instanceof node_opcua_address_space_1.SessionContext);
|
|
1103
|
-
(0, node_opcua_assert_1.assert)(typeof callback === "function");
|
|
1104
|
-
context.currentTime = (0, node_opcua_date_time_1.getCurrentClock)();
|
|
1105
|
-
(0, node_opcua_address_space_1.ensureDatatypeExtractedWithCallback)(this.addressSpace, (err2, extraDataTypeManager) => {
|
|
1106
|
-
if (err2) {
|
|
1107
|
-
return callback(err2);
|
|
1108
|
-
}
|
|
1109
|
-
const performWrite = (writeValue, inner_callback) => {
|
|
1110
|
-
(0, node_opcua_address_space_1.resolveOpaqueOnAddressSpace)(this.addressSpace, writeValue.value.value)
|
|
1111
|
-
.then(() => {
|
|
1112
|
-
this.writeSingleNode(context, writeValue, inner_callback);
|
|
1113
|
-
})
|
|
1114
|
-
.catch(inner_callback);
|
|
1115
|
-
};
|
|
1116
|
-
// tslint:disable:array-type
|
|
1117
|
-
async.map(nodesToWrite, performWrite, (err, statusCodes) => {
|
|
1118
|
-
(0, node_opcua_assert_1.assert)(Array.isArray(statusCodes));
|
|
1119
|
-
callback(err, statusCodes);
|
|
1120
|
-
});
|
|
1121
|
-
});
|
|
1122
|
-
}
|
|
1123
|
-
/**
|
|
1124
|
-
*
|
|
1125
|
-
*/
|
|
1126
|
-
historyReadSingleNode(context, nodeId, attributeId, historyReadDetails, timestampsToReturn, continuationData, callback) {
|
|
1127
|
-
if (timestampsToReturn === node_opcua_service_read_1.TimestampsToReturn.Invalid) {
|
|
1128
|
-
callback(null, new node_opcua_service_history_1.HistoryReadResult({
|
|
1129
|
-
statusCode: node_opcua_status_code_1.StatusCodes.BadTimestampsToReturnInvalid
|
|
1130
|
-
}));
|
|
1131
|
-
return;
|
|
1132
|
-
}
|
|
1133
|
-
(0, node_opcua_assert_1.assert)(context instanceof node_opcua_address_space_1.SessionContext);
|
|
1134
|
-
this._historyReadSingleNode(context, new node_opcua_service_history_1.HistoryReadValueId({
|
|
1135
|
-
nodeId
|
|
1136
|
-
}), historyReadDetails, timestampsToReturn, continuationData, callback);
|
|
1137
|
-
}
|
|
1138
|
-
/**
|
|
1139
|
-
*
|
|
1140
|
-
* @method historyRead
|
|
1141
|
-
* @param context {SessionContext}
|
|
1142
|
-
* @param historyReadRequest {HistoryReadRequest}
|
|
1143
|
-
* @param historyReadRequest.requestHeader {RequestHeader}
|
|
1144
|
-
* @param historyReadRequest.historyReadDetails {HistoryReadDetails}
|
|
1145
|
-
* @param historyReadRequest.timestampsToReturn {TimestampsToReturn}
|
|
1146
|
-
* @param historyReadRequest.releaseContinuationPoints {Boolean}
|
|
1147
|
-
* @param historyReadRequest.nodesToRead {HistoryReadValueId[]}
|
|
1148
|
-
* @param callback
|
|
1149
|
-
* @param callback.err
|
|
1150
|
-
* @param callback.results {HistoryReadResult[]}
|
|
1151
|
-
*/
|
|
1152
|
-
historyRead(context, historyReadRequest, callback) {
|
|
1153
|
-
(0, node_opcua_assert_1.assert)(context instanceof node_opcua_address_space_1.SessionContext);
|
|
1154
|
-
(0, node_opcua_assert_1.assert)(historyReadRequest instanceof node_opcua_service_history_1.HistoryReadRequest);
|
|
1155
|
-
(0, node_opcua_assert_1.assert)(typeof callback === "function");
|
|
1156
|
-
const timestampsToReturn = historyReadRequest.timestampsToReturn;
|
|
1157
|
-
const historyReadDetails = historyReadRequest.historyReadDetails;
|
|
1158
|
-
const releaseContinuationPoints = historyReadRequest.releaseContinuationPoints;
|
|
1159
|
-
(0, node_opcua_assert_1.assert)(historyReadDetails instanceof node_opcua_service_history_1.HistoryReadDetails);
|
|
1160
|
-
// ReadAnnotationDataDetails | ReadAtTimeDetails | ReadEventDetails | ReadProcessedDetails | ReadRawModifiedDetails;
|
|
1161
|
-
const nodesToRead = historyReadRequest.nodesToRead || [];
|
|
1162
|
-
(0, node_opcua_assert_1.assert)(Array.isArray(nodesToRead));
|
|
1163
|
-
const _q = (m) => __awaiter(this, void 0, void 0, function* () {
|
|
1164
|
-
return new Promise((resolve) => {
|
|
1165
|
-
const continuationPoint = m.nodeToRead.continuationPoint;
|
|
1166
|
-
this._historyReadSingleNode(context, m.nodeToRead, m.processDetail, timestampsToReturn, { continuationPoint, releaseContinuationPoints /**, index = ??? */ }, (err, result) => {
|
|
1167
|
-
if (err && !result) {
|
|
1168
|
-
errorLog("Internal error", err.message);
|
|
1169
|
-
result = new node_opcua_service_history_1.HistoryReadResult({ statusCode: node_opcua_status_code_1.StatusCodes.BadInternalError });
|
|
1170
|
-
}
|
|
1171
|
-
resolve(result);
|
|
1172
|
-
});
|
|
1173
|
-
});
|
|
1174
|
-
});
|
|
1175
|
-
if (historyReadDetails instanceof node_opcua_types_1.ReadProcessedDetails) {
|
|
1176
|
-
//
|
|
1177
|
-
if (!historyReadDetails.aggregateType || historyReadDetails.aggregateType.length !== nodesToRead.length) {
|
|
1178
|
-
return callback(null, [new node_opcua_service_history_1.HistoryReadResult({ statusCode: node_opcua_status_code_1.StatusCodes.BadInvalidArgument })]);
|
|
1179
|
-
}
|
|
1180
|
-
const promises = [];
|
|
1181
|
-
let index = 0;
|
|
1182
|
-
for (const nodeToRead of nodesToRead) {
|
|
1183
|
-
const aggregateType = historyReadDetails.aggregateType[index];
|
|
1184
|
-
const processDetail = new node_opcua_types_1.ReadProcessedDetails(Object.assign(Object.assign({}, historyReadDetails), { aggregateType: [aggregateType] }));
|
|
1185
|
-
promises.push(_q({ nodeToRead, processDetail, index }));
|
|
1186
|
-
index++;
|
|
1187
|
-
}
|
|
1188
|
-
Promise.all(promises).then((results) => {
|
|
1189
|
-
callback(null, results);
|
|
1190
|
-
});
|
|
1191
|
-
return;
|
|
1192
|
-
}
|
|
1193
|
-
const _r = (nodeToRead, index) => __awaiter(this, void 0, void 0, function* () {
|
|
1194
|
-
const continuationPoint = nodeToRead.continuationPoint;
|
|
1195
|
-
return new Promise((resolve, reject) => {
|
|
1196
|
-
this._historyReadSingleNode(context, nodeToRead, historyReadDetails, timestampsToReturn, { continuationPoint, releaseContinuationPoints, index }, (err, result) => {
|
|
1197
|
-
if (err && !result) {
|
|
1198
|
-
result = new node_opcua_service_history_1.HistoryReadResult({ statusCode: node_opcua_status_code_1.StatusCodes.BadInternalError });
|
|
1199
|
-
}
|
|
1200
|
-
resolve(result);
|
|
1201
|
-
// it's not guaranteed that the historical read process is really asynchronous
|
|
1202
|
-
});
|
|
1203
|
-
});
|
|
1204
|
-
});
|
|
1205
|
-
const promises = [];
|
|
1206
|
-
let index = 0;
|
|
1207
|
-
for (const nodeToRead of nodesToRead) {
|
|
1208
|
-
promises.push(_r(nodeToRead, index));
|
|
1209
|
-
index++;
|
|
1210
|
-
}
|
|
1211
|
-
Promise.all(promises).then((results) => {
|
|
1212
|
-
callback(null, results);
|
|
1213
|
-
});
|
|
1214
|
-
}
|
|
1215
|
-
getOldestInactiveSession() {
|
|
1216
|
-
// search screwed or closed session first
|
|
1217
|
-
let tmp = Object.values(this._sessions).filter((session1) => session1.status === "screwed" || session1.status === "disposed" || session1.status === "closed");
|
|
1218
|
-
if (tmp.length === 0) {
|
|
1219
|
-
// if none available, tap into the session that are not yet activated
|
|
1220
|
-
tmp = Object.values(this._sessions).filter((session1) => session1.status === "new");
|
|
1221
|
-
}
|
|
1222
|
-
if (tmp.length === 0)
|
|
1223
|
-
return null;
|
|
1224
|
-
let session = tmp[0];
|
|
1225
|
-
for (let i = 1; i < tmp.length; i++) {
|
|
1226
|
-
const c = tmp[i];
|
|
1227
|
-
if (session.creationDate.getTime() < c.creationDate.getTime()) {
|
|
1228
|
-
session = c;
|
|
1229
|
-
}
|
|
1230
|
-
}
|
|
1231
|
-
return session;
|
|
1232
|
-
}
|
|
1233
|
-
/**
|
|
1234
|
-
* create a new server session object.
|
|
1235
|
-
* @class ServerEngine
|
|
1236
|
-
* @method createSession
|
|
1237
|
-
* @param [options] {Object}
|
|
1238
|
-
* @param [options.sessionTimeout = 1000] {Number} sessionTimeout
|
|
1239
|
-
* @param [options.clientDescription] {ApplicationDescription}
|
|
1240
|
-
* @return {ServerSession}
|
|
1241
|
-
*/
|
|
1242
|
-
createSession(options) {
|
|
1243
|
-
options = options || {};
|
|
1244
|
-
debugLog("createSession : increasing serverDiagnosticsSummary cumulatedSessionCount/currentSessionCount ");
|
|
1245
|
-
this.serverDiagnosticsSummary.cumulatedSessionCount += 1;
|
|
1246
|
-
this.serverDiagnosticsSummary.currentSessionCount += 1;
|
|
1247
|
-
this.clientDescription = options.clientDescription || new node_opcua_service_endpoints_1.ApplicationDescription({});
|
|
1248
|
-
const sessionTimeout = options.sessionTimeout || 1000;
|
|
1249
|
-
(0, node_opcua_assert_1.assert)(typeof sessionTimeout === "number");
|
|
1250
|
-
const session = new server_session_1.ServerSession(this, sessionTimeout);
|
|
1251
|
-
debugLog("createSession :sessionTimeout = ", session.sessionTimeout);
|
|
1252
|
-
const key = session.authenticationToken.toString();
|
|
1253
|
-
this._sessions[key] = session;
|
|
1254
|
-
// see spec OPC Unified Architecture, Part 2 page 26 Release 1.02
|
|
1255
|
-
// TODO : When a Session is created, the Server adds an entry for the Client
|
|
1256
|
-
// in its SessionDiagnosticsArray Variable
|
|
1257
|
-
session.on("new_subscription", (subscription) => {
|
|
1258
|
-
this.serverDiagnosticsSummary.cumulatedSubscriptionCount += 1;
|
|
1259
|
-
// add the subscription diagnostics in our subscriptions diagnostics array
|
|
1260
|
-
// note currentSubscriptionCount is handled directly with a special getter
|
|
1261
|
-
});
|
|
1262
|
-
session.on("subscription_terminated", (subscription) => {
|
|
1263
|
-
// remove the subscription diagnostics in our subscriptions diagnostics array
|
|
1264
|
-
// note currentSubscriptionCount is handled directly with a special getter
|
|
1265
|
-
});
|
|
1266
|
-
// OPC Unified Architecture, Part 4 23 Release 1.03
|
|
1267
|
-
// Sessions are terminated by the Server automatically if the Client fails to issue a Service request on the
|
|
1268
|
-
// Session within the timeout period negotiated by the Server in the CreateSession Service response.
|
|
1269
|
-
// This protects the Server against Client failures and against situations where a failed underlying
|
|
1270
|
-
// connection cannot be re-established. Clients shall be prepared to submit requests in a timely manner
|
|
1271
|
-
// prevent the Session from closing automatically. Clients may explicitly terminate sessions using the
|
|
1272
|
-
// CloseSession Service.
|
|
1273
|
-
session.on("timeout", () => {
|
|
1274
|
-
// the session hasn't been active for a while , probably because the client has disconnected abruptly
|
|
1275
|
-
// it is now time to close the session completely
|
|
1276
|
-
this.serverDiagnosticsSummary.sessionTimeoutCount += 1;
|
|
1277
|
-
session.sessionName = session.sessionName || "";
|
|
1278
|
-
const channel = session.channel;
|
|
1279
|
-
errorLog(chalk.cyan("Server: closing SESSION "), session.status, chalk.yellow(session.sessionName), chalk.yellow(session.nodeId.toString()), chalk.cyan(" because of timeout = "), session.sessionTimeout, chalk.cyan(" has expired without a keep alive"), chalk.bgCyan("channel = "), channel === null || channel === void 0 ? void 0 : channel.remoteAddress, " port = ", channel === null || channel === void 0 ? void 0 : channel.remotePort);
|
|
1280
|
-
// If a Server terminates a Session for any other reason, Subscriptions associated with the Session,
|
|
1281
|
-
// are not deleted. => deleteSubscription= false
|
|
1282
|
-
this.closeSession(session.authenticationToken, /*deleteSubscription=*/ false, /* reason =*/ "Timeout");
|
|
1283
|
-
this.incrementSessionTimeoutCount();
|
|
1284
|
-
});
|
|
1285
|
-
return session;
|
|
1286
|
-
}
|
|
1287
|
-
/**
|
|
1288
|
-
* @method closeSession
|
|
1289
|
-
* @param authenticationToken
|
|
1290
|
-
* @param deleteSubscriptions {Boolean} : true if session's subscription shall be deleted
|
|
1291
|
-
* @param {String} [reason = "CloseSession"] the reason for closing the session (
|
|
1292
|
-
* shall be "Timeout", "Terminated" or "CloseSession")
|
|
1293
|
-
*
|
|
1294
|
-
*
|
|
1295
|
-
* what the specs say:
|
|
1296
|
-
* -------------------
|
|
1297
|
-
*
|
|
1298
|
-
* If a Client invokes the CloseSession Service then all Subscriptions associated with the Session are also deleted
|
|
1299
|
-
* if the deleteSubscriptions flag is set to TRUE. If a Server terminates a Session for any other reason,
|
|
1300
|
-
* Subscriptions associated with the Session, are not deleted. Each Subscription has its own lifetime to protect
|
|
1301
|
-
* against data loss in the case of a Session termination. In these cases, the Subscription can be reassigned to
|
|
1302
|
-
* another Client before its lifetime expires.
|
|
1303
|
-
*/
|
|
1304
|
-
closeSession(authenticationToken, deleteSubscriptions, reason) {
|
|
1305
|
-
reason = reason || "CloseSession";
|
|
1306
|
-
(0, node_opcua_assert_1.assert)(typeof reason === "string");
|
|
1307
|
-
(0, node_opcua_assert_1.assert)(reason === "Timeout" || reason === "Terminated" || reason === "CloseSession" || reason === "Forcing");
|
|
1308
|
-
debugLog("ServerEngine.closeSession ", authenticationToken.toString(), deleteSubscriptions);
|
|
1309
|
-
const session = this.getSession(authenticationToken);
|
|
1310
|
-
// istanbul ignore next
|
|
1311
|
-
if (!session) {
|
|
1312
|
-
throw new Error("cannot find session with this authenticationToken " + authenticationToken.toString());
|
|
1313
|
-
}
|
|
1314
|
-
if (!deleteSubscriptions) {
|
|
1315
|
-
// Live Subscriptions will not be deleted, but transferred to the orphanPublishEngine
|
|
1316
|
-
// until they time out or until a other session transfer them back to it.
|
|
1317
|
-
if (!this._orphanPublishEngine) {
|
|
1318
|
-
this._orphanPublishEngine = new server_publish_engine_for_orphan_subscriptions_1.ServerSidePublishEngineForOrphanSubscription({ maxPublishRequestInQueue: 0 });
|
|
1319
|
-
}
|
|
1320
|
-
debugLog("transferring remaining live subscription to orphanPublishEngine !");
|
|
1321
|
-
server_publish_engine_1.ServerSidePublishEngine.transferSubscriptionsToOrphan(session.publishEngine, this._orphanPublishEngine);
|
|
1322
|
-
}
|
|
1323
|
-
session.close(deleteSubscriptions, reason);
|
|
1324
|
-
(0, node_opcua_assert_1.assert)(session.status === "closed");
|
|
1325
|
-
debugLog(" engine.serverDiagnosticsSummary.currentSessionCount -= 1;");
|
|
1326
|
-
this.serverDiagnosticsSummary.currentSessionCount -= 1;
|
|
1327
|
-
// xx //TODO make sure _closedSessions gets cleaned at some point
|
|
1328
|
-
// xx self._closedSessions[key] = session;
|
|
1329
|
-
// remove sessionDiagnostics from server.ServerDiagnostics.SessionsDiagnosticsSummary.SessionDiagnosticsSummary
|
|
1330
|
-
delete this._sessions[authenticationToken.toString()];
|
|
1331
|
-
session.dispose();
|
|
1332
|
-
}
|
|
1333
|
-
findSubscription(subscriptionId) {
|
|
1334
|
-
const subscriptions = [];
|
|
1335
|
-
Object.values(this._sessions).map((session) => {
|
|
1336
|
-
if (subscriptions.length) {
|
|
1337
|
-
return;
|
|
1338
|
-
}
|
|
1339
|
-
const subscription = session.publishEngine.getSubscriptionById(subscriptionId);
|
|
1340
|
-
if (subscription) {
|
|
1341
|
-
subscriptions.push(subscription);
|
|
1342
|
-
}
|
|
1343
|
-
});
|
|
1344
|
-
if (subscriptions.length) {
|
|
1345
|
-
(0, node_opcua_assert_1.assert)(subscriptions.length === 1);
|
|
1346
|
-
return subscriptions[0];
|
|
1347
|
-
}
|
|
1348
|
-
return this.findOrphanSubscription(subscriptionId);
|
|
1349
|
-
}
|
|
1350
|
-
findOrphanSubscription(subscriptionId) {
|
|
1351
|
-
if (!this._orphanPublishEngine) {
|
|
1352
|
-
return null;
|
|
1353
|
-
}
|
|
1354
|
-
return this._orphanPublishEngine.getSubscriptionById(subscriptionId);
|
|
1355
|
-
}
|
|
1356
|
-
deleteOrphanSubscription(subscription) {
|
|
1357
|
-
if (!this._orphanPublishEngine) {
|
|
1358
|
-
return node_opcua_status_code_1.StatusCodes.BadInternalError;
|
|
1359
|
-
}
|
|
1360
|
-
(0, node_opcua_assert_1.assert)(this.findSubscription(subscription.id));
|
|
1361
|
-
const c = this._orphanPublishEngine.subscriptionCount;
|
|
1362
|
-
subscription.terminate();
|
|
1363
|
-
subscription.dispose();
|
|
1364
|
-
(0, node_opcua_assert_1.assert)(this._orphanPublishEngine.subscriptionCount === c - 1);
|
|
1365
|
-
return node_opcua_status_code_1.StatusCodes.Good;
|
|
1366
|
-
}
|
|
1367
|
-
/**
|
|
1368
|
-
* @method transferSubscription
|
|
1369
|
-
* @param session {ServerSession} - the new session that will own the subscription
|
|
1370
|
-
* @param subscriptionId {IntegerId} - the subscription Id to transfer
|
|
1371
|
-
* @param sendInitialValues {Boolean} - true if initial values will be resent.
|
|
1372
|
-
* @return {TransferResult}
|
|
1373
|
-
*/
|
|
1374
|
-
transferSubscription(session, subscriptionId, sendInitialValues) {
|
|
1375
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
1376
|
-
if (subscriptionId <= 0) {
|
|
1377
|
-
return new node_opcua_service_subscription_1.TransferResult({ statusCode: node_opcua_status_code_1.StatusCodes.BadSubscriptionIdInvalid });
|
|
1378
|
-
}
|
|
1379
|
-
const subscription = this.findSubscription(subscriptionId);
|
|
1380
|
-
if (!subscription) {
|
|
1381
|
-
return new node_opcua_service_subscription_1.TransferResult({ statusCode: node_opcua_status_code_1.StatusCodes.BadSubscriptionIdInvalid });
|
|
1382
|
-
}
|
|
1383
|
-
// check that session have same userIdentity
|
|
1384
|
-
if (!(0, sessions_compatible_for_transfer_1.sessionsCompatibleForTransfer)(subscription.$session, session)) {
|
|
1385
|
-
return new node_opcua_service_subscription_1.TransferResult({ statusCode: node_opcua_status_code_1.StatusCodes.BadUserAccessDenied });
|
|
1386
|
-
}
|
|
1387
|
-
// update diagnostics
|
|
1388
|
-
subscription.subscriptionDiagnostics.transferRequestCount++;
|
|
1389
|
-
// now check that new session has sufficient right
|
|
1390
|
-
// if (session.authenticationToken.toString() !== subscription.authenticationToken.toString()) {
|
|
1391
|
-
// console.log("ServerEngine#transferSubscription => BadUserAccessDenied");
|
|
1392
|
-
// return new TransferResult({ statusCode: StatusCodes.BadUserAccessDenied });
|
|
1393
|
-
// }
|
|
1394
|
-
if (session.publishEngine === subscription.publishEngine) {
|
|
1395
|
-
// subscription is already in this session !!
|
|
1396
|
-
return new node_opcua_service_subscription_1.TransferResult({ statusCode: node_opcua_status_code_1.StatusCodes.BadNothingToDo });
|
|
1397
|
-
}
|
|
1398
|
-
if (session === subscription.$session) {
|
|
1399
|
-
// subscription is already in this session !!
|
|
1400
|
-
return new node_opcua_service_subscription_1.TransferResult({ statusCode: node_opcua_status_code_1.StatusCodes.BadNothingToDo });
|
|
1401
|
-
}
|
|
1402
|
-
// The number of times the subscription has been transferred to an alternate client.
|
|
1403
|
-
subscription.subscriptionDiagnostics.transferredToAltClientCount++;
|
|
1404
|
-
// The number of times the subscription has been transferred to an alternate session for the same client.
|
|
1405
|
-
subscription.subscriptionDiagnostics.transferredToSameClientCount++;
|
|
1406
|
-
const nbSubscriptionBefore = session.publishEngine.subscriptionCount;
|
|
1407
|
-
if (subscription.$session) {
|
|
1408
|
-
subscription.$session._unexposeSubscriptionDiagnostics(subscription);
|
|
1409
|
-
}
|
|
1410
|
-
yield server_publish_engine_1.ServerSidePublishEngine.transferSubscription(subscription, session.publishEngine, sendInitialValues);
|
|
1411
|
-
subscription.$session = session;
|
|
1412
|
-
session._exposeSubscriptionDiagnostics(subscription);
|
|
1413
|
-
(0, node_opcua_assert_1.assert)(subscription.publishEngine === session.publishEngine);
|
|
1414
|
-
// assert(session.publishEngine.subscriptionCount === nbSubscriptionBefore + 1);
|
|
1415
|
-
const result = new node_opcua_service_subscription_1.TransferResult({
|
|
1416
|
-
availableSequenceNumbers: subscription.getAvailableSequenceNumbers(),
|
|
1417
|
-
statusCode: node_opcua_status_code_1.StatusCodes.Good
|
|
1418
|
-
});
|
|
1419
|
-
// istanbul ignore next
|
|
1420
|
-
if (doDebug) {
|
|
1421
|
-
debugLog("TransferResult", result.toString());
|
|
1422
|
-
}
|
|
1423
|
-
return result;
|
|
1424
|
-
});
|
|
1425
|
-
}
|
|
1426
|
-
/**
|
|
1427
|
-
* retrieve a session by its authenticationToken.
|
|
1428
|
-
*
|
|
1429
|
-
* @method getSession
|
|
1430
|
-
* @param authenticationToken
|
|
1431
|
-
* @param activeOnly
|
|
1432
|
-
* @return {ServerSession}
|
|
1433
|
-
*/
|
|
1434
|
-
getSession(authenticationToken, activeOnly) {
|
|
1435
|
-
if (!authenticationToken ||
|
|
1436
|
-
(authenticationToken.identifierType && authenticationToken.identifierType !== node_opcua_nodeid_1.NodeIdType.BYTESTRING)) {
|
|
1437
|
-
return null; // wrong type !
|
|
1438
|
-
}
|
|
1439
|
-
const key = authenticationToken.toString();
|
|
1440
|
-
let session = this._sessions[key];
|
|
1441
|
-
if (!activeOnly && !session) {
|
|
1442
|
-
session = this._closedSessions[key];
|
|
1443
|
-
}
|
|
1444
|
-
return session;
|
|
1445
|
-
}
|
|
1446
|
-
/**
|
|
1447
|
-
*/
|
|
1448
|
-
browsePath(browsePath) {
|
|
1449
|
-
return this.addressSpace.browsePath(browsePath);
|
|
1450
|
-
}
|
|
1451
|
-
/**
|
|
1452
|
-
*
|
|
1453
|
-
* performs a call to ```asyncRefresh``` on all variable nodes that provide an async refresh func.
|
|
1454
|
-
*
|
|
1455
|
-
* @method refreshValues
|
|
1456
|
-
* @param nodesToRefresh {Array<Object>} an array containing the node to consider
|
|
1457
|
-
* Each element of the array shall be of the form { nodeId: <xxx>, attributeIds: <value> }.
|
|
1458
|
-
* @param callback
|
|
1459
|
-
* @param callback.err
|
|
1460
|
-
* @param callback.data an array containing value read
|
|
1461
|
-
* The array length matches the number of nodeIds that are candidate for an async refresh (i.e: nodes that
|
|
1462
|
-
* are of type Variable with asyncRefresh func }
|
|
1463
|
-
*
|
|
1464
|
-
* @async
|
|
1465
|
-
*/
|
|
1466
|
-
refreshValues(nodesToRefresh, maxAge, callback) {
|
|
1467
|
-
const referenceTime = new Date(Date.now() - maxAge);
|
|
1468
|
-
(0, node_opcua_assert_1.assert)(typeof callback === "function");
|
|
1469
|
-
const objectMap = {};
|
|
1470
|
-
for (const nodeToRefresh of nodesToRefresh) {
|
|
1471
|
-
// only consider node for which the caller wants to read the Value attribute
|
|
1472
|
-
// assuming that Value is requested if attributeId is missing,
|
|
1473
|
-
if (nodeToRefresh instanceof node_opcua_types_1.ReadValueId && nodeToRefresh.attributeId !== node_opcua_data_model_1.AttributeIds.Value) {
|
|
1474
|
-
continue;
|
|
1475
|
-
}
|
|
1476
|
-
// ... and that are valid object and instances of Variables ...
|
|
1477
|
-
const obj = this.addressSpace.findNode(nodeToRefresh.nodeId);
|
|
1478
|
-
if (!obj || !(obj.nodeClass === node_opcua_data_model_1.NodeClass.Variable)) {
|
|
1479
|
-
continue;
|
|
1480
|
-
}
|
|
1481
|
-
// ... and that have been declared as asynchronously updating
|
|
1482
|
-
if (typeof obj.refreshFunc !== "function") {
|
|
1483
|
-
continue;
|
|
1484
|
-
}
|
|
1485
|
-
const key = obj.nodeId.toString();
|
|
1486
|
-
if (objectMap[key]) {
|
|
1487
|
-
continue;
|
|
1488
|
-
}
|
|
1489
|
-
objectMap[key] = obj;
|
|
1490
|
-
}
|
|
1491
|
-
const objectArray = Object.values(objectMap);
|
|
1492
|
-
if (objectArray.length === 0) {
|
|
1493
|
-
// nothing to do
|
|
1494
|
-
return callback(null, []);
|
|
1495
|
-
}
|
|
1496
|
-
// perform all asyncRefresh in parallel
|
|
1497
|
-
async.map(objectArray, (obj, inner_callback) => {
|
|
1498
|
-
if (obj.nodeClass !== node_opcua_data_model_1.NodeClass.Variable) {
|
|
1499
|
-
inner_callback(null, new node_opcua_data_value_1.DataValue({
|
|
1500
|
-
statusCode: node_opcua_status_code_1.StatusCodes.BadNodeClassInvalid
|
|
1501
|
-
}));
|
|
1502
|
-
return;
|
|
1503
|
-
}
|
|
1504
|
-
try {
|
|
1505
|
-
obj.asyncRefresh(referenceTime, (err, dataValue) => {
|
|
1506
|
-
inner_callback(err, dataValue);
|
|
1507
|
-
});
|
|
1508
|
-
}
|
|
1509
|
-
catch (err) {
|
|
1510
|
-
// istanbul ignore next
|
|
1511
|
-
if (!(err instanceof Error)) {
|
|
1512
|
-
throw new Error("internal error");
|
|
1513
|
-
}
|
|
1514
|
-
errorLog("asyncRefresh internal error", err);
|
|
1515
|
-
inner_callback(err);
|
|
1516
|
-
}
|
|
1517
|
-
}, (err, arrResult) => {
|
|
1518
|
-
callback(err || null, arrResult);
|
|
1519
|
-
});
|
|
1520
|
-
}
|
|
1521
|
-
_exposeSubscriptionDiagnostics(subscription) {
|
|
1522
|
-
debugLog("ServerEngine#_exposeSubscriptionDiagnostics", subscription.subscriptionId);
|
|
1523
|
-
const subscriptionDiagnosticsArray = this._getServerSubscriptionDiagnosticsArrayNode();
|
|
1524
|
-
const subscriptionDiagnostics = subscription.subscriptionDiagnostics;
|
|
1525
|
-
(0, node_opcua_assert_1.assert)(subscriptionDiagnostics.$subscription === subscription);
|
|
1526
|
-
(0, node_opcua_assert_1.assert)(subscriptionDiagnostics instanceof node_opcua_common_1.SubscriptionDiagnosticsDataType);
|
|
1527
|
-
if (subscriptionDiagnostics && subscriptionDiagnosticsArray) {
|
|
1528
|
-
(0, node_opcua_address_space_1.addElement)(subscriptionDiagnostics, subscriptionDiagnosticsArray);
|
|
1529
|
-
}
|
|
1530
|
-
}
|
|
1531
|
-
_unexposeSubscriptionDiagnostics(subscription) {
|
|
1532
|
-
const serverSubscriptionDiagnosticsArray = this._getServerSubscriptionDiagnosticsArrayNode();
|
|
1533
|
-
const subscriptionDiagnostics = subscription.subscriptionDiagnostics;
|
|
1534
|
-
(0, node_opcua_assert_1.assert)(subscriptionDiagnostics instanceof node_opcua_common_1.SubscriptionDiagnosticsDataType);
|
|
1535
|
-
if (subscriptionDiagnostics && serverSubscriptionDiagnosticsArray) {
|
|
1536
|
-
const node = serverSubscriptionDiagnosticsArray[subscription.id];
|
|
1537
|
-
(0, node_opcua_address_space_1.removeElement)(serverSubscriptionDiagnosticsArray, subscriptionDiagnostics);
|
|
1538
|
-
/*assert(
|
|
1539
|
-
!(subscriptionDiagnosticsArray as any)[subscription.id],
|
|
1540
|
-
" subscription node must have been removed from subscriptionDiagnosticsArray"
|
|
1541
|
-
);
|
|
1542
|
-
*/
|
|
1543
|
-
}
|
|
1544
|
-
debugLog("ServerEngine#_unexposeSubscriptionDiagnostics", subscription.subscriptionId);
|
|
1545
|
-
}
|
|
1546
|
-
/**
|
|
1547
|
-
* create a new subscription
|
|
1548
|
-
* @return {Subscription}
|
|
1549
|
-
*/
|
|
1550
|
-
_createSubscriptionOnSession(session, request) {
|
|
1551
|
-
(0, node_opcua_assert_1.assert)(Object.prototype.hasOwnProperty.call(request, "requestedPublishingInterval")); // Duration
|
|
1552
|
-
(0, node_opcua_assert_1.assert)(Object.prototype.hasOwnProperty.call(request, "requestedLifetimeCount")); // Counter
|
|
1553
|
-
(0, node_opcua_assert_1.assert)(Object.prototype.hasOwnProperty.call(request, "requestedMaxKeepAliveCount")); // Counter
|
|
1554
|
-
(0, node_opcua_assert_1.assert)(Object.prototype.hasOwnProperty.call(request, "maxNotificationsPerPublish")); // Counter
|
|
1555
|
-
(0, node_opcua_assert_1.assert)(Object.prototype.hasOwnProperty.call(request, "publishingEnabled")); // Boolean
|
|
1556
|
-
(0, node_opcua_assert_1.assert)(Object.prototype.hasOwnProperty.call(request, "priority")); // Byte
|
|
1557
|
-
// adjust publishing parameters
|
|
1558
|
-
const publishingInterval = request.requestedPublishingInterval || 0;
|
|
1559
|
-
const maxKeepAliveCount = request.requestedMaxKeepAliveCount || 0;
|
|
1560
|
-
const lifeTimeCount = request.requestedLifetimeCount || 0;
|
|
1561
|
-
const subscription = new server_subscription_1.Subscription({
|
|
1562
|
-
id: _get_next_subscriptionId(),
|
|
1563
|
-
lifeTimeCount,
|
|
1564
|
-
maxKeepAliveCount,
|
|
1565
|
-
maxNotificationsPerPublish: request.maxNotificationsPerPublish,
|
|
1566
|
-
priority: request.priority || 0,
|
|
1567
|
-
publishEngine: session.publishEngine,
|
|
1568
|
-
publishingEnabled: request.publishingEnabled,
|
|
1569
|
-
publishingInterval,
|
|
1570
|
-
// -------------------
|
|
1571
|
-
sessionId: node_opcua_nodeid_1.NodeId.nullNodeId,
|
|
1572
|
-
globalCounter: this._globalCounter,
|
|
1573
|
-
serverCapabilities: this.serverCapabilities // shared
|
|
1574
|
-
});
|
|
1575
|
-
// add subscriptionDiagnostics
|
|
1576
|
-
this._exposeSubscriptionDiagnostics(subscription);
|
|
1577
|
-
(0, node_opcua_assert_1.assert)(subscription.publishEngine === session.publishEngine);
|
|
1578
|
-
session.publishEngine.add_subscription(subscription);
|
|
1579
|
-
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
1580
|
-
const engine = this;
|
|
1581
|
-
subscription.once("terminated", function () {
|
|
1582
|
-
engine._unexposeSubscriptionDiagnostics(this);
|
|
1583
|
-
});
|
|
1584
|
-
return subscription;
|
|
1585
|
-
}
|
|
1586
|
-
__findNode(nodeId) {
|
|
1587
|
-
var _a;
|
|
1588
|
-
if (nodeId.namespace >= (((_a = this.addressSpace) === null || _a === void 0 ? void 0 : _a.getNamespaceArray().length) || 0)) {
|
|
1589
|
-
return null;
|
|
1590
|
-
}
|
|
1591
|
-
const namespace = this.addressSpace.getNamespace(nodeId.namespace);
|
|
1592
|
-
return namespace.findNode2(nodeId);
|
|
1593
|
-
}
|
|
1594
|
-
_readSingleNode(context, nodeToRead, timestampsToReturn) {
|
|
1595
|
-
(0, node_opcua_assert_1.assert)(context instanceof node_opcua_address_space_1.SessionContext);
|
|
1596
|
-
const nodeId = nodeToRead.nodeId;
|
|
1597
|
-
const attributeId = nodeToRead.attributeId;
|
|
1598
|
-
const indexRange = nodeToRead.indexRange;
|
|
1599
|
-
const dataEncoding = nodeToRead.dataEncoding;
|
|
1600
|
-
if (timestampsToReturn === node_opcua_service_read_1.TimestampsToReturn.Invalid) {
|
|
1601
|
-
return new node_opcua_data_value_1.DataValue({ statusCode: node_opcua_status_code_1.StatusCodes.BadTimestampsToReturnInvalid });
|
|
1602
|
-
}
|
|
1603
|
-
timestampsToReturn = (0, node_opcua_data_value_1.coerceTimestampsToReturn)(timestampsToReturn);
|
|
1604
|
-
const obj = this.__findNode(nodeId);
|
|
1605
|
-
let dataValue;
|
|
1606
|
-
if (!obj) {
|
|
1607
|
-
// may be return BadNodeIdUnknown in dataValue instead ?
|
|
1608
|
-
// Object Not Found
|
|
1609
|
-
return new node_opcua_data_value_1.DataValue({ statusCode: node_opcua_status_code_1.StatusCodes.BadNodeIdUnknown });
|
|
1610
|
-
}
|
|
1611
|
-
else {
|
|
1612
|
-
// check access
|
|
1613
|
-
// BadUserAccessDenied
|
|
1614
|
-
// BadNotReadable
|
|
1615
|
-
// invalid attributes : BadNodeAttributesInvalid
|
|
1616
|
-
// invalid range : BadIndexRangeInvalid
|
|
1617
|
-
dataValue = obj.readAttribute(context, attributeId, indexRange, dataEncoding);
|
|
1618
|
-
dataValue = (0, node_opcua_data_value_1.apply_timestamps_no_copy)(dataValue, timestampsToReturn, attributeId);
|
|
1619
|
-
return dataValue;
|
|
1620
|
-
}
|
|
1621
|
-
}
|
|
1622
|
-
_historyReadSingleNode(context, nodeToRead, historyReadDetails, timestampsToReturn, continuationData, callback) {
|
|
1623
|
-
(0, node_opcua_assert_1.assert)(context instanceof node_opcua_address_space_1.SessionContext);
|
|
1624
|
-
(0, node_opcua_assert_1.assert)(typeof callback === "function");
|
|
1625
|
-
const nodeId = nodeToRead.nodeId;
|
|
1626
|
-
const indexRange = nodeToRead.indexRange;
|
|
1627
|
-
const dataEncoding = nodeToRead.dataEncoding;
|
|
1628
|
-
const continuationPoint = nodeToRead.continuationPoint;
|
|
1629
|
-
timestampsToReturn = (0, node_opcua_data_value_1.coerceTimestampsToReturn)(timestampsToReturn);
|
|
1630
|
-
if (timestampsToReturn === node_opcua_service_read_1.TimestampsToReturn.Invalid) {
|
|
1631
|
-
return callback(null, new node_opcua_service_history_1.HistoryReadResult({ statusCode: node_opcua_status_code_1.StatusCodes.BadTimestampsToReturnInvalid }));
|
|
1632
|
-
}
|
|
1633
|
-
const obj = this.__findNode(nodeId);
|
|
1634
|
-
if (!obj) {
|
|
1635
|
-
// may be return BadNodeIdUnknown in dataValue instead ?
|
|
1636
|
-
// Object Not Found
|
|
1637
|
-
callback(null, new node_opcua_service_history_1.HistoryReadResult({ statusCode: node_opcua_status_code_1.StatusCodes.BadNodeIdUnknown }));
|
|
1638
|
-
return;
|
|
1639
|
-
}
|
|
1640
|
-
else {
|
|
1641
|
-
// istanbul ignore next
|
|
1642
|
-
if (!obj.historyRead) {
|
|
1643
|
-
// note : Object and View may also support historyRead to provide Event historical data
|
|
1644
|
-
// todo implement historyRead for Object and View
|
|
1645
|
-
const msg = " this node doesn't provide historyRead! probably not a UAVariable\n " +
|
|
1646
|
-
obj.nodeId.toString() +
|
|
1647
|
-
" " +
|
|
1648
|
-
obj.browseName.toString() +
|
|
1649
|
-
"\n" +
|
|
1650
|
-
"with " +
|
|
1651
|
-
nodeToRead.toString() +
|
|
1652
|
-
"\n" +
|
|
1653
|
-
"HistoryReadDetails " +
|
|
1654
|
-
historyReadDetails.toString();
|
|
1655
|
-
if (doDebug) {
|
|
1656
|
-
debugLog(chalk.cyan("ServerEngine#_historyReadSingleNode "), chalk.white.bold(msg));
|
|
1657
|
-
}
|
|
1658
|
-
const err = new Error(msg);
|
|
1659
|
-
// object has no historyRead method
|
|
1660
|
-
setImmediate(callback.bind(null, err));
|
|
1661
|
-
return;
|
|
1662
|
-
}
|
|
1663
|
-
// check access
|
|
1664
|
-
// BadUserAccessDenied
|
|
1665
|
-
// BadNotReadable
|
|
1666
|
-
// invalid attributes : BadNodeAttributesInvalid
|
|
1667
|
-
// invalid range : BadIndexRangeInvalid
|
|
1668
|
-
obj.historyRead(context, historyReadDetails, indexRange, dataEncoding, continuationData, (err, result) => {
|
|
1669
|
-
if (err || !result) {
|
|
1670
|
-
return callback(err);
|
|
1671
|
-
}
|
|
1672
|
-
(0, node_opcua_assert_1.assert)(result.statusCode instanceof node_opcua_status_code_1.StatusCode);
|
|
1673
|
-
(0, node_opcua_assert_1.assert)(result.isValid());
|
|
1674
|
-
// result = apply_timestamps(result, timestampsToReturn, attributeId);
|
|
1675
|
-
callback(err, result);
|
|
1676
|
-
});
|
|
1677
|
-
}
|
|
1678
|
-
}
|
|
1679
|
-
/**
|
|
1680
|
-
*/
|
|
1681
|
-
__internal_bindMethod(nodeId, func) {
|
|
1682
|
-
(0, node_opcua_assert_1.assert)(typeof func === "function");
|
|
1683
|
-
(0, node_opcua_assert_1.assert)(nodeId instanceof node_opcua_nodeid_1.NodeId);
|
|
1684
|
-
const methodNode = this.addressSpace.findNode(nodeId);
|
|
1685
|
-
if (!methodNode) {
|
|
1686
|
-
return;
|
|
1687
|
-
}
|
|
1688
|
-
// istanbul ignore else
|
|
1689
|
-
if (methodNode && methodNode.bindMethod) {
|
|
1690
|
-
methodNode.bindMethod(func);
|
|
1691
|
-
}
|
|
1692
|
-
else {
|
|
1693
|
-
warningLog(chalk.yellow("WARNING: cannot bind a method with id ") +
|
|
1694
|
-
chalk.cyan(nodeId.toString()) +
|
|
1695
|
-
chalk.yellow(". please check your nodeset.xml file or add this node programmatically"));
|
|
1696
|
-
warningLog((0, node_opcua_debug_1.traceFromThisProjectOnly)());
|
|
1697
|
-
}
|
|
1698
|
-
}
|
|
1699
|
-
_getServerSubscriptionDiagnosticsArrayNode() {
|
|
1700
|
-
// istanbul ignore next
|
|
1701
|
-
if (!this.addressSpace) {
|
|
1702
|
-
doDebug && debugLog("ServerEngine#_getServerSubscriptionDiagnosticsArray : no addressSpace");
|
|
1703
|
-
return null; // no addressSpace
|
|
1704
|
-
}
|
|
1705
|
-
const subscriptionDiagnosticsType = this.addressSpace.findVariableType("SubscriptionDiagnosticsType");
|
|
1706
|
-
if (!subscriptionDiagnosticsType) {
|
|
1707
|
-
doDebug &&
|
|
1708
|
-
debugLog("ServerEngine#_getServerSubscriptionDiagnosticsArray " + ": cannot find SubscriptionDiagnosticsType");
|
|
1709
|
-
}
|
|
1710
|
-
// SubscriptionDiagnosticsArray = i=2290
|
|
1711
|
-
const subscriptionDiagnosticsArrayNode = this.addressSpace.findNode((0, node_opcua_nodeid_1.makeNodeId)(node_opcua_constants_1.VariableIds.Server_ServerDiagnostics_SubscriptionDiagnosticsArray));
|
|
1712
|
-
return subscriptionDiagnosticsArrayNode;
|
|
1713
|
-
}
|
|
1714
|
-
}
|
|
1715
|
-
exports.ServerEngine = ServerEngine;
|
|
1716
|
-
ServerEngine.registry = new node_opcua_object_registry_1.ObjectRegistry();
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.ServerEngine = void 0;
|
|
13
|
+
/**
|
|
14
|
+
* @module node-opcua-server
|
|
15
|
+
*/
|
|
16
|
+
const events_1 = require("events");
|
|
17
|
+
const async = require("async");
|
|
18
|
+
const chalk = require("chalk");
|
|
19
|
+
const node_opcua_assert_1 = require("node-opcua-assert");
|
|
20
|
+
const node_opcua_binary_stream_1 = require("node-opcua-binary-stream");
|
|
21
|
+
const node_opcua_address_space_1 = require("node-opcua-address-space");
|
|
22
|
+
const nodeJS_1 = require("node-opcua-address-space/nodeJS");
|
|
23
|
+
const node_opcua_data_value_1 = require("node-opcua-data-value");
|
|
24
|
+
const node_opcua_common_1 = require("node-opcua-common");
|
|
25
|
+
const node_opcua_data_model_1 = require("node-opcua-data-model");
|
|
26
|
+
const node_opcua_nodeid_1 = require("node-opcua-nodeid");
|
|
27
|
+
const node_opcua_service_read_1 = require("node-opcua-service-read");
|
|
28
|
+
const node_opcua_constants_1 = require("node-opcua-constants");
|
|
29
|
+
const node_opcua_date_time_1 = require("node-opcua-date-time");
|
|
30
|
+
const node_opcua_debug_1 = require("node-opcua-debug");
|
|
31
|
+
const node_opcua_nodesets_1 = require("node-opcua-nodesets");
|
|
32
|
+
const node_opcua_object_registry_1 = require("node-opcua-object-registry");
|
|
33
|
+
const node_opcua_service_call_1 = require("node-opcua-service-call");
|
|
34
|
+
const node_opcua_service_subscription_1 = require("node-opcua-service-subscription");
|
|
35
|
+
const node_opcua_service_endpoints_1 = require("node-opcua-service-endpoints");
|
|
36
|
+
const node_opcua_service_history_1 = require("node-opcua-service-history");
|
|
37
|
+
const node_opcua_status_code_1 = require("node-opcua-status-code");
|
|
38
|
+
const node_opcua_types_1 = require("node-opcua-types");
|
|
39
|
+
const node_opcua_variant_1 = require("node-opcua-variant");
|
|
40
|
+
const history_server_capabilities_1 = require("./history_server_capabilities");
|
|
41
|
+
const monitored_item_1 = require("./monitored_item");
|
|
42
|
+
const server_capabilities_1 = require("./server_capabilities");
|
|
43
|
+
const server_publish_engine_1 = require("./server_publish_engine");
|
|
44
|
+
const server_publish_engine_for_orphan_subscriptions_1 = require("./server_publish_engine_for_orphan_subscriptions");
|
|
45
|
+
const server_session_1 = require("./server_session");
|
|
46
|
+
const server_subscription_1 = require("./server_subscription");
|
|
47
|
+
const sessions_compatible_for_transfer_1 = require("./sessions_compatible_for_transfer");
|
|
48
|
+
const debugLog = (0, node_opcua_debug_1.make_debugLog)(__filename);
|
|
49
|
+
const errorLog = (0, node_opcua_debug_1.make_errorLog)(__filename);
|
|
50
|
+
const warningLog = (0, node_opcua_debug_1.make_warningLog)(__filename);
|
|
51
|
+
const doDebug = (0, node_opcua_debug_1.checkDebugFlag)(__filename);
|
|
52
|
+
function upperCaseFirst(str) {
|
|
53
|
+
return str.slice(0, 1).toUpperCase() + str.slice(1);
|
|
54
|
+
}
|
|
55
|
+
function shutdownAndDisposeAddressSpace() {
|
|
56
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
57
|
+
if (this.addressSpace) {
|
|
58
|
+
yield this.addressSpace.shutdown();
|
|
59
|
+
this.addressSpace.dispose();
|
|
60
|
+
delete this.addressSpace;
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
function setSubscriptionDurable(inputArguments, context, callback) {
|
|
65
|
+
// see https://reference.opcfoundation.org/v104/Core/docs/Part5/9.3/
|
|
66
|
+
// https://reference.opcfoundation.org/v104/Core/docs/Part4/6.8/
|
|
67
|
+
(0, node_opcua_assert_1.assert)(Array.isArray(inputArguments));
|
|
68
|
+
(0, node_opcua_assert_1.assert)(typeof callback === "function");
|
|
69
|
+
(0, node_opcua_assert_1.assert)(Object.prototype.hasOwnProperty.call(context, "session"), " expecting a session id in the context object");
|
|
70
|
+
const session = context.session;
|
|
71
|
+
if (!session) {
|
|
72
|
+
return callback(null, { statusCode: node_opcua_status_code_1.StatusCodes.BadInternalError });
|
|
73
|
+
}
|
|
74
|
+
const subscriptionId = inputArguments[0].value;
|
|
75
|
+
const lifetimeInHours = inputArguments[1].value;
|
|
76
|
+
const subscription = session.getSubscription(subscriptionId);
|
|
77
|
+
if (!subscription) {
|
|
78
|
+
// subscription may belongs to a different session that ours
|
|
79
|
+
if (this.findSubscription(subscriptionId)) {
|
|
80
|
+
// if yes, then access to Subscription data should be denied
|
|
81
|
+
return callback(null, { statusCode: node_opcua_status_code_1.StatusCodes.BadUserAccessDenied });
|
|
82
|
+
}
|
|
83
|
+
return callback(null, { statusCode: node_opcua_status_code_1.StatusCodes.BadSubscriptionIdInvalid });
|
|
84
|
+
}
|
|
85
|
+
if (subscription.monitoredItemCount > 0) {
|
|
86
|
+
// This is returned when a Subscription already contains MonitoredItems.
|
|
87
|
+
return callback(null, { statusCode: node_opcua_status_code_1.StatusCodes.BadInvalidState });
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* MonitoredItems are used to monitor Variable Values for data changes and event notifier
|
|
91
|
+
* Objects for new Events. Subscriptions are used to combine data changes and events of
|
|
92
|
+
* the assigned MonitoredItems to an optimized stream of network messages. A reliable
|
|
93
|
+
* delivery is ensured as long as the lifetime of the Subscription and the queues in the
|
|
94
|
+
* MonitoredItems are long enough for a network interruption between OPC UA Client and
|
|
95
|
+
* Server. All queues that ensure reliable delivery are normally kept in memory and a
|
|
96
|
+
* Server restart would delete them.
|
|
97
|
+
* There are use cases where OPC UA Clients have no permanent network connection to the
|
|
98
|
+
* OPC UA Server or where reliable delivery of data changes and events is necessary
|
|
99
|
+
* even if the OPC UA Server is restarted or the network connection is interrupted
|
|
100
|
+
* for a longer time.
|
|
101
|
+
* To ensure this reliable delivery, the OPC UA Server must store collected data and
|
|
102
|
+
* events in non-volatile memory until the OPC UA Client has confirmed reception.
|
|
103
|
+
* It is possible that there will be data lost if the Server is not shut down gracefully
|
|
104
|
+
* or in case of power failure. But the OPC UA Server should store the queues frequently
|
|
105
|
+
* even if the Server is not shut down.
|
|
106
|
+
* The Method SetSubscriptionDurable defined in OPC 10000-5 is used to set a Subscription
|
|
107
|
+
* into this durable mode and to allow much longer lifetimes and queue sizes than for normal
|
|
108
|
+
* Subscriptions. The Method shall be called before the MonitoredItems are created in the
|
|
109
|
+
* durable Subscription. The Server shall verify that the Method is called within the
|
|
110
|
+
* Session context of the Session that owns the Subscription.
|
|
111
|
+
*
|
|
112
|
+
* A value of 0 for the parameter lifetimeInHours requests the highest lifetime supported by the Server.
|
|
113
|
+
*/
|
|
114
|
+
const highestLifetimeInHours = 24 * 100;
|
|
115
|
+
const revisedLifetimeInHours = lifetimeInHours === 0 ? highestLifetimeInHours : Math.max(1, Math.min(lifetimeInHours, highestLifetimeInHours));
|
|
116
|
+
// also adjust subscription life time
|
|
117
|
+
const currentLifeTimeInHours = (subscription.lifeTimeCount * subscription.publishingInterval) / (1000 * 60 * 60);
|
|
118
|
+
if (currentLifeTimeInHours < revisedLifetimeInHours) {
|
|
119
|
+
const requestedLifetimeCount = Math.ceil((revisedLifetimeInHours * (1000 * 60 * 60)) / subscription.publishingInterval);
|
|
120
|
+
subscription.modify({
|
|
121
|
+
requestedMaxKeepAliveCount: subscription.maxKeepAliveCount,
|
|
122
|
+
requestedPublishingInterval: subscription.publishingInterval,
|
|
123
|
+
maxNotificationsPerPublish: subscription.maxNotificationsPerPublish,
|
|
124
|
+
priority: subscription.priority,
|
|
125
|
+
requestedLifetimeCount
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
const callMethodResult = new node_opcua_service_call_1.CallMethodResult({
|
|
129
|
+
statusCode: node_opcua_status_code_1.StatusCodes.Good,
|
|
130
|
+
outputArguments: [{ dataType: node_opcua_variant_1.DataType.UInt32, arrayType: node_opcua_variant_1.VariantArrayType.Scalar, value: revisedLifetimeInHours }]
|
|
131
|
+
});
|
|
132
|
+
callback(null, callMethodResult);
|
|
133
|
+
}
|
|
134
|
+
// binding methods
|
|
135
|
+
function getMonitoredItemsId(inputArguments, context, callback) {
|
|
136
|
+
(0, node_opcua_assert_1.assert)(Array.isArray(inputArguments));
|
|
137
|
+
(0, node_opcua_assert_1.assert)(typeof callback === "function");
|
|
138
|
+
(0, node_opcua_assert_1.assert)(Object.prototype.hasOwnProperty.call(context, "session"), " expecting a session id in the context object");
|
|
139
|
+
const session = context.session;
|
|
140
|
+
if (!session) {
|
|
141
|
+
return callback(null, { statusCode: node_opcua_status_code_1.StatusCodes.BadInternalError });
|
|
142
|
+
}
|
|
143
|
+
const subscriptionId = inputArguments[0].value;
|
|
144
|
+
const subscription = session.getSubscription(subscriptionId);
|
|
145
|
+
if (!subscription) {
|
|
146
|
+
// subscription may belongs to a different session that ours
|
|
147
|
+
if (this.findSubscription(subscriptionId)) {
|
|
148
|
+
// if yes, then access to Subscription data should be denied
|
|
149
|
+
return callback(null, { statusCode: node_opcua_status_code_1.StatusCodes.BadUserAccessDenied });
|
|
150
|
+
}
|
|
151
|
+
return callback(null, { statusCode: node_opcua_status_code_1.StatusCodes.BadSubscriptionIdInvalid });
|
|
152
|
+
}
|
|
153
|
+
const result = subscription.getMonitoredItems();
|
|
154
|
+
(0, node_opcua_assert_1.assert)(result.statusCode);
|
|
155
|
+
(0, node_opcua_assert_1.assert)(result.serverHandles.length === result.clientHandles.length);
|
|
156
|
+
const callMethodResult = new node_opcua_service_call_1.CallMethodResult({
|
|
157
|
+
statusCode: result.statusCode,
|
|
158
|
+
outputArguments: [
|
|
159
|
+
{ dataType: node_opcua_variant_1.DataType.UInt32, arrayType: node_opcua_variant_1.VariantArrayType.Array, value: result.serverHandles },
|
|
160
|
+
{ dataType: node_opcua_variant_1.DataType.UInt32, arrayType: node_opcua_variant_1.VariantArrayType.Array, value: result.clientHandles }
|
|
161
|
+
]
|
|
162
|
+
});
|
|
163
|
+
callback(null, callMethodResult);
|
|
164
|
+
}
|
|
165
|
+
function __bindVariable(self, nodeId, options) {
|
|
166
|
+
options = options || {};
|
|
167
|
+
const variable = self.addressSpace.findNode(nodeId);
|
|
168
|
+
if (variable && variable.bindVariable) {
|
|
169
|
+
variable.bindVariable(options, true);
|
|
170
|
+
(0, node_opcua_assert_1.assert)(typeof variable.asyncRefresh === "function");
|
|
171
|
+
(0, node_opcua_assert_1.assert)(typeof variable.refreshFunc === "function");
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
warningLog("Warning: cannot bind object with id ", nodeId.toString(), " please check your nodeset.xml file or add this node programmatically");
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
// note OPCUA 1.03 part 4 page 76
|
|
178
|
+
// The Server-assigned identifier for the Subscription (see 7.14 for IntegerId definition). This identifier shall
|
|
179
|
+
// be unique for the entire Server, not just for the Session, in order to allow the Subscription to be transferred
|
|
180
|
+
// to another Session using the TransferSubscriptions service.
|
|
181
|
+
// After Server start-up the generation of subscriptionIds should start from a random IntegerId or continue from
|
|
182
|
+
// the point before the restart.
|
|
183
|
+
let next_subscriptionId = Math.ceil(Math.random() * 1000000);
|
|
184
|
+
function _get_next_subscriptionId() {
|
|
185
|
+
debugLog(" next_subscriptionId = ", next_subscriptionId);
|
|
186
|
+
return next_subscriptionId++;
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
*
|
|
190
|
+
*/
|
|
191
|
+
class ServerEngine extends events_1.EventEmitter {
|
|
192
|
+
constructor(options) {
|
|
193
|
+
super();
|
|
194
|
+
this._globalCounter = { totalMonitoredItemCount: 0 };
|
|
195
|
+
options = options || { applicationUri: "" };
|
|
196
|
+
options.buildInfo = options.buildInfo || {};
|
|
197
|
+
ServerEngine.registry.register(this);
|
|
198
|
+
this._sessions = {};
|
|
199
|
+
this._closedSessions = {};
|
|
200
|
+
this._orphanPublishEngine = undefined; // will be constructed on demand
|
|
201
|
+
this.isAuditing = typeof options.isAuditing === "boolean" ? options.isAuditing : false;
|
|
202
|
+
options.buildInfo.buildDate = options.buildInfo.buildDate || new Date();
|
|
203
|
+
// ---------------------------------------------------- ServerStatusDataType
|
|
204
|
+
this._serverStatus = new node_opcua_common_1.ServerStatusDataType({
|
|
205
|
+
buildInfo: options.buildInfo,
|
|
206
|
+
currentTime: new Date(),
|
|
207
|
+
secondsTillShutdown: 0,
|
|
208
|
+
shutdownReason: { text: "" },
|
|
209
|
+
startTime: new Date(),
|
|
210
|
+
state: node_opcua_common_1.ServerState.NoConfiguration
|
|
211
|
+
});
|
|
212
|
+
// --------------------------------------------------- ServerCapabilities
|
|
213
|
+
options.serverCapabilities = options.serverCapabilities || {};
|
|
214
|
+
// https://profiles.opcfoundation.org/profile
|
|
215
|
+
options.serverCapabilities.serverProfileArray = options.serverCapabilities.serverProfileArray || [
|
|
216
|
+
"http://opcfoundation.org/UA-Profile/Server/Standard",
|
|
217
|
+
"http://opcfoundation.org/UA-Profile/Server/DataAccess",
|
|
218
|
+
"http://opcfoundation.org/UA-Profile/Server/Events",
|
|
219
|
+
"http://opcfoundation.org/UA-Profile/Client/HistoricalAccess",
|
|
220
|
+
"http://opcfoundation.org/UA-Profile/Server/Methods",
|
|
221
|
+
"http://opcfoundation.org/UA-Profile/Server/StandardEventSubscription",
|
|
222
|
+
"http://opcfoundation.org/UA-Profile/Transport/uatcp-uasc-uabinary",
|
|
223
|
+
"http://opcfoundation.org/UA-Profile/Server/FileAccess",
|
|
224
|
+
"http://opcfoundation.org/UA-Profile/Server/StateMachine"
|
|
225
|
+
// "http://opcfoundation.org/UA-Profile/Transport/wss-uajson",
|
|
226
|
+
// "http://opcfoundation.org/UA-Profile/Transport/wss-uasc-uabinary"
|
|
227
|
+
// "http://opcfoundation.org/UA-Profile/Server/DurableSubscription"
|
|
228
|
+
// "http://opcfoundation.org/UA-Profile/Server/ReverseConnect",
|
|
229
|
+
// "http://opcfoundation.org/UAProfile/Server/NodeManagement",
|
|
230
|
+
// "Embedded UA Server Profile",
|
|
231
|
+
// "Micro Embedded Device Server Profile",
|
|
232
|
+
// "Nano Embedded Device Server Profile"
|
|
233
|
+
];
|
|
234
|
+
options.serverCapabilities.localeIdArray = options.serverCapabilities.localeIdArray || ["en-EN", "fr-FR"];
|
|
235
|
+
this.serverCapabilities = new server_capabilities_1.ServerCapabilities(options.serverCapabilities);
|
|
236
|
+
// to do when spec is clear about what goes here!
|
|
237
|
+
// spec 1.04 says (in Part 4 7.33 SignedSoftwareCertificate
|
|
238
|
+
// Note: Details on SoftwareCertificates need to be defined in a future version.
|
|
239
|
+
this.serverCapabilities.softwareCertificates = [
|
|
240
|
+
// new SignedSoftwareCertificate({})
|
|
241
|
+
];
|
|
242
|
+
// make sure minSupportedSampleRate matches MonitoredItem.minimumSamplingInterval
|
|
243
|
+
this.serverCapabilities.__defineGetter__("minSupportedSampleRate", () => {
|
|
244
|
+
return monitored_item_1.MonitoredItem.minimumSamplingInterval;
|
|
245
|
+
});
|
|
246
|
+
this.historyServerCapabilities = new history_server_capabilities_1.HistoryServerCapabilities(options.historyServerCapabilities);
|
|
247
|
+
// --------------------------------------------------- serverDiagnosticsSummary extension Object
|
|
248
|
+
this.serverDiagnosticsSummary = new node_opcua_common_1.ServerDiagnosticsSummaryDataType();
|
|
249
|
+
(0, node_opcua_assert_1.assert)(Object.prototype.hasOwnProperty.call(this.serverDiagnosticsSummary, "currentSessionCount"));
|
|
250
|
+
// note spelling is different for serverDiagnosticsSummary.currentSubscriptionCount
|
|
251
|
+
// and sessionDiagnostics.currentSubscriptionsCount ( with an s)
|
|
252
|
+
(0, node_opcua_assert_1.assert)(Object.prototype.hasOwnProperty.call(this.serverDiagnosticsSummary, "currentSubscriptionCount"));
|
|
253
|
+
this.serverDiagnosticsSummary.__defineGetter__("currentSubscriptionCount", () => {
|
|
254
|
+
// currentSubscriptionCount returns the total number of subscriptions
|
|
255
|
+
// that are currently active on all sessions
|
|
256
|
+
let counter = 0;
|
|
257
|
+
Object.values(this._sessions).forEach((session) => {
|
|
258
|
+
counter += session.currentSubscriptionCount;
|
|
259
|
+
});
|
|
260
|
+
// we also need to add the orphan subscriptions
|
|
261
|
+
counter += this._orphanPublishEngine ? this._orphanPublishEngine.subscriptions.length : 0;
|
|
262
|
+
return counter;
|
|
263
|
+
});
|
|
264
|
+
this._internalState = "creating";
|
|
265
|
+
this.setServerState(node_opcua_common_1.ServerState.NoConfiguration);
|
|
266
|
+
this.addressSpace = null;
|
|
267
|
+
this._shutdownTasks = [];
|
|
268
|
+
this._applicationUri = "";
|
|
269
|
+
if (typeof options.applicationUri === "function") {
|
|
270
|
+
this.__defineGetter__("_applicationUri", options.applicationUri);
|
|
271
|
+
}
|
|
272
|
+
else {
|
|
273
|
+
this._applicationUri = options.applicationUri || "<unset _applicationUri>";
|
|
274
|
+
}
|
|
275
|
+
options.serverDiagnosticsEnabled = Object.prototype.hasOwnProperty.call(options, "serverDiagnosticsEnable")
|
|
276
|
+
? options.serverDiagnosticsEnabled
|
|
277
|
+
: true;
|
|
278
|
+
this.serverDiagnosticsEnabled = options.serverDiagnosticsEnabled;
|
|
279
|
+
}
|
|
280
|
+
isStarted() {
|
|
281
|
+
return !!this._serverStatus;
|
|
282
|
+
}
|
|
283
|
+
dispose() {
|
|
284
|
+
this.addressSpace = null;
|
|
285
|
+
(0, node_opcua_assert_1.assert)(Object.keys(this._sessions).length === 0, "ServerEngine#_sessions not empty");
|
|
286
|
+
this._sessions = {};
|
|
287
|
+
// todo fix me
|
|
288
|
+
this._closedSessions = {};
|
|
289
|
+
(0, node_opcua_assert_1.assert)(Object.keys(this._closedSessions).length === 0, "ServerEngine#_closedSessions not empty");
|
|
290
|
+
this._closedSessions = {};
|
|
291
|
+
if (this._orphanPublishEngine) {
|
|
292
|
+
this._orphanPublishEngine.dispose();
|
|
293
|
+
this._orphanPublishEngine = undefined;
|
|
294
|
+
}
|
|
295
|
+
this._shutdownTasks = [];
|
|
296
|
+
this._serverStatus = null;
|
|
297
|
+
this._internalState = "disposed";
|
|
298
|
+
this.removeAllListeners();
|
|
299
|
+
ServerEngine.registry.unregister(this);
|
|
300
|
+
}
|
|
301
|
+
get startTime() {
|
|
302
|
+
return this._serverStatus.startTime;
|
|
303
|
+
}
|
|
304
|
+
get currentTime() {
|
|
305
|
+
return this._serverStatus.currentTime;
|
|
306
|
+
}
|
|
307
|
+
get buildInfo() {
|
|
308
|
+
return this._serverStatus.buildInfo;
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* register a function that will be called when the server will perform its shut down.
|
|
312
|
+
* @method registerShutdownTask
|
|
313
|
+
*/
|
|
314
|
+
registerShutdownTask(task) {
|
|
315
|
+
(0, node_opcua_assert_1.assert)(typeof task === "function");
|
|
316
|
+
this._shutdownTasks.push(task);
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* @method shutdown
|
|
320
|
+
*/
|
|
321
|
+
shutdown() {
|
|
322
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
323
|
+
debugLog("ServerEngine#shutdown");
|
|
324
|
+
this._internalState = "shutdown";
|
|
325
|
+
this.setServerState(node_opcua_common_1.ServerState.Shutdown);
|
|
326
|
+
// delete any existing sessions
|
|
327
|
+
const tokens = Object.keys(this._sessions).map((key) => {
|
|
328
|
+
const session = this._sessions[key];
|
|
329
|
+
return session.authenticationToken;
|
|
330
|
+
});
|
|
331
|
+
// delete and close any orphan subscriptions
|
|
332
|
+
if (this._orphanPublishEngine) {
|
|
333
|
+
this._orphanPublishEngine.shutdown();
|
|
334
|
+
}
|
|
335
|
+
for (const token of tokens) {
|
|
336
|
+
this.closeSession(token, true, "Terminated");
|
|
337
|
+
}
|
|
338
|
+
// all sessions must have been terminated
|
|
339
|
+
(0, node_opcua_assert_1.assert)(this.currentSessionCount === 0);
|
|
340
|
+
// all subscriptions must have been terminated
|
|
341
|
+
(0, node_opcua_assert_1.assert)(this.currentSubscriptionCount === 0, "all subscriptions must have been terminated");
|
|
342
|
+
this._shutdownTasks.push(shutdownAndDisposeAddressSpace);
|
|
343
|
+
// perform registerShutdownTask
|
|
344
|
+
for (const task of this._shutdownTasks) {
|
|
345
|
+
yield task.call(this);
|
|
346
|
+
}
|
|
347
|
+
this.dispose();
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* the number of active sessions
|
|
352
|
+
*/
|
|
353
|
+
get currentSessionCount() {
|
|
354
|
+
return this.serverDiagnosticsSummary.currentSessionCount;
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* the cumulated number of sessions that have been opened since this object exists
|
|
358
|
+
*/
|
|
359
|
+
get cumulatedSessionCount() {
|
|
360
|
+
return this.serverDiagnosticsSummary.cumulatedSessionCount;
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* the number of active subscriptions.
|
|
364
|
+
*/
|
|
365
|
+
get currentSubscriptionCount() {
|
|
366
|
+
return this.serverDiagnosticsSummary.currentSubscriptionCount;
|
|
367
|
+
}
|
|
368
|
+
/**
|
|
369
|
+
* the cumulated number of subscriptions that have been created since this object exists
|
|
370
|
+
*/
|
|
371
|
+
get cumulatedSubscriptionCount() {
|
|
372
|
+
return this.serverDiagnosticsSummary.cumulatedSubscriptionCount;
|
|
373
|
+
}
|
|
374
|
+
get rejectedSessionCount() {
|
|
375
|
+
return this.serverDiagnosticsSummary.rejectedSessionCount;
|
|
376
|
+
}
|
|
377
|
+
get rejectedRequestsCount() {
|
|
378
|
+
return this.serverDiagnosticsSummary.rejectedRequestsCount;
|
|
379
|
+
}
|
|
380
|
+
get sessionAbortCount() {
|
|
381
|
+
return this.serverDiagnosticsSummary.sessionAbortCount;
|
|
382
|
+
}
|
|
383
|
+
get sessionTimeoutCount() {
|
|
384
|
+
return this.serverDiagnosticsSummary.sessionTimeoutCount;
|
|
385
|
+
}
|
|
386
|
+
get publishingIntervalCount() {
|
|
387
|
+
return this.serverDiagnosticsSummary.publishingIntervalCount;
|
|
388
|
+
}
|
|
389
|
+
incrementSessionTimeoutCount() {
|
|
390
|
+
if (this.serverDiagnosticsSummary && this.serverDiagnosticsEnabled) {
|
|
391
|
+
// The requests include all Services defined in Part 4 of the OPC UA Specification, also requests to create sessions. This number includes the securityRejectedRequestsCount.
|
|
392
|
+
this.serverDiagnosticsSummary.sessionTimeoutCount += 1;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
incrementSessionAbortCount() {
|
|
396
|
+
if (this.serverDiagnosticsSummary && this.serverDiagnosticsEnabled) {
|
|
397
|
+
// The requests include all Services defined in Part 4 of the OPC UA Specification, also requests to create sessions. This number includes the securityRejectedRequestsCount.
|
|
398
|
+
this.serverDiagnosticsSummary.sessionAbortCount += 1;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
incrementRejectedRequestsCount() {
|
|
402
|
+
if (this.serverDiagnosticsSummary && this.serverDiagnosticsEnabled) {
|
|
403
|
+
// The requests include all Services defined in Part 4 of the OPC UA Specification, also requests to create sessions. This number includes the securityRejectedRequestsCount.
|
|
404
|
+
this.serverDiagnosticsSummary.rejectedRequestsCount += 1;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* increment rejected session count (also increment rejected requests count)
|
|
409
|
+
*/
|
|
410
|
+
incrementRejectedSessionCount() {
|
|
411
|
+
if (this.serverDiagnosticsSummary && this.serverDiagnosticsEnabled) {
|
|
412
|
+
// The requests include all Services defined in Part 4 of the OPC UA Specification, also requests to create sessions. This number includes the securityRejectedRequestsCount.
|
|
413
|
+
this.serverDiagnosticsSummary.rejectedSessionCount += 1;
|
|
414
|
+
}
|
|
415
|
+
this.incrementRejectedRequestsCount();
|
|
416
|
+
}
|
|
417
|
+
incrementSecurityRejectedRequestsCount() {
|
|
418
|
+
if (this.serverDiagnosticsSummary && this.serverDiagnosticsEnabled) {
|
|
419
|
+
// The requests include all Services defined in Part 4 of the OPC UA Specification, also requests to create sessions. This number includes the securityRejectedRequestsCount.
|
|
420
|
+
this.serverDiagnosticsSummary.securityRejectedRequestsCount += 1;
|
|
421
|
+
}
|
|
422
|
+
this.incrementRejectedRequestsCount();
|
|
423
|
+
}
|
|
424
|
+
/**
|
|
425
|
+
* increment rejected session count (also increment rejected requests count)
|
|
426
|
+
*/
|
|
427
|
+
incrementSecurityRejectedSessionCount() {
|
|
428
|
+
if (this.serverDiagnosticsSummary && this.serverDiagnosticsEnabled) {
|
|
429
|
+
// The requests include all Services defined in Part 4 of the OPC UA Specification, also requests to create sessions. This number includes the securityRejectedRequestsCount.
|
|
430
|
+
this.serverDiagnosticsSummary.securityRejectedSessionCount += 1;
|
|
431
|
+
}
|
|
432
|
+
this.incrementSecurityRejectedRequestsCount();
|
|
433
|
+
}
|
|
434
|
+
setShutdownTime(date) {
|
|
435
|
+
this._expectedShutdownTime = date;
|
|
436
|
+
}
|
|
437
|
+
setShutdownReason(reason) {
|
|
438
|
+
var _a;
|
|
439
|
+
(_a = this.addressSpace) === null || _a === void 0 ? void 0 : _a.rootFolder.objects.server.serverStatus.shutdownReason.setValueFromSource({
|
|
440
|
+
dataType: node_opcua_variant_1.DataType.LocalizedText,
|
|
441
|
+
value: (0, node_opcua_data_model_1.coerceLocalizedText)(reason)
|
|
442
|
+
});
|
|
443
|
+
}
|
|
444
|
+
/**
|
|
445
|
+
* @method secondsTillShutdown
|
|
446
|
+
* @return the approximate number of seconds until the server will be shut down. The
|
|
447
|
+
* value is only relevant once the state changes into SHUTDOWN.
|
|
448
|
+
*/
|
|
449
|
+
secondsTillShutdown() {
|
|
450
|
+
if (!this._expectedShutdownTime) {
|
|
451
|
+
return 0;
|
|
452
|
+
}
|
|
453
|
+
// ToDo: implement a correct solution here
|
|
454
|
+
const now = Date.now();
|
|
455
|
+
return Math.max(0, Math.ceil((this._expectedShutdownTime.getTime() - now) / 1000));
|
|
456
|
+
}
|
|
457
|
+
/**
|
|
458
|
+
* the name of the server
|
|
459
|
+
*/
|
|
460
|
+
get serverName() {
|
|
461
|
+
return this._serverStatus.buildInfo.productName;
|
|
462
|
+
}
|
|
463
|
+
/**
|
|
464
|
+
* the server urn
|
|
465
|
+
*/
|
|
466
|
+
get serverNameUrn() {
|
|
467
|
+
return this._applicationUri;
|
|
468
|
+
}
|
|
469
|
+
/**
|
|
470
|
+
* the urn of the server namespace
|
|
471
|
+
*/
|
|
472
|
+
get serverNamespaceUrn() {
|
|
473
|
+
return this._applicationUri; // "urn:" + engine.serverName;
|
|
474
|
+
}
|
|
475
|
+
get serverStatus() {
|
|
476
|
+
return this._serverStatus;
|
|
477
|
+
}
|
|
478
|
+
setServerState(serverState) {
|
|
479
|
+
var _a, _b, _c, _d, _e, _f;
|
|
480
|
+
(0, node_opcua_assert_1.assert)(serverState !== null && serverState !== undefined);
|
|
481
|
+
(_f = (_e = (_d = (_c = (_b = (_a = this.addressSpace) === null || _a === void 0 ? void 0 : _a.rootFolder) === null || _b === void 0 ? void 0 : _b.objects) === null || _c === void 0 ? void 0 : _c.server) === null || _d === void 0 ? void 0 : _d.serverStatus) === null || _e === void 0 ? void 0 : _e.state) === null || _f === void 0 ? void 0 : _f.setValueFromSource({
|
|
482
|
+
dataType: node_opcua_variant_1.DataType.Int32,
|
|
483
|
+
value: serverState
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
getServerDiagnosticsEnabledFlag() {
|
|
487
|
+
const server = this.addressSpace.rootFolder.objects.server;
|
|
488
|
+
const serverDiagnostics = server.getComponentByName("ServerDiagnostics");
|
|
489
|
+
if (!serverDiagnostics) {
|
|
490
|
+
return false;
|
|
491
|
+
}
|
|
492
|
+
return serverDiagnostics.readValue().value.value;
|
|
493
|
+
}
|
|
494
|
+
/**
|
|
495
|
+
* @method initialize
|
|
496
|
+
* @async
|
|
497
|
+
*
|
|
498
|
+
* @param options {Object}
|
|
499
|
+
* @param options.nodeset_filename {String} - [option](default : 'mini.Node.Set2.xml' )
|
|
500
|
+
* @param callback
|
|
501
|
+
*/
|
|
502
|
+
initialize(options, callback) {
|
|
503
|
+
(0, node_opcua_assert_1.assert)(!this.addressSpace); // check that 'initialize' has not been already called
|
|
504
|
+
this._internalState = "initializing";
|
|
505
|
+
options = options || {};
|
|
506
|
+
(0, node_opcua_assert_1.assert)(typeof callback === "function");
|
|
507
|
+
options.nodeset_filename = options.nodeset_filename || node_opcua_nodesets_1.nodesets.standard;
|
|
508
|
+
const startTime = new Date();
|
|
509
|
+
debugLog("Loading ", options.nodeset_filename, "...");
|
|
510
|
+
this.addressSpace = node_opcua_address_space_1.AddressSpace.create();
|
|
511
|
+
// register namespace 1 (our namespace);
|
|
512
|
+
const serverNamespace = this.addressSpace.registerNamespace(this.serverNamespaceUrn);
|
|
513
|
+
(0, node_opcua_assert_1.assert)(serverNamespace.index === 1);
|
|
514
|
+
// eslint-disable-next-line max-statements
|
|
515
|
+
(0, nodeJS_1.generateAddressSpace)(this.addressSpace, options.nodeset_filename, () => {
|
|
516
|
+
/* istanbul ignore next */
|
|
517
|
+
if (!this.addressSpace) {
|
|
518
|
+
throw new Error("Internal error");
|
|
519
|
+
}
|
|
520
|
+
const addressSpace = this.addressSpace;
|
|
521
|
+
const endTime = new Date();
|
|
522
|
+
debugLog("Loading ", options.nodeset_filename, " done : ", endTime.getTime() - startTime.getTime(), " ms");
|
|
523
|
+
const bindVariableIfPresent = (nodeId, opts) => {
|
|
524
|
+
(0, node_opcua_assert_1.assert)(nodeId instanceof node_opcua_nodeid_1.NodeId);
|
|
525
|
+
(0, node_opcua_assert_1.assert)(!nodeId.isEmpty());
|
|
526
|
+
const obj = addressSpace.findNode(nodeId);
|
|
527
|
+
if (obj) {
|
|
528
|
+
__bindVariable(this, nodeId, opts);
|
|
529
|
+
}
|
|
530
|
+
return obj;
|
|
531
|
+
};
|
|
532
|
+
// -------------------------------------------- install default get/put handler
|
|
533
|
+
const server_NamespaceArray_Id = (0, node_opcua_nodeid_1.makeNodeId)(node_opcua_constants_1.VariableIds.Server_NamespaceArray); // ns=0;i=2255
|
|
534
|
+
bindVariableIfPresent(server_NamespaceArray_Id, {
|
|
535
|
+
get() {
|
|
536
|
+
return new node_opcua_variant_1.Variant({
|
|
537
|
+
arrayType: node_opcua_variant_1.VariantArrayType.Array,
|
|
538
|
+
dataType: node_opcua_variant_1.DataType.String,
|
|
539
|
+
value: addressSpace.getNamespaceArray().map((x) => x.namespaceUri)
|
|
540
|
+
});
|
|
541
|
+
},
|
|
542
|
+
set: null // read only
|
|
543
|
+
});
|
|
544
|
+
const server_NameUrn_var = new node_opcua_variant_1.Variant({
|
|
545
|
+
arrayType: node_opcua_variant_1.VariantArrayType.Array,
|
|
546
|
+
dataType: node_opcua_variant_1.DataType.String,
|
|
547
|
+
value: [
|
|
548
|
+
this.serverNameUrn // this is us !
|
|
549
|
+
]
|
|
550
|
+
});
|
|
551
|
+
const server_ServerArray_Id = (0, node_opcua_nodeid_1.makeNodeId)(node_opcua_constants_1.VariableIds.Server_ServerArray); // ns=0;i=2254
|
|
552
|
+
bindVariableIfPresent(server_ServerArray_Id, {
|
|
553
|
+
get() {
|
|
554
|
+
return server_NameUrn_var;
|
|
555
|
+
},
|
|
556
|
+
set: null // read only
|
|
557
|
+
});
|
|
558
|
+
// fix DefaultUserRolePermissions and DefaultUserRolePermissions
|
|
559
|
+
// of namespaces
|
|
560
|
+
const namespaces = (0, node_opcua_nodeid_1.makeNodeId)(node_opcua_constants_1.ObjectIds.Server_Namespaces);
|
|
561
|
+
const namespacesNode = addressSpace.findNode(namespaces);
|
|
562
|
+
if (namespacesNode) {
|
|
563
|
+
for (const ns of namespacesNode.getComponents()) {
|
|
564
|
+
const defaultUserRolePermissions = ns.getChildByName("DefaultUserRolePermissions");
|
|
565
|
+
if (defaultUserRolePermissions) {
|
|
566
|
+
defaultUserRolePermissions.setValueFromSource({ dataType: node_opcua_variant_1.DataType.Null });
|
|
567
|
+
}
|
|
568
|
+
const defaultRolePermissions = ns.getChildByName("DefaultRolePermissions");
|
|
569
|
+
if (defaultRolePermissions) {
|
|
570
|
+
defaultRolePermissions.setValueFromSource({ dataType: node_opcua_variant_1.DataType.Null });
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
const bindStandardScalar = (id, dataType, func, setter_func) => {
|
|
575
|
+
(0, node_opcua_assert_1.assert)(typeof id === "number", "expecting id to be a number");
|
|
576
|
+
(0, node_opcua_assert_1.assert)(typeof func === "function");
|
|
577
|
+
(0, node_opcua_assert_1.assert)(typeof setter_func === "function" || !setter_func);
|
|
578
|
+
(0, node_opcua_assert_1.assert)(dataType !== null); // check invalid dataType
|
|
579
|
+
let setter_func2 = null;
|
|
580
|
+
if (setter_func) {
|
|
581
|
+
setter_func2 = (variant) => {
|
|
582
|
+
const variable2 = !!variant.value;
|
|
583
|
+
setter_func(variable2);
|
|
584
|
+
return node_opcua_status_code_1.StatusCodes.Good;
|
|
585
|
+
};
|
|
586
|
+
}
|
|
587
|
+
const nodeId = (0, node_opcua_nodeid_1.makeNodeId)(id);
|
|
588
|
+
// make sur the provided function returns a valid value for the variant type
|
|
589
|
+
// This test may not be exhaustive but it will detect obvious mistakes.
|
|
590
|
+
/* istanbul ignore next */
|
|
591
|
+
if (!(0, node_opcua_variant_1.isValidVariant)(node_opcua_variant_1.VariantArrayType.Scalar, dataType, func())) {
|
|
592
|
+
errorLog("func", func());
|
|
593
|
+
throw new Error("bindStandardScalar : func doesn't provide an value of type " + node_opcua_variant_1.DataType[dataType]);
|
|
594
|
+
}
|
|
595
|
+
return bindVariableIfPresent(nodeId, {
|
|
596
|
+
get() {
|
|
597
|
+
return new node_opcua_variant_1.Variant({
|
|
598
|
+
arrayType: node_opcua_variant_1.VariantArrayType.Scalar,
|
|
599
|
+
dataType,
|
|
600
|
+
value: func()
|
|
601
|
+
});
|
|
602
|
+
},
|
|
603
|
+
set: setter_func2
|
|
604
|
+
});
|
|
605
|
+
};
|
|
606
|
+
const bindStandardArray = (id, variantDataType, dataType, func) => {
|
|
607
|
+
(0, node_opcua_assert_1.assert)(typeof func === "function");
|
|
608
|
+
(0, node_opcua_assert_1.assert)(variantDataType !== null); // check invalid dataType
|
|
609
|
+
const nodeId = (0, node_opcua_nodeid_1.makeNodeId)(id);
|
|
610
|
+
// make sur the provided function returns a valid value for the variant type
|
|
611
|
+
// This test may not be exhaustive but it will detect obvious mistakes.
|
|
612
|
+
(0, node_opcua_assert_1.assert)((0, node_opcua_variant_1.isValidVariant)(node_opcua_variant_1.VariantArrayType.Array, variantDataType, func()));
|
|
613
|
+
bindVariableIfPresent(nodeId, {
|
|
614
|
+
get() {
|
|
615
|
+
const value = func();
|
|
616
|
+
(0, node_opcua_assert_1.assert)(Array.isArray(value));
|
|
617
|
+
return new node_opcua_variant_1.Variant({
|
|
618
|
+
arrayType: node_opcua_variant_1.VariantArrayType.Array,
|
|
619
|
+
dataType: variantDataType,
|
|
620
|
+
value
|
|
621
|
+
});
|
|
622
|
+
},
|
|
623
|
+
set: null // read only
|
|
624
|
+
});
|
|
625
|
+
};
|
|
626
|
+
bindStandardScalar(node_opcua_constants_1.VariableIds.Server_EstimatedReturnTime, node_opcua_variant_1.DataType.DateTime, () => node_opcua_date_time_1.minOPCUADate);
|
|
627
|
+
// TimeZoneDataType
|
|
628
|
+
const timeZoneDataType = addressSpace.findDataType((0, node_opcua_nodeid_1.resolveNodeId)(node_opcua_constants_1.DataTypeIds.TimeZoneDataType));
|
|
629
|
+
const timeZone = new node_opcua_types_1.TimeZoneDataType({
|
|
630
|
+
daylightSavingInOffset: /* boolean*/ false,
|
|
631
|
+
offset: /* int16 */ 0
|
|
632
|
+
});
|
|
633
|
+
bindStandardScalar(node_opcua_constants_1.VariableIds.Server_LocalTime, node_opcua_variant_1.DataType.ExtensionObject, () => {
|
|
634
|
+
return timeZone;
|
|
635
|
+
});
|
|
636
|
+
bindStandardScalar(node_opcua_constants_1.VariableIds.Server_ServiceLevel, node_opcua_variant_1.DataType.Byte, () => {
|
|
637
|
+
return 255;
|
|
638
|
+
});
|
|
639
|
+
bindStandardScalar(node_opcua_constants_1.VariableIds.Server_Auditing, node_opcua_variant_1.DataType.Boolean, () => {
|
|
640
|
+
return this.isAuditing;
|
|
641
|
+
});
|
|
642
|
+
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
643
|
+
const engine = this;
|
|
644
|
+
const makeNotReadableIfEnabledFlagIsFalse = (variable) => {
|
|
645
|
+
const originalIsReadable = variable.isReadable;
|
|
646
|
+
variable.isUserReadable = checkReadableFlag;
|
|
647
|
+
function checkReadableFlag(context) {
|
|
648
|
+
const isEnabled = engine.serverDiagnosticsEnabled;
|
|
649
|
+
return originalIsReadable.call(this, context) && isEnabled;
|
|
650
|
+
}
|
|
651
|
+
for (const c of variable.getAggregates()) {
|
|
652
|
+
if (c.nodeClass === node_opcua_data_model_1.NodeClass.Variable) {
|
|
653
|
+
makeNotReadableIfEnabledFlagIsFalse(c);
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
};
|
|
657
|
+
const bindServerDiagnostics = () => {
|
|
658
|
+
bindStandardScalar(node_opcua_constants_1.VariableIds.Server_ServerDiagnostics_EnabledFlag, node_opcua_variant_1.DataType.Boolean, () => {
|
|
659
|
+
return this.serverDiagnosticsEnabled;
|
|
660
|
+
}, (newFlag) => {
|
|
661
|
+
this.serverDiagnosticsEnabled = newFlag;
|
|
662
|
+
});
|
|
663
|
+
const nodeId = (0, node_opcua_nodeid_1.makeNodeId)(node_opcua_constants_1.VariableIds.Server_ServerDiagnostics_ServerDiagnosticsSummary);
|
|
664
|
+
const serverDiagnosticsSummaryNode = addressSpace.findNode(nodeId);
|
|
665
|
+
if (serverDiagnosticsSummaryNode) {
|
|
666
|
+
serverDiagnosticsSummaryNode.bindExtensionObject(this.serverDiagnosticsSummary);
|
|
667
|
+
this.serverDiagnosticsSummary = serverDiagnosticsSummaryNode.$extensionObject;
|
|
668
|
+
makeNotReadableIfEnabledFlagIsFalse(serverDiagnosticsSummaryNode);
|
|
669
|
+
}
|
|
670
|
+
};
|
|
671
|
+
const bindServerStatus = () => {
|
|
672
|
+
const serverStatusNode = addressSpace.findNode((0, node_opcua_nodeid_1.makeNodeId)(node_opcua_constants_1.VariableIds.Server_ServerStatus));
|
|
673
|
+
if (!serverStatusNode) {
|
|
674
|
+
return;
|
|
675
|
+
}
|
|
676
|
+
if (serverStatusNode) {
|
|
677
|
+
serverStatusNode.bindExtensionObject(this._serverStatus);
|
|
678
|
+
serverStatusNode.minimumSamplingInterval = 1000;
|
|
679
|
+
}
|
|
680
|
+
const currentTimeNode = addressSpace.findNode((0, node_opcua_nodeid_1.makeNodeId)(node_opcua_constants_1.VariableIds.Server_ServerStatus_CurrentTime));
|
|
681
|
+
if (currentTimeNode) {
|
|
682
|
+
currentTimeNode.minimumSamplingInterval = 1000;
|
|
683
|
+
}
|
|
684
|
+
const secondsTillShutdown = addressSpace.findNode((0, node_opcua_nodeid_1.makeNodeId)(node_opcua_constants_1.VariableIds.Server_ServerStatus_SecondsTillShutdown));
|
|
685
|
+
if (secondsTillShutdown) {
|
|
686
|
+
secondsTillShutdown.minimumSamplingInterval = 1000;
|
|
687
|
+
}
|
|
688
|
+
(0, node_opcua_assert_1.assert)(serverStatusNode.$extensionObject);
|
|
689
|
+
serverStatusNode.$extensionObject = new Proxy(serverStatusNode.$extensionObject, {
|
|
690
|
+
get(target, prop) {
|
|
691
|
+
if (prop === "currentTime") {
|
|
692
|
+
serverStatusNode.currentTime.touchValue();
|
|
693
|
+
return new Date();
|
|
694
|
+
}
|
|
695
|
+
else if (prop === "secondsTillShutdown") {
|
|
696
|
+
serverStatusNode.secondsTillShutdown.touchValue();
|
|
697
|
+
return engine.secondsTillShutdown();
|
|
698
|
+
}
|
|
699
|
+
return target[prop];
|
|
700
|
+
}
|
|
701
|
+
});
|
|
702
|
+
this._serverStatus = serverStatusNode.$extensionObject;
|
|
703
|
+
};
|
|
704
|
+
const bindServerCapabilities = () => {
|
|
705
|
+
bindStandardArray(node_opcua_constants_1.VariableIds.Server_ServerCapabilities_ServerProfileArray, node_opcua_variant_1.DataType.String, node_opcua_variant_1.DataType.String, () => {
|
|
706
|
+
return this.serverCapabilities.serverProfileArray;
|
|
707
|
+
});
|
|
708
|
+
bindStandardArray(node_opcua_constants_1.VariableIds.Server_ServerCapabilities_LocaleIdArray, node_opcua_variant_1.DataType.String, "LocaleId", () => {
|
|
709
|
+
return this.serverCapabilities.localeIdArray;
|
|
710
|
+
});
|
|
711
|
+
bindStandardScalar(node_opcua_constants_1.VariableIds.Server_ServerCapabilities_MinSupportedSampleRate, node_opcua_variant_1.DataType.Double, () => {
|
|
712
|
+
return this.serverCapabilities.minSupportedSampleRate;
|
|
713
|
+
});
|
|
714
|
+
bindStandardScalar(node_opcua_constants_1.VariableIds.Server_ServerCapabilities_MaxBrowseContinuationPoints, node_opcua_variant_1.DataType.UInt16, () => {
|
|
715
|
+
return this.serverCapabilities.maxBrowseContinuationPoints;
|
|
716
|
+
});
|
|
717
|
+
bindStandardScalar(node_opcua_constants_1.VariableIds.Server_ServerCapabilities_MaxQueryContinuationPoints, node_opcua_variant_1.DataType.UInt16, () => {
|
|
718
|
+
return this.serverCapabilities.maxQueryContinuationPoints;
|
|
719
|
+
});
|
|
720
|
+
bindStandardScalar(node_opcua_constants_1.VariableIds.Server_ServerCapabilities_MaxHistoryContinuationPoints, node_opcua_variant_1.DataType.UInt16, () => {
|
|
721
|
+
return this.serverCapabilities.maxHistoryContinuationPoints;
|
|
722
|
+
});
|
|
723
|
+
// new in 1.05
|
|
724
|
+
bindStandardScalar(node_opcua_constants_1.VariableIds.Server_ServerCapabilities_MaxSessions, node_opcua_variant_1.DataType.UInt32, () => {
|
|
725
|
+
return this.serverCapabilities.maxSessions;
|
|
726
|
+
});
|
|
727
|
+
bindStandardScalar(node_opcua_constants_1.VariableIds.Server_ServerCapabilities_MaxSubscriptions, node_opcua_variant_1.DataType.UInt32, () => {
|
|
728
|
+
return this.serverCapabilities.maxSubscriptions;
|
|
729
|
+
});
|
|
730
|
+
bindStandardScalar(node_opcua_constants_1.VariableIds.Server_ServerCapabilities_MaxMonitoredItems, node_opcua_variant_1.DataType.UInt32, () => {
|
|
731
|
+
return this.serverCapabilities.maxMonitoredItems;
|
|
732
|
+
});
|
|
733
|
+
bindStandardScalar(node_opcua_constants_1.VariableIds.Server_ServerCapabilities_MaxSubscriptionsPerSession, node_opcua_variant_1.DataType.UInt32, () => {
|
|
734
|
+
return this.serverCapabilities.maxSubscriptionsPerSession;
|
|
735
|
+
});
|
|
736
|
+
bindStandardScalar(node_opcua_constants_1.VariableIds.Server_ServerCapabilities_MaxSelectClauseParameters, node_opcua_variant_1.DataType.UInt32, () => {
|
|
737
|
+
return this.serverCapabilities.maxSelectClauseParameters;
|
|
738
|
+
});
|
|
739
|
+
bindStandardScalar(node_opcua_constants_1.VariableIds.Server_ServerCapabilities_MaxWhereClauseParameters, node_opcua_variant_1.DataType.UInt32, () => {
|
|
740
|
+
return this.serverCapabilities.maxWhereClauseParameters;
|
|
741
|
+
});
|
|
742
|
+
//bindStandardArray(VariableIds.Server_ServerCapabilities_ConformanceUnits, DataType.QualifiedName, () => {
|
|
743
|
+
// return this.serverCapabilities.conformanceUnits;
|
|
744
|
+
//});
|
|
745
|
+
bindStandardScalar(node_opcua_constants_1.VariableIds.Server_ServerCapabilities_MaxMonitoredItemsPerSubscription, node_opcua_variant_1.DataType.UInt32, () => {
|
|
746
|
+
return this.serverCapabilities.maxMonitoredItemsPerSubscription;
|
|
747
|
+
});
|
|
748
|
+
// added by DI : Server-specific period of time in milliseconds until the Server will revoke a lock.
|
|
749
|
+
// TODO bindStandardScalar(VariableIds.Server_ServerCapabilities_MaxInactiveLockTime,
|
|
750
|
+
// TODO DataType.UInt16, function () {
|
|
751
|
+
// TODO return self.serverCapabilities.maxInactiveLockTime;
|
|
752
|
+
// TODO });
|
|
753
|
+
bindStandardArray(node_opcua_constants_1.VariableIds.Server_ServerCapabilities_SoftwareCertificates, node_opcua_variant_1.DataType.ExtensionObject, "SoftwareCertificates", () => {
|
|
754
|
+
return this.serverCapabilities.softwareCertificates;
|
|
755
|
+
});
|
|
756
|
+
bindStandardScalar(node_opcua_constants_1.VariableIds.Server_ServerCapabilities_MaxArrayLength, node_opcua_variant_1.DataType.UInt32, () => {
|
|
757
|
+
return Math.min(this.serverCapabilities.maxArrayLength, node_opcua_variant_1.Variant.maxArrayLength);
|
|
758
|
+
});
|
|
759
|
+
bindStandardScalar(node_opcua_constants_1.VariableIds.Server_ServerCapabilities_MaxStringLength, node_opcua_variant_1.DataType.UInt32, () => {
|
|
760
|
+
return Math.min(this.serverCapabilities.maxStringLength, node_opcua_binary_stream_1.BinaryStream.maxStringLength);
|
|
761
|
+
});
|
|
762
|
+
bindStandardScalar(node_opcua_constants_1.VariableIds.Server_ServerCapabilities_MaxByteStringLength, node_opcua_variant_1.DataType.UInt32, () => {
|
|
763
|
+
return Math.min(this.serverCapabilities.maxByteStringLength, node_opcua_binary_stream_1.BinaryStream.maxByteStringLength);
|
|
764
|
+
});
|
|
765
|
+
const bindOperationLimits = (operationLimits) => {
|
|
766
|
+
(0, node_opcua_assert_1.assert)(operationLimits !== null && typeof operationLimits === "object");
|
|
767
|
+
const keys = Object.keys(operationLimits);
|
|
768
|
+
keys.forEach((key) => {
|
|
769
|
+
const uid = "Server_ServerCapabilities_OperationLimits_" + upperCaseFirst(key);
|
|
770
|
+
const nodeId = (0, node_opcua_nodeid_1.makeNodeId)(node_opcua_constants_1.VariableIds[uid]);
|
|
771
|
+
(0, node_opcua_assert_1.assert)(!nodeId.isEmpty());
|
|
772
|
+
bindStandardScalar(node_opcua_constants_1.VariableIds[uid], node_opcua_variant_1.DataType.UInt32, () => {
|
|
773
|
+
return operationLimits[key];
|
|
774
|
+
});
|
|
775
|
+
});
|
|
776
|
+
};
|
|
777
|
+
bindOperationLimits(this.serverCapabilities.operationLimits);
|
|
778
|
+
// i=2399 [ProgramStateMachineType_ProgramDiagnostics];
|
|
779
|
+
function fix_ProgramStateMachineType_ProgramDiagnostics() {
|
|
780
|
+
const nodeId = (0, node_opcua_nodeid_1.coerceNodeId)("i=2399"); // ProgramStateMachineType_ProgramDiagnostics
|
|
781
|
+
const variable = addressSpace.findNode(nodeId);
|
|
782
|
+
if (variable) {
|
|
783
|
+
variable.$extensionObject = new node_opcua_types_1.ProgramDiagnosticDataType({});
|
|
784
|
+
// variable.setValueFromSource({
|
|
785
|
+
// dataType: DataType.ExtensionObject,
|
|
786
|
+
// // value: new ProgramDiagnostic2DataType()
|
|
787
|
+
// value: new ProgramDiagnosticDataType({})
|
|
788
|
+
// });
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
fix_ProgramStateMachineType_ProgramDiagnostics();
|
|
792
|
+
};
|
|
793
|
+
const bindHistoryServerCapabilities = () => {
|
|
794
|
+
bindStandardScalar(node_opcua_constants_1.VariableIds.HistoryServerCapabilities_MaxReturnDataValues, node_opcua_variant_1.DataType.UInt32, () => {
|
|
795
|
+
return this.historyServerCapabilities.maxReturnDataValues;
|
|
796
|
+
});
|
|
797
|
+
bindStandardScalar(node_opcua_constants_1.VariableIds.HistoryServerCapabilities_MaxReturnEventValues, node_opcua_variant_1.DataType.UInt32, () => {
|
|
798
|
+
return this.historyServerCapabilities.maxReturnEventValues;
|
|
799
|
+
});
|
|
800
|
+
bindStandardScalar(node_opcua_constants_1.VariableIds.HistoryServerCapabilities_AccessHistoryDataCapability, node_opcua_variant_1.DataType.Boolean, () => {
|
|
801
|
+
return this.historyServerCapabilities.accessHistoryDataCapability;
|
|
802
|
+
});
|
|
803
|
+
bindStandardScalar(node_opcua_constants_1.VariableIds.HistoryServerCapabilities_AccessHistoryEventsCapability, node_opcua_variant_1.DataType.Boolean, () => {
|
|
804
|
+
return this.historyServerCapabilities.accessHistoryEventsCapability;
|
|
805
|
+
});
|
|
806
|
+
bindStandardScalar(node_opcua_constants_1.VariableIds.HistoryServerCapabilities_InsertDataCapability, node_opcua_variant_1.DataType.Boolean, () => {
|
|
807
|
+
return this.historyServerCapabilities.insertDataCapability;
|
|
808
|
+
});
|
|
809
|
+
bindStandardScalar(node_opcua_constants_1.VariableIds.HistoryServerCapabilities_ReplaceDataCapability, node_opcua_variant_1.DataType.Boolean, () => {
|
|
810
|
+
return this.historyServerCapabilities.replaceDataCapability;
|
|
811
|
+
});
|
|
812
|
+
bindStandardScalar(node_opcua_constants_1.VariableIds.HistoryServerCapabilities_UpdateDataCapability, node_opcua_variant_1.DataType.Boolean, () => {
|
|
813
|
+
return this.historyServerCapabilities.updateDataCapability;
|
|
814
|
+
});
|
|
815
|
+
bindStandardScalar(node_opcua_constants_1.VariableIds.HistoryServerCapabilities_InsertEventCapability, node_opcua_variant_1.DataType.Boolean, () => {
|
|
816
|
+
return this.historyServerCapabilities.insertEventCapability;
|
|
817
|
+
});
|
|
818
|
+
bindStandardScalar(node_opcua_constants_1.VariableIds.HistoryServerCapabilities_ReplaceEventCapability, node_opcua_variant_1.DataType.Boolean, () => {
|
|
819
|
+
return this.historyServerCapabilities.replaceEventCapability;
|
|
820
|
+
});
|
|
821
|
+
bindStandardScalar(node_opcua_constants_1.VariableIds.HistoryServerCapabilities_UpdateEventCapability, node_opcua_variant_1.DataType.Boolean, () => {
|
|
822
|
+
return this.historyServerCapabilities.updateEventCapability;
|
|
823
|
+
});
|
|
824
|
+
bindStandardScalar(node_opcua_constants_1.VariableIds.HistoryServerCapabilities_DeleteEventCapability, node_opcua_variant_1.DataType.Boolean, () => {
|
|
825
|
+
return this.historyServerCapabilities.deleteEventCapability;
|
|
826
|
+
});
|
|
827
|
+
bindStandardScalar(node_opcua_constants_1.VariableIds.HistoryServerCapabilities_DeleteRawCapability, node_opcua_variant_1.DataType.Boolean, () => {
|
|
828
|
+
return this.historyServerCapabilities.deleteRawCapability;
|
|
829
|
+
});
|
|
830
|
+
bindStandardScalar(node_opcua_constants_1.VariableIds.HistoryServerCapabilities_DeleteAtTimeCapability, node_opcua_variant_1.DataType.Boolean, () => {
|
|
831
|
+
return this.historyServerCapabilities.deleteAtTimeCapability;
|
|
832
|
+
});
|
|
833
|
+
bindStandardScalar(node_opcua_constants_1.VariableIds.HistoryServerCapabilities_InsertAnnotationCapability, node_opcua_variant_1.DataType.Boolean, () => {
|
|
834
|
+
return this.historyServerCapabilities.insertAnnotationCapability;
|
|
835
|
+
});
|
|
836
|
+
};
|
|
837
|
+
bindServerDiagnostics();
|
|
838
|
+
bindServerStatus();
|
|
839
|
+
bindServerCapabilities();
|
|
840
|
+
bindHistoryServerCapabilities();
|
|
841
|
+
const bindExtraStuff = () => {
|
|
842
|
+
// mainly for compliance
|
|
843
|
+
/*
|
|
844
|
+
// The version number for the data type description. i=104
|
|
845
|
+
bindStandardScalar(VariableIds.DataTypeDescriptionType_DataTypeVersion, DataType.String, () => {
|
|
846
|
+
return "0";
|
|
847
|
+
});
|
|
848
|
+
|
|
849
|
+
const namingRuleDataTypeNode = addressSpace.findDataType(resolveNodeId(DataTypeIds.NamingRuleType))! as UADataType;
|
|
850
|
+
|
|
851
|
+
if (namingRuleDataTypeNode) {
|
|
852
|
+
const namingRuleType = (namingRuleDataTypeNode as any)._getEnumerationInfo().nameIndex; // getEnumeration("NamingRuleType");
|
|
853
|
+
if (!namingRuleType) {
|
|
854
|
+
throw new Error("Cannot find Enumeration definition for NamingRuleType");
|
|
855
|
+
}
|
|
856
|
+
// i=111
|
|
857
|
+
bindStandardScalar(VariableIds.ModellingRuleType_NamingRule, DataType.Int32, () => {
|
|
858
|
+
return 0;
|
|
859
|
+
});
|
|
860
|
+
|
|
861
|
+
// i=112
|
|
862
|
+
bindStandardScalar(VariableIds.ModellingRule_Mandatory_NamingRule, DataType.Int32, () => {
|
|
863
|
+
return namingRuleType.Mandatory ? namingRuleType.Mandatory.value : 0;
|
|
864
|
+
});
|
|
865
|
+
|
|
866
|
+
// i=113
|
|
867
|
+
bindStandardScalar(VariableIds.ModellingRule_Optional_NamingRule, DataType.Int32, () => {
|
|
868
|
+
return namingRuleType.Optional ? namingRuleType.Optional.value : 0;
|
|
869
|
+
});
|
|
870
|
+
// i=114
|
|
871
|
+
bindStandardScalar(VariableIds.ModellingRule_ExposesItsArray_NamingRule, DataType.Int32, () => {
|
|
872
|
+
return namingRuleType.ExposesItsArray ? namingRuleType.ExposesItsArray.value : 0;
|
|
873
|
+
});
|
|
874
|
+
bindStandardScalar(VariableIds.ModellingRule_MandatoryPlaceholder_NamingRule, DataType.Int32, () => {
|
|
875
|
+
return namingRuleType.MandatoryPlaceholder ? namingRuleType.MandatoryPlaceholder.value : 0;
|
|
876
|
+
});
|
|
877
|
+
}
|
|
878
|
+
*/
|
|
879
|
+
};
|
|
880
|
+
bindExtraStuff();
|
|
881
|
+
this.__internal_bindMethod((0, node_opcua_nodeid_1.makeNodeId)(node_opcua_constants_1.MethodIds.Server_GetMonitoredItems), getMonitoredItemsId.bind(this));
|
|
882
|
+
this.__internal_bindMethod((0, node_opcua_nodeid_1.makeNodeId)(node_opcua_constants_1.MethodIds.Server_SetSubscriptionDurable), setSubscriptionDurable.bind(this));
|
|
883
|
+
// fix getMonitoredItems.outputArguments arrayDimensions
|
|
884
|
+
const fixGetMonitoredItemArgs = () => {
|
|
885
|
+
var _a;
|
|
886
|
+
const objects = (_a = this.addressSpace.rootFolder) === null || _a === void 0 ? void 0 : _a.objects;
|
|
887
|
+
if (!objects || !objects.server || !objects.server.getMonitoredItems) {
|
|
888
|
+
return;
|
|
889
|
+
}
|
|
890
|
+
const outputArguments = objects.server.getMonitoredItems.outputArguments;
|
|
891
|
+
const dataValue = outputArguments.readValue();
|
|
892
|
+
(0, node_opcua_assert_1.assert)(dataValue.value.value[0].arrayDimensions.length === 1 && dataValue.value.value[0].arrayDimensions[0] === 0);
|
|
893
|
+
(0, node_opcua_assert_1.assert)(dataValue.value.value[1].arrayDimensions.length === 1 && dataValue.value.value[1].arrayDimensions[0] === 0);
|
|
894
|
+
};
|
|
895
|
+
fixGetMonitoredItemArgs();
|
|
896
|
+
const prepareServerDiagnostics = () => {
|
|
897
|
+
const addressSpace1 = this.addressSpace;
|
|
898
|
+
if (!addressSpace1.rootFolder.objects) {
|
|
899
|
+
return;
|
|
900
|
+
}
|
|
901
|
+
const server = addressSpace1.rootFolder.objects.server;
|
|
902
|
+
if (!server) {
|
|
903
|
+
return;
|
|
904
|
+
}
|
|
905
|
+
// create SessionsDiagnosticsSummary
|
|
906
|
+
const serverDiagnosticsNode = server.getComponentByName("ServerDiagnostics");
|
|
907
|
+
if (!serverDiagnosticsNode) {
|
|
908
|
+
return;
|
|
909
|
+
}
|
|
910
|
+
if (true) {
|
|
911
|
+
// set serverDiagnosticsNode enabledFlag writeable for admin user only
|
|
912
|
+
// TO DO ...
|
|
913
|
+
serverDiagnosticsNode.enabledFlag.userAccessLevel = (0, node_opcua_data_model_1.makeAccessLevelFlag)("CurrentRead");
|
|
914
|
+
serverDiagnosticsNode.enabledFlag.accessLevel = (0, node_opcua_data_model_1.makeAccessLevelFlag)("CurrentRead");
|
|
915
|
+
}
|
|
916
|
+
// A Server may not expose the SamplingIntervalDiagnosticsArray if it does not use fixed sampling rates.
|
|
917
|
+
// because we are not using fixed sampling rate, we need to remove the optional SamplingIntervalDiagnosticsArray
|
|
918
|
+
// component
|
|
919
|
+
const samplingIntervalDiagnosticsArray = serverDiagnosticsNode.getComponentByName("SamplingIntervalDiagnosticsArray");
|
|
920
|
+
if (samplingIntervalDiagnosticsArray) {
|
|
921
|
+
addressSpace.deleteNode(samplingIntervalDiagnosticsArray);
|
|
922
|
+
const s = serverDiagnosticsNode.getComponents();
|
|
923
|
+
}
|
|
924
|
+
const subscriptionDiagnosticsArrayNode = serverDiagnosticsNode.getComponentByName("SubscriptionDiagnosticsArray");
|
|
925
|
+
(0, node_opcua_assert_1.assert)(subscriptionDiagnosticsArrayNode.nodeClass === node_opcua_data_model_1.NodeClass.Variable);
|
|
926
|
+
(0, node_opcua_address_space_1.bindExtObjArrayNode)(subscriptionDiagnosticsArrayNode, "SubscriptionDiagnosticsType", "subscriptionId");
|
|
927
|
+
makeNotReadableIfEnabledFlagIsFalse(subscriptionDiagnosticsArrayNode);
|
|
928
|
+
const sessionsDiagnosticsSummary = serverDiagnosticsNode.getComponentByName("SessionsDiagnosticsSummary");
|
|
929
|
+
const sessionDiagnosticsArray = sessionsDiagnosticsSummary.getComponentByName("SessionDiagnosticsArray");
|
|
930
|
+
(0, node_opcua_assert_1.assert)(sessionDiagnosticsArray.nodeClass === node_opcua_data_model_1.NodeClass.Variable);
|
|
931
|
+
(0, node_opcua_address_space_1.bindExtObjArrayNode)(sessionDiagnosticsArray, "SessionDiagnosticsVariableType", "sessionId");
|
|
932
|
+
const varType = addressSpace.findVariableType("SessionSecurityDiagnosticsType");
|
|
933
|
+
if (!varType) {
|
|
934
|
+
debugLog("Warning cannot find SessionSecurityDiagnosticsType variable Type");
|
|
935
|
+
}
|
|
936
|
+
else {
|
|
937
|
+
const sessionSecurityDiagnosticsArray = sessionsDiagnosticsSummary.getComponentByName("SessionSecurityDiagnosticsArray");
|
|
938
|
+
(0, node_opcua_assert_1.assert)(sessionSecurityDiagnosticsArray.nodeClass === node_opcua_data_model_1.NodeClass.Variable);
|
|
939
|
+
(0, node_opcua_address_space_1.bindExtObjArrayNode)(sessionSecurityDiagnosticsArray, "SessionSecurityDiagnosticsType", "sessionId");
|
|
940
|
+
(0, node_opcua_address_space_1.ensureObjectIsSecure)(sessionSecurityDiagnosticsArray);
|
|
941
|
+
}
|
|
942
|
+
};
|
|
943
|
+
prepareServerDiagnostics();
|
|
944
|
+
this._internalState = "initialized";
|
|
945
|
+
this.setServerState(node_opcua_common_1.ServerState.Running);
|
|
946
|
+
setImmediate(() => callback());
|
|
947
|
+
});
|
|
948
|
+
}
|
|
949
|
+
/**
|
|
950
|
+
*
|
|
951
|
+
* @method browseSingleNode
|
|
952
|
+
* @param nodeId {NodeId|String} : the nodeid of the element to browse
|
|
953
|
+
* @param browseDescription
|
|
954
|
+
* @param browseDescription.browseDirection {BrowseDirection} :
|
|
955
|
+
* @param browseDescription.referenceTypeId {String|NodeId}
|
|
956
|
+
* @param [context]
|
|
957
|
+
* @return the browse result
|
|
958
|
+
*/
|
|
959
|
+
browseSingleNode(nodeId, browseDescription, context) {
|
|
960
|
+
const addressSpace = this.addressSpace;
|
|
961
|
+
return addressSpace.browseSingleNode(nodeId, browseDescription, context);
|
|
962
|
+
}
|
|
963
|
+
browseWithAutomaticExpansion(nodesToBrowse, context) {
|
|
964
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
965
|
+
// do expansion first
|
|
966
|
+
for (const browseDescription of nodesToBrowse) {
|
|
967
|
+
const nodeId = (0, node_opcua_nodeid_1.resolveNodeId)(browseDescription.nodeId);
|
|
968
|
+
const node = this.addressSpace.findNode(nodeId);
|
|
969
|
+
if (node) {
|
|
970
|
+
if (node.onFirstBrowseAction) {
|
|
971
|
+
try {
|
|
972
|
+
yield node.onFirstBrowseAction();
|
|
973
|
+
node.onFirstBrowseAction = undefined;
|
|
974
|
+
}
|
|
975
|
+
catch (err) {
|
|
976
|
+
if (err instanceof Error) {
|
|
977
|
+
errorLog("onFirstBrowseAction method has failed", err.message);
|
|
978
|
+
}
|
|
979
|
+
errorLog(err);
|
|
980
|
+
}
|
|
981
|
+
(0, node_opcua_assert_1.assert)(node.onFirstBrowseAction === undefined, "expansion can only be made once");
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
return this.browse(nodesToBrowse, context);
|
|
986
|
+
});
|
|
987
|
+
}
|
|
988
|
+
/**
|
|
989
|
+
*
|
|
990
|
+
*/
|
|
991
|
+
browse(nodesToBrowse, context) {
|
|
992
|
+
const results = [];
|
|
993
|
+
for (const browseDescription of nodesToBrowse) {
|
|
994
|
+
const nodeId = (0, node_opcua_nodeid_1.resolveNodeId)(browseDescription.nodeId);
|
|
995
|
+
const r = this.browseSingleNode(nodeId, browseDescription, context);
|
|
996
|
+
results.push(r);
|
|
997
|
+
}
|
|
998
|
+
return results;
|
|
999
|
+
}
|
|
1000
|
+
/**
|
|
1001
|
+
*
|
|
1002
|
+
* @method readSingleNode
|
|
1003
|
+
* @param context
|
|
1004
|
+
* @param nodeId
|
|
1005
|
+
* @param attributeId
|
|
1006
|
+
* @param [timestampsToReturn=TimestampsToReturn.Neither]
|
|
1007
|
+
* @return DataValue
|
|
1008
|
+
*/
|
|
1009
|
+
readSingleNode(context, nodeId, attributeId, timestampsToReturn) {
|
|
1010
|
+
context.currentTime = (0, node_opcua_date_time_1.getCurrentClock)();
|
|
1011
|
+
return this._readSingleNode(context, new node_opcua_types_1.ReadValueId({
|
|
1012
|
+
attributeId,
|
|
1013
|
+
nodeId: (0, node_opcua_nodeid_1.resolveNodeId)(nodeId)
|
|
1014
|
+
}), timestampsToReturn);
|
|
1015
|
+
}
|
|
1016
|
+
/**
|
|
1017
|
+
*
|
|
1018
|
+
*
|
|
1019
|
+
* Maximum age of the value to be read in milliseconds. The age of the value is based on the difference between
|
|
1020
|
+
* the ServerTimestamp and the time when the Server starts processing the request. For example if the Client
|
|
1021
|
+
* specifies a maxAge of 500 milliseconds and it takes 100 milliseconds until the Server starts processing
|
|
1022
|
+
* the request, the age of the returned value could be 600 milliseconds prior to the time it was requested.
|
|
1023
|
+
* If the Server has one or more values of an Attribute that are within the maximum age, it can return any one
|
|
1024
|
+
* of the values or it can read a new value from the data source. The number of values of an Attribute that
|
|
1025
|
+
* a Server has depends on the number of MonitoredItems that are defined for the Attribute. In any case,
|
|
1026
|
+
* the Client can make no assumption about which copy of the data will be returned.
|
|
1027
|
+
* If the Server does not have a value that is within the maximum age, it shall attempt to read a new value
|
|
1028
|
+
* from the data source.
|
|
1029
|
+
* If the Server cannot meet the requested maxAge, it returns its 'best effort' value rather than rejecting the
|
|
1030
|
+
* request.
|
|
1031
|
+
* This may occur when the time it takes the Server to process and return the new data value after it has been
|
|
1032
|
+
* accessed is greater than the specified maximum age.
|
|
1033
|
+
* If maxAge is set to 0, the Server shall attempt to read a new value from the data source.
|
|
1034
|
+
* If maxAge is set to the max Int32 value or greater, the Server shall attempt to get a cached value.
|
|
1035
|
+
* Negative values are invalid for maxAge.
|
|
1036
|
+
*
|
|
1037
|
+
* @return an array of DataValue
|
|
1038
|
+
*/
|
|
1039
|
+
read(context, readRequest) {
|
|
1040
|
+
(0, node_opcua_assert_1.assert)(context instanceof node_opcua_address_space_1.SessionContext);
|
|
1041
|
+
(0, node_opcua_assert_1.assert)(readRequest instanceof node_opcua_service_read_1.ReadRequest);
|
|
1042
|
+
(0, node_opcua_assert_1.assert)(readRequest.maxAge >= 0);
|
|
1043
|
+
const timestampsToReturn = readRequest.timestampsToReturn;
|
|
1044
|
+
const nodesToRead = readRequest.nodesToRead || [];
|
|
1045
|
+
(0, node_opcua_assert_1.assert)(Array.isArray(nodesToRead));
|
|
1046
|
+
context.currentTime = (0, node_opcua_date_time_1.getCurrentClock)();
|
|
1047
|
+
const dataValues = [];
|
|
1048
|
+
for (const readValueId of nodesToRead) {
|
|
1049
|
+
const dataValue = this._readSingleNode(context, readValueId, timestampsToReturn);
|
|
1050
|
+
if (timestampsToReturn === node_opcua_service_read_1.TimestampsToReturn.Server) {
|
|
1051
|
+
dataValue.sourceTimestamp = null;
|
|
1052
|
+
dataValue.sourcePicoseconds = 0;
|
|
1053
|
+
}
|
|
1054
|
+
if ((timestampsToReturn === node_opcua_service_read_1.TimestampsToReturn.Both || timestampsToReturn === node_opcua_service_read_1.TimestampsToReturn.Server) &&
|
|
1055
|
+
(!dataValue.serverTimestamp || dataValue.serverTimestamp.getTime() === node_opcua_date_time_1.minOPCUADate.getTime())) {
|
|
1056
|
+
dataValue.serverTimestamp = context.currentTime.timestamp;
|
|
1057
|
+
dataValue.sourcePicoseconds = 0; // context.currentTime.picosecond // do we really need picosecond here ? this would inflate binary data
|
|
1058
|
+
}
|
|
1059
|
+
dataValues.push(dataValue);
|
|
1060
|
+
}
|
|
1061
|
+
return dataValues;
|
|
1062
|
+
}
|
|
1063
|
+
/**
|
|
1064
|
+
*
|
|
1065
|
+
* @method writeSingleNode
|
|
1066
|
+
* @param context
|
|
1067
|
+
* @param writeValue
|
|
1068
|
+
* @param callback
|
|
1069
|
+
* @param callback.err
|
|
1070
|
+
* @param callback.statusCode
|
|
1071
|
+
* @async
|
|
1072
|
+
*/
|
|
1073
|
+
writeSingleNode(context, writeValue, callback) {
|
|
1074
|
+
(0, node_opcua_assert_1.assert)(context instanceof node_opcua_address_space_1.SessionContext);
|
|
1075
|
+
(0, node_opcua_assert_1.assert)(typeof callback === "function");
|
|
1076
|
+
(0, node_opcua_assert_1.assert)(writeValue.schema.name === "WriteValue");
|
|
1077
|
+
(0, node_opcua_assert_1.assert)(writeValue.value instanceof node_opcua_data_value_1.DataValue);
|
|
1078
|
+
if (writeValue.value.value === null) {
|
|
1079
|
+
return callback(null, node_opcua_status_code_1.StatusCodes.BadTypeMismatch);
|
|
1080
|
+
}
|
|
1081
|
+
(0, node_opcua_assert_1.assert)(writeValue.value.value instanceof node_opcua_variant_1.Variant);
|
|
1082
|
+
const nodeId = writeValue.nodeId;
|
|
1083
|
+
const obj = this.__findNode(nodeId);
|
|
1084
|
+
if (!obj) {
|
|
1085
|
+
return callback(null, node_opcua_status_code_1.StatusCodes.BadNodeIdUnknown);
|
|
1086
|
+
}
|
|
1087
|
+
else {
|
|
1088
|
+
obj.writeAttribute(context, writeValue, callback);
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
/**
|
|
1092
|
+
* write a collection of nodes
|
|
1093
|
+
* @method write
|
|
1094
|
+
* @param context
|
|
1095
|
+
* @param nodesToWrite
|
|
1096
|
+
* @param callback
|
|
1097
|
+
* @param callback.err
|
|
1098
|
+
* @param callback.results
|
|
1099
|
+
* @async
|
|
1100
|
+
*/
|
|
1101
|
+
write(context, nodesToWrite, callback) {
|
|
1102
|
+
(0, node_opcua_assert_1.assert)(context instanceof node_opcua_address_space_1.SessionContext);
|
|
1103
|
+
(0, node_opcua_assert_1.assert)(typeof callback === "function");
|
|
1104
|
+
context.currentTime = (0, node_opcua_date_time_1.getCurrentClock)();
|
|
1105
|
+
(0, node_opcua_address_space_1.ensureDatatypeExtractedWithCallback)(this.addressSpace, (err2, extraDataTypeManager) => {
|
|
1106
|
+
if (err2) {
|
|
1107
|
+
return callback(err2);
|
|
1108
|
+
}
|
|
1109
|
+
const performWrite = (writeValue, inner_callback) => {
|
|
1110
|
+
(0, node_opcua_address_space_1.resolveOpaqueOnAddressSpace)(this.addressSpace, writeValue.value.value)
|
|
1111
|
+
.then(() => {
|
|
1112
|
+
this.writeSingleNode(context, writeValue, inner_callback);
|
|
1113
|
+
})
|
|
1114
|
+
.catch(inner_callback);
|
|
1115
|
+
};
|
|
1116
|
+
// tslint:disable:array-type
|
|
1117
|
+
async.map(nodesToWrite, performWrite, (err, statusCodes) => {
|
|
1118
|
+
(0, node_opcua_assert_1.assert)(Array.isArray(statusCodes));
|
|
1119
|
+
callback(err, statusCodes);
|
|
1120
|
+
});
|
|
1121
|
+
});
|
|
1122
|
+
}
|
|
1123
|
+
/**
|
|
1124
|
+
*
|
|
1125
|
+
*/
|
|
1126
|
+
historyReadSingleNode(context, nodeId, attributeId, historyReadDetails, timestampsToReturn, continuationData, callback) {
|
|
1127
|
+
if (timestampsToReturn === node_opcua_service_read_1.TimestampsToReturn.Invalid) {
|
|
1128
|
+
callback(null, new node_opcua_service_history_1.HistoryReadResult({
|
|
1129
|
+
statusCode: node_opcua_status_code_1.StatusCodes.BadTimestampsToReturnInvalid
|
|
1130
|
+
}));
|
|
1131
|
+
return;
|
|
1132
|
+
}
|
|
1133
|
+
(0, node_opcua_assert_1.assert)(context instanceof node_opcua_address_space_1.SessionContext);
|
|
1134
|
+
this._historyReadSingleNode(context, new node_opcua_service_history_1.HistoryReadValueId({
|
|
1135
|
+
nodeId
|
|
1136
|
+
}), historyReadDetails, timestampsToReturn, continuationData, callback);
|
|
1137
|
+
}
|
|
1138
|
+
/**
|
|
1139
|
+
*
|
|
1140
|
+
* @method historyRead
|
|
1141
|
+
* @param context {SessionContext}
|
|
1142
|
+
* @param historyReadRequest {HistoryReadRequest}
|
|
1143
|
+
* @param historyReadRequest.requestHeader {RequestHeader}
|
|
1144
|
+
* @param historyReadRequest.historyReadDetails {HistoryReadDetails}
|
|
1145
|
+
* @param historyReadRequest.timestampsToReturn {TimestampsToReturn}
|
|
1146
|
+
* @param historyReadRequest.releaseContinuationPoints {Boolean}
|
|
1147
|
+
* @param historyReadRequest.nodesToRead {HistoryReadValueId[]}
|
|
1148
|
+
* @param callback
|
|
1149
|
+
* @param callback.err
|
|
1150
|
+
* @param callback.results {HistoryReadResult[]}
|
|
1151
|
+
*/
|
|
1152
|
+
historyRead(context, historyReadRequest, callback) {
|
|
1153
|
+
(0, node_opcua_assert_1.assert)(context instanceof node_opcua_address_space_1.SessionContext);
|
|
1154
|
+
(0, node_opcua_assert_1.assert)(historyReadRequest instanceof node_opcua_service_history_1.HistoryReadRequest);
|
|
1155
|
+
(0, node_opcua_assert_1.assert)(typeof callback === "function");
|
|
1156
|
+
const timestampsToReturn = historyReadRequest.timestampsToReturn;
|
|
1157
|
+
const historyReadDetails = historyReadRequest.historyReadDetails;
|
|
1158
|
+
const releaseContinuationPoints = historyReadRequest.releaseContinuationPoints;
|
|
1159
|
+
(0, node_opcua_assert_1.assert)(historyReadDetails instanceof node_opcua_service_history_1.HistoryReadDetails);
|
|
1160
|
+
// ReadAnnotationDataDetails | ReadAtTimeDetails | ReadEventDetails | ReadProcessedDetails | ReadRawModifiedDetails;
|
|
1161
|
+
const nodesToRead = historyReadRequest.nodesToRead || [];
|
|
1162
|
+
(0, node_opcua_assert_1.assert)(Array.isArray(nodesToRead));
|
|
1163
|
+
const _q = (m) => __awaiter(this, void 0, void 0, function* () {
|
|
1164
|
+
return new Promise((resolve) => {
|
|
1165
|
+
const continuationPoint = m.nodeToRead.continuationPoint;
|
|
1166
|
+
this._historyReadSingleNode(context, m.nodeToRead, m.processDetail, timestampsToReturn, { continuationPoint, releaseContinuationPoints /**, index = ??? */ }, (err, result) => {
|
|
1167
|
+
if (err && !result) {
|
|
1168
|
+
errorLog("Internal error", err.message);
|
|
1169
|
+
result = new node_opcua_service_history_1.HistoryReadResult({ statusCode: node_opcua_status_code_1.StatusCodes.BadInternalError });
|
|
1170
|
+
}
|
|
1171
|
+
resolve(result);
|
|
1172
|
+
});
|
|
1173
|
+
});
|
|
1174
|
+
});
|
|
1175
|
+
if (historyReadDetails instanceof node_opcua_types_1.ReadProcessedDetails) {
|
|
1176
|
+
//
|
|
1177
|
+
if (!historyReadDetails.aggregateType || historyReadDetails.aggregateType.length !== nodesToRead.length) {
|
|
1178
|
+
return callback(null, [new node_opcua_service_history_1.HistoryReadResult({ statusCode: node_opcua_status_code_1.StatusCodes.BadInvalidArgument })]);
|
|
1179
|
+
}
|
|
1180
|
+
const promises = [];
|
|
1181
|
+
let index = 0;
|
|
1182
|
+
for (const nodeToRead of nodesToRead) {
|
|
1183
|
+
const aggregateType = historyReadDetails.aggregateType[index];
|
|
1184
|
+
const processDetail = new node_opcua_types_1.ReadProcessedDetails(Object.assign(Object.assign({}, historyReadDetails), { aggregateType: [aggregateType] }));
|
|
1185
|
+
promises.push(_q({ nodeToRead, processDetail, index }));
|
|
1186
|
+
index++;
|
|
1187
|
+
}
|
|
1188
|
+
Promise.all(promises).then((results) => {
|
|
1189
|
+
callback(null, results);
|
|
1190
|
+
});
|
|
1191
|
+
return;
|
|
1192
|
+
}
|
|
1193
|
+
const _r = (nodeToRead, index) => __awaiter(this, void 0, void 0, function* () {
|
|
1194
|
+
const continuationPoint = nodeToRead.continuationPoint;
|
|
1195
|
+
return new Promise((resolve, reject) => {
|
|
1196
|
+
this._historyReadSingleNode(context, nodeToRead, historyReadDetails, timestampsToReturn, { continuationPoint, releaseContinuationPoints, index }, (err, result) => {
|
|
1197
|
+
if (err && !result) {
|
|
1198
|
+
result = new node_opcua_service_history_1.HistoryReadResult({ statusCode: node_opcua_status_code_1.StatusCodes.BadInternalError });
|
|
1199
|
+
}
|
|
1200
|
+
resolve(result);
|
|
1201
|
+
// it's not guaranteed that the historical read process is really asynchronous
|
|
1202
|
+
});
|
|
1203
|
+
});
|
|
1204
|
+
});
|
|
1205
|
+
const promises = [];
|
|
1206
|
+
let index = 0;
|
|
1207
|
+
for (const nodeToRead of nodesToRead) {
|
|
1208
|
+
promises.push(_r(nodeToRead, index));
|
|
1209
|
+
index++;
|
|
1210
|
+
}
|
|
1211
|
+
Promise.all(promises).then((results) => {
|
|
1212
|
+
callback(null, results);
|
|
1213
|
+
});
|
|
1214
|
+
}
|
|
1215
|
+
getOldestInactiveSession() {
|
|
1216
|
+
// search screwed or closed session first
|
|
1217
|
+
let tmp = Object.values(this._sessions).filter((session1) => session1.status === "screwed" || session1.status === "disposed" || session1.status === "closed");
|
|
1218
|
+
if (tmp.length === 0) {
|
|
1219
|
+
// if none available, tap into the session that are not yet activated
|
|
1220
|
+
tmp = Object.values(this._sessions).filter((session1) => session1.status === "new");
|
|
1221
|
+
}
|
|
1222
|
+
if (tmp.length === 0)
|
|
1223
|
+
return null;
|
|
1224
|
+
let session = tmp[0];
|
|
1225
|
+
for (let i = 1; i < tmp.length; i++) {
|
|
1226
|
+
const c = tmp[i];
|
|
1227
|
+
if (session.creationDate.getTime() < c.creationDate.getTime()) {
|
|
1228
|
+
session = c;
|
|
1229
|
+
}
|
|
1230
|
+
}
|
|
1231
|
+
return session;
|
|
1232
|
+
}
|
|
1233
|
+
/**
|
|
1234
|
+
* create a new server session object.
|
|
1235
|
+
* @class ServerEngine
|
|
1236
|
+
* @method createSession
|
|
1237
|
+
* @param [options] {Object}
|
|
1238
|
+
* @param [options.sessionTimeout = 1000] {Number} sessionTimeout
|
|
1239
|
+
* @param [options.clientDescription] {ApplicationDescription}
|
|
1240
|
+
* @return {ServerSession}
|
|
1241
|
+
*/
|
|
1242
|
+
createSession(options) {
|
|
1243
|
+
options = options || {};
|
|
1244
|
+
debugLog("createSession : increasing serverDiagnosticsSummary cumulatedSessionCount/currentSessionCount ");
|
|
1245
|
+
this.serverDiagnosticsSummary.cumulatedSessionCount += 1;
|
|
1246
|
+
this.serverDiagnosticsSummary.currentSessionCount += 1;
|
|
1247
|
+
this.clientDescription = options.clientDescription || new node_opcua_service_endpoints_1.ApplicationDescription({});
|
|
1248
|
+
const sessionTimeout = options.sessionTimeout || 1000;
|
|
1249
|
+
(0, node_opcua_assert_1.assert)(typeof sessionTimeout === "number");
|
|
1250
|
+
const session = new server_session_1.ServerSession(this, sessionTimeout);
|
|
1251
|
+
debugLog("createSession :sessionTimeout = ", session.sessionTimeout);
|
|
1252
|
+
const key = session.authenticationToken.toString();
|
|
1253
|
+
this._sessions[key] = session;
|
|
1254
|
+
// see spec OPC Unified Architecture, Part 2 page 26 Release 1.02
|
|
1255
|
+
// TODO : When a Session is created, the Server adds an entry for the Client
|
|
1256
|
+
// in its SessionDiagnosticsArray Variable
|
|
1257
|
+
session.on("new_subscription", (subscription) => {
|
|
1258
|
+
this.serverDiagnosticsSummary.cumulatedSubscriptionCount += 1;
|
|
1259
|
+
// add the subscription diagnostics in our subscriptions diagnostics array
|
|
1260
|
+
// note currentSubscriptionCount is handled directly with a special getter
|
|
1261
|
+
});
|
|
1262
|
+
session.on("subscription_terminated", (subscription) => {
|
|
1263
|
+
// remove the subscription diagnostics in our subscriptions diagnostics array
|
|
1264
|
+
// note currentSubscriptionCount is handled directly with a special getter
|
|
1265
|
+
});
|
|
1266
|
+
// OPC Unified Architecture, Part 4 23 Release 1.03
|
|
1267
|
+
// Sessions are terminated by the Server automatically if the Client fails to issue a Service request on the
|
|
1268
|
+
// Session within the timeout period negotiated by the Server in the CreateSession Service response.
|
|
1269
|
+
// This protects the Server against Client failures and against situations where a failed underlying
|
|
1270
|
+
// connection cannot be re-established. Clients shall be prepared to submit requests in a timely manner
|
|
1271
|
+
// prevent the Session from closing automatically. Clients may explicitly terminate sessions using the
|
|
1272
|
+
// CloseSession Service.
|
|
1273
|
+
session.on("timeout", () => {
|
|
1274
|
+
// the session hasn't been active for a while , probably because the client has disconnected abruptly
|
|
1275
|
+
// it is now time to close the session completely
|
|
1276
|
+
this.serverDiagnosticsSummary.sessionTimeoutCount += 1;
|
|
1277
|
+
session.sessionName = session.sessionName || "";
|
|
1278
|
+
const channel = session.channel;
|
|
1279
|
+
errorLog(chalk.cyan("Server: closing SESSION "), session.status, chalk.yellow(session.sessionName), chalk.yellow(session.nodeId.toString()), chalk.cyan(" because of timeout = "), session.sessionTimeout, chalk.cyan(" has expired without a keep alive"), chalk.bgCyan("channel = "), channel === null || channel === void 0 ? void 0 : channel.remoteAddress, " port = ", channel === null || channel === void 0 ? void 0 : channel.remotePort);
|
|
1280
|
+
// If a Server terminates a Session for any other reason, Subscriptions associated with the Session,
|
|
1281
|
+
// are not deleted. => deleteSubscription= false
|
|
1282
|
+
this.closeSession(session.authenticationToken, /*deleteSubscription=*/ false, /* reason =*/ "Timeout");
|
|
1283
|
+
this.incrementSessionTimeoutCount();
|
|
1284
|
+
});
|
|
1285
|
+
return session;
|
|
1286
|
+
}
|
|
1287
|
+
/**
|
|
1288
|
+
* @method closeSession
|
|
1289
|
+
* @param authenticationToken
|
|
1290
|
+
* @param deleteSubscriptions {Boolean} : true if session's subscription shall be deleted
|
|
1291
|
+
* @param {String} [reason = "CloseSession"] the reason for closing the session (
|
|
1292
|
+
* shall be "Timeout", "Terminated" or "CloseSession")
|
|
1293
|
+
*
|
|
1294
|
+
*
|
|
1295
|
+
* what the specs say:
|
|
1296
|
+
* -------------------
|
|
1297
|
+
*
|
|
1298
|
+
* If a Client invokes the CloseSession Service then all Subscriptions associated with the Session are also deleted
|
|
1299
|
+
* if the deleteSubscriptions flag is set to TRUE. If a Server terminates a Session for any other reason,
|
|
1300
|
+
* Subscriptions associated with the Session, are not deleted. Each Subscription has its own lifetime to protect
|
|
1301
|
+
* against data loss in the case of a Session termination. In these cases, the Subscription can be reassigned to
|
|
1302
|
+
* another Client before its lifetime expires.
|
|
1303
|
+
*/
|
|
1304
|
+
closeSession(authenticationToken, deleteSubscriptions, reason) {
|
|
1305
|
+
reason = reason || "CloseSession";
|
|
1306
|
+
(0, node_opcua_assert_1.assert)(typeof reason === "string");
|
|
1307
|
+
(0, node_opcua_assert_1.assert)(reason === "Timeout" || reason === "Terminated" || reason === "CloseSession" || reason === "Forcing");
|
|
1308
|
+
debugLog("ServerEngine.closeSession ", authenticationToken.toString(), deleteSubscriptions);
|
|
1309
|
+
const session = this.getSession(authenticationToken);
|
|
1310
|
+
// istanbul ignore next
|
|
1311
|
+
if (!session) {
|
|
1312
|
+
throw new Error("cannot find session with this authenticationToken " + authenticationToken.toString());
|
|
1313
|
+
}
|
|
1314
|
+
if (!deleteSubscriptions) {
|
|
1315
|
+
// Live Subscriptions will not be deleted, but transferred to the orphanPublishEngine
|
|
1316
|
+
// until they time out or until a other session transfer them back to it.
|
|
1317
|
+
if (!this._orphanPublishEngine) {
|
|
1318
|
+
this._orphanPublishEngine = new server_publish_engine_for_orphan_subscriptions_1.ServerSidePublishEngineForOrphanSubscription({ maxPublishRequestInQueue: 0 });
|
|
1319
|
+
}
|
|
1320
|
+
debugLog("transferring remaining live subscription to orphanPublishEngine !");
|
|
1321
|
+
server_publish_engine_1.ServerSidePublishEngine.transferSubscriptionsToOrphan(session.publishEngine, this._orphanPublishEngine);
|
|
1322
|
+
}
|
|
1323
|
+
session.close(deleteSubscriptions, reason);
|
|
1324
|
+
(0, node_opcua_assert_1.assert)(session.status === "closed");
|
|
1325
|
+
debugLog(" engine.serverDiagnosticsSummary.currentSessionCount -= 1;");
|
|
1326
|
+
this.serverDiagnosticsSummary.currentSessionCount -= 1;
|
|
1327
|
+
// xx //TODO make sure _closedSessions gets cleaned at some point
|
|
1328
|
+
// xx self._closedSessions[key] = session;
|
|
1329
|
+
// remove sessionDiagnostics from server.ServerDiagnostics.SessionsDiagnosticsSummary.SessionDiagnosticsSummary
|
|
1330
|
+
delete this._sessions[authenticationToken.toString()];
|
|
1331
|
+
session.dispose();
|
|
1332
|
+
}
|
|
1333
|
+
findSubscription(subscriptionId) {
|
|
1334
|
+
const subscriptions = [];
|
|
1335
|
+
Object.values(this._sessions).map((session) => {
|
|
1336
|
+
if (subscriptions.length) {
|
|
1337
|
+
return;
|
|
1338
|
+
}
|
|
1339
|
+
const subscription = session.publishEngine.getSubscriptionById(subscriptionId);
|
|
1340
|
+
if (subscription) {
|
|
1341
|
+
subscriptions.push(subscription);
|
|
1342
|
+
}
|
|
1343
|
+
});
|
|
1344
|
+
if (subscriptions.length) {
|
|
1345
|
+
(0, node_opcua_assert_1.assert)(subscriptions.length === 1);
|
|
1346
|
+
return subscriptions[0];
|
|
1347
|
+
}
|
|
1348
|
+
return this.findOrphanSubscription(subscriptionId);
|
|
1349
|
+
}
|
|
1350
|
+
findOrphanSubscription(subscriptionId) {
|
|
1351
|
+
if (!this._orphanPublishEngine) {
|
|
1352
|
+
return null;
|
|
1353
|
+
}
|
|
1354
|
+
return this._orphanPublishEngine.getSubscriptionById(subscriptionId);
|
|
1355
|
+
}
|
|
1356
|
+
deleteOrphanSubscription(subscription) {
|
|
1357
|
+
if (!this._orphanPublishEngine) {
|
|
1358
|
+
return node_opcua_status_code_1.StatusCodes.BadInternalError;
|
|
1359
|
+
}
|
|
1360
|
+
(0, node_opcua_assert_1.assert)(this.findSubscription(subscription.id));
|
|
1361
|
+
const c = this._orphanPublishEngine.subscriptionCount;
|
|
1362
|
+
subscription.terminate();
|
|
1363
|
+
subscription.dispose();
|
|
1364
|
+
(0, node_opcua_assert_1.assert)(this._orphanPublishEngine.subscriptionCount === c - 1);
|
|
1365
|
+
return node_opcua_status_code_1.StatusCodes.Good;
|
|
1366
|
+
}
|
|
1367
|
+
/**
|
|
1368
|
+
* @method transferSubscription
|
|
1369
|
+
* @param session {ServerSession} - the new session that will own the subscription
|
|
1370
|
+
* @param subscriptionId {IntegerId} - the subscription Id to transfer
|
|
1371
|
+
* @param sendInitialValues {Boolean} - true if initial values will be resent.
|
|
1372
|
+
* @return {TransferResult}
|
|
1373
|
+
*/
|
|
1374
|
+
transferSubscription(session, subscriptionId, sendInitialValues) {
|
|
1375
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1376
|
+
if (subscriptionId <= 0) {
|
|
1377
|
+
return new node_opcua_service_subscription_1.TransferResult({ statusCode: node_opcua_status_code_1.StatusCodes.BadSubscriptionIdInvalid });
|
|
1378
|
+
}
|
|
1379
|
+
const subscription = this.findSubscription(subscriptionId);
|
|
1380
|
+
if (!subscription) {
|
|
1381
|
+
return new node_opcua_service_subscription_1.TransferResult({ statusCode: node_opcua_status_code_1.StatusCodes.BadSubscriptionIdInvalid });
|
|
1382
|
+
}
|
|
1383
|
+
// check that session have same userIdentity
|
|
1384
|
+
if (!(0, sessions_compatible_for_transfer_1.sessionsCompatibleForTransfer)(subscription.$session, session)) {
|
|
1385
|
+
return new node_opcua_service_subscription_1.TransferResult({ statusCode: node_opcua_status_code_1.StatusCodes.BadUserAccessDenied });
|
|
1386
|
+
}
|
|
1387
|
+
// update diagnostics
|
|
1388
|
+
subscription.subscriptionDiagnostics.transferRequestCount++;
|
|
1389
|
+
// now check that new session has sufficient right
|
|
1390
|
+
// if (session.authenticationToken.toString() !== subscription.authenticationToken.toString()) {
|
|
1391
|
+
// console.log("ServerEngine#transferSubscription => BadUserAccessDenied");
|
|
1392
|
+
// return new TransferResult({ statusCode: StatusCodes.BadUserAccessDenied });
|
|
1393
|
+
// }
|
|
1394
|
+
if (session.publishEngine === subscription.publishEngine) {
|
|
1395
|
+
// subscription is already in this session !!
|
|
1396
|
+
return new node_opcua_service_subscription_1.TransferResult({ statusCode: node_opcua_status_code_1.StatusCodes.BadNothingToDo });
|
|
1397
|
+
}
|
|
1398
|
+
if (session === subscription.$session) {
|
|
1399
|
+
// subscription is already in this session !!
|
|
1400
|
+
return new node_opcua_service_subscription_1.TransferResult({ statusCode: node_opcua_status_code_1.StatusCodes.BadNothingToDo });
|
|
1401
|
+
}
|
|
1402
|
+
// The number of times the subscription has been transferred to an alternate client.
|
|
1403
|
+
subscription.subscriptionDiagnostics.transferredToAltClientCount++;
|
|
1404
|
+
// The number of times the subscription has been transferred to an alternate session for the same client.
|
|
1405
|
+
subscription.subscriptionDiagnostics.transferredToSameClientCount++;
|
|
1406
|
+
const nbSubscriptionBefore = session.publishEngine.subscriptionCount;
|
|
1407
|
+
if (subscription.$session) {
|
|
1408
|
+
subscription.$session._unexposeSubscriptionDiagnostics(subscription);
|
|
1409
|
+
}
|
|
1410
|
+
yield server_publish_engine_1.ServerSidePublishEngine.transferSubscription(subscription, session.publishEngine, sendInitialValues);
|
|
1411
|
+
subscription.$session = session;
|
|
1412
|
+
session._exposeSubscriptionDiagnostics(subscription);
|
|
1413
|
+
(0, node_opcua_assert_1.assert)(subscription.publishEngine === session.publishEngine);
|
|
1414
|
+
// assert(session.publishEngine.subscriptionCount === nbSubscriptionBefore + 1);
|
|
1415
|
+
const result = new node_opcua_service_subscription_1.TransferResult({
|
|
1416
|
+
availableSequenceNumbers: subscription.getAvailableSequenceNumbers(),
|
|
1417
|
+
statusCode: node_opcua_status_code_1.StatusCodes.Good
|
|
1418
|
+
});
|
|
1419
|
+
// istanbul ignore next
|
|
1420
|
+
if (doDebug) {
|
|
1421
|
+
debugLog("TransferResult", result.toString());
|
|
1422
|
+
}
|
|
1423
|
+
return result;
|
|
1424
|
+
});
|
|
1425
|
+
}
|
|
1426
|
+
/**
|
|
1427
|
+
* retrieve a session by its authenticationToken.
|
|
1428
|
+
*
|
|
1429
|
+
* @method getSession
|
|
1430
|
+
* @param authenticationToken
|
|
1431
|
+
* @param activeOnly
|
|
1432
|
+
* @return {ServerSession}
|
|
1433
|
+
*/
|
|
1434
|
+
getSession(authenticationToken, activeOnly) {
|
|
1435
|
+
if (!authenticationToken ||
|
|
1436
|
+
(authenticationToken.identifierType && authenticationToken.identifierType !== node_opcua_nodeid_1.NodeIdType.BYTESTRING)) {
|
|
1437
|
+
return null; // wrong type !
|
|
1438
|
+
}
|
|
1439
|
+
const key = authenticationToken.toString();
|
|
1440
|
+
let session = this._sessions[key];
|
|
1441
|
+
if (!activeOnly && !session) {
|
|
1442
|
+
session = this._closedSessions[key];
|
|
1443
|
+
}
|
|
1444
|
+
return session;
|
|
1445
|
+
}
|
|
1446
|
+
/**
|
|
1447
|
+
*/
|
|
1448
|
+
browsePath(browsePath) {
|
|
1449
|
+
return this.addressSpace.browsePath(browsePath);
|
|
1450
|
+
}
|
|
1451
|
+
/**
|
|
1452
|
+
*
|
|
1453
|
+
* performs a call to ```asyncRefresh``` on all variable nodes that provide an async refresh func.
|
|
1454
|
+
*
|
|
1455
|
+
* @method refreshValues
|
|
1456
|
+
* @param nodesToRefresh {Array<Object>} an array containing the node to consider
|
|
1457
|
+
* Each element of the array shall be of the form { nodeId: <xxx>, attributeIds: <value> }.
|
|
1458
|
+
* @param callback
|
|
1459
|
+
* @param callback.err
|
|
1460
|
+
* @param callback.data an array containing value read
|
|
1461
|
+
* The array length matches the number of nodeIds that are candidate for an async refresh (i.e: nodes that
|
|
1462
|
+
* are of type Variable with asyncRefresh func }
|
|
1463
|
+
*
|
|
1464
|
+
* @async
|
|
1465
|
+
*/
|
|
1466
|
+
refreshValues(nodesToRefresh, maxAge, callback) {
|
|
1467
|
+
const referenceTime = new Date(Date.now() - maxAge);
|
|
1468
|
+
(0, node_opcua_assert_1.assert)(typeof callback === "function");
|
|
1469
|
+
const objectMap = {};
|
|
1470
|
+
for (const nodeToRefresh of nodesToRefresh) {
|
|
1471
|
+
// only consider node for which the caller wants to read the Value attribute
|
|
1472
|
+
// assuming that Value is requested if attributeId is missing,
|
|
1473
|
+
if (nodeToRefresh instanceof node_opcua_types_1.ReadValueId && nodeToRefresh.attributeId !== node_opcua_data_model_1.AttributeIds.Value) {
|
|
1474
|
+
continue;
|
|
1475
|
+
}
|
|
1476
|
+
// ... and that are valid object and instances of Variables ...
|
|
1477
|
+
const obj = this.addressSpace.findNode(nodeToRefresh.nodeId);
|
|
1478
|
+
if (!obj || !(obj.nodeClass === node_opcua_data_model_1.NodeClass.Variable)) {
|
|
1479
|
+
continue;
|
|
1480
|
+
}
|
|
1481
|
+
// ... and that have been declared as asynchronously updating
|
|
1482
|
+
if (typeof obj.refreshFunc !== "function") {
|
|
1483
|
+
continue;
|
|
1484
|
+
}
|
|
1485
|
+
const key = obj.nodeId.toString();
|
|
1486
|
+
if (objectMap[key]) {
|
|
1487
|
+
continue;
|
|
1488
|
+
}
|
|
1489
|
+
objectMap[key] = obj;
|
|
1490
|
+
}
|
|
1491
|
+
const objectArray = Object.values(objectMap);
|
|
1492
|
+
if (objectArray.length === 0) {
|
|
1493
|
+
// nothing to do
|
|
1494
|
+
return callback(null, []);
|
|
1495
|
+
}
|
|
1496
|
+
// perform all asyncRefresh in parallel
|
|
1497
|
+
async.map(objectArray, (obj, inner_callback) => {
|
|
1498
|
+
if (obj.nodeClass !== node_opcua_data_model_1.NodeClass.Variable) {
|
|
1499
|
+
inner_callback(null, new node_opcua_data_value_1.DataValue({
|
|
1500
|
+
statusCode: node_opcua_status_code_1.StatusCodes.BadNodeClassInvalid
|
|
1501
|
+
}));
|
|
1502
|
+
return;
|
|
1503
|
+
}
|
|
1504
|
+
try {
|
|
1505
|
+
obj.asyncRefresh(referenceTime, (err, dataValue) => {
|
|
1506
|
+
inner_callback(err, dataValue);
|
|
1507
|
+
});
|
|
1508
|
+
}
|
|
1509
|
+
catch (err) {
|
|
1510
|
+
// istanbul ignore next
|
|
1511
|
+
if (!(err instanceof Error)) {
|
|
1512
|
+
throw new Error("internal error");
|
|
1513
|
+
}
|
|
1514
|
+
errorLog("asyncRefresh internal error", err);
|
|
1515
|
+
inner_callback(err);
|
|
1516
|
+
}
|
|
1517
|
+
}, (err, arrResult) => {
|
|
1518
|
+
callback(err || null, arrResult);
|
|
1519
|
+
});
|
|
1520
|
+
}
|
|
1521
|
+
_exposeSubscriptionDiagnostics(subscription) {
|
|
1522
|
+
debugLog("ServerEngine#_exposeSubscriptionDiagnostics", subscription.subscriptionId);
|
|
1523
|
+
const subscriptionDiagnosticsArray = this._getServerSubscriptionDiagnosticsArrayNode();
|
|
1524
|
+
const subscriptionDiagnostics = subscription.subscriptionDiagnostics;
|
|
1525
|
+
(0, node_opcua_assert_1.assert)(subscriptionDiagnostics.$subscription === subscription);
|
|
1526
|
+
(0, node_opcua_assert_1.assert)(subscriptionDiagnostics instanceof node_opcua_common_1.SubscriptionDiagnosticsDataType);
|
|
1527
|
+
if (subscriptionDiagnostics && subscriptionDiagnosticsArray) {
|
|
1528
|
+
(0, node_opcua_address_space_1.addElement)(subscriptionDiagnostics, subscriptionDiagnosticsArray);
|
|
1529
|
+
}
|
|
1530
|
+
}
|
|
1531
|
+
_unexposeSubscriptionDiagnostics(subscription) {
|
|
1532
|
+
const serverSubscriptionDiagnosticsArray = this._getServerSubscriptionDiagnosticsArrayNode();
|
|
1533
|
+
const subscriptionDiagnostics = subscription.subscriptionDiagnostics;
|
|
1534
|
+
(0, node_opcua_assert_1.assert)(subscriptionDiagnostics instanceof node_opcua_common_1.SubscriptionDiagnosticsDataType);
|
|
1535
|
+
if (subscriptionDiagnostics && serverSubscriptionDiagnosticsArray) {
|
|
1536
|
+
const node = serverSubscriptionDiagnosticsArray[subscription.id];
|
|
1537
|
+
(0, node_opcua_address_space_1.removeElement)(serverSubscriptionDiagnosticsArray, subscriptionDiagnostics);
|
|
1538
|
+
/*assert(
|
|
1539
|
+
!(subscriptionDiagnosticsArray as any)[subscription.id],
|
|
1540
|
+
" subscription node must have been removed from subscriptionDiagnosticsArray"
|
|
1541
|
+
);
|
|
1542
|
+
*/
|
|
1543
|
+
}
|
|
1544
|
+
debugLog("ServerEngine#_unexposeSubscriptionDiagnostics", subscription.subscriptionId);
|
|
1545
|
+
}
|
|
1546
|
+
/**
|
|
1547
|
+
* create a new subscription
|
|
1548
|
+
* @return {Subscription}
|
|
1549
|
+
*/
|
|
1550
|
+
_createSubscriptionOnSession(session, request) {
|
|
1551
|
+
(0, node_opcua_assert_1.assert)(Object.prototype.hasOwnProperty.call(request, "requestedPublishingInterval")); // Duration
|
|
1552
|
+
(0, node_opcua_assert_1.assert)(Object.prototype.hasOwnProperty.call(request, "requestedLifetimeCount")); // Counter
|
|
1553
|
+
(0, node_opcua_assert_1.assert)(Object.prototype.hasOwnProperty.call(request, "requestedMaxKeepAliveCount")); // Counter
|
|
1554
|
+
(0, node_opcua_assert_1.assert)(Object.prototype.hasOwnProperty.call(request, "maxNotificationsPerPublish")); // Counter
|
|
1555
|
+
(0, node_opcua_assert_1.assert)(Object.prototype.hasOwnProperty.call(request, "publishingEnabled")); // Boolean
|
|
1556
|
+
(0, node_opcua_assert_1.assert)(Object.prototype.hasOwnProperty.call(request, "priority")); // Byte
|
|
1557
|
+
// adjust publishing parameters
|
|
1558
|
+
const publishingInterval = request.requestedPublishingInterval || 0;
|
|
1559
|
+
const maxKeepAliveCount = request.requestedMaxKeepAliveCount || 0;
|
|
1560
|
+
const lifeTimeCount = request.requestedLifetimeCount || 0;
|
|
1561
|
+
const subscription = new server_subscription_1.Subscription({
|
|
1562
|
+
id: _get_next_subscriptionId(),
|
|
1563
|
+
lifeTimeCount,
|
|
1564
|
+
maxKeepAliveCount,
|
|
1565
|
+
maxNotificationsPerPublish: request.maxNotificationsPerPublish,
|
|
1566
|
+
priority: request.priority || 0,
|
|
1567
|
+
publishEngine: session.publishEngine,
|
|
1568
|
+
publishingEnabled: request.publishingEnabled,
|
|
1569
|
+
publishingInterval,
|
|
1570
|
+
// -------------------
|
|
1571
|
+
sessionId: node_opcua_nodeid_1.NodeId.nullNodeId,
|
|
1572
|
+
globalCounter: this._globalCounter,
|
|
1573
|
+
serverCapabilities: this.serverCapabilities // shared
|
|
1574
|
+
});
|
|
1575
|
+
// add subscriptionDiagnostics
|
|
1576
|
+
this._exposeSubscriptionDiagnostics(subscription);
|
|
1577
|
+
(0, node_opcua_assert_1.assert)(subscription.publishEngine === session.publishEngine);
|
|
1578
|
+
session.publishEngine.add_subscription(subscription);
|
|
1579
|
+
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
1580
|
+
const engine = this;
|
|
1581
|
+
subscription.once("terminated", function () {
|
|
1582
|
+
engine._unexposeSubscriptionDiagnostics(this);
|
|
1583
|
+
});
|
|
1584
|
+
return subscription;
|
|
1585
|
+
}
|
|
1586
|
+
__findNode(nodeId) {
|
|
1587
|
+
var _a;
|
|
1588
|
+
if (nodeId.namespace >= (((_a = this.addressSpace) === null || _a === void 0 ? void 0 : _a.getNamespaceArray().length) || 0)) {
|
|
1589
|
+
return null;
|
|
1590
|
+
}
|
|
1591
|
+
const namespace = this.addressSpace.getNamespace(nodeId.namespace);
|
|
1592
|
+
return namespace.findNode2(nodeId);
|
|
1593
|
+
}
|
|
1594
|
+
_readSingleNode(context, nodeToRead, timestampsToReturn) {
|
|
1595
|
+
(0, node_opcua_assert_1.assert)(context instanceof node_opcua_address_space_1.SessionContext);
|
|
1596
|
+
const nodeId = nodeToRead.nodeId;
|
|
1597
|
+
const attributeId = nodeToRead.attributeId;
|
|
1598
|
+
const indexRange = nodeToRead.indexRange;
|
|
1599
|
+
const dataEncoding = nodeToRead.dataEncoding;
|
|
1600
|
+
if (timestampsToReturn === node_opcua_service_read_1.TimestampsToReturn.Invalid) {
|
|
1601
|
+
return new node_opcua_data_value_1.DataValue({ statusCode: node_opcua_status_code_1.StatusCodes.BadTimestampsToReturnInvalid });
|
|
1602
|
+
}
|
|
1603
|
+
timestampsToReturn = (0, node_opcua_data_value_1.coerceTimestampsToReturn)(timestampsToReturn);
|
|
1604
|
+
const obj = this.__findNode(nodeId);
|
|
1605
|
+
let dataValue;
|
|
1606
|
+
if (!obj) {
|
|
1607
|
+
// may be return BadNodeIdUnknown in dataValue instead ?
|
|
1608
|
+
// Object Not Found
|
|
1609
|
+
return new node_opcua_data_value_1.DataValue({ statusCode: node_opcua_status_code_1.StatusCodes.BadNodeIdUnknown });
|
|
1610
|
+
}
|
|
1611
|
+
else {
|
|
1612
|
+
// check access
|
|
1613
|
+
// BadUserAccessDenied
|
|
1614
|
+
// BadNotReadable
|
|
1615
|
+
// invalid attributes : BadNodeAttributesInvalid
|
|
1616
|
+
// invalid range : BadIndexRangeInvalid
|
|
1617
|
+
dataValue = obj.readAttribute(context, attributeId, indexRange, dataEncoding);
|
|
1618
|
+
dataValue = (0, node_opcua_data_value_1.apply_timestamps_no_copy)(dataValue, timestampsToReturn, attributeId);
|
|
1619
|
+
return dataValue;
|
|
1620
|
+
}
|
|
1621
|
+
}
|
|
1622
|
+
_historyReadSingleNode(context, nodeToRead, historyReadDetails, timestampsToReturn, continuationData, callback) {
|
|
1623
|
+
(0, node_opcua_assert_1.assert)(context instanceof node_opcua_address_space_1.SessionContext);
|
|
1624
|
+
(0, node_opcua_assert_1.assert)(typeof callback === "function");
|
|
1625
|
+
const nodeId = nodeToRead.nodeId;
|
|
1626
|
+
const indexRange = nodeToRead.indexRange;
|
|
1627
|
+
const dataEncoding = nodeToRead.dataEncoding;
|
|
1628
|
+
const continuationPoint = nodeToRead.continuationPoint;
|
|
1629
|
+
timestampsToReturn = (0, node_opcua_data_value_1.coerceTimestampsToReturn)(timestampsToReturn);
|
|
1630
|
+
if (timestampsToReturn === node_opcua_service_read_1.TimestampsToReturn.Invalid) {
|
|
1631
|
+
return callback(null, new node_opcua_service_history_1.HistoryReadResult({ statusCode: node_opcua_status_code_1.StatusCodes.BadTimestampsToReturnInvalid }));
|
|
1632
|
+
}
|
|
1633
|
+
const obj = this.__findNode(nodeId);
|
|
1634
|
+
if (!obj) {
|
|
1635
|
+
// may be return BadNodeIdUnknown in dataValue instead ?
|
|
1636
|
+
// Object Not Found
|
|
1637
|
+
callback(null, new node_opcua_service_history_1.HistoryReadResult({ statusCode: node_opcua_status_code_1.StatusCodes.BadNodeIdUnknown }));
|
|
1638
|
+
return;
|
|
1639
|
+
}
|
|
1640
|
+
else {
|
|
1641
|
+
// istanbul ignore next
|
|
1642
|
+
if (!obj.historyRead) {
|
|
1643
|
+
// note : Object and View may also support historyRead to provide Event historical data
|
|
1644
|
+
// todo implement historyRead for Object and View
|
|
1645
|
+
const msg = " this node doesn't provide historyRead! probably not a UAVariable\n " +
|
|
1646
|
+
obj.nodeId.toString() +
|
|
1647
|
+
" " +
|
|
1648
|
+
obj.browseName.toString() +
|
|
1649
|
+
"\n" +
|
|
1650
|
+
"with " +
|
|
1651
|
+
nodeToRead.toString() +
|
|
1652
|
+
"\n" +
|
|
1653
|
+
"HistoryReadDetails " +
|
|
1654
|
+
historyReadDetails.toString();
|
|
1655
|
+
if (doDebug) {
|
|
1656
|
+
debugLog(chalk.cyan("ServerEngine#_historyReadSingleNode "), chalk.white.bold(msg));
|
|
1657
|
+
}
|
|
1658
|
+
const err = new Error(msg);
|
|
1659
|
+
// object has no historyRead method
|
|
1660
|
+
setImmediate(callback.bind(null, err));
|
|
1661
|
+
return;
|
|
1662
|
+
}
|
|
1663
|
+
// check access
|
|
1664
|
+
// BadUserAccessDenied
|
|
1665
|
+
// BadNotReadable
|
|
1666
|
+
// invalid attributes : BadNodeAttributesInvalid
|
|
1667
|
+
// invalid range : BadIndexRangeInvalid
|
|
1668
|
+
obj.historyRead(context, historyReadDetails, indexRange, dataEncoding, continuationData, (err, result) => {
|
|
1669
|
+
if (err || !result) {
|
|
1670
|
+
return callback(err);
|
|
1671
|
+
}
|
|
1672
|
+
(0, node_opcua_assert_1.assert)(result.statusCode instanceof node_opcua_status_code_1.StatusCode);
|
|
1673
|
+
(0, node_opcua_assert_1.assert)(result.isValid());
|
|
1674
|
+
// result = apply_timestamps(result, timestampsToReturn, attributeId);
|
|
1675
|
+
callback(err, result);
|
|
1676
|
+
});
|
|
1677
|
+
}
|
|
1678
|
+
}
|
|
1679
|
+
/**
|
|
1680
|
+
*/
|
|
1681
|
+
__internal_bindMethod(nodeId, func) {
|
|
1682
|
+
(0, node_opcua_assert_1.assert)(typeof func === "function");
|
|
1683
|
+
(0, node_opcua_assert_1.assert)(nodeId instanceof node_opcua_nodeid_1.NodeId);
|
|
1684
|
+
const methodNode = this.addressSpace.findNode(nodeId);
|
|
1685
|
+
if (!methodNode) {
|
|
1686
|
+
return;
|
|
1687
|
+
}
|
|
1688
|
+
// istanbul ignore else
|
|
1689
|
+
if (methodNode && methodNode.bindMethod) {
|
|
1690
|
+
methodNode.bindMethod(func);
|
|
1691
|
+
}
|
|
1692
|
+
else {
|
|
1693
|
+
warningLog(chalk.yellow("WARNING: cannot bind a method with id ") +
|
|
1694
|
+
chalk.cyan(nodeId.toString()) +
|
|
1695
|
+
chalk.yellow(". please check your nodeset.xml file or add this node programmatically"));
|
|
1696
|
+
warningLog((0, node_opcua_debug_1.traceFromThisProjectOnly)());
|
|
1697
|
+
}
|
|
1698
|
+
}
|
|
1699
|
+
_getServerSubscriptionDiagnosticsArrayNode() {
|
|
1700
|
+
// istanbul ignore next
|
|
1701
|
+
if (!this.addressSpace) {
|
|
1702
|
+
doDebug && debugLog("ServerEngine#_getServerSubscriptionDiagnosticsArray : no addressSpace");
|
|
1703
|
+
return null; // no addressSpace
|
|
1704
|
+
}
|
|
1705
|
+
const subscriptionDiagnosticsType = this.addressSpace.findVariableType("SubscriptionDiagnosticsType");
|
|
1706
|
+
if (!subscriptionDiagnosticsType) {
|
|
1707
|
+
doDebug &&
|
|
1708
|
+
debugLog("ServerEngine#_getServerSubscriptionDiagnosticsArray " + ": cannot find SubscriptionDiagnosticsType");
|
|
1709
|
+
}
|
|
1710
|
+
// SubscriptionDiagnosticsArray = i=2290
|
|
1711
|
+
const subscriptionDiagnosticsArrayNode = this.addressSpace.findNode((0, node_opcua_nodeid_1.makeNodeId)(node_opcua_constants_1.VariableIds.Server_ServerDiagnostics_SubscriptionDiagnosticsArray));
|
|
1712
|
+
return subscriptionDiagnosticsArrayNode;
|
|
1713
|
+
}
|
|
1714
|
+
}
|
|
1715
|
+
exports.ServerEngine = ServerEngine;
|
|
1716
|
+
ServerEngine.registry = new node_opcua_object_registry_1.ObjectRegistry();
|
|
1717
1717
|
//# sourceMappingURL=server_engine.js.map
|