node-opcua-samples 2.66.3 → 2.68.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/di_server.js +5 -6
- package/bin/interactive_client.js +6 -6
- package/bin/simple_client.js +1 -1
- package/bin/simple_client_ts.ts +1 -1
- package/bin/simple_server.js +516 -505
- package/dist/simple_client_ts.js +1 -1
- package/dist/simple_client_ts.js.map +1 -1
- package/package.json +4 -4
package/bin/simple_server.js
CHANGED
|
@@ -4,70 +4,70 @@
|
|
|
4
4
|
"use strict";
|
|
5
5
|
const path = require("path");
|
|
6
6
|
const fs = require("fs");
|
|
7
|
+
const os = require("os");
|
|
7
8
|
const assert = require("assert");
|
|
8
9
|
const chalk = require("chalk");
|
|
9
10
|
const yargs = require("yargs/yargs");
|
|
10
11
|
const envPaths = require("env-paths");
|
|
11
12
|
|
|
12
13
|
const {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
14
|
+
OPCUAServer,
|
|
15
|
+
OPCUACertificateManager,
|
|
16
|
+
Variant,
|
|
17
|
+
DataType,
|
|
18
|
+
VariantArrayType,
|
|
19
|
+
DataValue,
|
|
20
|
+
standardUnits,
|
|
21
|
+
makeApplicationUrn,
|
|
22
|
+
nodesets,
|
|
23
|
+
install_optional_cpu_and_memory_usage_node,
|
|
24
|
+
build_address_space_for_conformance_testing,
|
|
25
|
+
RegisterServerMethod,
|
|
26
|
+
extractFullyQualifiedDomainName,
|
|
27
|
+
makeRoles,
|
|
28
|
+
WellKnownRoles
|
|
26
29
|
} = require("node-opcua");
|
|
27
30
|
|
|
28
31
|
Error.stackTraceLimit = Infinity;
|
|
29
32
|
|
|
30
|
-
|
|
31
33
|
const argv = yargs(process.argv)
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
.string("alternateHostname")
|
|
35
|
-
.describe("alternateHostname")
|
|
34
|
+
.wrap(132)
|
|
36
35
|
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
.string("alternateHostname")
|
|
37
|
+
.describe("alternateHostname")
|
|
39
38
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
.default("maxAllowedSessionNumber", 500)
|
|
39
|
+
.number("port")
|
|
40
|
+
.default("port", 26543)
|
|
43
41
|
|
|
44
|
-
|
|
45
|
-
|
|
42
|
+
.number("maxAllowedSessionNumber")
|
|
43
|
+
.describe("maxAllowedSessionNumber", "the maximum number of concurrent client session that the server will accept")
|
|
44
|
+
.default("maxAllowedSessionNumber", 500)
|
|
46
45
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
.describe("silent", "no trace")
|
|
46
|
+
.number("maxAllowedSubscriptionNumber")
|
|
47
|
+
.describe("maxAllowedSubscriptionNumber", "the maximum number of concurrent subscriptions")
|
|
50
48
|
|
|
49
|
+
.boolean("silent")
|
|
50
|
+
.default("silent", false)
|
|
51
|
+
.describe("silent", "no trace")
|
|
51
52
|
|
|
52
|
-
|
|
53
|
-
|
|
53
|
+
.string("alternateHostname")
|
|
54
|
+
.default("alternateHostname", null)
|
|
54
55
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
56
|
+
.number("keySize")
|
|
57
|
+
.describe("keySize", "certificate keySize [1024|2048|3072|4096]")
|
|
58
|
+
.default("keySize", 2048)
|
|
59
|
+
.alias("k", "keySize")
|
|
59
60
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
61
|
+
.string("applicationName")
|
|
62
|
+
.describe("applicationName", "the application name")
|
|
63
|
+
.default("applicationName", "NodeOPCUA-Server")
|
|
63
64
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
65
|
+
.alias("a", "alternateHostname")
|
|
66
|
+
.alias("m", "maxAllowedSessionNumber")
|
|
67
|
+
.alias("n", "applicationName")
|
|
68
|
+
.alias("p", "port")
|
|
68
69
|
|
|
69
|
-
|
|
70
|
-
.argv;
|
|
70
|
+
.help(true).argv;
|
|
71
71
|
|
|
72
72
|
const port = argv.port;
|
|
73
73
|
const maxAllowedSessionNumber = argv.maxAllowedSessionNumber;
|
|
@@ -75,47 +75,60 @@ const maxConnectionsPerEndpoint = maxAllowedSessionNumber;
|
|
|
75
75
|
const maxAllowedSubscriptionNumber = argv.maxAllowedSubscriptionNumber || 50;
|
|
76
76
|
OPCUAServer.MAX_SUBSCRIPTION = maxAllowedSubscriptionNumber;
|
|
77
77
|
|
|
78
|
-
|
|
79
|
-
const os = require('os');
|
|
80
|
-
|
|
81
78
|
async function getIpAddresses() {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
79
|
+
const ipAddresses = [];
|
|
80
|
+
const interfaces = os.networkInterfaces();
|
|
81
|
+
Object.keys(interfaces).forEach(function (interfaceName) {
|
|
82
|
+
let alias = 0;
|
|
83
|
+
|
|
84
|
+
interfaces[interfaceName].forEach(function (iface) {
|
|
85
|
+
if ("IPv4" !== iface.family || iface.internal !== false) {
|
|
86
|
+
// skip over internal (i.e. 127.0.0.1) and non-ipv4 addresses
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
if (alias >= 1) {
|
|
90
|
+
// this single interface has multiple ipv4 addresses
|
|
91
|
+
console.log(interfaceName + ":" + alias, iface.address);
|
|
92
|
+
ipAddresses.push(iface.address);
|
|
93
|
+
} else {
|
|
94
|
+
// this interface has only one ipv4 address
|
|
95
|
+
console.log(interfaceName, iface.address);
|
|
96
|
+
ipAddresses.push(iface.address);
|
|
97
|
+
}
|
|
98
|
+
++alias;
|
|
99
|
+
});
|
|
103
100
|
});
|
|
104
|
-
|
|
105
|
-
return ipAddresses;
|
|
101
|
+
return ipAddresses;
|
|
106
102
|
}
|
|
107
103
|
|
|
108
|
-
const
|
|
109
|
-
|
|
104
|
+
const users = [
|
|
105
|
+
{
|
|
106
|
+
username: "user1",
|
|
107
|
+
password: "password1",
|
|
108
|
+
role: makeRoles([WellKnownRoles.AuthenticatedUser, WellKnownRoles.ConfigureAdmin])
|
|
109
|
+
},
|
|
110
|
+
{ username: "user2", password: "password2", role: makeRoles([WellKnownRoles.AuthenticatedUser, WellKnownRoles.Operator]) }
|
|
111
|
+
];
|
|
110
112
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
113
|
+
const userManager = {
|
|
114
|
+
isValidUser(username, password) {
|
|
115
|
+
const uIndex = users.findIndex((x) => x.username === username);
|
|
116
|
+
if (uIndex < 0) {
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
if (users[uIndex].password !== password) {
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
return true;
|
|
123
|
+
},
|
|
124
|
+
getUserRoles(username) {
|
|
125
|
+
const uIndex = users.findIndex((x) => x.username === username);
|
|
126
|
+
if (uIndex < 0) {
|
|
127
|
+
return [];
|
|
128
|
+
}
|
|
129
|
+
const userRole = users[uIndex].role;
|
|
130
|
+
return userRole;
|
|
116
131
|
}
|
|
117
|
-
return false;
|
|
118
|
-
}
|
|
119
132
|
};
|
|
120
133
|
|
|
121
134
|
const keySize = argv.keySize;
|
|
@@ -125,464 +138,462 @@ const productUri = argv.applicationName || "NodeOPCUASample-Simple-Server";
|
|
|
125
138
|
const paths = envPaths(productUri);
|
|
126
139
|
|
|
127
140
|
(async function main() {
|
|
141
|
+
const fqdn = await extractFullyQualifiedDomainName();
|
|
142
|
+
console.log("FQDN = ", fqdn);
|
|
143
|
+
|
|
144
|
+
const applicationUri = makeApplicationUrn(fqdn, productUri);
|
|
145
|
+
// -----------------------------------------------
|
|
146
|
+
const configFolder = paths.config;
|
|
147
|
+
const pkiFolder = path.join(configFolder, "PKI");
|
|
148
|
+
const userPkiFolder = path.join(configFolder, "UserPKI");
|
|
149
|
+
|
|
150
|
+
const userCertificateManager = new OPCUACertificateManager({
|
|
151
|
+
automaticallyAcceptUnknownCertificate: true,
|
|
152
|
+
name: "UserPKI",
|
|
153
|
+
rootFolder: userPkiFolder
|
|
154
|
+
});
|
|
155
|
+
await userCertificateManager.initialize();
|
|
128
156
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
const configFolder = paths.config;
|
|
135
|
-
const pkiFolder = path.join(configFolder, "PKI");
|
|
136
|
-
const userPkiFolder = path.join(configFolder, "UserPKI");
|
|
137
|
-
|
|
138
|
-
const userCertificateManager = new OPCUACertificateManager({
|
|
139
|
-
automaticallyAcceptUnknownCertificate: true,
|
|
140
|
-
name: "UserPKI",
|
|
141
|
-
rootFolder: userPkiFolder,
|
|
142
|
-
});
|
|
143
|
-
await userCertificateManager.initialize();
|
|
144
|
-
|
|
145
|
-
const serverCertificateManager = new OPCUACertificateManager({
|
|
146
|
-
automaticallyAcceptUnknownCertificate: true,
|
|
147
|
-
name: "PKI",
|
|
148
|
-
rootFolder: pkiFolder,
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
await serverCertificateManager.initialize();
|
|
152
|
-
|
|
153
|
-
const certificateFile = path.join(pkiFolder, `server_certificate1.pem`);
|
|
154
|
-
if (!fs.existsSync(certificateFile)) {
|
|
155
|
-
|
|
156
|
-
console.log("Creating self-signed certificate");
|
|
157
|
-
|
|
158
|
-
await serverCertificateManager.createSelfSignedCertificate({
|
|
159
|
-
applicationUri: applicationUri,
|
|
160
|
-
dns: argv.alternateHostname ? [argv.alternateHostname, fqdn] : [fqdn],
|
|
161
|
-
ip: await getIpAddresses(),
|
|
162
|
-
outputFile: certificateFile,
|
|
163
|
-
subject: "/CN=Sterfive/DC=Test",
|
|
164
|
-
startDate: new Date(),
|
|
165
|
-
validity: 365 * 10,
|
|
166
|
-
})
|
|
167
|
-
}
|
|
168
|
-
assert(fs.existsSync(certificateFile));
|
|
169
|
-
// ------------------------------------------------------------------
|
|
170
|
-
|
|
171
|
-
const server_options = {
|
|
172
|
-
|
|
173
|
-
serverCertificateManager,
|
|
174
|
-
certificateFile,
|
|
175
|
-
|
|
176
|
-
userCertificateManager,
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
port,
|
|
180
|
-
|
|
181
|
-
maxAllowedSessionNumber: maxAllowedSessionNumber,
|
|
182
|
-
maxConnectionsPerEndpoint: maxConnectionsPerEndpoint,
|
|
183
|
-
|
|
184
|
-
nodeset_filename: [
|
|
185
|
-
nodesets.standard,
|
|
186
|
-
nodesets.di
|
|
187
|
-
],
|
|
188
|
-
|
|
189
|
-
serverInfo: {
|
|
190
|
-
applicationName: { text: "NodeOPCUA", locale: "en" },
|
|
191
|
-
applicationUri: applicationUri,
|
|
192
|
-
gatewayServerUri: null,
|
|
193
|
-
productUri: productUri,
|
|
194
|
-
discoveryProfileUri: null,
|
|
195
|
-
discoveryUrls: []
|
|
196
|
-
},
|
|
197
|
-
buildInfo: {
|
|
198
|
-
buildNumber: "1234"
|
|
199
|
-
},
|
|
200
|
-
serverCapabilities: {
|
|
201
|
-
maxBrowseContinuationPoints: 10,
|
|
202
|
-
maxHistoryContinuationPoints: 10,
|
|
203
|
-
// maxInactiveLockTime
|
|
204
|
-
operationLimits: {
|
|
205
|
-
maxNodesPerRead: 1000,
|
|
206
|
-
maxNodesPerWrite: 1000,
|
|
207
|
-
maxNodesPerHistoryReadData: 100,
|
|
208
|
-
maxNodesPerBrowse: 1000,
|
|
209
|
-
maxNodesPerMethodCall: 200,
|
|
210
|
-
}
|
|
211
|
-
},
|
|
212
|
-
userManager: userManager,
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
isAuditing: false,
|
|
216
|
-
//xx registerServerMethod: RegisterServerMethod.HIDDEN,
|
|
217
|
-
//xx registerServerMethod: RegisterServerMethod.MDNS,
|
|
218
|
-
registerServerMethod: RegisterServerMethod.LDS,
|
|
219
|
-
discoveryServerEndpointUrl: "opc.tcp://localhost:4840",
|
|
220
|
-
|
|
221
|
-
};
|
|
157
|
+
const serverCertificateManager = new OPCUACertificateManager({
|
|
158
|
+
automaticallyAcceptUnknownCertificate: true,
|
|
159
|
+
name: "PKI",
|
|
160
|
+
rootFolder: pkiFolder
|
|
161
|
+
});
|
|
222
162
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
163
|
+
await serverCertificateManager.initialize();
|
|
164
|
+
|
|
165
|
+
const certificateFile = path.join(pkiFolder, `server_certificate1.pem`);
|
|
166
|
+
if (!fs.existsSync(certificateFile)) {
|
|
167
|
+
console.log("Creating self-signed certificate");
|
|
168
|
+
|
|
169
|
+
await serverCertificateManager.createSelfSignedCertificate({
|
|
170
|
+
applicationUri: applicationUri,
|
|
171
|
+
dns: argv.alternateHostname ? [argv.alternateHostname, fqdn] : [fqdn],
|
|
172
|
+
ip: await getIpAddresses(),
|
|
173
|
+
outputFile: certificateFile,
|
|
174
|
+
subject: "/CN=Sterfive/DC=Test",
|
|
175
|
+
startDate: new Date(),
|
|
176
|
+
validity: 365 * 10
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
assert(fs.existsSync(certificateFile));
|
|
180
|
+
// ------------------------------------------------------------------
|
|
181
|
+
|
|
182
|
+
const server_options = {
|
|
183
|
+
serverCertificateManager,
|
|
184
|
+
certificateFile,
|
|
185
|
+
|
|
186
|
+
userCertificateManager,
|
|
187
|
+
|
|
188
|
+
port,
|
|
189
|
+
|
|
190
|
+
maxAllowedSessionNumber: maxAllowedSessionNumber,
|
|
191
|
+
maxConnectionsPerEndpoint: maxConnectionsPerEndpoint,
|
|
192
|
+
|
|
193
|
+
nodeset_filename: [nodesets.standard, nodesets.di],
|
|
194
|
+
|
|
195
|
+
serverInfo: {
|
|
196
|
+
applicationName: { text: "NodeOPCUA", locale: "en" },
|
|
197
|
+
applicationUri: applicationUri,
|
|
198
|
+
gatewayServerUri: null,
|
|
199
|
+
productUri: productUri,
|
|
200
|
+
discoveryProfileUri: null,
|
|
201
|
+
discoveryUrls: []
|
|
202
|
+
},
|
|
203
|
+
buildInfo: {
|
|
204
|
+
buildNumber: "1234"
|
|
205
|
+
},
|
|
206
|
+
serverCapabilities: {
|
|
207
|
+
maxBrowseContinuationPoints: 10,
|
|
208
|
+
maxHistoryContinuationPoints: 10,
|
|
209
|
+
// maxInactiveLockTime
|
|
210
|
+
operationLimits: {
|
|
211
|
+
maxNodesPerRead: 1000,
|
|
212
|
+
maxNodesPerWrite: 1000,
|
|
213
|
+
maxNodesPerHistoryReadData: 100,
|
|
214
|
+
maxNodesPerBrowse: 1000,
|
|
215
|
+
maxNodesPerMethodCall: 200
|
|
216
|
+
}
|
|
217
|
+
},
|
|
218
|
+
userManager: userManager,
|
|
219
|
+
|
|
220
|
+
isAuditing: false,
|
|
221
|
+
//xx registerServerMethod: RegisterServerMethod.HIDDEN,
|
|
222
|
+
//xx registerServerMethod: RegisterServerMethod.MDNS,
|
|
223
|
+
registerServerMethod: RegisterServerMethod.LDS,
|
|
224
|
+
discoveryServerEndpointUrl: "opc.tcp://localhost:4840"
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
process.title = "Node OPCUA Server on port : " + server_options.port;
|
|
228
|
+
server_options.alternateHostname = argv.alternateHostname;
|
|
229
|
+
const server = new OPCUAServer(server_options);
|
|
230
|
+
|
|
231
|
+
const hostname = require("os").hostname();
|
|
232
|
+
|
|
233
|
+
await server.initialize();
|
|
234
|
+
|
|
235
|
+
function post_initialize() {
|
|
236
|
+
const addressSpace = server.engine.addressSpace;
|
|
237
|
+
|
|
238
|
+
build_address_space_for_conformance_testing(addressSpace);
|
|
239
|
+
|
|
240
|
+
install_optional_cpu_and_memory_usage_node(server);
|
|
241
|
+
|
|
242
|
+
addressSpace.installAlarmsAndConditionsService();
|
|
243
|
+
|
|
244
|
+
const rootFolder = addressSpace.findNode("RootFolder");
|
|
245
|
+
assert(rootFolder.browseName.toString() === "Root");
|
|
246
|
+
|
|
247
|
+
const namespace = addressSpace.getOwnNamespace();
|
|
248
|
+
|
|
249
|
+
const myDevices = namespace.addFolder(rootFolder.objects, { browseName: "MyDevices" });
|
|
250
|
+
|
|
251
|
+
/*
|
|
252
|
+
* variation 0:
|
|
253
|
+
* ------------
|
|
254
|
+
*
|
|
255
|
+
* Add a variable in folder using a raw Variant.
|
|
256
|
+
* Use this variation when the variable has to be read or written by the OPCUA clients
|
|
257
|
+
*/
|
|
258
|
+
const variable0 = namespace.addVariable({
|
|
259
|
+
organizedBy: myDevices,
|
|
260
|
+
browseName: "FanSpeed",
|
|
261
|
+
nodeId: "ns=1;s=FanSpeed",
|
|
262
|
+
dataType: "Double",
|
|
263
|
+
value: new Variant({ dataType: DataType.Double, value: 1000.0 })
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
setInterval(function () {
|
|
267
|
+
const fluctuation = Math.random() * 100 - 50;
|
|
268
|
+
variable0.setValueFromSource(new Variant({ dataType: DataType.Double, value: 1000.0 + fluctuation }));
|
|
269
|
+
}, 10);
|
|
270
|
+
|
|
271
|
+
/*
|
|
272
|
+
* variation 1:
|
|
273
|
+
* ------------
|
|
274
|
+
*
|
|
275
|
+
* Add a variable in folder using a single get function which returns the up to date variable value in Variant.
|
|
276
|
+
* The server will set the timestamps automatically for us.
|
|
277
|
+
* Use this variation when the variable value is controlled by the getter function
|
|
278
|
+
* Avoid using this variation if the variable has to be made writable, as the server will call the getter
|
|
279
|
+
* function prior to returning its value upon client read requests.
|
|
280
|
+
*/
|
|
281
|
+
namespace.addVariable({
|
|
282
|
+
organizedBy: myDevices,
|
|
283
|
+
browseName: "PumpSpeed",
|
|
284
|
+
nodeId: "ns=1;s=PumpSpeed",
|
|
285
|
+
dataType: "Double",
|
|
286
|
+
value: {
|
|
287
|
+
/**
|
|
288
|
+
* returns the current value as a Variant
|
|
289
|
+
* @method get
|
|
290
|
+
* @return {Variant}
|
|
291
|
+
*/
|
|
292
|
+
get: function () {
|
|
293
|
+
const pump_speed = 200 + 100 * Math.sin(Date.now() / 10000);
|
|
294
|
+
return new Variant({ dataType: DataType.Double, value: pump_speed });
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
namespace.addVariable({
|
|
300
|
+
organizedBy: myDevices,
|
|
301
|
+
browseName: "SomeDate",
|
|
302
|
+
nodeId: "ns=1;s=SomeDate",
|
|
303
|
+
dataType: "DateTime",
|
|
304
|
+
value: {
|
|
305
|
+
get: function () {
|
|
306
|
+
return new Variant({ dataType: DataType.DateTime, value: new Date(Date.UTC(2016, 9, 13, 8, 40, 0)) });
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
/*
|
|
312
|
+
* variation 2:
|
|
313
|
+
* ------------
|
|
314
|
+
*
|
|
315
|
+
* Add a variable in folder. This variable gets its value and source timestamps from the provided function.
|
|
316
|
+
* The value and source timestamps are held in a external object.
|
|
317
|
+
* The value and source timestamps are updated on a regular basis using a timer function.
|
|
318
|
+
*/
|
|
319
|
+
const external_value_with_sourceTimestamp = new DataValue({
|
|
320
|
+
value: new Variant({ dataType: DataType.Double, value: 10.0 }),
|
|
321
|
+
sourceTimestamp: null,
|
|
322
|
+
sourcePicoseconds: 0
|
|
323
|
+
});
|
|
324
|
+
setInterval(function () {
|
|
325
|
+
external_value_with_sourceTimestamp.value.value = Math.random();
|
|
326
|
+
external_value_with_sourceTimestamp.sourceTimestamp = new Date();
|
|
327
|
+
}, 1000);
|
|
328
|
+
|
|
329
|
+
namespace.addVariable({
|
|
330
|
+
organizedBy: myDevices,
|
|
331
|
+
browseName: "Pressure",
|
|
332
|
+
nodeId: "ns=1;s=Pressure",
|
|
333
|
+
dataType: "Double",
|
|
334
|
+
value: {
|
|
335
|
+
timestamped_get: function () {
|
|
336
|
+
return external_value_with_sourceTimestamp;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
/*
|
|
342
|
+
* variation 3:
|
|
343
|
+
* ------------
|
|
344
|
+
*
|
|
345
|
+
* Add a variable in a folder. This variable gets its value and source timestamps from the provided
|
|
346
|
+
* asynchronous function.
|
|
347
|
+
* The asynchronous function is called only when needed by the opcua Server read services and monitored item services
|
|
348
|
+
*
|
|
349
|
+
*/
|
|
226
350
|
|
|
227
|
-
|
|
351
|
+
namespace.addVariable({
|
|
352
|
+
organizedBy: myDevices,
|
|
353
|
+
browseName: "Temperature",
|
|
354
|
+
nodeId: "s=Temperature",
|
|
355
|
+
dataType: "Double",
|
|
356
|
+
|
|
357
|
+
value: {
|
|
358
|
+
refreshFunc: function (callback) {
|
|
359
|
+
const temperature = 20 + 10 * Math.sin(Date.now() / 10000);
|
|
360
|
+
const value = new Variant({ dataType: DataType.Double, value: temperature });
|
|
361
|
+
const sourceTimestamp = new Date();
|
|
362
|
+
|
|
363
|
+
// simulate a asynchronous behaviour
|
|
364
|
+
setTimeout(function () {
|
|
365
|
+
callback(null, new DataValue({ value: value, sourceTimestamp: sourceTimestamp }));
|
|
366
|
+
}, 100);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
// UAAnalogItem
|
|
372
|
+
// add a UAAnalogItem
|
|
373
|
+
const node = namespace.addAnalogDataItem({
|
|
374
|
+
organizedBy: myDevices,
|
|
375
|
+
|
|
376
|
+
nodeId: "s=TemperatureAnalogItem",
|
|
377
|
+
browseName: "TemperatureAnalogItem",
|
|
378
|
+
definition: "(tempA -25) + tempB",
|
|
379
|
+
valuePrecision: 0.5,
|
|
380
|
+
engineeringUnitsRange: { low: 100, high: 200 },
|
|
381
|
+
instrumentRange: { low: -100, high: +200 },
|
|
382
|
+
engineeringUnits: standardUnits.degree_celsius,
|
|
383
|
+
dataType: "Double",
|
|
384
|
+
value: {
|
|
385
|
+
get: function () {
|
|
386
|
+
return new Variant({ dataType: DataType.Double, value: Math.random() + 19.0 });
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
const m3x3 = namespace.addVariable({
|
|
392
|
+
organizedBy: addressSpace.rootFolder.objects,
|
|
393
|
+
nodeId: "s=Matrix",
|
|
394
|
+
browseName: "Matrix",
|
|
395
|
+
dataType: "Double",
|
|
396
|
+
valueRank: 2,
|
|
397
|
+
arrayDimensions: [3, 3],
|
|
398
|
+
value: {
|
|
399
|
+
get: function () {
|
|
400
|
+
return new Variant({
|
|
401
|
+
dataType: DataType.Double,
|
|
402
|
+
arrayType: VariantArrayType.Matrix,
|
|
403
|
+
dimensions: [3, 3],
|
|
404
|
+
value: [1, 2, 3, 4, 5, 6, 7, 8, 9]
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
const xyz = namespace.addVariable({
|
|
411
|
+
organizedBy: addressSpace.rootFolder.objects,
|
|
412
|
+
nodeId: "s=Position",
|
|
413
|
+
browseName: "Position",
|
|
414
|
+
dataType: "Double",
|
|
415
|
+
valueRank: 1,
|
|
416
|
+
arrayDimensions: null,
|
|
417
|
+
value: {
|
|
418
|
+
get: function () {
|
|
419
|
+
return new Variant({
|
|
420
|
+
dataType: DataType.Double,
|
|
421
|
+
arrayType: VariantArrayType.Array,
|
|
422
|
+
value: [1, 2, 3, 4]
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
//------------------------------------------------------------------------------
|
|
429
|
+
// Add a view
|
|
430
|
+
//------------------------------------------------------------------------------
|
|
431
|
+
const view = namespace.addView({
|
|
432
|
+
organizedBy: rootFolder.views,
|
|
433
|
+
browseName: "MyView"
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
view.addReference({
|
|
437
|
+
referenceType: "Organizes",
|
|
438
|
+
nodeId: node.nodeId
|
|
439
|
+
});
|
|
440
|
+
}
|
|
228
441
|
|
|
229
|
-
|
|
442
|
+
post_initialize();
|
|
230
443
|
|
|
231
|
-
|
|
444
|
+
function dumpObject(node) {
|
|
445
|
+
function w(str, width) {
|
|
446
|
+
return ("" + str).padEnd(width).substring(0, width);
|
|
447
|
+
}
|
|
448
|
+
return Object.entries(node)
|
|
449
|
+
.map((key, value) => " " + w(key, 30) + " : " + (value === null ? null : value.toString()))
|
|
450
|
+
.join("\n");
|
|
451
|
+
}
|
|
232
452
|
|
|
233
|
-
|
|
453
|
+
console.log(chalk.yellow(" server PID :"), process.pid);
|
|
454
|
+
console.log(chalk.yellow(" silent :"), argv.silent);
|
|
234
455
|
|
|
235
|
-
|
|
456
|
+
await server.start();
|
|
236
457
|
|
|
237
|
-
|
|
458
|
+
console.log(chalk.yellow("\nregistering server to :") + server.discoveryServerEndpointUrl);
|
|
238
459
|
|
|
239
|
-
|
|
460
|
+
const endpointUrl = server.getEndpointUrl();
|
|
240
461
|
|
|
241
|
-
|
|
242
|
-
|
|
462
|
+
console.log(chalk.yellow(" server on port :"), server.endpoints[0].port.toString());
|
|
463
|
+
console.log(chalk.yellow(" endpointUrl :"), endpointUrl);
|
|
243
464
|
|
|
244
|
-
|
|
465
|
+
console.log(chalk.yellow(" serverInfo :"));
|
|
466
|
+
console.log(dumpObject(server.serverInfo));
|
|
467
|
+
console.log(chalk.yellow(" buildInfo :"));
|
|
468
|
+
console.log(dumpObject(server.engine.buildInfo));
|
|
245
469
|
|
|
246
|
-
|
|
470
|
+
console.log(chalk.yellow(" Certificate rejected folder "), server.serverCertificateManager.rejectedFolder);
|
|
471
|
+
console.log(chalk.yellow(" Certificate trusted folder "), server.serverCertificateManager.trustedFolder);
|
|
472
|
+
console.log(chalk.yellow(" Server private key "), server.serverCertificateManager.privateKey);
|
|
473
|
+
console.log(chalk.yellow(" Server public key "), server.certificateFile);
|
|
474
|
+
console.log(chalk.yellow(" X509 User rejected folder "), server.userCertificateManager.trustedFolder);
|
|
475
|
+
console.log(chalk.yellow(" X509 User trusted folder "), server.userCertificateManager.rejectedFolder);
|
|
247
476
|
|
|
477
|
+
console.log(chalk.yellow("\n server now waiting for connections. CTRL+C to stop"));
|
|
248
478
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
479
|
+
if (argv.silent) {
|
|
480
|
+
console.log(" silent");
|
|
481
|
+
console.log = function () {
|
|
482
|
+
/** */
|
|
483
|
+
};
|
|
484
|
+
}
|
|
485
|
+
// console.log = function(){};
|
|
486
|
+
|
|
487
|
+
server.on("create_session", function (session) {
|
|
488
|
+
console.log(" SESSION CREATED");
|
|
489
|
+
console.log(chalk.cyan(" client application URI: "), session.clientDescription.applicationUri);
|
|
490
|
+
console.log(chalk.cyan(" client product URI: "), session.clientDescription.productUri);
|
|
491
|
+
console.log(chalk.cyan(" client application name: "), session.clientDescription.applicationName.toString());
|
|
492
|
+
console.log(chalk.cyan(" client application type: "), session.clientDescription.applicationType.toString());
|
|
493
|
+
console.log(chalk.cyan(" session name: "), session.sessionName ? session.sessionName.toString() : "<null>");
|
|
494
|
+
console.log(chalk.cyan(" session timeout: "), session.sessionTimeout);
|
|
495
|
+
console.log(chalk.cyan(" session id: "), session.sessionId);
|
|
262
496
|
});
|
|
263
497
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
}, 10);
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
/*
|
|
271
|
-
* variation 1:
|
|
272
|
-
* ------------
|
|
273
|
-
*
|
|
274
|
-
* Add a variable in folder using a single get function which returns the up to date variable value in Variant.
|
|
275
|
-
* The server will set the timestamps automatically for us.
|
|
276
|
-
* Use this variation when the variable value is controlled by the getter function
|
|
277
|
-
* Avoid using this variation if the variable has to be made writable, as the server will call the getter
|
|
278
|
-
* function prior to returning its value upon client read requests.
|
|
279
|
-
*/
|
|
280
|
-
namespace.addVariable({
|
|
281
|
-
organizedBy: myDevices,
|
|
282
|
-
browseName: "PumpSpeed",
|
|
283
|
-
nodeId: "ns=1;s=PumpSpeed",
|
|
284
|
-
dataType: "Double",
|
|
285
|
-
value: {
|
|
286
|
-
/**
|
|
287
|
-
* returns the current value as a Variant
|
|
288
|
-
* @method get
|
|
289
|
-
* @return {Variant}
|
|
290
|
-
*/
|
|
291
|
-
get: function() {
|
|
292
|
-
const pump_speed = 200 + 100 * Math.sin(Date.now() / 10000);
|
|
293
|
-
return new Variant({ dataType: DataType.Double, value: pump_speed });
|
|
294
|
-
}
|
|
295
|
-
}
|
|
498
|
+
server.on("session_closed", function (session, reason) {
|
|
499
|
+
console.log(" SESSION CLOSED :", reason);
|
|
500
|
+
console.log(chalk.cyan(" session name: "), session.sessionName ? session.sessionName.toString() : "<null>");
|
|
296
501
|
});
|
|
297
502
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
503
|
+
function w(s, w) {
|
|
504
|
+
return (" " + s).padStart(w, "0");
|
|
505
|
+
}
|
|
506
|
+
function t(d) {
|
|
507
|
+
return w(d.getHours(), 2) + ":" + w(d.getMinutes(), 2) + ":" + w(d.getSeconds(), 2) + ":" + w(d.getMilliseconds(), 3);
|
|
508
|
+
}
|
|
509
|
+
function indent(str, nb) {
|
|
510
|
+
const spacer = " ".slice(0, nb);
|
|
511
|
+
return str
|
|
512
|
+
.split("\n")
|
|
513
|
+
.map(function (s) {
|
|
514
|
+
return spacer + s;
|
|
515
|
+
})
|
|
516
|
+
.join("\n");
|
|
517
|
+
}
|
|
518
|
+
function isIn(obj, arr) {
|
|
519
|
+
try {
|
|
520
|
+
return arr.findIndex((a) => a === obj.constructor.name.replace(/Response|Request/, "")) >= 0;
|
|
521
|
+
} catch (err) {
|
|
522
|
+
return true;
|
|
306
523
|
}
|
|
307
|
-
|
|
308
|
-
});
|
|
309
|
-
|
|
524
|
+
}
|
|
310
525
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
* Add a variable in folder. This variable gets its value and source timestamps from the provided function.
|
|
316
|
-
* The value and source timestamps are held in a external object.
|
|
317
|
-
* The value and source timestamps are updated on a regular basis using a timer function.
|
|
318
|
-
*/
|
|
319
|
-
const external_value_with_sourceTimestamp = new DataValue({
|
|
320
|
-
value: new Variant({ dataType: DataType.Double, value: 10.0 }),
|
|
321
|
-
sourceTimestamp: null,
|
|
322
|
-
sourcePicoseconds: 0
|
|
323
|
-
});
|
|
324
|
-
setInterval(function() {
|
|
325
|
-
external_value_with_sourceTimestamp.value.value = Math.random();
|
|
326
|
-
external_value_with_sourceTimestamp.sourceTimestamp = new Date();
|
|
327
|
-
}, 1000);
|
|
328
|
-
|
|
329
|
-
namespace.addVariable({
|
|
330
|
-
organizedBy: myDevices,
|
|
331
|
-
browseName: "Pressure",
|
|
332
|
-
nodeId: "ns=1;s=Pressure",
|
|
333
|
-
dataType: "Double",
|
|
334
|
-
value: {
|
|
335
|
-
timestamped_get: function() {
|
|
336
|
-
return external_value_with_sourceTimestamp;
|
|
526
|
+
const servicesToTrace = ["Publish", "TransferSubscriptions", "Republish", "CreateSubscription", "CreateMonitoredItems"];
|
|
527
|
+
server.on("response", function (response) {
|
|
528
|
+
if (argv.silent) {
|
|
529
|
+
return;
|
|
337
530
|
}
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
* asynchronous function.
|
|
348
|
-
* The asynchronous function is called only when needed by the opcua Server read services and monitored item services
|
|
349
|
-
*
|
|
350
|
-
*/
|
|
351
|
-
|
|
352
|
-
namespace.addVariable({
|
|
353
|
-
organizedBy: myDevices,
|
|
354
|
-
browseName: "Temperature",
|
|
355
|
-
nodeId: "s=Temperature",
|
|
356
|
-
dataType: "Double",
|
|
357
|
-
|
|
358
|
-
value: {
|
|
359
|
-
refreshFunc: function(callback) {
|
|
360
|
-
|
|
361
|
-
const temperature = 20 + 10 * Math.sin(Date.now() / 10000);
|
|
362
|
-
const value = new Variant({ dataType: DataType.Double, value: temperature });
|
|
363
|
-
const sourceTimestamp = new Date();
|
|
364
|
-
|
|
365
|
-
// simulate a asynchronous behaviour
|
|
366
|
-
setTimeout(function() {
|
|
367
|
-
callback(null, new DataValue({ value: value, sourceTimestamp: sourceTimestamp }));
|
|
368
|
-
}, 100);
|
|
531
|
+
if (isIn(response, servicesToTrace)) {
|
|
532
|
+
console.log(
|
|
533
|
+
t(response.responseHeader.timestamp),
|
|
534
|
+
response.responseHeader.requestHandle,
|
|
535
|
+
response.schema.name.padEnd(30, " "),
|
|
536
|
+
" status = ",
|
|
537
|
+
response.responseHeader.serviceResult.toString()
|
|
538
|
+
);
|
|
539
|
+
console.log(response.constructor.name, response.toString());
|
|
369
540
|
}
|
|
370
|
-
}
|
|
371
541
|
});
|
|
372
542
|
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
organizedBy: myDevices,
|
|
378
|
-
|
|
379
|
-
nodeId: "s=TemperatureAnalogItem",
|
|
380
|
-
browseName: "TemperatureAnalogItem",
|
|
381
|
-
definition: "(tempA -25) + tempB",
|
|
382
|
-
valuePrecision: 0.5,
|
|
383
|
-
engineeringUnitsRange: { low: 100, high: 200 },
|
|
384
|
-
instrumentRange: { low: -100, high: +200 },
|
|
385
|
-
engineeringUnits: standardUnits.degree_celsius,
|
|
386
|
-
dataType: "Double",
|
|
387
|
-
value: {
|
|
388
|
-
get: function() {
|
|
389
|
-
return new Variant({ dataType: DataType.Double, value: Math.random() + 19.0 });
|
|
543
|
+
server.on("request", function (request, channel) {
|
|
544
|
+
if (argv.silent) {
|
|
545
|
+
return;
|
|
390
546
|
}
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
valueRank: 2,
|
|
401
|
-
arrayDimensions: [3, 3],
|
|
402
|
-
value: {
|
|
403
|
-
get: function() {
|
|
404
|
-
return new Variant({
|
|
405
|
-
dataType: DataType.Double,
|
|
406
|
-
arrayType: VariantArrayType.Matrix,
|
|
407
|
-
dimensions: [3, 3],
|
|
408
|
-
value: [1, 2, 3, 4, 5, 6, 7, 8, 9]
|
|
409
|
-
});
|
|
547
|
+
if (isIn(request, servicesToTrace)) {
|
|
548
|
+
console.log(
|
|
549
|
+
t(request.requestHeader.timestamp),
|
|
550
|
+
request.requestHeader.requestHandle,
|
|
551
|
+
request.schema.name.padEnd(30, " "),
|
|
552
|
+
" ID =",
|
|
553
|
+
channel.channelId.toString()
|
|
554
|
+
);
|
|
555
|
+
console.log(request.constructor.name, request.toString());
|
|
410
556
|
}
|
|
411
|
-
}
|
|
412
557
|
});
|
|
413
558
|
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
dataType: DataType.Double,
|
|
425
|
-
arrayType: VariantArrayType.Array,
|
|
426
|
-
value: [1, 2, 3, 4]
|
|
427
|
-
});
|
|
428
|
-
}
|
|
429
|
-
}
|
|
559
|
+
process.once("SIGINT", function () {
|
|
560
|
+
// only work on linux apparently
|
|
561
|
+
console.error(chalk.red.bold(" Received server interruption from user "));
|
|
562
|
+
console.error(chalk.red.bold(" shutting down ..."));
|
|
563
|
+
server.shutdown(1000, function () {
|
|
564
|
+
console.error(chalk.red.bold(" shutting down completed "));
|
|
565
|
+
console.error(chalk.red.bold(" done "));
|
|
566
|
+
console.error("");
|
|
567
|
+
process.exit(-1);
|
|
568
|
+
});
|
|
430
569
|
});
|
|
431
570
|
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
// Add a view
|
|
435
|
-
//------------------------------------------------------------------------------
|
|
436
|
-
const view = namespace.addView({
|
|
437
|
-
organizedBy: rootFolder.views,
|
|
438
|
-
browseName: "MyView"
|
|
571
|
+
server.on("serverRegistered", () => {
|
|
572
|
+
console.log("server has been registered");
|
|
439
573
|
});
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
referenceType: "Organizes",
|
|
443
|
-
nodeId: node.nodeId
|
|
574
|
+
server.on("serverUnregistered", () => {
|
|
575
|
+
console.log("server has been unregistered");
|
|
444
576
|
});
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
await server.start();
|
|
467
|
-
|
|
468
|
-
console.log(chalk.yellow("\nregistering server to :") + server.discoveryServerEndpointUrl);
|
|
469
|
-
|
|
470
|
-
const endpointUrl = server.getEndpointUrl();
|
|
471
|
-
|
|
472
|
-
console.log(chalk.yellow(" server on port :"), server.endpoints[0].port.toString());
|
|
473
|
-
console.log(chalk.yellow(" endpointUrl :"), endpointUrl);
|
|
474
|
-
|
|
475
|
-
console.log(chalk.yellow(" serverInfo :"));
|
|
476
|
-
console.log(dumpObject(server.serverInfo));
|
|
477
|
-
console.log(chalk.yellow(" buildInfo :"));
|
|
478
|
-
console.log(dumpObject(server.engine.buildInfo));
|
|
479
|
-
|
|
480
|
-
console.log(chalk.yellow(" Certificate rejected folder "), server.serverCertificateManager.rejectedFolder);
|
|
481
|
-
console.log(chalk.yellow(" Certificate trusted folder "), server.serverCertificateManager.trustedFolder);
|
|
482
|
-
console.log(chalk.yellow(" Server private key "), server.serverCertificateManager.privateKey);
|
|
483
|
-
console.log(chalk.yellow(" Server public key "), server.certificateFile);
|
|
484
|
-
console.log(chalk.yellow(" X509 User rejected folder "), server.userCertificateManager.trustedFolder);
|
|
485
|
-
console.log(chalk.yellow(" X509 User trusted folder "), server.userCertificateManager.rejectedFolder);
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
console.log(chalk.yellow("\n server now waiting for connections. CTRL+C to stop"));
|
|
490
|
-
|
|
491
|
-
if (argv.silent) {
|
|
492
|
-
console.log(" silent");
|
|
493
|
-
console.log = function() { };
|
|
494
|
-
}
|
|
495
|
-
// console.log = function(){};
|
|
496
|
-
|
|
497
|
-
server.on("create_session", function(session) {
|
|
498
|
-
console.log(" SESSION CREATED");
|
|
499
|
-
console.log(chalk.cyan(" client application URI: "), session.clientDescription.applicationUri);
|
|
500
|
-
console.log(chalk.cyan(" client product URI: "), session.clientDescription.productUri);
|
|
501
|
-
console.log(chalk.cyan(" client application name: "), session.clientDescription.applicationName.toString());
|
|
502
|
-
console.log(chalk.cyan(" client application type: "), session.clientDescription.applicationType.toString());
|
|
503
|
-
console.log(chalk.cyan(" session name: "), session.sessionName ? session.sessionName.toString() : "<null>");
|
|
504
|
-
console.log(chalk.cyan(" session timeout: "), session.sessionTimeout);
|
|
505
|
-
console.log(chalk.cyan(" session id: "), session.sessionId);
|
|
506
|
-
});
|
|
507
|
-
|
|
508
|
-
server.on("session_closed", function(session, reason) {
|
|
509
|
-
console.log(" SESSION CLOSED :", reason);
|
|
510
|
-
console.log(chalk.cyan(" session name: "), session.sessionName ? session.sessionName.toString() : "<null>");
|
|
511
|
-
});
|
|
512
|
-
|
|
513
|
-
function w(s, w) {
|
|
514
|
-
return ("000" + s).substr(-w);
|
|
515
|
-
}
|
|
516
|
-
function t(d) {
|
|
517
|
-
return w(d.getHours(), 2) + ":" + w(d.getMinutes(), 2) + ":" + w(d.getSeconds(), 2) + ":" + w(d.getMilliseconds(), 3);
|
|
518
|
-
}
|
|
519
|
-
function indent(str, nb) {
|
|
520
|
-
const spacer = " ".slice(0, nb);
|
|
521
|
-
return str.split("\n").map(function(s) {
|
|
522
|
-
return spacer + s;
|
|
523
|
-
}).join("\n");
|
|
524
|
-
}
|
|
525
|
-
function isIn(obj, arr) {
|
|
526
|
-
try {
|
|
527
|
-
return arr.findIndex((a) => a === obj.constructor.name.replace(/Response|Request/, "")) >= 0;
|
|
528
|
-
|
|
529
|
-
} catch (err) {
|
|
530
|
-
return true;
|
|
531
|
-
}
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
const servicesToTrace = ["Publish", "TransferSubscriptions", "Republish", "CreateSubscription", "CreateMonitoredItems"];
|
|
535
|
-
server.on("response", function(response) {
|
|
536
|
-
|
|
537
|
-
if (argv.silent) { return; }
|
|
538
|
-
if (isIn(response, servicesToTrace)) {
|
|
539
|
-
console.log(t(response.responseHeader.timestamp), response.responseHeader.requestHandle,
|
|
540
|
-
response.schema.name.padEnd(30, " "), " status = ", response.responseHeader.serviceResult.toString());
|
|
541
|
-
console.log(response.constructor.name, response.toString());
|
|
542
|
-
}
|
|
543
|
-
});
|
|
544
|
-
|
|
545
|
-
server.on("request", function(request, channel) {
|
|
546
|
-
if (argv.silent) { return; }
|
|
547
|
-
if (isIn(request, servicesToTrace)) {
|
|
548
|
-
console.log(t(request.requestHeader.timestamp), request.requestHeader.requestHandle,
|
|
549
|
-
request.schema.name.padEnd(30, " "), " ID =", channel.channelId.toString());
|
|
550
|
-
console.log(request.constructor.name, request.toString());
|
|
551
|
-
}
|
|
552
|
-
});
|
|
553
|
-
|
|
554
|
-
process.on("SIGINT", function() {
|
|
555
|
-
// only work on linux apparently
|
|
556
|
-
console.error(chalk.red.bold(" Received server interruption from user "));
|
|
557
|
-
console.error(chalk.red.bold(" shutting down ..."));
|
|
558
|
-
server.shutdown(1000, function() {
|
|
559
|
-
console.error(chalk.red.bold(" shutting down completed "));
|
|
560
|
-
console.error(chalk.red.bold(" done "));
|
|
561
|
-
console.error("");
|
|
562
|
-
process.exit(-1);
|
|
577
|
+
server.on("serverRegistrationRenewed", () => {
|
|
578
|
+
console.log("server registration has been renewed");
|
|
579
|
+
});
|
|
580
|
+
server.on("serverRegistrationPending", () => {
|
|
581
|
+
console.log("server registration is still pending (is Local Discovery Server up and running ?)");
|
|
582
|
+
});
|
|
583
|
+
server.on("newChannel", (channel) => {
|
|
584
|
+
console.log(
|
|
585
|
+
chalk.bgYellow("Client connected with address = "),
|
|
586
|
+
channel.remoteAddress,
|
|
587
|
+
" port = ",
|
|
588
|
+
channel.remotePort,
|
|
589
|
+
"timeout=",
|
|
590
|
+
channel.timeout
|
|
591
|
+
);
|
|
592
|
+
});
|
|
593
|
+
server.on("closeChannel", (channel) => {
|
|
594
|
+
console.log(chalk.bgCyan("Client disconnected with address = "), channel.remoteAddress, " port = ", channel.remotePort);
|
|
595
|
+
if (global.gc) {
|
|
596
|
+
global.gc();
|
|
597
|
+
}
|
|
563
598
|
});
|
|
564
|
-
});
|
|
565
|
-
|
|
566
|
-
server.on("serverRegistered", () => {
|
|
567
|
-
console.log("server has been registered");
|
|
568
|
-
});
|
|
569
|
-
server.on("serverUnregistered", () => {
|
|
570
|
-
console.log("server has been unregistered");
|
|
571
|
-
});
|
|
572
|
-
server.on("serverRegistrationRenewed", () => {
|
|
573
|
-
console.log("server registration has been renewed");
|
|
574
|
-
});
|
|
575
|
-
server.on("serverRegistrationPending", () => {
|
|
576
|
-
console.log("server registration is still pending (is Local Discovery Server up and running ?)");
|
|
577
|
-
});
|
|
578
|
-
server.on("newChannel", (channel) => {
|
|
579
|
-
console.log(chalk.bgYellow("Client connected with address = "), channel.remoteAddress, " port = ", channel.remotePort, "timeout=", channel.timeout);
|
|
580
|
-
});
|
|
581
|
-
server.on("closeChannel", (channel) => {
|
|
582
|
-
console.log(chalk.bgCyan("Client disconnected with address = "), channel.remoteAddress, " port = ", channel.remotePort);
|
|
583
|
-
if (global.gc) {
|
|
584
|
-
global.gc();
|
|
585
|
-
}
|
|
586
|
-
});
|
|
587
|
-
|
|
588
599
|
})();
|