nep-cli 0.1.4 → 0.1.6
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/image.html +57 -0
- package/bin/index.js +196 -4
- package/package.json +5 -3
package/bin/image.html
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
|
|
4
|
+
<head>
|
|
5
|
+
<meta charset="UTF-8">
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
7
|
+
<title>NEP+ Image</title>
|
|
8
|
+
<!-- Include Vue 2 and Vuetify 2 from CDN -->
|
|
9
|
+
<link href="https://cdn.jsdelivr.net/npm/vuetify@2.6.3/dist/vuetify.min.css" rel="stylesheet">
|
|
10
|
+
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
|
|
11
|
+
<script src="https://cdn.jsdelivr.net/npm/vuetify@2.6.3/dist/vuetify.js"></script>
|
|
12
|
+
</head>
|
|
13
|
+
|
|
14
|
+
<body>
|
|
15
|
+
<div id="app">
|
|
16
|
+
<v-app>
|
|
17
|
+
<v-container>
|
|
18
|
+
<v-toolbar dense color="transparent">
|
|
19
|
+
<v-toolbar-title>Camera USB</v-toolbar-title>
|
|
20
|
+
<v-spacer></v-spacer>
|
|
21
|
+
|
|
22
|
+
</v-toolbar>
|
|
23
|
+
<v-row>
|
|
24
|
+
<v-col>
|
|
25
|
+
<v-card color="#1A1A1A">
|
|
26
|
+
<v-img :src="imageSrc" alt="Streamed Image" contain></v-img>
|
|
27
|
+
</v-card>
|
|
28
|
+
</v-col>
|
|
29
|
+
</v-row>
|
|
30
|
+
</v-container>
|
|
31
|
+
</v-app>
|
|
32
|
+
</div>
|
|
33
|
+
<script src="/socket.io/socket.io.js"></script> <!-- Include the Socket.IO client library -->
|
|
34
|
+
<script>
|
|
35
|
+
// Initialize Vue and Vuetify
|
|
36
|
+
new Vue({
|
|
37
|
+
el: '#app',
|
|
38
|
+
vuetify: new Vuetify(),
|
|
39
|
+
data() {
|
|
40
|
+
return {
|
|
41
|
+
imageSrc: '', // Initialize the image source as empty
|
|
42
|
+
};
|
|
43
|
+
},
|
|
44
|
+
mounted() {
|
|
45
|
+
const image = document.getElementById('image');
|
|
46
|
+
const socket = io.connect('http://localhost:3000'); // Connect to your server
|
|
47
|
+
|
|
48
|
+
socket.on('image', (data) => {
|
|
49
|
+
// Set the received image data as the image source
|
|
50
|
+
this.imageSrc = `data:image/jpeg;base64,${data.image}`;
|
|
51
|
+
});
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
</script>
|
|
55
|
+
</body>
|
|
56
|
+
|
|
57
|
+
</html>
|
package/bin/index.js
CHANGED
|
@@ -8,6 +8,14 @@ var nep_configuration = { current_port: 10000, IP: "127.0.0.1", brokers: {} }
|
|
|
8
8
|
const { exec } = require('child_process');
|
|
9
9
|
const { AutoComplete } = require('enquirer');
|
|
10
10
|
|
|
11
|
+
const open = require('open'); // Import the 'open' package
|
|
12
|
+
|
|
13
|
+
const express = require('express');
|
|
14
|
+
const http = require('http');
|
|
15
|
+
const socketIo = require('socket.io');
|
|
16
|
+
const fs = require('fs');
|
|
17
|
+
const zmqc = require("zeromq/v5-compat");
|
|
18
|
+
|
|
11
19
|
|
|
12
20
|
// Node saved here
|
|
13
21
|
var nodes_register = {};
|
|
@@ -246,6 +254,12 @@ var processMsg = function (json_msg, nodes_register, topic_register) {
|
|
|
246
254
|
}
|
|
247
255
|
|
|
248
256
|
const PORT_MASTER_INFO = 7010; // Default port for master info
|
|
257
|
+
const PORT_IMAGE = 3000;
|
|
258
|
+
const PORT_IMAGES = [];
|
|
259
|
+
for (let i = 3020; i <= 3040; i++) {
|
|
260
|
+
PORT_IMAGES.push(i);
|
|
261
|
+
}
|
|
262
|
+
|
|
249
263
|
|
|
250
264
|
program
|
|
251
265
|
.version('"0.1.2')
|
|
@@ -310,9 +324,9 @@ program
|
|
|
310
324
|
|
|
311
325
|
program
|
|
312
326
|
.command('open [programName]')
|
|
313
|
-
.description('Open
|
|
327
|
+
.description('Open NEP+ GUI')
|
|
314
328
|
.action(async (programName) => {
|
|
315
|
-
const programs = ['
|
|
329
|
+
const programs = ['cameras', 'hxri'];
|
|
316
330
|
|
|
317
331
|
// If a program name is provided as an argument, use it; otherwise, show autocomplete
|
|
318
332
|
if (!programName) {
|
|
@@ -350,6 +364,9 @@ program
|
|
|
350
364
|
});
|
|
351
365
|
});
|
|
352
366
|
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
|
|
353
370
|
program
|
|
354
371
|
.command('master')
|
|
355
372
|
.description('Start NEP master in Local host')
|
|
@@ -369,11 +386,11 @@ program
|
|
|
369
386
|
});
|
|
370
387
|
|
|
371
388
|
program
|
|
372
|
-
.command('master-ip <ip>'
|
|
389
|
+
.command('master-ip <ip>')
|
|
373
390
|
.description('Start NEP master in some specific IP')
|
|
374
391
|
.action(async (ip) => {
|
|
375
392
|
|
|
376
|
-
|
|
393
|
+
|
|
377
394
|
console.log(`IP Address: ${ip}`);
|
|
378
395
|
var node = new nep.Node("nep-cli");
|
|
379
396
|
nep_configuration["IP"] = ip;
|
|
@@ -811,6 +828,103 @@ program
|
|
|
811
828
|
run();
|
|
812
829
|
});
|
|
813
830
|
|
|
831
|
+
program
|
|
832
|
+
.command('monitor <topic>')
|
|
833
|
+
.description('Monitor the publishing rate of NEP+ topic in local host')
|
|
834
|
+
.action(async (topic) => {
|
|
835
|
+
const interfaces = os.networkInterfaces();
|
|
836
|
+
const master_ip = "127.0.0.1"; // Change to the appropriate master IP
|
|
837
|
+
|
|
838
|
+
const opensub = function () {
|
|
839
|
+
const timestamps = []; // To store message timestamps
|
|
840
|
+
|
|
841
|
+
const callback = function (msg) {
|
|
842
|
+
const date = new Date();
|
|
843
|
+
const timestamp = date.getTime();
|
|
844
|
+
|
|
845
|
+
timestamps.push(timestamp);
|
|
846
|
+
|
|
847
|
+
// Remove timestamps older than ten seconds
|
|
848
|
+
const tenSecondsAgo = timestamp - 10000;
|
|
849
|
+
while (timestamps[0] < tenSecondsAgo) {
|
|
850
|
+
timestamps.shift();
|
|
851
|
+
}
|
|
852
|
+
};
|
|
853
|
+
|
|
854
|
+
const messageFormat = topic.endsWith("/image") ? "string" : "string";
|
|
855
|
+
const node = new nep.Node("nep-cli-sub");
|
|
856
|
+
const conf = node.hybrid(master_ip);
|
|
857
|
+
const sub = node.new_sub(topic, messageFormat, callback, conf);
|
|
858
|
+
|
|
859
|
+
setInterval(() => {
|
|
860
|
+
// Calculate statistics for the last 10 seconds
|
|
861
|
+
const now = Date.now();
|
|
862
|
+
const tenSecondsAgo = now - 10000;
|
|
863
|
+
const recentTimestamps = timestamps.filter((ts) => ts > tenSecondsAgo);
|
|
864
|
+
const rate = recentTimestamps.length / 10; // Messages per second for a 10-second window
|
|
865
|
+
|
|
866
|
+
console.log("Average rate:", rate.toFixed(2), "Hz");
|
|
867
|
+
console.log("Min:", (10 / Math.max(rate, 0.1)).toFixed(2), "s");
|
|
868
|
+
console.log("Max:", (10 / Math.min(rate, 10)).toFixed(2), "s");
|
|
869
|
+
console.log("Std dev:", calculateStdDev(recentTimestamps, now).toFixed(2), "s");
|
|
870
|
+
console.log("Window:", recentTimestamps.length);
|
|
871
|
+
console.log("");
|
|
872
|
+
}, 1000);
|
|
873
|
+
};
|
|
874
|
+
|
|
875
|
+
const calculateStdDev = (timestamps) => {
|
|
876
|
+
if (timestamps.length < 2) {
|
|
877
|
+
return 0; // Standard deviation is not meaningful with less than two timestamps
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
// Calculate the time intervals between consecutive timestamps
|
|
881
|
+
const timeIntervals = [];
|
|
882
|
+
for (let i = 1; i < timestamps.length; i++) {
|
|
883
|
+
const interval = timestamps[i] - timestamps[i - 1];
|
|
884
|
+
timeIntervals.push(interval);
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
// Calculate the mean (average) of the time intervals
|
|
888
|
+
const mean = timeIntervals.reduce((acc, val) => acc + val, 0) / timeIntervals.length;
|
|
889
|
+
|
|
890
|
+
// Calculate the squared differences from the mean
|
|
891
|
+
const squaredDifferences = timeIntervals.map((interval) => Math.pow(interval - mean, 2));
|
|
892
|
+
|
|
893
|
+
// Calculate the mean of the squared differences
|
|
894
|
+
const meanSquaredDifference = squaredDifferences.reduce((acc, val) => acc + val, 0) / squaredDifferences.length;
|
|
895
|
+
|
|
896
|
+
// Calculate the square root of the mean squared difference
|
|
897
|
+
const stdDeviation = Math.sqrt(meanSquaredDifference);
|
|
898
|
+
|
|
899
|
+
return stdDeviation;
|
|
900
|
+
};
|
|
901
|
+
|
|
902
|
+
|
|
903
|
+
async function run() {
|
|
904
|
+
const requester = new zmq.Request();
|
|
905
|
+
requester.connect(`tcp://${master_ip}:${PORT_MASTER_INFO}`);
|
|
906
|
+
|
|
907
|
+
const msg = { "input": "topics" };
|
|
908
|
+
const message = JSON.stringify(msg);
|
|
909
|
+
await requester.send(message);
|
|
910
|
+
const [result] = await requester.receive();
|
|
911
|
+
const results = JSON.parse(result.toString());
|
|
912
|
+
|
|
913
|
+
if (results["state"] === "failure") {
|
|
914
|
+
console.log("Topic is not registered, use *nep topics* to see the list of avaliable topics");
|
|
915
|
+
} else {
|
|
916
|
+
console.log("");
|
|
917
|
+
if (results["input"].includes(topic)) {
|
|
918
|
+
opensub();
|
|
919
|
+
} else {
|
|
920
|
+
console.log("Topic is not registered, use *nep topics* to see the list of avaliable topics");
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
run();
|
|
926
|
+
});
|
|
927
|
+
|
|
814
928
|
program
|
|
815
929
|
.command('eth-listen <topic>')
|
|
816
930
|
.description('Subscribe to a NEP+ topic over Ethernet')
|
|
@@ -994,6 +1108,84 @@ async function getAndDisplayTopics(master_ip) {
|
|
|
994
1108
|
}
|
|
995
1109
|
}
|
|
996
1110
|
|
|
1111
|
+
program
|
|
1112
|
+
.command('image <topic>')
|
|
1113
|
+
.description('Start image server')
|
|
1114
|
+
.action((topic, options) => {
|
|
1115
|
+
async function run() {
|
|
1116
|
+
var requester = new zmq.Request;
|
|
1117
|
+
var master_ip = "127.0.0.1"
|
|
1118
|
+
var port = PORT_IMAGE
|
|
1119
|
+
|
|
1120
|
+
|
|
1121
|
+
|
|
1122
|
+
requester.connect("tcp://" + master_ip + ":" + PORT_MASTER_INFO);
|
|
1123
|
+
|
|
1124
|
+
let msg = { "input": "topics" }
|
|
1125
|
+
var message = JSON.stringify(msg);
|
|
1126
|
+
await requester.send(message)
|
|
1127
|
+
const [result] = await requester.receive()
|
|
1128
|
+
var results = JSON.parse(result.toString())
|
|
1129
|
+
//console.log(results);
|
|
1130
|
+
|
|
1131
|
+
|
|
1132
|
+
const programs = results["input"];
|
|
1133
|
+
const filteredList = programs.filter(value => value.includes("image"));
|
|
1134
|
+
|
|
1135
|
+
if (!topic) {
|
|
1136
|
+
const autoComplete = new AutoComplete({
|
|
1137
|
+
name: 'program',
|
|
1138
|
+
message: 'Select a program:',
|
|
1139
|
+
choices: filteredList,
|
|
1140
|
+
});
|
|
1141
|
+
topic = await autoComplete.run();
|
|
1142
|
+
} else if (!filteredList.includes(topic)) {
|
|
1143
|
+
const autoComplete = new AutoComplete({
|
|
1144
|
+
name: 'program',
|
|
1145
|
+
message: 'Invalid program name. Select a valid program:',
|
|
1146
|
+
choices: filteredList,
|
|
1147
|
+
});
|
|
1148
|
+
topic = await autoComplete.run();
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
|
|
1152
|
+
startServer(port, topic);
|
|
1153
|
+
open(`http://localhost:${port}`);
|
|
1154
|
+
|
|
1155
|
+
}
|
|
1156
|
+
run()
|
|
1157
|
+
|
|
1158
|
+
});
|
|
1159
|
+
|
|
1160
|
+
// Function to start the server
|
|
1161
|
+
function startServer(port, topic) {
|
|
1162
|
+
const app = express();
|
|
1163
|
+
const server = http.createServer(app);
|
|
1164
|
+
const io = socketIo(server);
|
|
1165
|
+
|
|
1166
|
+
var node = new nep.Node("nep-cli");
|
|
1167
|
+
|
|
1168
|
+
function getImage(msg) {
|
|
1169
|
+
// Send the received image data as-is to connected clients
|
|
1170
|
+
io.sockets.emit('image', { image: msg });
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
var sub = node.new_sub(topic, "image", getImage);
|
|
1174
|
+
|
|
1175
|
+
app.get('/', (req, res) => {
|
|
1176
|
+
res.sendFile(__dirname + '/image.html'); // Serve your HTML file
|
|
1177
|
+
});
|
|
1178
|
+
|
|
1179
|
+
server.listen(port, () => {
|
|
1180
|
+
console.log(`Server is running on http://localhost:${port}`);
|
|
1181
|
+
});
|
|
1182
|
+
|
|
1183
|
+
io.on('connection', (socket) => {
|
|
1184
|
+
const frameRate = 30; // Desired frame rate
|
|
1185
|
+
const interval = 1000 / frameRate; // Interval between frames
|
|
1186
|
+
});
|
|
1187
|
+
}
|
|
1188
|
+
|
|
997
1189
|
program.parse(process.argv);
|
|
998
1190
|
|
|
999
1191
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nep-cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"main": "./lib/nep.js",
|
|
5
5
|
"bin": {
|
|
6
6
|
"nep": "./bin/index.js"
|
|
@@ -11,10 +11,12 @@
|
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"commander": "^11.0.0",
|
|
13
13
|
"enquirer": "^2.4.1",
|
|
14
|
+
"express": "^4.18.2",
|
|
14
15
|
"fix-path": "^2.1.0",
|
|
15
16
|
"inquirer": "^9.2.10",
|
|
16
|
-
"nep-js": "^0.
|
|
17
|
-
"open": "
|
|
17
|
+
"nep-js": "^0.3.0",
|
|
18
|
+
"open": "7.4.2",
|
|
19
|
+
"socket.io": "^4.7.2",
|
|
18
20
|
"zeromq": "6.0.0-beta.6"
|
|
19
21
|
}
|
|
20
22
|
}
|