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
103
Merge Requests
103
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
Show 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 @@
...
@@ -14,7 +14,7 @@
# not need these here).
# not need these here).
[index-html]
[index-html]
_update_hash_filename_ = web-gui/index.html.jinja2
_update_hash_filename_ = web-gui/index.html.jinja2
md5sum =
1eedc017ecc9d1a6761dc2fff3bbab9b
md5sum =
94479f7e7299650fc41878fab06e6555
[instance-profile]
[instance-profile]
filename = instance.cfg.in
filename = instance.cfg.in
...
@@ -38,8 +38,8 @@ md5sum = 1555496ad591a31a845f33488d5c335d
...
@@ -38,8 +38,8 @@ md5sum = 1555496ad591a31a845f33488d5c335d
[script-js]
[script-js]
_update_hash_filename_ = web-gui/script.js.jinja2
_update_hash_filename_ = web-gui/script.js.jinja2
md5sum =
e28492276416c2d84e770217ae97a88f
md5sum =
a88028c1248d2b6a91b7d3cb03047196
[worker]
[worker]
_update_hash_filename_ = drone-scripts/worker.js.jinja2
_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 {
...
@@ -11,6 +11,7 @@ import {
getClimbRate,
getClimbRate,
getInitialAltitude,
getInitialAltitude,
gpsIsOk,
gpsIsOk,
getLog,
getPosition,
getPosition,
getYaw,
getYaw,
initPubsub,
initPubsub,
...
@@ -36,8 +37,8 @@ import { evalScript, fdopen, loadFile, open } from "std";
...
@@ -36,8 +37,8 @@ import { evalScript, fdopen, loadFile, open } from "std";
(function (Drone, SIGTERM, WNOHANG, Worker, close, console, evalScript, exec,
(function (Drone, SIGTERM, WNOHANG, Worker, close, console, evalScript, exec,
fdopen, getAltitude, getInitialAltitude, gpsIsOk, getPosition,
fdopen, getAltitude, getInitialAltitude, gpsIsOk, getPosition,
get
Yaw, initPubsub, kill, isLanding, loadFile, loiter, open, pipe
,
get
Log, getYaw, initPubsub, isLanding, kill, loadFile, loiter, open
,
setAirSpeed, setMessage, setReadHandler, setTargetCoordinates,
pipe,
setAirSpeed, setMessage, setReadHandler, setTargetCoordinates,
triggerParachute, updateLogAndProjection, waitpid) {
triggerParachute, updateLogAndProjection, waitpid) {
// Every script is evaluated per drone
// Every script is evaluated per drone
"use strict";
"use strict";
...
@@ -200,7 +201,7 @@ import { evalScript, fdopen, loadFile, open } from "std";
...
@@ -200,7 +201,7 @@ import { evalScript, fdopen, loadFile, open } from "std";
}
}
function handleMainMessage(evt) {
function handleMainMessage(evt) {
var type = evt.data.type, message, peer_id;
var type = evt.data.type, message, peer_id
, log
;
switch (type) {
switch (type) {
...
@@ -234,7 +235,15 @@ import { evalScript, fdopen, loadFile, open } from "std";
...
@@ -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")) {
if (user_me.hasOwnProperty("onUpdate")) {
user_me.onUpdate(evt.data.timestamp);
user_me.onUpdate(evt.data.timestamp);
}
}
...
@@ -267,7 +276,7 @@ import { evalScript, fdopen, loadFile, open } from "std";
...
@@ -267,7 +276,7 @@ import { evalScript, fdopen, loadFile, open } from "std";
}
}
};
};
}(Drone, SIGTERM, WNOHANG, Worker, close, console, evalScript, exec,
}(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,
initPubsub, isLanding, kill, loadFile, loiter, open, pipe, setAirSpeed,
setMessage, setReadHandler, setTargetCoordinates, triggerParachute,
setMessage, setReadHandler, setTargetCoordinates, triggerParachute,
updateLogAndProjection, waitpid));
updateLogAndProjection, waitpid));
software/js-drone/test/test.py
View file @
632ad024
...
@@ -284,6 +284,49 @@ class SubscriberTestCase(SlapOSInstanceTestCase):
...
@@ -284,6 +284,49 @@ class SubscriberTestCase(SlapOSInstanceTestCase):
b'Unknown instruction %s'
%
ws
.
sock
.
getsockname
()[
0
].
encode
(),
b'Unknown instruction %s'
%
ws
.
sock
.
getsockname
()[
0
].
encode
(),
ws
.
recv_frame
().
data
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
(
self
.
assertEqual
(
ws
.
recv_frame
().
data
,
ws
.
recv_frame
().
data
,
b''
.
join
((
b''
.
join
((
...
@@ -296,6 +339,10 @@ class SubscriberTestCase(SlapOSInstanceTestCase):
...
@@ -296,6 +339,10 @@ class SubscriberTestCase(SlapOSInstanceTestCase):
self
.
send_ua_networkMessage
()
self
.
send_ua_networkMessage
()
time
.
sleep
(
0.1
)
time
.
sleep
(
0.1
)
self
.
assertEqual
(
ws
.
recv_frame
().
data
,
MESSAGE_CONTENT
.
replace
(
b'
\
\
'
,
b''
))
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
(
self
.
assertEqual
(
ws
.
recv_frame
().
data
,
ws
.
recv_frame
().
data
,
b''
.
join
((
b''
.
join
((
...
@@ -305,4 +352,8 @@ class SubscriberTestCase(SlapOSInstanceTestCase):
...
@@ -305,4 +352,8 @@ class SubscriberTestCase(SlapOSInstanceTestCase):
b'"timestamp":%d}}}'
%
POSITION_ARRAY_INPUT_VALUES
[
-
1
],
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
()
ws
.
close
()
software/js-drone/web-gui/index.html.jinja2
View file @
632ad024
...
@@ -9,7 +9,8 @@
...
@@ -9,7 +9,8 @@
<style>
<style>
button
{
button
{
padding
:
0.5%
;
margin
:
2vh
;
padding
:
2vh
;
font-size
:
24px
;
font-size
:
24px
;
cursor
:
pointer
;
cursor
:
pointer
;
border
:
none
;
border
:
none
;
...
@@ -20,38 +21,60 @@
...
@@ -20,38 +21,60 @@
box-shadow
:
0
2px
#666
;
box-shadow
:
0
2px
#666
;
transform
:
translateY
(
4px
);
transform
:
translateY
(
4px
);
}
}
div
>
*
{
margin
:
1%
}
label
{
margin
:
auto
2%
}
label
{
margin
:
2%
}
table
{
table
{
width
:
30%
}
min-width
:
1028px
;
height
:
max-content
;
}
th
,
td
{
th
,
td
{
padding
:
1%
;
padding
:
1%
;
text-align
:
center
;
text-align
:
center
;
vertical-align
:
middle
;
vertical-align
:
middle
;
}
}
.blue-text
{
color
:
blue
}
.connected
{
color
:
green
}
.connected
{
color
:
green
}
.container
{
.container
{
display
:
flex
;
display
:
flex
;
align-items
:
center
;
align-items
:
center
;
justify-content
:
center
;
justify-content
:
center
;
}
}
.cyan-text
{
color
:
cyan
}
.disconnected
{
color
:
red
}
.disconnected
{
color
:
red
}
.gray-button
{
background-color
:
lightgray
}
.gray-button
{
background-color
:
lightgray
}
.gray-button
:hover
{
background-color
:
gray
}
.gray-button
:hover
{
background-color
:
gray
}
.green-text
{
color
:
green
}
.green-button
{
background-color
:
#4caf50
}
.green-button
{
background-color
:
#4caf50
}
.green-button
:hover
{
background-color
:
#3e8e41
}
.green-button
:hover
{
background-color
:
#3e8e41
}
.magenta-text
{
color
:
magenta
}
.red-button
{
background-color
:
red
}
.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>
</style>
</head>
</head>
<body>
<body>
<header
class=
"container"
>
<div
id=
"prompt-div"
>
<div
class=
"container"
>
<label
for=
"web-socket-status"
>
web socket status:
</label>
<label
for=
"web-socket-status"
>
web socket status:
</label>
<output
class=
"disconnected"
id=
"web-socket-status"
>
Disconnected
</output>
<output
class=
"disconnected"
id=
"web-socket-status"
>
Disconnected
</output>
</header>
</div>
<pre
id=
"prompt"
></pre>
</div>
<div
class=
"container"
>
<div
class=
"container"
id=
"drones-status"
>
<table>
<table>
<tr>
<tr>
<th></th>
<th></th>
...
...
software/js-drone/web-gui/script.js.jinja2
View file @
632ad024
...
@@ -13,6 +13,9 @@
...
@@ -13,6 +13,9 @@
GREEN_BTN_CLASS_NAME = "green-button",
GREEN_BTN_CLASS_NAME = "green-button",
LATITUDE_BASE_ID = "latitude_",
LATITUDE_BASE_ID = "latitude_",
LONGITUDE_BASE_ID = "longitude_",
LONGITUDE_BASE_ID = "longitude_",
PROMPT_COLOR_RE = /\u001b.{2,3}m/g,
PROMPT_ID = "prompt",
PROMPT_MAX_MSG,
QUIT_BTN_ID = "quit-btn",
QUIT_BTN_ID = "quit-btn",
RED_BTN_CLASS_NAME = "red-button",
RED_BTN_CLASS_NAME = "red-button",
SWITCH_BTN_ID = "switch-btn",
SWITCH_BTN_ID = "switch-btn",
...
@@ -21,8 +24,12 @@
...
@@ -21,8 +24,12 @@
socket;
socket;
function updateConnexionClass(element, status) {
function updateConnexionClass(element, status) {
element.classList.remove(status ? DISCONNECTED_CLASS_NAME : CONNECTED_CLASS_NAME);
element.classList.remove(
element.classList.add(status ? CONNECTED_CLASS_NAME : DISCONNECTED_CLASS_NAME);
status ? DISCONNECTED_CLASS_NAME : CONNECTED_CLASS_NAME
);
element.classList.add(
status ? CONNECTED_CLASS_NAME : DISCONNECTED_CLASS_NAME
);
}
}
function setWebSocketStatus(connected, status) {
function setWebSocketStatus(connected, status) {
...
@@ -49,15 +56,22 @@
...
@@ -49,15 +56,22 @@
socket = new WebSocket('ws://{{ websocket_url }}');
socket = new WebSocket('ws://{{ websocket_url }}');
socket.onopen = function(event) {
socket.onopen = function
(event) {
setWebSocketStatus(true, "Connected");
setWebSocketStatus(true, "Connected");
};
};
socket.onmessage = function(event) {
socket.onmessage = function (event) {
var message = JSON.parse(event.data),
var color_array,
flight_state_cell;
flight_state_cell,
i,
message = JSON.parse(event.data),
prompt,
new_div,
new_span,
text_array;
if (message.hasOwnProperty("drone_dict")) {
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(LATITUDE_BASE_ID + id).innerHTML = drone["latitude"];
document.getElementById(LONGITUDE_BASE_ID + id).innerHTML = drone["longitude"];
document.getElementById(LONGITUDE_BASE_ID + id).innerHTML = drone["longitude"];
document.getElementById(ALTITUDE_BASE_ID + id).innerHTML = drone["altitude"];
document.getElementById(ALTITUDE_BASE_ID + id).innerHTML = drone["altitude"];
...
@@ -69,6 +83,56 @@
...
@@ -69,6 +83,56 @@
flight_state_cell = document.getElementById(FLIGHT_STATUS_BASE_ID + message['id']);
flight_state_cell = document.getElementById(FLIGHT_STATUS_BASE_ID + message['id']);
flight_state_cell.innerHTML = message['state'];
flight_state_cell.innerHTML = message['state'];
updateConnexionClass(flight_state_cell, message['inAir']);
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 {
} else {
console.info(message);
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