nep-cli 0.2.1 → 0.2.3
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/assets/vue.js +11944 -0
- package/bin/assets/vuetify/json/attributes.json +9158 -0
- package/bin/assets/vuetify/json/tags.json +3063 -0
- package/bin/assets/vuetify/json/web-types.json +27671 -0
- package/bin/assets/vuetify/vuetify.css +24876 -0
- package/bin/assets/vuetify/vuetify.css.map +1 -0
- package/bin/assets/vuetify/vuetify.js +41073 -0
- package/bin/assets/vuetify/vuetify.js.map +1 -0
- package/bin/assets/vuetify/vuetify.min.css +8 -0
- package/bin/assets/vuetify/vuetify.min.js +6 -0
- package/bin/image.html +14 -6
- package/bin/index.js +611 -271
- package/bin/json.html +9 -4
- package/package.json +3 -4
package/bin/index.js
CHANGED
|
@@ -18,8 +18,8 @@ const fs = require('fs');
|
|
|
18
18
|
const zmqc = require("zeromq/v5-compat");
|
|
19
19
|
|
|
20
20
|
const PORT_MASTER_INFO = 50001; // Default port for master info
|
|
21
|
-
const
|
|
22
|
-
|
|
21
|
+
const PORT_SERVER = 50050;
|
|
22
|
+
|
|
23
23
|
|
|
24
24
|
|
|
25
25
|
program
|
|
@@ -78,7 +78,8 @@ class MasterLocal {
|
|
|
78
78
|
const reply = new zmq.Reply
|
|
79
79
|
await reply.bind(address)
|
|
80
80
|
for await (const [node_request] of reply) {
|
|
81
|
-
await
|
|
81
|
+
var msg = await processMsg(node_request.toString(), nodes_register, topic_register)
|
|
82
|
+
await reply.send(msg)
|
|
82
83
|
}
|
|
83
84
|
}
|
|
84
85
|
run()
|
|
@@ -120,109 +121,173 @@ var onRegisteredTopic = function (node_request, topic_register, topic) {
|
|
|
120
121
|
}
|
|
121
122
|
|
|
122
123
|
|
|
123
|
-
var
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
nep_configuration["current_port"] = Math.max(nep_configuration["current_port"], node_request["port"] + 2);
|
|
127
|
-
|
|
128
|
-
if (node_request["socket"] === "publisher" || node_request["socket"] === "subscriber") {
|
|
129
|
-
// Create new broker for many2many communication
|
|
130
|
-
if (node_request["mode"] === "many2many") {
|
|
131
|
-
restartBroker(node_request["topic"], node_request["port"]);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
if ("msg_type" in node_request) {
|
|
135
|
-
topic_register[topic] = { "port": node_request["port"], "socket": node_request["socket"], 'ip': nep_configuration["IP"], "mode": node_request["mode"], "msg_type": node_request["msg_type"] }
|
|
136
|
-
}
|
|
137
|
-
else {
|
|
138
|
-
topic_register[topic] = { "port": node_request["port"], "socket": node_request["socket"], 'ip': nep_configuration["IP"], "mode": node_request["mode"], "msg_type": "json" }
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
topic_register[topic]["nodes"] = [];
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
}
|
|
124
|
+
var onNewTopic = async function (node_request, topic_register) {
|
|
125
|
+
// Assign new ports
|
|
126
|
+
nep_configuration["current_port"] += 2;
|
|
145
127
|
|
|
146
|
-
|
|
128
|
+
// Topic must be a string
|
|
129
|
+
var topic = String(node_request['topic']);
|
|
130
|
+
var msg = {};
|
|
147
131
|
|
|
148
|
-
// Asign new ports
|
|
149
|
-
nep_configuration["current_port"] = nep_configuration["current_port"] + 2
|
|
150
|
-
// Topic must be an string
|
|
151
|
-
topic = String(node_request['topic'])
|
|
152
|
-
var msg = {}
|
|
153
132
|
// If publisher or subscriber
|
|
154
133
|
if (node_request["socket"] === "publisher" || node_request["socket"] === "subscriber") {
|
|
155
134
|
// Create new broker for many2many communication
|
|
156
135
|
if (node_request["mode"] === "many2many") {
|
|
157
|
-
createBroker(topic);
|
|
158
|
-
msg =
|
|
136
|
+
await createBroker(topic);
|
|
137
|
+
msg = ManyToManyResponse(node_request, topic_register, topic);
|
|
159
138
|
}
|
|
160
139
|
}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
}
|
|
140
|
+
|
|
141
|
+
// If client or server
|
|
164
142
|
else if (node_request["socket"] === "client" || node_request["socket"] === "server") {
|
|
165
|
-
msg = csResponse(node_request, topic_register, topic)
|
|
143
|
+
msg = csResponse(node_request, topic_register, topic);
|
|
166
144
|
}
|
|
145
|
+
|
|
167
146
|
// Key for related nodes
|
|
168
|
-
topic_register[topic]["nodes"] = {}
|
|
147
|
+
topic_register[topic]["nodes"] = {};
|
|
148
|
+
|
|
169
149
|
// Set PID of node
|
|
170
150
|
updatePID(node_request, topic_register, topic);
|
|
171
151
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
msg.port = msg.port.toString();
|
|
176
|
-
}
|
|
177
|
-
|
|
152
|
+
// If the language is C++, convert the port to a string
|
|
153
|
+
if (node_request["language"] === "C++") {
|
|
154
|
+
msg.port = msg.port.toString();
|
|
178
155
|
}
|
|
179
156
|
|
|
180
|
-
return msg
|
|
157
|
+
return msg;
|
|
181
158
|
}
|
|
182
159
|
|
|
183
160
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
161
|
+
class Broker {
|
|
162
|
+
constructor(IP, PORT_XPUB, PORT_XSUB, topic) {
|
|
163
|
+
this.pubListener = "tcp://" + IP + ":" + String(PORT_XPUB);
|
|
164
|
+
this.subListener = "tcp://" + IP + ":" + String(PORT_XSUB);
|
|
165
|
+
this.hwm = 1000;
|
|
166
|
+
this.verbose = 0;
|
|
167
|
+
this.topic = topic;
|
|
188
168
|
|
|
189
|
-
//
|
|
190
|
-
|
|
191
|
-
} catch (error) {
|
|
192
|
-
// console.log("NEP ERROR: ports " + String(nep_configuration["current_port"]) + " and " + String(nep_configuration["current_port"] + 1) + " not avaliable")
|
|
193
|
-
nep_configuration["current_port"] = nep_configuration["current_port"] + 2
|
|
194
|
-
var broker = new nep.Broker(nep_configuration["IP"], nep_configuration["current_port"] + 1, nep_configuration["current_port"])
|
|
195
|
-
// Add broker defined to list of brokers
|
|
196
|
-
nep_configuration["brokers"][topic] = broker
|
|
169
|
+
// Use ZMQ version 5
|
|
170
|
+
this.zmqc = require("zeromq/v5-compat");
|
|
197
171
|
}
|
|
198
|
-
}
|
|
199
172
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
173
|
+
bindSockets() {
|
|
174
|
+
try {
|
|
175
|
+
// The xsub listener is where pubs connect to
|
|
176
|
+
this.subSock = this.zmqc.socket('xsub');
|
|
177
|
+
this.subSock.bind(this.subListener);
|
|
178
|
+
|
|
179
|
+
// The xpub listener is where subs connect to
|
|
180
|
+
this.pubSock = this.zmqc.socket('xpub');
|
|
181
|
+
this.pubSock.setsockopt(this.zmqc.ZMQ_SNDHWM, this.hwm);
|
|
182
|
+
|
|
183
|
+
// By default xpub only signals new subscriptions
|
|
184
|
+
// Settings it to verbose = 1 , will signal on every new subscribe
|
|
185
|
+
this.pubSock.setsockopt(this.zmqc.ZMQ_XPUB_VERBOSE, this.verbose);
|
|
186
|
+
this.pubSock.bind(this.pubListener);
|
|
187
|
+
return true;
|
|
188
|
+
} catch (error) {
|
|
189
|
+
if (error.code === 'EADDRINUSE') {
|
|
190
|
+
throw new Error(`Port ${this.pubListener.split(':')[2]} or ${this.subListener.split(':')[2]} is in use.`);
|
|
191
|
+
} else {
|
|
192
|
+
console.error(error);
|
|
193
|
+
return false;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
208
196
|
}
|
|
209
|
-
}
|
|
210
197
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
198
|
+
start() {
|
|
199
|
+
// When we receive data on subSock , it means someone is publishing
|
|
200
|
+
this.subSock.on('message', (data) => {
|
|
201
|
+
// We just relay it to the pubSock, so subscribers can receive it
|
|
202
|
+
this.pubSock.send(data);
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
// When Pubsock receives a message , it's subscribe requests
|
|
206
|
+
this.pubSock.on('message', (data) => {
|
|
207
|
+
// The data is a slow Buffer
|
|
208
|
+
// The first byte is the subscribe (1) /unsubscribe flag (0)
|
|
209
|
+
var type = data[0] === 0 ? 'unsubscribe' : 'subscribe';
|
|
210
|
+
// The channel name is the rest of the buffer
|
|
211
|
+
var channel = data.slice(1).toString();
|
|
212
|
+
console.log(type + ' :' + this.topic);
|
|
213
|
+
// We send it to subSock, so it knows to what channels to listen to
|
|
214
|
+
this.subSock.send(data);
|
|
215
|
+
});
|
|
216
|
+
console.log("BROKER: " + "XPUB: " + String(this.pubListener) + " - bind , XSUB: " + String(this.subListener) + " - bind");
|
|
217
217
|
}
|
|
218
|
-
return { 'topic': topic, 'port': nep_configuration["current_port"], 'mode': node_request["mode"], 'ip': nep_configuration["IP"], 'socket': node_request["socket"], "state": "success" }
|
|
219
218
|
}
|
|
220
219
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
220
|
+
function checkPort(port) {
|
|
221
|
+
return new Promise((resolve) => {
|
|
222
|
+
const sock = new zmq.Publisher;
|
|
223
|
+
sock.bind(`tcp://127.0.0.1:${port}`)
|
|
224
|
+
.then(() => {
|
|
225
|
+
sock.unbind(`tcp://127.0.0.1:${port}`);
|
|
226
|
+
resolve(false); // Puerto disponible
|
|
227
|
+
})
|
|
228
|
+
.catch((err) => {
|
|
229
|
+
if (err.code === 'EADDRINUSE') {
|
|
230
|
+
resolve(true); // Puerto en uso
|
|
231
|
+
} else {
|
|
232
|
+
console.error(err);
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
});
|
|
224
236
|
}
|
|
225
237
|
|
|
238
|
+
var createBroker = async function (topic) {
|
|
239
|
+
var bindResult = false;
|
|
240
|
+
do {
|
|
241
|
+
var portInUse = await checkPort(nep_configuration["current_port"]);
|
|
242
|
+
var nextPortInUse = await checkPort(nep_configuration["current_port"] + 1);
|
|
243
|
+
if (portInUse || nextPortInUse) {
|
|
244
|
+
console.log(`LOG: Port ${nep_configuration["current_port"]} in use: ${portInUse}, Port ${nep_configuration["current_port"] + 1} in use: ${nextPortInUse}`);
|
|
245
|
+
nep_configuration["current_port"] += 2;
|
|
246
|
+
continue;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
var broker = new Broker(nep_configuration["IP"], nep_configuration["current_port"] + 1, nep_configuration["current_port"], topic);
|
|
250
|
+
bindResult = broker.bindSockets();
|
|
251
|
+
if (bindResult) {
|
|
252
|
+
broker.start();
|
|
253
|
+
nep_configuration["brokers"][topic] = broker;
|
|
254
|
+
}
|
|
255
|
+
} while (!bindResult);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Generate a response for many-to-many communication.
|
|
261
|
+
* @param {Object} node_request - The request from the node.
|
|
262
|
+
* @param {Object} topic_register - The register of topics.
|
|
263
|
+
* @param {string} topic - The topic of the request.
|
|
264
|
+
* @return {Object} The response to be sent back to the node.
|
|
265
|
+
*/
|
|
266
|
+
var ManyToManyResponse = function (node_request, topic_register, topic) {
|
|
267
|
+
// If the request contains a message type, use it. Otherwise, default to "json".
|
|
268
|
+
var msg_type = "msg_type" in node_request ? node_request["msg_type"] : "json";
|
|
269
|
+
|
|
270
|
+
// Register the topic with the current port, socket, IP, mode, and message type.
|
|
271
|
+
topic_register[topic] = {
|
|
272
|
+
"port": nep_configuration["current_port"],
|
|
273
|
+
"socket": node_request["socket"],
|
|
274
|
+
'ip': nep_configuration["IP"],
|
|
275
|
+
"mode": node_request["mode"],
|
|
276
|
+
"msg_type": msg_type
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
// Return the response with the topic, port, mode, IP, socket, and a success state.
|
|
280
|
+
return {
|
|
281
|
+
'topic': topic,
|
|
282
|
+
'port': nep_configuration["current_port"],
|
|
283
|
+
'mode': node_request["mode"],
|
|
284
|
+
'ip': nep_configuration["IP"],
|
|
285
|
+
'socket': node_request["socket"],
|
|
286
|
+
"state": "success"
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
|
|
226
291
|
var csResponse = function (node_request, topic_register, topic) {
|
|
227
292
|
topic_register[topic] = { "port": nep_configuration["current_port"], "socket": node_request["socket"], 'ip': nep_configuration["IP"], "msg_type": "json" }
|
|
228
293
|
return { 'topic': topic, 'port': nep_configuration["current_port"], 'ip': nep_configuration["IP"], 'socket': node_request["socket"], "state": "success" }
|
|
@@ -239,27 +304,16 @@ var updatePID = function (node_request, topic_register, topic) {
|
|
|
239
304
|
}
|
|
240
305
|
|
|
241
306
|
|
|
242
|
-
|
|
243
|
-
console.log(node_request)
|
|
244
|
-
// Get topic name
|
|
245
|
-
var topic = String(node_request['topic'])
|
|
246
|
-
|
|
247
|
-
console.log(" --- Reset topic : *" + topic + "* ---")
|
|
248
|
-
// Create new broker
|
|
249
|
-
onResetTopic(node_request);
|
|
250
|
-
onUpdateTopicList(topic_register)
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
const processMsg = (json_msg, nodes_register, topic_register) => {
|
|
307
|
+
const processMsg = async (json_msg, nodes_register, topic_register) => {
|
|
254
308
|
const node_request = JSON.parse(json_msg);
|
|
255
309
|
const { node, socket, topic, pid } = node_request;
|
|
256
|
-
console.log(
|
|
310
|
+
console.log(`\n --- REQUEST: node: ${node}, socket: ${socket}, topic: ${topic} ---`);
|
|
257
311
|
|
|
258
312
|
// Check node status
|
|
259
313
|
if (node) {
|
|
260
314
|
// Kill previous node with the same name
|
|
261
315
|
if (node in nodes_register) {
|
|
262
|
-
console.log(
|
|
316
|
+
console.log(`NODE: *${node}* already registered`);
|
|
263
317
|
const current_pid = nodes_register[node];
|
|
264
318
|
if (pid !== current_pid) {
|
|
265
319
|
if (!node.startsWith('nep-cli')) {
|
|
@@ -269,7 +323,7 @@ const processMsg = (json_msg, nodes_register, topic_register) => {
|
|
|
269
323
|
}
|
|
270
324
|
}
|
|
271
325
|
} else {
|
|
272
|
-
console.log(
|
|
326
|
+
console.log(`NODE: *${node}* was registered`);
|
|
273
327
|
}
|
|
274
328
|
}
|
|
275
329
|
|
|
@@ -279,14 +333,14 @@ const processMsg = (json_msg, nodes_register, topic_register) => {
|
|
|
279
333
|
// Check topic status
|
|
280
334
|
const topicStr = String(topic);
|
|
281
335
|
if (topicStr in topic_register) {
|
|
282
|
-
console.log(
|
|
336
|
+
console.log(`TOPIC: *${topicStr}* already registered ---`);
|
|
283
337
|
// Send information of topic already defined
|
|
284
338
|
const response = onRegisteredTopic(node_request, topic_register, topicStr);
|
|
285
339
|
return JSON.stringify(response);
|
|
286
340
|
} else {
|
|
287
|
-
console.log(
|
|
341
|
+
console.log(`TOPIC: *${topicStr}* was registered`);
|
|
288
342
|
// Create new broker
|
|
289
|
-
const response = onNewTopic(node_request, topic_register);
|
|
343
|
+
const response = await onNewTopic(node_request, topic_register);
|
|
290
344
|
return JSON.stringify(response);
|
|
291
345
|
}
|
|
292
346
|
};
|
|
@@ -297,6 +351,10 @@ function startShowServer(port, topic, ip) {
|
|
|
297
351
|
const io = socketIo(server);
|
|
298
352
|
const path = require('path');
|
|
299
353
|
|
|
354
|
+
// Serve static files from node_modules
|
|
355
|
+
app.use(express.static(path.join(__dirname)));
|
|
356
|
+
|
|
357
|
+
|
|
300
358
|
const node = new nep.Node("nep-cli-sub");
|
|
301
359
|
const config = node.hybrid(ip)
|
|
302
360
|
|
|
@@ -330,6 +388,7 @@ function startJsonServer(port, topic, ip, msg_type = "json") {
|
|
|
330
388
|
const server = http.createServer(app);
|
|
331
389
|
const io = socketIo(server);
|
|
332
390
|
const path = require('path');
|
|
391
|
+
app.use(express.static(path.join(__dirname)));
|
|
333
392
|
|
|
334
393
|
const node = new nep.Node("nep-cli-sub");
|
|
335
394
|
const config = node.hybrid(ip)
|
|
@@ -364,76 +423,83 @@ function startJsonServer(port, topic, ip, msg_type = "json") {
|
|
|
364
423
|
}
|
|
365
424
|
|
|
366
425
|
|
|
367
|
-
|
|
368
|
-
.
|
|
369
|
-
.
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
const username = os.userInfo().username;
|
|
426
|
+
const createRequester = (master_ip, port) => {
|
|
427
|
+
const requester = new zmq.Request();
|
|
428
|
+
requester.connect(`tcp://${master_ip}:${port}`);
|
|
429
|
+
return requester;
|
|
430
|
+
};
|
|
373
431
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
432
|
+
const sendRequest = async (requester, msg) => {
|
|
433
|
+
const message = JSON.stringify(msg);
|
|
434
|
+
await requester.send(message);
|
|
435
|
+
const [result] = await requester.receive();
|
|
436
|
+
return JSON.parse(result.toString());
|
|
437
|
+
};
|
|
378
438
|
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
439
|
+
const selectTopic = async (results, topic) => {
|
|
440
|
+
try {
|
|
441
|
+
if (!topic || !results["input"].includes(topic)) {
|
|
442
|
+
const autoComplete = new AutoComplete({
|
|
443
|
+
name: 'topic',
|
|
444
|
+
message: !topic ? 'Select a topic:' : 'Invalid topic. Select a valid topic:',
|
|
445
|
+
choices: results["input"],
|
|
446
|
+
});
|
|
447
|
+
topic = await autoComplete.run();
|
|
448
|
+
}
|
|
449
|
+
return topic;
|
|
450
|
+
} catch (error) {
|
|
451
|
+
process.exit(1);
|
|
452
|
+
}
|
|
453
|
+
};
|
|
454
|
+
|
|
455
|
+
const selectIndex = async (index, maxIndex = 9) => {
|
|
456
|
+
try {
|
|
457
|
+
const choices = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"];
|
|
458
|
+
|
|
459
|
+
const autoComplete = new AutoComplete({
|
|
460
|
+
name: 'index',
|
|
461
|
+
message: !index ? 'Select a index:' : 'Invalid index. Select a valid index:',
|
|
462
|
+
choices: choices,
|
|
387
463
|
});
|
|
388
|
-
|
|
464
|
+
index = await autoComplete.run();
|
|
389
465
|
|
|
466
|
+
return index;
|
|
467
|
+
} catch (error) {
|
|
468
|
+
process.exit(1);
|
|
469
|
+
}
|
|
470
|
+
};
|
|
390
471
|
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
472
|
+
const selectMsgType = async (allowedFormats, msg_type, topic) => {
|
|
473
|
+
try {
|
|
474
|
+
var msgType = topic.split('/').pop();
|
|
475
|
+
if (msgType && msgType !== topic) {
|
|
476
|
+
msg_type = msgType;
|
|
477
|
+
}
|
|
396
478
|
|
|
397
|
-
|
|
398
|
-
if (!programName) {
|
|
479
|
+
if (!msg_type || !allowedFormats.includes(msg_type)) {
|
|
399
480
|
const autoComplete = new AutoComplete({
|
|
400
|
-
name: '
|
|
401
|
-
message: 'Select a
|
|
402
|
-
choices:
|
|
481
|
+
name: 'msg_type',
|
|
482
|
+
message: !msg_type ? 'Select a message type:' : `Select a valid format:`,
|
|
483
|
+
choices: allowedFormats,
|
|
403
484
|
});
|
|
404
|
-
|
|
405
|
-
} else if (!programs.includes(programName)) {
|
|
406
|
-
const autoComplete = new AutoComplete({
|
|
407
|
-
name: 'program',
|
|
408
|
-
message: 'Invalid program name. Select a valid program:',
|
|
409
|
-
choices: programs,
|
|
410
|
-
});
|
|
411
|
-
programName = await autoComplete.run();
|
|
485
|
+
msg_type = await autoComplete.run();
|
|
412
486
|
}
|
|
487
|
+
return msg_type;
|
|
488
|
+
} catch (error) {
|
|
489
|
+
process.exit(1);
|
|
490
|
+
}
|
|
491
|
+
};
|
|
413
492
|
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
const executableName = 'nepplus' + "-" + `${programName}.exe`;
|
|
420
|
-
const executablePath = `C:\\Users\\${username}\\AppData\\Local\\Programs\\${appFolderName}\\${executableName}`;
|
|
421
|
-
|
|
422
|
-
// Run the executable with proper escaping
|
|
423
|
-
exec(`"${executablePath}"`, (error, stdout, stderr) => {
|
|
424
|
-
if (error) {
|
|
425
|
-
console.error(`Error: ${error.message}`);
|
|
426
|
-
return;
|
|
427
|
-
}
|
|
428
|
-
console.log(`stdout: ${stdout}`);
|
|
429
|
-
console.error(`stderr: ${stderr}`);
|
|
430
|
-
});
|
|
431
|
-
});
|
|
493
|
+
const openSub = (master_ip, topic, msg_type, callback) => {
|
|
494
|
+
const node = new nep.Node("nep-cli-sub");
|
|
495
|
+
const conf = node.hybrid(master_ip);
|
|
496
|
+
const sub = node.new_sub(topic, msg_type, callback, conf);
|
|
497
|
+
};
|
|
432
498
|
|
|
433
499
|
|
|
434
500
|
program
|
|
435
501
|
.command('master')
|
|
436
|
-
.description('Start
|
|
502
|
+
.description('Start master for managing NEP+ topics')
|
|
437
503
|
.action(async () => {
|
|
438
504
|
try {
|
|
439
505
|
var node = new nep.Node("nep-cli");
|
|
@@ -452,7 +518,7 @@ program
|
|
|
452
518
|
|
|
453
519
|
program
|
|
454
520
|
.command('topics')
|
|
455
|
-
.description('
|
|
521
|
+
.description('Displays the list of NEP+ topics')
|
|
456
522
|
.action(async () => {
|
|
457
523
|
const master_ip = "127.0.0.1";
|
|
458
524
|
const TIMEOUT_DURATION = 5000; // Timeout duration in milliseconds
|
|
@@ -503,141 +569,118 @@ program
|
|
|
503
569
|
await getTopics(); // Call the function to get topics
|
|
504
570
|
});
|
|
505
571
|
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
const allowedFormats = ["json", "images", "dictionary"];
|
|
515
|
-
if (!allowedFormats.includes(msg_type)) {
|
|
516
|
-
console.error(`Format '${msg_type}' no allowed. Allowed formats are: ${allowedFormats.join(', ')}`);
|
|
517
|
-
return;
|
|
518
|
-
}
|
|
572
|
+
program
|
|
573
|
+
.command('show [topic] [index]')
|
|
574
|
+
.description('Displays messages published to a specified NEP+ topic in the default browser. ' +
|
|
575
|
+
'The [index] parameter specifies the index of the message to display and must be a number between 0 and 49. '
|
|
576
|
+
)
|
|
577
|
+
.action(async (topic, index) => {
|
|
578
|
+
const ip = "127.0.0.1";
|
|
519
579
|
|
|
520
|
-
|
|
580
|
+
const allowedFormats = ["json", "msgpack", "bytes", "images", "dictionary", "string"];
|
|
581
|
+
const master_ip = "127.0.0.1";
|
|
582
|
+
|
|
583
|
+
const requester = createRequester(master_ip, PORT_MASTER_INFO);
|
|
584
|
+
const results = await sendRequest(requester, { "input": "topics" });
|
|
585
|
+
|
|
586
|
+
topic = await selectTopic(results, topic);
|
|
587
|
+
var msg_type = await selectMsgType(allowedFormats, msg_type, topic);
|
|
588
|
+
index = await selectIndex(index);
|
|
589
|
+
|
|
590
|
+
const topicid = `${index}: ${topic}`;
|
|
591
|
+
if (parseInt(index) < 50) {
|
|
592
|
+
const port = PORT_SERVER + parseInt(index);
|
|
593
|
+
console.log(PORT_SERVER)
|
|
594
|
+
console.log(index)
|
|
595
|
+
console.log(PORT_SERVER + parseInt(index))
|
|
596
|
+
|
|
597
|
+
if (msg_type === "images") {
|
|
521
598
|
open(`http://localhost:${port}/?port=${port}&topic=${encodeURIComponent(topicid)}`);
|
|
522
599
|
startShowServer(port, topic, ip);
|
|
523
600
|
}
|
|
524
|
-
else if (msg_type === "json" || msg_type === "dictionary")
|
|
525
|
-
{
|
|
601
|
+
else if (msg_type === "json" || msg_type === "dictionary") {
|
|
526
602
|
open(`http://localhost:${port}/?port=${port}&topic=${encodeURIComponent(topicid)}`);
|
|
527
603
|
startJsonServer(port, topic, ip, msg_type);
|
|
528
604
|
}
|
|
529
|
-
});
|
|
530
605
|
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
.action(async (topic, msg_type) => {
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
const allowedFormats = ["json", "msgpack", "bytes", "images", "dictionary", "string"];
|
|
539
|
-
if (!allowedFormats.includes(msg_type)) {
|
|
540
|
-
console.error(`Format '${msg_type}' no allowed. Allowed formats are: ${allowedFormats.join(', ')}`);
|
|
541
|
-
return;
|
|
606
|
+
}
|
|
607
|
+
else {
|
|
608
|
+
console.log("Index must be between 0 and 49");
|
|
542
609
|
}
|
|
543
610
|
|
|
544
|
-
|
|
545
|
-
var callback = function (msg) {
|
|
546
|
-
var date = new Date();
|
|
547
|
-
var dateString = date.toISOString();
|
|
548
|
-
console.log(dateString);
|
|
611
|
+
});
|
|
549
612
|
|
|
550
|
-
if (msg.length > 10000) {
|
|
551
|
-
console.log("the message is too long to be displayed");
|
|
552
|
-
} else {
|
|
553
|
-
console.log(msg);
|
|
554
|
-
}
|
|
555
|
-
};
|
|
556
613
|
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
614
|
+
program
|
|
615
|
+
.command('echo [topic]')
|
|
616
|
+
.description('Subscribe to a NEP+ topic and display raw string messages')
|
|
617
|
+
.action(async (topic, msg_type) => {
|
|
618
|
+
const allowedFormats = ["json", "msgpack", "bytes", "images", "dictionary", "string"];
|
|
619
|
+
const master_ip = "127.0.0.1";
|
|
561
620
|
|
|
621
|
+
const requester = createRequester(master_ip, PORT_MASTER_INFO);
|
|
622
|
+
const results = await sendRequest(requester, { "input": "topics" });
|
|
562
623
|
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
var requester = new zmq.Request();
|
|
566
|
-
requester.connect("tcp://" + master_ip + ":" + PORT_MASTER_INFO);
|
|
624
|
+
topic = await selectTopic(results, topic);
|
|
625
|
+
msg_type = await selectMsgType(allowedFormats, msg_type, topic);
|
|
567
626
|
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
var results = JSON.parse(result.toString());
|
|
627
|
+
const callback = (msg) => {
|
|
628
|
+
const date = new Date();
|
|
629
|
+
const dateString = date.toISOString();
|
|
630
|
+
console.log(dateString);
|
|
573
631
|
|
|
574
|
-
if (
|
|
575
|
-
console.log("
|
|
632
|
+
if (msg.length > 10000) {
|
|
633
|
+
console.log("the message is too long to be displayed");
|
|
576
634
|
} else {
|
|
577
|
-
console.log(
|
|
578
|
-
if (results["input"].includes(topic)) {
|
|
579
|
-
opensub(master_ip, msg_type);
|
|
580
|
-
} else {
|
|
581
|
-
console.log("Topic is not registered");
|
|
582
|
-
}
|
|
635
|
+
console.log(msg);
|
|
583
636
|
}
|
|
584
|
-
}
|
|
637
|
+
};
|
|
585
638
|
|
|
586
|
-
|
|
639
|
+
openSub(master_ip, topic, msg_type, callback);
|
|
587
640
|
});
|
|
588
641
|
|
|
589
642
|
program
|
|
590
|
-
.command('hz
|
|
591
|
-
.description('Monitor the publishing rate of a NEP+ topic
|
|
643
|
+
.command('hz [topic]')
|
|
644
|
+
.description('Monitor the publishing rate of a NEP+ topic')
|
|
592
645
|
.action(async (topic, msg_type) => {
|
|
593
|
-
const
|
|
594
|
-
const master_ip = "127.0.0.1";
|
|
646
|
+
const allowedFormats = ["json", "msgpack", "bytes", "images", "dictionary", "string"];
|
|
647
|
+
const master_ip = "127.0.0.1";
|
|
595
648
|
|
|
596
|
-
const
|
|
597
|
-
|
|
649
|
+
const requester = createRequester(master_ip, PORT_MASTER_INFO);
|
|
650
|
+
const results = await sendRequest(requester, { "input": "topics" });
|
|
651
|
+
const timestamps = []; // Store timestamps of received messages
|
|
598
652
|
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
const timestamp = date.getTime();
|
|
653
|
+
topic = await selectTopic(results, topic);
|
|
654
|
+
msg_type = await selectMsgType(allowedFormats, msg_type, topic);
|
|
602
655
|
|
|
603
|
-
|
|
656
|
+
const callback = function (msg) {
|
|
657
|
+
const date = new Date();
|
|
658
|
+
const timestamp = date.getTime();
|
|
604
659
|
|
|
605
|
-
|
|
606
|
-
const tenSecondsAgo = timestamp - 10000;
|
|
607
|
-
while (timestamps[0] < tenSecondsAgo) {
|
|
608
|
-
timestamps.shift();
|
|
609
|
-
}
|
|
610
|
-
};
|
|
660
|
+
timestamps.push(timestamp);
|
|
611
661
|
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
662
|
+
// Remove timestamps older than ten seconds
|
|
663
|
+
const tenSecondsAgo = timestamp - 10000;
|
|
664
|
+
while (timestamps[0] < tenSecondsAgo) {
|
|
665
|
+
timestamps.shift();
|
|
616
666
|
}
|
|
617
|
-
|
|
618
|
-
const conf = node.hybrid(master_ip);
|
|
619
|
-
|
|
620
|
-
console.log("Topic:", topic)
|
|
621
|
-
console.log("Message type:", msg_type)
|
|
667
|
+
};
|
|
622
668
|
|
|
623
|
-
|
|
669
|
+
setInterval(() => {
|
|
670
|
+
// Calculate statistics for the last 10 seconds
|
|
671
|
+
const now = Date.now();
|
|
672
|
+
const tenSecondsAgo = now - 10000;
|
|
673
|
+
const recentTimestamps = timestamps.filter((ts) => ts > tenSecondsAgo);
|
|
674
|
+
const rate = recentTimestamps.length / 10; // Messages per second for a 10-second window
|
|
675
|
+
|
|
676
|
+
console.log("Average rate:", rate.toFixed(2), "Hz");
|
|
677
|
+
console.log("Min:", (10 / Math.max(rate, 0.1)).toFixed(2), "s");
|
|
678
|
+
console.log("Max:", (10 / Math.min(rate, 10)).toFixed(2), "s");
|
|
679
|
+
console.log("Std dev:", calculateStdDev(recentTimestamps, now).toFixed(2), "s");
|
|
680
|
+
console.log("Window:", recentTimestamps.length);
|
|
624
681
|
console.log("");
|
|
682
|
+
}, 1000);
|
|
625
683
|
|
|
626
|
-
setInterval(() => {
|
|
627
|
-
// Calculate statistics for the last 10 seconds
|
|
628
|
-
const now = Date.now();
|
|
629
|
-
const tenSecondsAgo = now - 10000;
|
|
630
|
-
const recentTimestamps = timestamps.filter((ts) => ts > tenSecondsAgo);
|
|
631
|
-
const rate = recentTimestamps.length / 10; // Messages per second for a 10-second window
|
|
632
|
-
|
|
633
|
-
console.log("Average rate:", rate.toFixed(2), "Hz");
|
|
634
|
-
console.log("Min:", (10 / Math.max(rate, 0.1)).toFixed(2), "s");
|
|
635
|
-
console.log("Max:", (10 / Math.min(rate, 10)).toFixed(2), "s");
|
|
636
|
-
console.log("Std dev:", calculateStdDev(recentTimestamps, now).toFixed(2), "s");
|
|
637
|
-
console.log("Window:", recentTimestamps.length);
|
|
638
|
-
console.log("");
|
|
639
|
-
}, 1000);
|
|
640
|
-
};
|
|
641
684
|
|
|
642
685
|
const calculateStdDev = (timestamps) => {
|
|
643
686
|
if (timestamps.length < 2) {
|
|
@@ -666,33 +709,330 @@ program
|
|
|
666
709
|
return stdDeviation;
|
|
667
710
|
};
|
|
668
711
|
|
|
712
|
+
openSub(master_ip, topic, msg_type, callback);
|
|
669
713
|
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
714
|
+
});
|
|
715
|
+
|
|
716
|
+
program
|
|
717
|
+
.command('delay [topic]')
|
|
718
|
+
.description('Monitor the delay between messages of a NEP+ topic')
|
|
719
|
+
.action(async (topic, msg_type) => {
|
|
720
|
+
const allowedFormats = ["json", "msgpack", "bytes", "images", "dictionary", "string"];
|
|
721
|
+
const master_ip = "127.0.0.1";
|
|
722
|
+
|
|
723
|
+
const requester = createRequester(master_ip, PORT_MASTER_INFO);
|
|
724
|
+
const results = await sendRequest(requester, { "input": "topics" });
|
|
725
|
+
|
|
726
|
+
topic = await selectTopic(results, topic);
|
|
727
|
+
msg_type = await selectMsgType(allowedFormats, msg_type, topic);
|
|
728
|
+
|
|
729
|
+
let lastTimestamp = null; // Store timestamp of the last received message
|
|
730
|
+
|
|
731
|
+
const callback = function (msg) {
|
|
732
|
+
const date = new Date();
|
|
733
|
+
const timestamp = date.getTime();
|
|
734
|
+
|
|
735
|
+
if (lastTimestamp !== null) {
|
|
736
|
+
const delay = timestamp - lastTimestamp;
|
|
737
|
+
console.log("Delay:", delay.toFixed(2), "ms");
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
lastTimestamp = timestamp;
|
|
741
|
+
};
|
|
742
|
+
|
|
743
|
+
openSub(master_ip, topic, msg_type, callback);
|
|
744
|
+
});
|
|
745
|
+
|
|
746
|
+
program
|
|
747
|
+
.command('bw [topic]')
|
|
748
|
+
.description('Monitor the bandwidth used by a NEP+ topic')
|
|
749
|
+
.action(async (topic, msg_type) => {
|
|
750
|
+
const allowedFormats = ["json", "msgpack", "bytes", "images", "dictionary", "string"];
|
|
751
|
+
const master_ip = "127.0.0.1";
|
|
673
752
|
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
await requester.send(message);
|
|
677
|
-
const [result] = await requester.receive();
|
|
678
|
-
const results = JSON.parse(result.toString());
|
|
753
|
+
const requester = createRequester(master_ip, PORT_MASTER_INFO);
|
|
754
|
+
const results = await sendRequest(requester, { "input": "topics" });
|
|
679
755
|
|
|
680
|
-
|
|
681
|
-
|
|
756
|
+
topic = await selectTopic(results, topic);
|
|
757
|
+
msg_type = await selectMsgType(allowedFormats, msg_type, topic);
|
|
758
|
+
|
|
759
|
+
let totalBytes = 0; // Store total bytes received
|
|
760
|
+
|
|
761
|
+
const callback = function (msg) {
|
|
762
|
+
let strMsg;
|
|
763
|
+
|
|
764
|
+
if (typeof msg === 'string') {
|
|
765
|
+
strMsg = msg;
|
|
766
|
+
} else if (Buffer.isBuffer(msg)) {
|
|
767
|
+
strMsg = msg.toString('utf8');
|
|
682
768
|
} else {
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
769
|
+
// Convert JSON to string
|
|
770
|
+
strMsg = JSON.stringify(msg);
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
const bytes = Buffer.byteLength(strMsg, 'utf8');
|
|
774
|
+
totalBytes += bytes;
|
|
775
|
+
};
|
|
776
|
+
|
|
777
|
+
setInterval(() => {
|
|
778
|
+
console.log("Bandwidth:", (totalBytes / (1024 * 1024)).toFixed(2), "MB");
|
|
779
|
+
totalBytes = 0; // Reset total bytes for the next interval
|
|
780
|
+
}, 1000);
|
|
781
|
+
|
|
782
|
+
openSub(master_ip, topic, msg_type, callback);
|
|
783
|
+
});
|
|
784
|
+
|
|
785
|
+
|
|
786
|
+
program
|
|
787
|
+
.command('publish <topic> <message>')
|
|
788
|
+
.description('Publish a message to a NEP+ topic (only JSON messages are supported)')
|
|
789
|
+
.action((topic, message) => {
|
|
790
|
+
|
|
791
|
+
var openpub = function (master_ip = "127.0.0.1") {
|
|
792
|
+
|
|
793
|
+
var pubFunction = function () {
|
|
794
|
+
|
|
795
|
+
var msg = message.replace(/'/g, '"')
|
|
796
|
+
console.log(JSON.parse(msg))
|
|
797
|
+
pub.publish(JSON.parse(msg))
|
|
798
|
+
console.log("Message published")
|
|
799
|
+
|
|
800
|
+
process.exit(1)
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
|
|
804
|
+
var node = new nep.Node("nep-cli-pub")
|
|
805
|
+
var pub = node.new_pub(topic, "json")
|
|
806
|
+
setTimeout(pubFunction, 1000)
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
async function run() {
|
|
810
|
+
var requester = new zmq.Request;
|
|
811
|
+
var master_ip = "127.0.0.1"
|
|
812
|
+
|
|
813
|
+
requester.connect("tcp://" + master_ip + ":" + PORT_MASTER_INFO);
|
|
814
|
+
|
|
815
|
+
let msg = { "input": "topics" }
|
|
816
|
+
var message = JSON.stringify(msg);
|
|
817
|
+
await requester.send(message)
|
|
818
|
+
const [result] = await requester.receive()
|
|
819
|
+
var results = JSON.parse(result.toString())
|
|
820
|
+
//console.log(results);
|
|
821
|
+
if (results["input"].includes(topic)) {
|
|
822
|
+
console.log("")
|
|
823
|
+
openpub(master_ip)
|
|
824
|
+
}
|
|
825
|
+
else {
|
|
826
|
+
console.log("Topic is not registered");
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
run()
|
|
830
|
+
});
|
|
831
|
+
|
|
832
|
+
|
|
833
|
+
const { Input } = require('enquirer');
|
|
834
|
+
|
|
835
|
+
const selectTimeout = async (defaultTimeout = 1000) => {
|
|
836
|
+
try {
|
|
837
|
+
const prompt = new Input({
|
|
838
|
+
name: 'timeout',
|
|
839
|
+
message: 'Enter the timeout in milliseconds:',
|
|
840
|
+
initial: defaultTimeout,
|
|
841
|
+
validate: value => /^\d+$/.test(value) ? true : 'Timeout must be a number'
|
|
842
|
+
});
|
|
843
|
+
|
|
844
|
+
const timeout = await prompt.run();
|
|
845
|
+
return Number(timeout);
|
|
846
|
+
} catch (error) {
|
|
847
|
+
process.exit(1);
|
|
848
|
+
}
|
|
849
|
+
};
|
|
850
|
+
|
|
851
|
+
program
|
|
852
|
+
.command('record [topic] [msg_type] [timeout]')
|
|
853
|
+
.description('Record messages from a NEP+ topic to a file')
|
|
854
|
+
.action(async (topic, msg_type, timeout = 60000) => {
|
|
855
|
+
const allowedFormats = ["json", "msgpack", "bytes", "images", "dictionary", "string"];
|
|
856
|
+
const master_ip = "127.0.0.1";
|
|
857
|
+
|
|
858
|
+
const requester = createRequester(master_ip, PORT_MASTER_INFO);
|
|
859
|
+
const results = await sendRequest(requester, { "input": "topics" });
|
|
860
|
+
|
|
861
|
+
topic = await selectTopic(results, topic);
|
|
862
|
+
msg_type = await selectMsgType(allowedFormats, msg_type, topic);
|
|
863
|
+
timeout = await selectTimeout(timeout);
|
|
864
|
+
|
|
865
|
+
const date = new Date();
|
|
866
|
+
const timestamp = date.toISOString().replace(/:/g, '-');
|
|
867
|
+
const writeStream = fs.createWriteStream(`${topic.replace('/', '_')}_bag_${timestamp}.json`);
|
|
868
|
+
|
|
869
|
+
let lastTimestamp = null;
|
|
870
|
+
|
|
871
|
+
const callback = function (msg) {
|
|
872
|
+
const timestamp = Date.now();
|
|
873
|
+
const delay = lastTimestamp !== null ? timestamp - lastTimestamp : 0;
|
|
874
|
+
lastTimestamp = timestamp;
|
|
875
|
+
|
|
876
|
+
const record = { msg, delay };
|
|
877
|
+
writeStream.write(JSON.stringify(record) + '\n');
|
|
878
|
+
};
|
|
879
|
+
|
|
880
|
+
|
|
881
|
+
setTimeout(() => {
|
|
882
|
+
writeStream.end();
|
|
883
|
+
console.log('Recording stopped and file saved.');
|
|
884
|
+
process.exit();
|
|
885
|
+
}, timeout);
|
|
886
|
+
|
|
887
|
+
openSub(master_ip, topic, msg_type, callback);
|
|
888
|
+
setTimeout(() => {
|
|
889
|
+
console.log('\nRecording has started. Press Ctrl+C to stop and save the file');
|
|
890
|
+
}, 1000);
|
|
891
|
+
|
|
892
|
+
process.on('SIGINT', () => {
|
|
893
|
+
writeStream.end();
|
|
894
|
+
console.log('Recording stopped and file saved.');
|
|
895
|
+
process.exit();
|
|
896
|
+
});
|
|
897
|
+
});
|
|
898
|
+
|
|
899
|
+
|
|
900
|
+
|
|
901
|
+
|
|
902
|
+
const readline = require('readline');
|
|
903
|
+
const e = require('express');
|
|
904
|
+
|
|
905
|
+
program
|
|
906
|
+
.command('play <bagfile> <topic>')
|
|
907
|
+
.description('Play messages from a bag file to a NEP+ topic')
|
|
908
|
+
.action(async (bagfile, topic) => {
|
|
909
|
+
const allowedFormats = ["json", "msgpack", "bytes", "images", "dictionary", "string"];
|
|
910
|
+
const master_ip = "127.0.0.1";
|
|
911
|
+
|
|
912
|
+
const requester = createRequester(master_ip, PORT_MASTER_INFO);
|
|
913
|
+
const results = await sendRequest(requester, { "input": "topics" });
|
|
914
|
+
|
|
915
|
+
topic = await selectTopic(results, topic);
|
|
916
|
+
|
|
917
|
+
const node = new nep.Node("nep-cli-pub");
|
|
918
|
+
const pub = node.new_pub(topic, "json");
|
|
919
|
+
|
|
920
|
+
const readInterface = readline.createInterface({
|
|
921
|
+
input: fs.createReadStream(bagfile),
|
|
922
|
+
console: false
|
|
923
|
+
});
|
|
924
|
+
|
|
925
|
+
let records = [];
|
|
926
|
+
|
|
927
|
+
readInterface.on('line', function (line) {
|
|
928
|
+
const record = JSON.parse(line);
|
|
929
|
+
records.push(record);
|
|
930
|
+
});
|
|
931
|
+
|
|
932
|
+
readInterface.on('close', function () {
|
|
933
|
+
let index = 0;
|
|
934
|
+
|
|
935
|
+
setTimeout(() => {
|
|
936
|
+
console.log("\nPublishing messages from bag file. Press Ctrl+C to stop.");
|
|
937
|
+
}, 500);
|
|
938
|
+
|
|
939
|
+
|
|
940
|
+
function publishNext() {
|
|
941
|
+
if (index < records.length) {
|
|
942
|
+
const record = records[index];
|
|
943
|
+
pub.publish(record.msg);
|
|
944
|
+
index++;
|
|
945
|
+
let delay = 0;
|
|
946
|
+
if (index < records.length) {
|
|
947
|
+
delay = records[index].delay;
|
|
948
|
+
}
|
|
949
|
+
setTimeout(publishNext, delay);
|
|
686
950
|
} else {
|
|
687
|
-
|
|
951
|
+
process.exit();
|
|
688
952
|
}
|
|
689
953
|
}
|
|
690
|
-
}
|
|
691
954
|
|
|
692
|
-
|
|
955
|
+
publishNext();
|
|
956
|
+
});
|
|
957
|
+
});
|
|
958
|
+
|
|
959
|
+
|
|
960
|
+
|
|
961
|
+
program
|
|
962
|
+
.command('app')
|
|
963
|
+
.description('Open NEP+ App (only if installed)')
|
|
964
|
+
.action(() => {
|
|
965
|
+
// Get the username of the logged-in user
|
|
966
|
+
const username = os.userInfo().username;
|
|
967
|
+
|
|
968
|
+
// Specify the path to the executable
|
|
969
|
+
const appFolderName = 'nepplus-app';
|
|
970
|
+
const executableName = 'nepplus-app.exe';
|
|
971
|
+
const executablePath = `C:\\Users\\${username}\\AppData\\Local\\Programs\\${appFolderName}\\${executableName}`;
|
|
972
|
+
|
|
973
|
+
// Run the executable with proper escaping
|
|
974
|
+
exec(`"${executablePath}"`, (error, stdout, stderr) => {
|
|
975
|
+
if (error) {
|
|
976
|
+
console.error(`Error: ${error.message}`);
|
|
977
|
+
return;
|
|
978
|
+
}
|
|
979
|
+
console.log(`stdout: ${stdout}`);
|
|
980
|
+
console.error(`stderr: ${stderr}`);
|
|
981
|
+
});
|
|
693
982
|
});
|
|
694
983
|
|
|
695
984
|
|
|
985
|
+
program
|
|
986
|
+
.command('open [programName]')
|
|
987
|
+
.description('Open a NEP+ GUIs (only if installed)')
|
|
988
|
+
.action(async (programName) => {
|
|
989
|
+
const programs = ['cameras', 'hxri'];
|
|
990
|
+
|
|
991
|
+
// If a program name is provided as an argument, use it; otherwise, show autocomplete
|
|
992
|
+
if (!programName) {
|
|
993
|
+
try {
|
|
994
|
+
const autoComplete = new AutoComplete({
|
|
995
|
+
name: 'program',
|
|
996
|
+
message: 'Select a program:',
|
|
997
|
+
choices: programs,
|
|
998
|
+
});
|
|
999
|
+
programName = await autoComplete.run();
|
|
1000
|
+
} catch (error) {
|
|
1001
|
+
process.exit(1);
|
|
1002
|
+
}
|
|
1003
|
+
} else if (!programs.includes(programName)) {
|
|
1004
|
+
|
|
1005
|
+
try {
|
|
1006
|
+
const autoComplete = new AutoComplete({
|
|
1007
|
+
name: 'program',
|
|
1008
|
+
message: 'Invalid program name. Select a valid program:',
|
|
1009
|
+
choices: programs,
|
|
1010
|
+
});
|
|
1011
|
+
programName = await autoComplete.run();
|
|
1012
|
+
} catch (error) {
|
|
1013
|
+
process.exit(1);
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
// Get the username of the logged-in user
|
|
1018
|
+
const username = os.userInfo().username;
|
|
1019
|
+
|
|
1020
|
+
// Specify the path to the executable
|
|
1021
|
+
const appFolderName = 'nepplus' + "-" + programName;
|
|
1022
|
+
const executableName = 'nepplus' + "-" + `${programName}.exe`;
|
|
1023
|
+
const executablePath = `C:\\Users\\${username}\\AppData\\Local\\Programs\\${appFolderName}\\${executableName}`;
|
|
1024
|
+
|
|
1025
|
+
// Run the executable with proper escaping
|
|
1026
|
+
exec(`"${executablePath}"`, (error, stdout, stderr) => {
|
|
1027
|
+
if (error) {
|
|
1028
|
+
console.error(`Error: ${error.message}`);
|
|
1029
|
+
return;
|
|
1030
|
+
}
|
|
1031
|
+
console.log(`stdout: ${stdout}`);
|
|
1032
|
+
console.error(`stderr: ${stderr}`);
|
|
1033
|
+
});
|
|
1034
|
+
});
|
|
1035
|
+
|
|
696
1036
|
program
|
|
697
1037
|
.command('docs')
|
|
698
1038
|
.description('Open the NEP+ documentation in the default browser')
|