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

software/js-drone: add operator script

Handle operator script parameter.
parent 74702ed7
......@@ -16,6 +16,8 @@
* droneNetIf: Drone network interface used for multicast traffic
* debug: Must be set to 'true' to send drone logs through OPC-UA
* multicastIp: IPv6 of the multicast group of the swarm
* operatorScript: URL of operator's script to prepare the flight
* mapJson: URL of terrain's map used by the operator script
* flightScript: URL of user's script to execute to fly drone swarm
* loopPeriod: Minimal period (in milliseconds) between 2 executions of the flight script loop
* subscriberGuidList: List of computer id on which a GUI must be deployed
......@@ -54,7 +56,7 @@ For each drone is displayed:
### Buttons
* Start: sends a "start" message to the swarm and changes into a stop button
* Start: loads operator script and changes into a stop button
* Stop: sends a "stop" message to the swarm
* Switch leader: sends a "switch" message to the swarm, it is usually used to change the leader
* Quit: exits (closes websocket and stops pub/sub)
......
......@@ -22,19 +22,19 @@ md5sum = 01425a1c77e79788e1948398b9136724
[instance-profile]
filename = instance.cfg.in
md5sum = b9f2301782bee12f9ff257a8f0a192f5
md5sum = 44d78f9ee8bb4475d521bce8694cc963
[instance-root]
filename = instance-root.cfg.jinja2
md5sum = ed319ac31addb65842e38d5b20d2d8e7
md5sum = 2bec05e86d88d27c3c672a6be9a0d0b4
[instance-subscriber]
filename = instance-subscriber.cfg.in
md5sum = 8559dc8c95e9232060be6db3e0af4379
md5sum = 9418396fc75e820b9dd5e913855aafe2
[main]
_update_hash_filename_ = drone-scripts/main.js.jinja2
md5sum = 85135842e42a1f48a474467508723c0c
md5sum = 4d13e382ba6462e19b67aa67207324dc
[pubsub]
_update_hash_filename_ = drone-scripts/pubsub.js.jinja2
......@@ -46,4 +46,4 @@ md5sum = c3858b5ec7373a0932fcda911a8177b5
[worker]
_update_hash_filename_ = drone-scripts/worker.js.jinja2
md5sum = d0fb066afb7a93fd7000953dde3a3fca
md5sum = a587a4be94dc7bc9de7b385828f2134f
......@@ -39,11 +39,11 @@ import { err, exit, open, out } from "std";
(function (arm, console, dup2, err, exec, exit, kill, open, out, pipe, read,
scriptArgs, setLog, setReadHandler, setTimeout, signal, start, stop,
stopPubsub, waitpid, Worker, SIGINT, SIGTERM, WNOHANG) {
{% else %}
{% else -%}
(function (console, dup2, err, exec, exit, kill, open, out, pipe, read,
scriptArgs, setReadHandler, setTimeout, signal, stopPubsub, waitpid,
Worker, SIGINT, SIGTERM, WNOHANG) {
{% endif %}
{% endif -%}
"use strict";
var CONF_PATH = "{{ configuration }}",
......@@ -61,6 +61,10 @@ import { err, exit, open, out } from "std";
tail_pid,
worker,
user_script = scriptArgs[1],
{% if not isADrone -%}
operator_script = scriptArgs[2],
map_json = scriptArgs[3],
{% endif -%}
LOOP_EXECUTION_PERIOD = configuration.loopPeriod,
previous_timestamp,
can_update = false;
......@@ -99,9 +103,9 @@ import { err, exit, open, out } from "std";
function quit(exit_code) {
worker.onmessage = null;
stopPubsub();
{% if isADrone %}
{% if isADrone -%}
stop();
{% endif %}
{% endif -%}
QUICKJS_LOG_FILE.close();
if (log_r_pipe) {
setReadHandler(log_r_pipe[0], null);
......@@ -128,7 +132,7 @@ import { err, exit, open, out } from "std";
}
}
{% if isADrone %}
{% if isADrone -%}
function connect() {
var address = configuration.autopilotIp + ":" + configuration.autopilotPort;
console.log("Will connect to", address);
......@@ -145,7 +149,7 @@ import { err, exit, open, out } from "std";
console.log("Connecting to aupilot\n");
connect();
{% endif %}
{% endif -%}
pubsubWorker = new Worker("{{ pubsub_script }}");
pubsubWorker.onmessage = function (e) {
......@@ -157,9 +161,9 @@ import { err, exit, open, out } from "std";
worker.postMessage({type: "initPubsub"});
function load() {
{% if isADrone %}
{% if isADrone -%}
exitOnFail(arm(), "Failed to arm");
{% endif %}
{% endif -%}
// First argument must provide the user script path
if (user_script === undefined) {
......@@ -169,7 +173,11 @@ import { err, exit, open, out } from "std";
worker.postMessage({
type: "load",
path: user_script
{% if not isADrone -%}
operator_path: operator_script,
map_path: map_json,
{% endif -%}
user_path: user_script
});
}
......
......@@ -94,17 +94,19 @@ import { evalScript, fdopen, loadFile, open } from "std";
setTargetCoordinates: setTargetCoordinates,
takeOff: takeOff,
{% endif -%}
sendMsg: function (msg, id) {
if (id === undefined) { id = -1; }
setMessage(JSON.stringify({
content: msg,
timestamp: Date.now(),
dest_id: id
}));
}
sendMsg: sendMsg
};
conf_file.close();
function sendMsg(msg, id) {
if (id === undefined) { id = -1; }
setMessage(JSON.stringify({
content: msg,
timestamp: Date.now(),
dest_id: id
}));
}
function exitWorker(exit_code) {
if (user_me.hasOwnProperty("onWebSocketMessage")) {
stopGwsocket();
......@@ -192,12 +194,17 @@ import { evalScript, fdopen, loadFile, open } from "std";
waitpid(gwsocket_pid, WNOHANG);
}
function loadUserScript(path) {
function getFileContent(path) {
var script_content = loadFile(path);
if (script_content === null) {
console.log("Failed to load user script " + path);
console.log("Failed to load script", path);
exitWorker(1);
}
return script_content;
}
function loadUserScript(path) {
var script_content = getFileContent(path);
try {
evalScript(
"function execUserScript(from, me) {" + script_content + "};"
......@@ -218,6 +225,23 @@ import { evalScript, fdopen, loadFile, open } from "std";
}
}
function loadOperatorScript(operator_path, map_path) {
var script_content = getFileContent(operator_path),
map_content = getFileContent.bind(map_path),
operator = {
getMapJSON: function () { return map_content; },
sendMsg: sendMsg
};
try {
evalScript(
"function execOperatorScript(operator) {" + script_content + "};"
);
user_me.execOperatorScript = execOperatorScript.bind(null, operator);
} catch (e) {
console.log("Failed to evaluate operator script", e);
}
}
function handleMainMessage(evt) {
var type = evt.data.type, message, peer_id, log;
......@@ -237,7 +261,10 @@ import { evalScript, fdopen, loadFile, open } from "std";
break;
case "load":
loadUserScript(evt.data.path);
loadUserScript(evt.data.user_path);
if (evt.data.hasOwnProperty("operator_path") && evt.data.hasOwnProperty("map_path")) {
loadOperatorScript(evt.data.operator_path, evt.data.map_path);
}
parent.postMessage({type: "loaded"});
break;
......
......@@ -50,11 +50,23 @@
"type": "string",
"default": "ff15::1111"
},
"operatorScript": {
"title": "Script's URL to prepare the flight",
"description": "URL of the operator script. The operator script is used to send a first message to the swarm.\nThis can be useful to determine some path planning depending on map informations.\nThis URL must be publicly accesible so that the drone can fetch the script.",
"type": "string",
"default": "https://lab.nexedi.com/nexedi/flight-scripts/-/raw/operator_script/operator.js"
},
"mapJson": {
"title": "Map's URL",
"description": "URL of the terrain's map. It is used as a data source by the operator script.",
"type": "string",
"default": "https://lab.nexedi.com/nexedi/flight-scripts/-/raw/operator_script/map.json"
},
"flightScript": {
"title": "Script's URL of the flight",
"description": "URL of the script which will be executed for the flight. This URL must be publicly accesible so that the drone can fetch the script.",
"type": "string",
"default": "https://lab.nexedi.com/nexedi/flight-scripts/-/raw/v2.0/default.js"
"default": "https://lab.nexedi.com/nexedi/flight-scripts/-/raw/operator_script/default.js"
},
"loopPeriod": {
"title": "Loop execution period",
......
......@@ -35,7 +35,9 @@ config-netIf = {{ parameter_dict['droneNetIf'] }}
{% else -%}
{% do subscriber_id_list.append(id) %}
config-isADrone = {{ dumps(False) }}
config-flightScript = https://lab.nexedi.com/nexedi/flight-scripts/-/raw/v2.0/subscribe.js
config-operatorScript = {{ parameter_dict['operatorScript'] }}
config-mapJson = {{ parameter_dict['mapJson'] }}
config-flightScript = https://lab.nexedi.com/nexedi/flight-scripts/-/raw/operator_script/subscribe.js
config-netIf = {{ parameter_dict['subscriberNetIf'] }}
{% endif -%}
config-multicastIp = {{ parameter_dict['multicastIp'] }}
......
......@@ -20,6 +20,21 @@ inline =
{% do parameter_dict.__setitem__('websocketPort', websocket_port) -%}
{{ json_module.dumps(parameter_dict) }}
[operator]
recipe = slapos.recipe.build:download
url = $${slap-configuration:configuration.operatorScript}
destination = $${directory:etc}/operator.js
offline = false
[map]
recipe = slapos.recipe.build:download
url = $${slap-configuration:configuration.mapJson}
destination = $${directory:etc}/map.json
offline = false
[qjs-launcher]
command-line += $${operator:target} $${map:target}
[script-js]
recipe = slapos.recipe.template:jinja2
template = ${script-js:target}
......
......@@ -37,7 +37,9 @@ default-parameters =
"debug": false,
"droneGuidList": [],
"droneNetIf": "eth0",
"flightScript": "https://lab.nexedi.com/nexedi/flight-scripts/-/raw/v2.0/default.js",
"operatorScript": "https://lab.nexedi.com/nexedi/flight-scripts/-/raw/operator_script/operator.js",
"mapJson": "https://lab.nexedi.com/nexedi/flight-scripts/-/raw/operator_script/map.json",
"flightScript": "https://lab.nexedi.com/nexedi/flight-scripts/-/raw/operator_script/default.js",
"loopPeriod": 200,
"multicastIp": "ff15::1111",
"subscriberGuidList":[],
......
......@@ -282,7 +282,9 @@ class SubscriberTestCase(SlapOSInstanceTestCase):
'debug': False,
'loopPeriod': LOOP_PERIOD,
'isADrone': False,
'flightScript': 'https://lab.nexedi.com/nexedi/flight-scripts/-/raw/v2.0/subscribe.js',
'operatorScript': 'https://lab.nexedi.com/nexedi/flight-scripts/-/raw/operator_script/operator.js',
'mapJson': 'https://lab.nexedi.com/nexedi/flight-scripts/-/raw/operator_script/map.json',
'flightScript': 'https://lab.nexedi.com/nexedi/flight-scripts/-/raw/operator_script/subscribe.js',
'netIf': OPC_UA_NET_IF,
'multicastIp': MCAST_GRP
}
......
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