Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
slapos
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Labels
Merge Requests
104
Merge Requests
104
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Jobs
Commits
Open sidebar
nexedi
slapos
Commits
632ad024
Commit
632ad024
authored
Jun 03, 2023
by
Léo-Paul Géneau
👾
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
software/js-drone: print pubsub logs in the GUI
parent
eae3a24e
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
172 additions
and
25 deletions
+172
-25
software/js-drone/buildout.hash.cfg
software/js-drone/buildout.hash.cfg
+3
-3
software/js-drone/drone-scripts/worker.js.jinja2
software/js-drone/drone-scripts/worker.js.jinja2
+14
-5
software/js-drone/test/test.py
software/js-drone/test/test.py
+51
-0
software/js-drone/web-gui/index.html.jinja2
software/js-drone/web-gui/index.html.jinja2
+33
-10
software/js-drone/web-gui/script.js.jinja2
software/js-drone/web-gui/script.js.jinja2
+71
-7
No files found.
software/js-drone/buildout.hash.cfg
View file @
632ad024
...
...
@@ -14,7 +14,7 @@
# not need these here).
[index-html]
_update_hash_filename_ = web-gui/index.html.jinja2
md5sum =
1eedc017ecc9d1a6761dc2fff3bbab9b
md5sum =
94479f7e7299650fc41878fab06e6555
[instance-profile]
filename = instance.cfg.in
...
...
@@ -38,8 +38,8 @@ md5sum = 1555496ad591a31a845f33488d5c335d
[script-js]
_update_hash_filename_ = web-gui/script.js.jinja2
md5sum =
e28492276416c2d84e770217ae97a88f
md5sum =
a88028c1248d2b6a91b7d3cb03047196
[worker]
_update_hash_filename_ = drone-scripts/worker.js.jinja2
md5sum =
48540afedd5437129196d84832d2ed40
md5sum =
06a322cc711830262fbbfdc1bfe7947a
software/js-drone/drone-scripts/worker.js.jinja2
View file @
632ad024
...
...
@@ -11,6 +11,7 @@ import {
getClimbRate,
getInitialAltitude,
gpsIsOk,
getLog,
getPosition,
getYaw,
initPubsub,
...
...
@@ -36,8 +37,8 @@ import { evalScript, fdopen, loadFile, open } from "std";
(function (Drone, SIGTERM, WNOHANG, Worker, close, console, evalScript, exec,
fdopen, getAltitude, getInitialAltitude, gpsIsOk, getPosition,
get
Yaw, initPubsub, kill, isLanding, loadFile, loiter, open, pipe
,
setAirSpeed, setMessage, setReadHandler, setTargetCoordinates,
get
Log, getYaw, initPubsub, isLanding, kill, loadFile, loiter, open
,
pipe,
setAirSpeed, setMessage, setReadHandler, setTargetCoordinates,
triggerParachute, updateLogAndProjection, waitpid) {
// Every script is evaluated per drone
"use strict";
...
...
@@ -200,7 +201,7 @@ import { evalScript, fdopen, loadFile, open } from "std";
}
function handleMainMessage(evt) {
var type = evt.data.type, message, peer_id;
var type = evt.data.type, message, peer_id
, log
;
switch (type) {
...
...
@@ -234,7 +235,15 @@ import { evalScript, fdopen, loadFile, open } from "std";
}
}
});
// Call the drone onStart function
if (clientId !== undefined) {
log = getLog();
if (log.length > 0) {
user_me.writeWebsocketMessage(JSON.stringify({log: log}));
}
}
// Call the drone onUpdate function
if (user_me.hasOwnProperty("onUpdate")) {
user_me.onUpdate(evt.data.timestamp);
}
...
...
@@ -267,7 +276,7 @@ import { evalScript, fdopen, loadFile, open } from "std";
}
};
}(Drone, SIGTERM, WNOHANG, Worker, close, console, evalScript, exec,
fdopen, getAltitude, getInitialAltitude, gpsIsOk, getPosition, getYaw,
fdopen, getAltitude, getInitialAltitude, gpsIsOk, getPosition, get
Log, get
Yaw,
initPubsub, isLanding, kill, loadFile, loiter, open, pipe, setAirSpeed,
setMessage, setReadHandler, setTargetCoordinates, triggerParachute,
updateLogAndProjection, waitpid));
software/js-drone/test/test.py
View file @
632ad024
...
...
@@ -284,6 +284,49 @@ class SubscriberTestCase(SlapOSInstanceTestCase):
b'Unknown instruction %s'
%
ws
.
sock
.
getsockname
()[
0
].
encode
(),
ws
.
recv_frame
().
data
)
self
.
assertIn
(
b'
\
\
u001b[32minfo/userland
\
\
u001b[0m
\
\
tfieldsSize 3
\
\
n"}'
,
ws
.
recv_frame
().
data
,
)
self
.
assertEqual
(
ws
.
recv_frame
().
data
,
b''
.
join
((
b'{"drone_dict":{"0":{"latitude":'
,
b'"%.6f","longitude":"%.6f","altitude":"%.2f",'
%
(
0
,
0
,
0
),
b'"yaw":"%.2f","speed":"%.2f","climbRate":"%.2f",'
%
(
0
,
0
,
0
),
b'"timestamp":%d}}}'
%
0
,
))
)
self
.
assertIn
(
b'
\
\
u001b[32minfo/client
\
\
u001b[0m
\
\
tReceived position of drone 0: %.6f ? %.6f ? %.2f m %.2f m
\
\
n"}'
%
(
0
,
0
,
0
,
0
),
ws
.
recv_frame
().
data
,
)
self
.
assertEqual
(
ws
.
recv_frame
().
data
,
b''
.
join
((
b'{"drone_dict":{"0":{"latitude":'
,
b'"%.6f","longitude":"%.6f","altitude":"%.2f",'
%
(
0
,
0
,
0
),
b'"yaw":"%.2f","speed":"%.2f","climbRate":"%.2f",'
%
(
0
,
0
,
0
),
b'"timestamp":%d}}}'
%
0
,
))
)
self
.
assertIn
(
b'
\
\
u001b[32minfo/client
\
\
u001b[0m
\
\
tReceived speed of drone 0: %.2f ? %.2f m/s %.2f m/s
\
\
n"}'
%
(
0
,
0
,
0
),
ws
.
recv_frame
().
data
,
)
self
.
assertEqual
(
ws
.
recv_frame
().
data
,
b''
.
join
((
b'{"drone_dict":{"0":{"latitude":'
,
b'"%.6f","longitude":"%.6f","altitude":"%.2f",'
%
(
0
,
0
,
0
),
b'"yaw":"%.2f","speed":"%.2f","climbRate":"%.2f",'
%
(
0
,
0
,
0
),
b'"timestamp":%d}}}'
%
0
,
))
)
self
.
assertIn
(
b'
\
\
u001b[32minfo/userland
\
\
u001b[0m
\
\
tfieldsSize 1
\
\
n"}'
,
ws
.
recv_frame
().
data
,
)
self
.
assertEqual
(
ws
.
recv_frame
().
data
,
b''
.
join
((
...
...
@@ -296,6 +339,10 @@ class SubscriberTestCase(SlapOSInstanceTestCase):
self
.
send_ua_networkMessage
()
time
.
sleep
(
0.1
)
self
.
assertEqual
(
ws
.
recv_frame
().
data
,
MESSAGE_CONTENT
.
replace
(
b'
\
\
'
,
b''
))
self
.
assertIn
(
b'
\
\
u001b[32minfo/client
\
\
u001b[0m
\
\
tReceived position of drone 0: %.6f ? %.6f ? %.2f m %.2f m
\
\
n"}'
%
POSITION_ARRAY_OUTPUT_VALUES
,
ws
.
recv_frame
().
data
,
)
self
.
assertEqual
(
ws
.
recv_frame
().
data
,
b''
.
join
((
...
...
@@ -305,4 +352,8 @@ class SubscriberTestCase(SlapOSInstanceTestCase):
b'"timestamp":%d}}}'
%
POSITION_ARRAY_INPUT_VALUES
[
-
1
],
))
)
self
.
assertIn
(
b'
\
\
u001b[32minfo/client
\
\
u001b[0m
\
\
tReceived speed of drone 0: %.2f ? %.2f m/s %.2f m/s
\
\
n"}'
%
SPEED_ARRAY_VALUES
,
ws
.
recv_frame
().
data
,
)
ws
.
close
()
software/js-drone/web-gui/index.html.jinja2
View file @
632ad024
...
...
@@ -9,7 +9,8 @@
<style>
button
{
padding
:
0.5%
;
margin
:
2vh
;
padding
:
2vh
;
font-size
:
24px
;
cursor
:
pointer
;
border
:
none
;
...
...
@@ -20,38 +21,60 @@
box-shadow
:
0
2px
#666
;
transform
:
translateY
(
4px
);
}
div
>
*
{
margin
:
1%
}
label
{
margin
:
2%
}
table
{
width
:
30%
}
label
{
margin
:
auto
2%
}
table
{
min-width
:
1028px
;
height
:
max-content
;
}
th
,
td
{
padding
:
1%
;
text-align
:
center
;
vertical-align
:
middle
;
}
.blue-text
{
color
:
blue
}
.connected
{
color
:
green
}
.container
{
display
:
flex
;
align-items
:
center
;
justify-content
:
center
;
}
.cyan-text
{
color
:
cyan
}
.disconnected
{
color
:
red
}
.gray-button
{
background-color
:
lightgray
}
.gray-button
:hover
{
background-color
:
gray
}
.green-text
{
color
:
green
}
.green-button
{
background-color
:
#4caf50
}
.green-button
:hover
{
background-color
:
#3e8e41
}
.magenta-text
{
color
:
magenta
}
.red-button
{
background-color
:
red
}
.red-button
{
background-color
:
#e42828
}
.red-button
:hover
{
background-color
:
#e42828
}
.red-text
{
color
:
red
}
.white-text
{
color
:
white
}
.yellow-text
{
color
:
yellow
}
#drones-status
{
height
:
50vh
}
#prompt
{
background-color
:
rgb
(
18
,
19
,
20
);
max-width
:
1028px
;
height
:
20vh
;
margin
:
auto
;
}
#prompt-div
{
height
:
max-content
;
}
</style>
</head>
<body>
<header
class=
"container"
>
<label
for=
"web-socket-status"
>
web socket status:
</label>
<output
class=
"disconnected"
id=
"web-socket-status"
>
Disconnected
</output>
</header>
<div
id=
"prompt-div"
>
<div
class=
"container"
>
<label
for=
"web-socket-status"
>
web socket status:
</label>
<output
class=
"disconnected"
id=
"web-socket-status"
>
Disconnected
</output>
</div>
<pre
id=
"prompt"
></pre>
</div>
<div
class=
"container"
>
<div
class=
"container"
id=
"drones-status"
>
<table>
<tr>
<th></th>
...
...
software/js-drone/web-gui/script.js.jinja2
View file @
632ad024
...
...
@@ -13,6 +13,9 @@
GREEN_BTN_CLASS_NAME = "green-button",
LATITUDE_BASE_ID = "latitude_",
LONGITUDE_BASE_ID = "longitude_",
PROMPT_COLOR_RE = /\u001b.{2,3}m/g,
PROMPT_ID = "prompt",
PROMPT_MAX_MSG,
QUIT_BTN_ID = "quit-btn",
RED_BTN_CLASS_NAME = "red-button",
SWITCH_BTN_ID = "switch-btn",
...
...
@@ -21,8 +24,12 @@
socket;
function updateConnexionClass(element, status) {
element.classList.remove(status ? DISCONNECTED_CLASS_NAME : CONNECTED_CLASS_NAME);
element.classList.add(status ? CONNECTED_CLASS_NAME : DISCONNECTED_CLASS_NAME);
element.classList.remove(
status ? DISCONNECTED_CLASS_NAME : CONNECTED_CLASS_NAME
);
element.classList.add(
status ? CONNECTED_CLASS_NAME : DISCONNECTED_CLASS_NAME
);
}
function setWebSocketStatus(connected, status) {
...
...
@@ -49,15 +56,22 @@
socket = new WebSocket('ws://{{ websocket_url }}');
socket.onopen = function(event) {
socket.onopen = function
(event) {
setWebSocketStatus(true, "Connected");
};
socket.onmessage = function(event) {
var message = JSON.parse(event.data),
flight_state_cell;
socket.onmessage = function (event) {
var color_array,
flight_state_cell,
i,
message = JSON.parse(event.data),
prompt,
new_div,
new_span,
text_array;
if (message.hasOwnProperty("drone_dict")) {
Object.entries(message
["drone_dict"]
).forEach(function ([id, drone]) {
Object.entries(message
.drone_dict
).forEach(function ([id, drone]) {
document.getElementById(LATITUDE_BASE_ID + id).innerHTML = drone["latitude"];
document.getElementById(LONGITUDE_BASE_ID + id).innerHTML = drone["longitude"];
document.getElementById(ALTITUDE_BASE_ID + id).innerHTML = drone["altitude"];
...
...
@@ -69,6 +83,56 @@
flight_state_cell = document.getElementById(FLIGHT_STATUS_BASE_ID + message['id']);
flight_state_cell.innerHTML = message['state'];
updateConnexionClass(flight_state_cell, message['inAir']);
} else if(message.hasOwnProperty("log")) {
prompt = document.getElementById(PROMPT_ID);
if (PROMPT_MAX_MSG === undefined && prompt.children.length > 0) {
PROMPT_MAX_MSG = Math.trunc(
prompt.offsetHeight / prompt.children[0].offsetHeight
);
}
new_div = document.createElement("div");
text_array = message['log'].split(PROMPT_COLOR_RE);
color_array = message['log'].match(PROMPT_COLOR_RE);
for (i = 0; i < text_array.length; i++) {
new_span = document.createElement("span");
new_span.appendChild(document.createTextNode(text_array[i]));
if (i > 0 && i < color_array.length + 1) {
switch (color_array[i - 1]) {
case "\u001b[31m":
new_span.classList.add('red-text');
break;
case "\u001b[32m":
new_span.classList.add('green-text');
break;
case "\u001b[33m":
new_span.classList.add('yellow-text');
break;
case "\u001b[34m":
new_span.classList.add('blue-text');
break;
case "\u001b[35m":
new_span.classList.add('magenta-text');
break;
case "\u001b[36m":
new_span.classList.add('cyan-text');
break;
default:
new_span.classList.add('white-text');
break;
};
} else {
new_span.classList.add('white-text');
}
new_div.appendChild(new_span);
}
if (prompt.children.length === PROMPT_MAX_MSG) {
prompt.removeChild(prompt.firstElementChild);
}
prompt.appendChild(new_div);
} else {
console.info(message);
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment