Commit ead1b62e authored by Léo-Paul Géneau's avatar Léo-Paul Géneau 👾

component/qjs-wrapper: add message API

Add support of the message API (send and receive custom messages between drones).
parent aa71ff0a
...@@ -10,8 +10,8 @@ parts = qjs-wrapper ...@@ -10,8 +10,8 @@ parts = qjs-wrapper
recipe = slapos.recipe.cmmi recipe = slapos.recipe.cmmi
shared = true shared = true
configure-command = true configure-command = true
url = https://lab.nexedi.com/nexedi/qjs-wrapper/-/archive/v0.3/qjs-wrapper-v0.3.tar.gz url = https://lab.nexedi.com/nexedi/qjs-wrapper/-/archive/v1.0/qjs-wrapper-v1.0.tar.gz
md5sum = 850b9d5b8530521635a08baa1a2aa9e0 md5sum = 0f1393fa15d46b2b551836197af9de46
environment = environment =
C_INCLUDE_PATH=include:${open62541:location}/include:${open62541:location}/deps:${open62541:location}/src/pubsub:${quickjs:location}/include C_INCLUDE_PATH=include:${open62541:location}/include:${open62541:location}/deps:${open62541:location}/src/pubsub:${quickjs:location}/include
CPLUS_INCLUDE_PATH=include:${mavsdk:location}/include:${mavsdk:location}/include/mavsdk CPLUS_INCLUDE_PATH=include:${mavsdk:location}/include:${mavsdk:location}/include/mavsdk
......
...@@ -14,8 +14,6 @@ ...@@ -14,8 +14,6 @@
* is-a-simulation: Must be set to 'true' to automatically take off during simulation * is-a-simulation: Must be set to 'true' to automatically take off during simulation
* leader-id: Id of the drone chosen to be the leader of the swarm
* multicast-ipv6: IPv6 of the multicast group of the swarm * multicast-ipv6: IPv6 of the multicast group of the swarm
* net-if: Network interface used for multicast traffic * net-if: Network interface used for multicast traffic
......
...@@ -14,16 +14,16 @@ ...@@ -14,16 +14,16 @@
# not need these here). # not need these here).
[instance-profile] [instance-profile]
filename = instance.cfg filename = instance.cfg
md5sum = bf69b4317283f59acda44e341c0a644e md5sum = 7d4969239eb9d46bb44d57fc32b68c44
[main] [main]
filename = main.js filename = main.js
md5sum = 271736c5286f0032b22878f915964429 md5sum = 4b1b27ea3e06b8d40cbc33f0ec617601
[pubsub] [pubsub]
filename = pubsub.js filename = pubsub.js
md5sum = d8798c3206f129e8715afd3ca23afa1a md5sum = 4a0c63f9e088fa525a3699484d193c4d
[worker] [worker]
filename = worker.js filename = worker.js
md5sum = 2f3761f14a4cbd557c91f949fba3471e md5sum = 5ed534e9ca56b9c0ee321b96b5d7c458
...@@ -29,8 +29,6 @@ init = ...@@ -29,8 +29,6 @@ init =
options['autopilot-ip'] = options['slapparameter-dict'].get('autopilot-ip', '192.168.27.1') options['autopilot-ip'] = options['slapparameter-dict'].get('autopilot-ip', '192.168.27.1')
options['id'] = options['slapparameter-dict'].get('id', 1) options['id'] = options['slapparameter-dict'].get('id', 1)
options['is-a-simulation'] = options['slapparameter-dict'].get('is-a-simulation', False) options['is-a-simulation'] = options['slapparameter-dict'].get('is-a-simulation', False)
options['leader-id'] = options['slapparameter-dict'].get('leader-id', 1)
options['is-leader'] = options['id'] == options['leader-id']
options['multicast-ipv6'] = options['slapparameter-dict'].get('multicast-ip', 'ff15::1111') options['multicast-ipv6'] = options['slapparameter-dict'].get('multicast-ip', 'ff15::1111')
options['net-if'] = options['slapparameter-dict'].get('net-if', 'eth0') options['net-if'] = options['slapparameter-dict'].get('net-if', 'eth0')
options['drone-id-list'] = options['slapparameter-dict'].get('drone-id-list', []) options['drone-id-list'] = options['slapparameter-dict'].get('drone-id-list', [])
...@@ -71,8 +69,6 @@ context = ...@@ -71,8 +69,6 @@ context =
extra-context = extra-context =
key autopilot_ip drone:autopilot-ip key autopilot_ip drone:autopilot-ip
key id drone:id key id drone:id
key leader_id drone:leader-id
key is_leader drone:is-leader
key is_a_simulation drone:is-a-simulation key is_a_simulation drone:is-a-simulation
key is_a_drone drone:is-a-drone key is_a_drone drone:is-a-drone
key log_dir directory:log key log_dir directory:log
......
/*global console*/ /* global console */
import { import {
arm, arm,
start, start,
...@@ -29,15 +29,14 @@ import { exit } from "std"; ...@@ -29,15 +29,14 @@ import { exit } from "std";
// Use the same FPS than browser's requestAnimationFrame // Use the same FPS than browser's requestAnimationFrame
FPS = 1000 / 60, FPS = 1000 / 60,
previous_timestamp, previous_timestamp,
can_update = false, can_update = false;
i = 0;
function connect() { function connect() {
console.log("Will connect to", URL); console.log("Will connect to", URL);
exit_on_fail(start(URL, LOG_FILE, 60), "Failed to connect to " + URL); exitOnFail(start(URL, LOG_FILE, 60), "Failed to connect to " + URL);
} }
function exit_on_fail(ret, msg) { function exitOnFail(ret, msg) {
if (ret) { if (ret) {
console.log(msg); console.log(msg);
quit(1); quit(1);
...@@ -59,14 +58,15 @@ import { exit } from "std"; ...@@ -59,14 +58,15 @@ import { exit } from "std";
pubsubWorker = new Worker("{{ pubsub_script }}"); pubsubWorker = new Worker("{{ pubsub_script }}");
pubsubWorker.onmessage = function(e) { pubsubWorker.onmessage = function(e) {
if (!e.data.publishing) if (!e.data.publishing) {
pubsubWorker.onmessage = null; pubsubWorker.onmessage = null;
}
} }
worker.postMessage({type: "initPubsub"}); worker.postMessage({type: "initPubsub"});
function takeOff() { function takeOff() {
exit_on_fail(arm(), "Failed to arm"); exitOnFail(arm(), "Failed to arm");
takeOffAndWait(); takeOffAndWait();
} }
...@@ -88,7 +88,7 @@ import { exit } from "std"; ...@@ -88,7 +88,7 @@ import { exit } from "std";
} }
function loop() { function loop() {
var timestamp = Date.now(), let timestamp = Date.now(),
timeout; timeout;
if (can_update) { if (can_update) {
if (FPS <= (timestamp - previous_timestamp)) { if (FPS <= (timestamp - previous_timestamp)) {
...@@ -114,11 +114,12 @@ import { exit } from "std"; ...@@ -114,11 +114,12 @@ import { exit } from "std";
} }
worker.onmessage = function (e) { worker.onmessage = function (e) {
var type = e.data.type; let type = e.data.type;
if (type === 'initialized') { if (type === 'initialized') {
pubsubWorker.postMessage({ pubsubWorker.postMessage({
action: "run", action: "run",
id: {{ id }}, id: {{ id }},
interval: FPS,
publish: IS_A_DRONE publish: IS_A_DRONE
}); });
load(); load();
......
...@@ -4,12 +4,12 @@ import {Worker} from "os"; ...@@ -4,12 +4,12 @@ import {Worker} from "os";
const PORT = "4840"; const PORT = "4840";
const IPV6 = "{{ ipv6 }}"; const IPV6 = "{{ ipv6 }}";
var parent = Worker.parent; let parent = Worker.parent;
function handle_msg(e) { function handle_msg(e) {
switch(e.data.action) { switch(e.data.action) {
case "run": case "run":
runPubsub(IPV6, PORT, "{{ net_if }}", e.data.id, e.data.publish); runPubsub(IPV6, PORT, "{{ net_if }}", e.data.id, e.data.interval, e.data.publish);
parent.postMessage({running: false}); parent.postMessage({running: false});
parent.onmessage = null; parent.onmessage = null;
break; break;
......
/*global console*/ /* global console, std */
import { import {
Drone, Drone,
triggerParachute, triggerParachute,
...@@ -14,12 +14,12 @@ import { ...@@ -14,12 +14,12 @@ import {
loiter, loiter,
setAirspeed, setAirspeed,
setAltitude, setAltitude,
setCheckpoint,
setManualControlInput, setManualControlInput,
setMessage,
setTargetCoordinates setTargetCoordinates
} from "{{ qjs_wrapper }}"; } from "{{ qjs_wrapper }}";
import { Worker } from "os"
import * as std from "std"; import * as std from "std";
import { Worker } from "os";
(function (console, Worker) { (function (console, Worker) {
// Every script is evaluated per drone // Every script is evaluated per drone
...@@ -28,13 +28,11 @@ import * as std from "std"; ...@@ -28,13 +28,11 @@ import * as std from "std";
drone_id_list = [{{ drone_id_list }}], drone_id_list = [{{ drone_id_list }}],
IS_A_DRONE = {{ 'true' if is_a_drone else 'false' }}; IS_A_DRONE = {{ 'true' if is_a_drone else 'false' }};
var parent = Worker.parent, let parent = Worker.parent,
user_me = { user_me = {
//for debugging purpose //for debugging purpose
fdopen: std.fdopen, fdopen: std.fdopen,
in: std.in, in: std.in,
//to move into user script
setCheckpoint: setCheckpoint,
//required to fly //required to fly
triggerParachute: triggerParachute, triggerParachute: triggerParachute,
drone_dict: {}, drone_dict: {},
...@@ -55,51 +53,68 @@ import * as std from "std"; ...@@ -55,51 +53,68 @@ import * as std from "std";
id: {{ id }}, id: {{ id }},
landed: landed, landed: landed,
loiter: loiter, loiter: loiter,
sendMsg: function(msg, id = -1) {
setMessage(JSON.stringify({ content: msg, dest_id: id }));
},
setAirspeed: setAirspeed, setAirspeed: setAirspeed,
setAltitude: setAltitude, setAltitude: setAltitude,
setTargetCoordinates: setTargetCoordinates setTargetCoordinates: setTargetCoordinates
}; };
function loadUserScript(path) { function loadUserScript(path) {
var script_content = std.loadFile(path); let script_content = std.loadFile(path);
if (script_content === null) { if (script_content === null) {
console.log('Failed to load user script ' + path); console.log("Failed to load user script " + path);
std.exit(1); std.exit(1);
} }
try { try {
std.evalScript( std.evalScript(
'function execUserScript(from, me) {' + "function execUserScript(from, me) {" +
script_content + script_content +
'};' "};"
); );
} catch (e) { } catch (e) {
console.log('Failed to evaluate user script', e); console.log("Failed to evaluate user script", e);
std.exit(1); std.exit(1);
} }
execUserScript(null, user_me); execUserScript(null, user_me);
// Call the drone onStart function // Call the drone onStart function
if (user_me.hasOwnProperty('onStart')) { if (user_me.hasOwnProperty("onStart")) {
user_me.onStart(); user_me.onStart();
} }
} }
function handleMainMessage(evt) { function handleMainMessage(evt) {
var type = evt.data.type; let type = evt.data.type,
message,
drone_id;
if (type === 'initPubsub') { if (type === "initPubsub") {
initPubsub(drone_id_list.length); initPubsub(drone_id_list.length);
for (let i = 0; i < drone_id_list.length; i++) { for (let i = 0; i < drone_id_list.length; i++) {
let id = drone_id_list[i]; drone_id = drone_id_list[i];
user_me.drone_dict[id] = new Drone(id); user_me.drone_dict[drone_id] = new Drone(drone_id);
user_me.drone_dict[id].init(i); user_me.drone_dict[drone_id].init(i);
} }
parent.postMessage({type: "initialized"}); parent.postMessage({type: "initialized"});
} } else if (type === "load") {
else if (type === 'load') {
loadUserScript(evt.data.path); loadUserScript(evt.data.path);
parent.postMessage({type: "loaded"}); parent.postMessage({type: "loaded"});
} else if (type === 'update') { } else if (type === "update") {
for (const [id, drone] of Object.entries(user_me.drone_dict)) {
message = drone.message
if (message.length > 0) {
message = JSON.parse(message);
if (user_me.id === id) {
continue;
}
if (user_me.hasOwnProperty("onGetMsg") &&
[-1, user_me.id].includes(message.dest_id)) {
user_me.onGetMsg(message.content);
}
}
}
// Call the drone onStart function // Call the drone onStart function
if (user_me.hasOwnProperty("onUpdate")) { if (user_me.hasOwnProperty("onUpdate")) {
if (IS_A_DRONE && isInManualMode()) { if (IS_A_DRONE && isInManualMode()) {
...@@ -109,7 +124,7 @@ import * as std from "std"; ...@@ -109,7 +124,7 @@ import * as std from "std";
} }
parent.postMessage({type: "updated"}); parent.postMessage({type: "updated"});
} else { } else {
throw new Error('Unsupported message type', type); throw new Error("Unsupported message type", type);
} }
} }
...@@ -123,5 +138,4 @@ import * as std from "std"; ...@@ -123,5 +138,4 @@ import * as std from "std";
std.exit(1); std.exit(1);
} }
}; };
}(console, Worker)); }(console, Worker));
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment