mirror of
https://github.com/CopterExpress/clover.git
synced 2026-06-01 15:39:32 +00:00
Compare commits
542 Commits
travis_siz
...
vuepress
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
72b343575f | ||
|
|
0849413fe2 | ||
|
|
2cab14c52f | ||
|
|
c20957cbf1 | ||
|
|
e9f892466f | ||
|
|
91061cc9f1 | ||
|
|
9db0c44177 | ||
|
|
5b3c3f0722 | ||
|
|
84b1318f3d | ||
|
|
955011e812 | ||
|
|
2561e8e6cb | ||
|
|
d1209fd064 | ||
|
|
e68fac8aad | ||
|
|
4f64fdf2e4 | ||
|
|
b77d4ed045 | ||
|
|
e2b8cb4be2 | ||
|
|
8db8075f15 | ||
|
|
578728b3a9 | ||
|
|
7154f5afc2 | ||
|
|
48fd45ea9a | ||
|
|
12ca9c0eb9 | ||
|
|
784ce35080 | ||
|
|
6c42c522ce | ||
|
|
b36d69b54f | ||
|
|
0056bb1810 | ||
|
|
37ec19a19f | ||
|
|
ea5151db51 | ||
|
|
bc032e5afb | ||
|
|
ed619935ce | ||
|
|
3b3b5b6a89 | ||
|
|
4190353569 | ||
|
|
7c2e020a89 | ||
|
|
6321ef8aa0 | ||
|
|
172890ed13 | ||
|
|
ae1e39dd82 | ||
|
|
241b766bad | ||
|
|
84bbe2e565 | ||
|
|
169680129b | ||
|
|
6541d60d08 | ||
|
|
e3addb9eb0 | ||
|
|
b7d74ef6c9 | ||
|
|
da92aea727 | ||
|
|
0b78c84ac0 | ||
|
|
de2467acb1 | ||
|
|
3d6b8b6a10 | ||
|
|
b6f1ca5d20 | ||
|
|
850b49b2b6 | ||
|
|
f21ba3feb4 | ||
|
|
9c3a97f945 | ||
|
|
293448028a | ||
|
|
b5cd9512ef | ||
|
|
dd74ceb383 | ||
|
|
e217678f7d | ||
|
|
dc06ba1bd2 | ||
|
|
21bbc8a86c | ||
|
|
76ef764143 | ||
|
|
d282098134 | ||
|
|
0f37f19b40 | ||
|
|
e9c3c6ff72 | ||
|
|
7909756046 | ||
|
|
1e8a4841af | ||
|
|
6ec574e193 | ||
|
|
8381aecd50 | ||
|
|
f5eb475660 | ||
|
|
928f4f135e | ||
|
|
8d15de0849 | ||
|
|
826f631b97 | ||
|
|
52b5d7b04e | ||
|
|
455d52007e | ||
|
|
e9e6cabbb9 | ||
|
|
8fcd6e9b9e | ||
|
|
24d3a1df8d | ||
|
|
9784e7bfa1 | ||
|
|
fbad85d87f | ||
|
|
c1ca40187e | ||
|
|
c1179869cd | ||
|
|
2096be5080 | ||
|
|
0c879f2aad | ||
|
|
f34e8b4774 | ||
|
|
be76ea82d7 | ||
|
|
6a8806c476 | ||
|
|
00a76a306e | ||
|
|
f66b53f9cb | ||
|
|
28927246db | ||
|
|
ca5817c3d2 | ||
|
|
7717461631 | ||
|
|
3f352ebc06 | ||
|
|
8c8fe5c40c | ||
|
|
d89e5eada7 | ||
|
|
2ee90e62fc | ||
|
|
848d9dcbe4 | ||
|
|
6d68d06787 | ||
|
|
d18ca32688 | ||
|
|
bf9f7d035f | ||
|
|
1aec5063d6 | ||
|
|
e7eae1c02d | ||
|
|
e3958d7fef | ||
|
|
fb47858010 | ||
|
|
a525714e3a | ||
|
|
29fdbf23af | ||
|
|
6eacb8966a | ||
|
|
d8afb711f0 | ||
|
|
cba12e115e | ||
|
|
bb6a6c81f3 | ||
|
|
d27bbf31bd | ||
|
|
8668295cfe | ||
|
|
535b366bab | ||
|
|
9f6aa7dabd | ||
|
|
f4d00a47af | ||
|
|
0f438235c2 | ||
|
|
e4ad687e28 | ||
|
|
5d58ffd1db | ||
|
|
b2ed1fccc6 | ||
|
|
aa136e7f15 | ||
|
|
9743bcbaaf | ||
|
|
75aed624db | ||
|
|
36a4962bc0 | ||
|
|
2cd3be1139 | ||
|
|
6909ba5819 | ||
|
|
f1783bdd0b | ||
|
|
528be179e6 | ||
|
|
fe588e7af9 | ||
|
|
15551db840 | ||
|
|
9dc4407afc | ||
|
|
365bd4146a | ||
|
|
fc99269404 | ||
|
|
9231679353 | ||
|
|
4defe2c7ef | ||
|
|
9f3410847f | ||
|
|
fa8da1cb33 | ||
|
|
3bb285fd35 | ||
|
|
ec1829e60c | ||
|
|
c32a412f42 | ||
|
|
810ddb4157 | ||
|
|
3656c1714a | ||
|
|
937b68aa43 | ||
|
|
bdd1b06541 | ||
|
|
dd96c91b55 | ||
|
|
8f3d64e9aa | ||
|
|
cfd413ffc1 | ||
|
|
ca054c88ba | ||
|
|
d55576bf4f | ||
|
|
470e6ff0e9 | ||
|
|
441cf7fcf7 | ||
|
|
fc5960586b | ||
|
|
4aef1e616c | ||
|
|
463c08da96 | ||
|
|
ebaaa14a7e | ||
|
|
c0d33abff6 | ||
|
|
3c4ef56b4e | ||
|
|
17e806601d | ||
|
|
3e3c5aa453 | ||
|
|
7fd463d1cb | ||
|
|
64b083b242 | ||
|
|
b2ec48f0f3 | ||
|
|
b249524828 | ||
|
|
d0dcc0e72a | ||
|
|
4c6e7029e8 | ||
|
|
613f70fd25 | ||
|
|
77832e65fa | ||
|
|
01edd129ab | ||
|
|
d03acff31b | ||
|
|
22e74febd6 | ||
|
|
989d9b66f1 | ||
|
|
f8eb8e1e67 | ||
|
|
b92ebe7d60 | ||
|
|
af51e88179 | ||
|
|
59c8debcab | ||
|
|
ec6f3089e3 | ||
|
|
2b88d21792 | ||
|
|
274b81c50f | ||
|
|
33a6dffb1f | ||
|
|
5f9b3e82db | ||
|
|
5f43367d82 | ||
|
|
7809e7ed2d | ||
|
|
1688b97091 | ||
|
|
1c6129fde8 | ||
|
|
dae9599d64 | ||
|
|
c0677f6aa3 | ||
|
|
e7bbf21700 | ||
|
|
58c10d7cb8 | ||
|
|
b6bd6bdde8 | ||
|
|
3374c7756c | ||
|
|
0dffeca55f | ||
|
|
8cb911854d | ||
|
|
a1b3efe67d | ||
|
|
6700d8728f | ||
|
|
be2e6ae198 | ||
|
|
b9eed0f3ad | ||
|
|
853a7fcf67 | ||
|
|
e342796f07 | ||
|
|
4fa70aa73a | ||
|
|
226c91c3d8 | ||
|
|
e33c9e78ea | ||
|
|
18c927469e | ||
|
|
a465afd65c | ||
|
|
a2c65d2466 | ||
|
|
ef7faa126a | ||
|
|
d0666ca9d7 | ||
|
|
b48f22cd35 | ||
|
|
731f908053 | ||
|
|
505a1efebd | ||
|
|
f1b5484e65 | ||
|
|
3343477a02 | ||
|
|
60da608191 | ||
|
|
7e383d713d | ||
|
|
c2a60380b7 | ||
|
|
7f82f8683f | ||
|
|
e28cbea8d9 | ||
|
|
27528c20dc | ||
|
|
e3503fca35 | ||
|
|
40b8941cab | ||
|
|
0a9b6fff95 | ||
|
|
4a4e539edd | ||
|
|
8d24a737ac | ||
|
|
20af13947d | ||
|
|
e91609ff61 | ||
|
|
0024372c45 | ||
|
|
b62d202a29 | ||
|
|
84d685469a | ||
|
|
3f6bb0cd68 | ||
|
|
c01a145a16 | ||
|
|
6d28bf4ef9 | ||
|
|
f6ea7209db | ||
|
|
21727ef76d | ||
|
|
9847b7a71c | ||
|
|
a5d44ff63a | ||
|
|
391a2f9af9 | ||
|
|
5918702fbd | ||
|
|
69fe288a41 | ||
|
|
5154720348 | ||
|
|
cc1694661d | ||
|
|
d5efa962d8 | ||
|
|
1195336cbc | ||
|
|
5fc67b8e65 | ||
|
|
c1409d4467 | ||
|
|
7a1f09da98 | ||
|
|
6934cc7a1a | ||
|
|
2550ffe532 | ||
|
|
9def866a30 | ||
|
|
802f8eeaa4 | ||
|
|
504aa53b1a | ||
|
|
015cf730c2 | ||
|
|
8e6ef727ce | ||
|
|
973e1f1181 | ||
|
|
275faa78a4 | ||
|
|
fc7d010881 | ||
|
|
242e35f84a | ||
|
|
3f07d28e0f | ||
|
|
613d378e66 | ||
|
|
769c421898 | ||
|
|
3830ceea04 | ||
|
|
df3a11b035 | ||
|
|
ef5700845f | ||
|
|
921084504e | ||
|
|
6550780afb | ||
|
|
2448915300 | ||
|
|
247a7917d9 | ||
|
|
37f2c78b36 | ||
|
|
e76618bd3b | ||
|
|
9fbfcfbd2e | ||
|
|
2003b4516a | ||
|
|
03985ae1b8 | ||
|
|
a47d5d1bfe | ||
|
|
20075dd40f | ||
|
|
c247c75d17 | ||
|
|
c36279e536 | ||
|
|
1471a53b3a | ||
|
|
931f50a458 | ||
|
|
118b4573fe | ||
|
|
f77843f4a5 | ||
|
|
5f62a8639a | ||
|
|
fa1db1d90b | ||
|
|
1a2e87bb6a | ||
|
|
7dbd983ec5 | ||
|
|
d2d395f1fc | ||
|
|
ff93f79c0a | ||
|
|
5deb09eb45 | ||
|
|
70b8be5c5d | ||
|
|
2a08e20b47 | ||
|
|
3328d8f4ac | ||
|
|
f7fb814894 | ||
|
|
3a3b0bbd80 | ||
|
|
ca095f3f16 | ||
|
|
baf2467939 | ||
|
|
abba3bf876 | ||
|
|
346373ed23 | ||
|
|
bb996056c9 | ||
|
|
0e0b1cdc31 | ||
|
|
eceaa0ec91 | ||
|
|
f29686b9f4 | ||
|
|
b7f1f2b391 | ||
|
|
6b0bb41564 | ||
|
|
563e5acad6 | ||
|
|
5932faa29c | ||
|
|
bcc2e86e6f | ||
|
|
e80a1cc7d6 | ||
|
|
5fd3a92c7b | ||
|
|
84b87055df | ||
|
|
7cc0f066c7 | ||
|
|
868fc728dd | ||
|
|
faa90b89f6 | ||
|
|
f4d07e2c2c | ||
|
|
fad7886012 | ||
|
|
7eb139fd22 | ||
|
|
855d13e210 | ||
|
|
781b8962be | ||
|
|
047a965f9f | ||
|
|
47060db84b | ||
|
|
2693fd4ace | ||
|
|
faa702cab0 | ||
|
|
150ecbe29d | ||
|
|
df5e83e416 | ||
|
|
1f2ba65669 | ||
|
|
27be9eb281 | ||
|
|
f8222e1028 | ||
|
|
dce0c00773 | ||
|
|
dc8c5d9db9 | ||
|
|
261faaec0e | ||
|
|
dbd9a4a238 | ||
|
|
80d446e857 | ||
|
|
609a7ab014 | ||
|
|
c0d9bd7ef0 | ||
|
|
cdd6000c58 | ||
|
|
480a9b1f0a | ||
|
|
4943cb94b0 | ||
|
|
e0ca1272bb | ||
|
|
cb88537ddc | ||
|
|
659380c575 | ||
|
|
b4a8119bd7 | ||
|
|
c72eb1b440 | ||
|
|
f825901a19 | ||
|
|
200c5dea57 | ||
|
|
0504569b0c | ||
|
|
9829ee2e72 | ||
|
|
dfdaf3aa4f | ||
|
|
63f979c2ff | ||
|
|
b8dafce816 | ||
|
|
19e0b94fda | ||
|
|
957e57692c | ||
|
|
0d49c426eb | ||
|
|
b1f104ce5e | ||
|
|
80177b3ea4 | ||
|
|
3223d3817e | ||
|
|
4612f7e9f0 | ||
|
|
a026410fdb | ||
|
|
dd1a212cd0 | ||
|
|
20b6824012 | ||
|
|
6f6933234c | ||
|
|
3edafbef97 | ||
|
|
7740a136ce | ||
|
|
380112de6a | ||
|
|
79f5c6d0e7 | ||
|
|
e207b55966 | ||
|
|
043a4ad67c | ||
|
|
dbeb2b354d | ||
|
|
6134965f2a | ||
|
|
976bb7aeea | ||
|
|
faa0e6d8d2 | ||
|
|
d02151aedd | ||
|
|
7f0606397e | ||
|
|
fb2842a0a1 | ||
|
|
9a9621ab4b | ||
|
|
171804149c | ||
|
|
0cb7494023 | ||
|
|
e0a81e0ca8 | ||
|
|
1e5e9cdc43 | ||
|
|
5e315c477e | ||
|
|
f45000f595 | ||
|
|
48a1385a1a | ||
|
|
765c470baa | ||
|
|
fd69beed7b | ||
|
|
1d4179bccf | ||
|
|
0b2095bbb8 | ||
|
|
a0436fbcc5 | ||
|
|
aee867d6bc | ||
|
|
3c078ab92f | ||
|
|
d780aedb88 | ||
|
|
a16d9d80fc | ||
|
|
10d250d96a | ||
|
|
acdcf20392 | ||
|
|
796d614f5e | ||
|
|
f8de7443d7 | ||
|
|
5c3ffdbeb6 | ||
|
|
1c732137c6 | ||
|
|
345aad9e64 | ||
|
|
02c67ea71a | ||
|
|
050e0fedb9 | ||
|
|
0e9b54934c | ||
|
|
793b614b7b | ||
|
|
62ab5c2357 | ||
|
|
181a78e4a9 | ||
|
|
c72eb0c027 | ||
|
|
5d99e44c30 | ||
|
|
5eb9b4acbe | ||
|
|
30ada8f311 | ||
|
|
e717829945 | ||
|
|
50dc17badb | ||
|
|
1dea541df2 | ||
|
|
d6b950b726 | ||
|
|
e2a1d3aaeb | ||
|
|
165e4d1a61 | ||
|
|
4f631300d4 | ||
|
|
e252a1cddc | ||
|
|
25dd17c286 | ||
|
|
b9395e3d18 | ||
|
|
a32dd7dcdd | ||
|
|
1e12e34070 | ||
|
|
3ff675d794 | ||
|
|
bb3e4befe5 | ||
|
|
fe71007ebd | ||
|
|
68cec159f7 | ||
|
|
4e8127f690 | ||
|
|
8f78f2b6e4 | ||
|
|
c8163cd38b | ||
|
|
7831992d6a | ||
|
|
873befdba9 | ||
|
|
c3cbc305c3 | ||
|
|
b71e802a2e | ||
|
|
3c5f2c958e | ||
|
|
267993aec4 | ||
|
|
86dd42c3b3 | ||
|
|
9d338d843b | ||
|
|
3e100bee91 | ||
|
|
8a29b9a37a | ||
|
|
2e80a06db1 | ||
|
|
0003985c3b | ||
|
|
f250916ede | ||
|
|
ee2944a1d3 | ||
|
|
a088524468 | ||
|
|
215fe237ca | ||
|
|
8c1b5c19d0 | ||
|
|
779dfb3f4f | ||
|
|
23d503adc5 | ||
|
|
0350ecbff7 | ||
|
|
12bed337dc | ||
|
|
6a1b609ccd | ||
|
|
3d5c51a42e | ||
|
|
3702ed0c86 | ||
|
|
741abadb54 | ||
|
|
c6dc732867 | ||
|
|
ba76e51966 | ||
|
|
7951f0e2ba | ||
|
|
cd58c03c0f | ||
|
|
ce6b2530c4 | ||
|
|
14e4af76aa | ||
|
|
f3f1557b0b | ||
|
|
18d410db24 | ||
|
|
207dc88579 | ||
|
|
58f6ac4b39 | ||
|
|
688e4f0ca9 | ||
|
|
7cbe823700 | ||
|
|
df681e0a79 | ||
|
|
8aad2fc363 | ||
|
|
3c8dd14c9d | ||
|
|
3a20bc3212 | ||
|
|
1105cd8750 | ||
|
|
43d7e7c70b | ||
|
|
7a1e885df1 | ||
|
|
9a9c2d5c9f | ||
|
|
eaeb146878 | ||
|
|
2452be05ff | ||
|
|
a4336a39c9 | ||
|
|
9cdf7dea41 | ||
|
|
b90dc3c020 | ||
|
|
91252d8d50 | ||
|
|
c4b94390e9 | ||
|
|
1b4167365e | ||
|
|
01ec592abb | ||
|
|
e2e2e04381 | ||
|
|
27e0189cf5 | ||
|
|
e3d89cbc4c | ||
|
|
a0ac85e0d3 | ||
|
|
83e5911110 | ||
|
|
05d634d2d3 | ||
|
|
4967d651bd | ||
|
|
91f948d3f4 | ||
|
|
ebf55244f4 | ||
|
|
5b6d08e25d | ||
|
|
8036214406 | ||
|
|
5d3c8c89cb | ||
|
|
2075fa52ef | ||
|
|
b0e1e1ffae | ||
|
|
4482f973db | ||
|
|
b1c7ee6b66 | ||
|
|
ff9e669352 | ||
|
|
6c8291749f | ||
|
|
039d2438cd | ||
|
|
048a605f2f | ||
|
|
0e0d4fe5cc | ||
|
|
37bbd7522c | ||
|
|
f206b2ea18 | ||
|
|
ae7f3ccfbc | ||
|
|
da28c61f1e | ||
|
|
b7ce588b07 | ||
|
|
f663a62c1e | ||
|
|
5b370ee96b | ||
|
|
e0512c209e | ||
|
|
7385f673de | ||
|
|
5629b0a9b3 | ||
|
|
9de0034fb9 | ||
|
|
ae29fe00d3 | ||
|
|
7295c6c040 | ||
|
|
11bdf2d3da | ||
|
|
89ccca3c30 | ||
|
|
50218a8822 | ||
|
|
11d5da5db2 | ||
|
|
757129782e | ||
|
|
f7c1f4330d | ||
|
|
b36292cdb8 | ||
|
|
a9f6fe329b | ||
|
|
f89cc92736 | ||
|
|
60755fa1b5 | ||
|
|
792352d072 | ||
|
|
2f6d9639c1 | ||
|
|
d52175bb30 | ||
|
|
be6e894b80 | ||
|
|
2f6125ce54 | ||
|
|
c6a238c671 | ||
|
|
28851f39ad | ||
|
|
4a2fbf3e22 | ||
|
|
c3b549df0d | ||
|
|
21d922beb2 | ||
|
|
88ef00e043 | ||
|
|
1b85a9cdf2 | ||
|
|
d5a79babad | ||
|
|
4d80e6f6cf | ||
|
|
8ce4de191b | ||
|
|
ecaab1650f | ||
|
|
a62107132d | ||
|
|
423490304b | ||
|
|
ee9c956fc7 | ||
|
|
14e49a4f7b | ||
|
|
d4d25c61a2 | ||
|
|
106209d79b | ||
|
|
333cf9655f | ||
|
|
2ddf831842 | ||
|
|
68f23babcc | ||
|
|
4f0a099152 | ||
|
|
acfb858598 | ||
|
|
c0f15fc1e6 | ||
|
|
e94e552da3 |
2
.gitattributes
vendored
2
.gitattributes
vendored
@@ -4,3 +4,5 @@ eventemitter2.js linguist-vendored
|
|||||||
ros3d.js linguist-vendored
|
ros3d.js linguist-vendored
|
||||||
three.min.js linguist-vendored
|
three.min.js linguist-vendored
|
||||||
aruco_pose/vendor/* linguist-vendored
|
aruco_pose/vendor/* linguist-vendored
|
||||||
|
blockly/* linguist-vendored
|
||||||
|
highlight/* linguist-vendored
|
||||||
|
|||||||
29
.github/workflows/build-image.yaml
vendored
Normal file
29
.github/workflows/build-image.yaml
vendored
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
name: RPi image
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ '*' ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ master ]
|
||||||
|
release:
|
||||||
|
types: [ created ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Build image
|
||||||
|
run: |
|
||||||
|
docker run --privileged --rm -v /dev:/dev -v $(pwd):/builder/repo -e TRAVIS_TAG="${{ github.event.release.tag_name }}" sfalexrog/img-tool:qemu-update
|
||||||
|
- name: Compress image
|
||||||
|
run: |
|
||||||
|
cd images && sudo chmod -R 777 . && zip -9 $(echo clover_*).zip clover_* && ls -l . && unzip -l clover_*.zip
|
||||||
|
- name: Upload image
|
||||||
|
uses: softprops/action-gh-release@v1
|
||||||
|
if: ${{ github.event_name == 'release' }}
|
||||||
|
with:
|
||||||
|
files: images/clover_*.zip
|
||||||
|
prerelease: true
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
23
.github/workflows/build.yml
vendored
Normal file
23
.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
name: Build
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ '*' ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ master ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
# melodic:
|
||||||
|
# runs-on: ubuntu-latest
|
||||||
|
# steps:
|
||||||
|
# - uses: actions/checkout@v2
|
||||||
|
# - name: Native Melodic build
|
||||||
|
# run: |
|
||||||
|
# docker run --rm -v $(pwd):/root/catkin_ws/src/clover ros:melodic-ros-base /root/catkin_ws/src/clover/builder/standalone-install.sh
|
||||||
|
noetic:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Native Noetic build
|
||||||
|
run: |
|
||||||
|
docker run --rm -v $(pwd):/root/catkin_ws/src/clover ros:noetic-ros-base /root/catkin_ws/src/clover/builder/standalone-install.sh
|
||||||
68
.github/workflows/docs.yml
vendored
Normal file
68
.github/workflows/docs.yml
vendored
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
name: Documentation
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ '*' ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ master ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
docs:
|
||||||
|
runs-on: ubuntu-18.04
|
||||||
|
steps:
|
||||||
|
- name: Cancel previous runs
|
||||||
|
uses: styfle/cancel-workflow-action@0.9.1
|
||||||
|
with:
|
||||||
|
access_token: ${{ github.token }}
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Use Node.js
|
||||||
|
uses: actions/setup-node@v1
|
||||||
|
with: { node-version: '10' }
|
||||||
|
- name: Setup tools
|
||||||
|
run: |
|
||||||
|
sudo sh -c "echo ttf-mscorefonts-installer msttcorefonts/accepted-mscorefonts-eula select true | debconf-set-selections"
|
||||||
|
sudo apt-get update && sudo apt-get install -y calibre msttcorefonts
|
||||||
|
builder/assets/install_gitbook.sh
|
||||||
|
npm install markdownlint-cli@0.28.1 -g # FIXME: https://github.com/DavidAnson/markdownlint/issues/435
|
||||||
|
npm install svgexport -g
|
||||||
|
gitbook -V
|
||||||
|
markdownlint -V
|
||||||
|
- name: Run markdownlint
|
||||||
|
run: markdownlint docs
|
||||||
|
- name: Check Assets
|
||||||
|
run: |
|
||||||
|
./check_assets_size.py
|
||||||
|
./check_unused_assets.py
|
||||||
|
- name: Build GitBook
|
||||||
|
run: |
|
||||||
|
gitbook install
|
||||||
|
gitbook build
|
||||||
|
- name: Generate PDF
|
||||||
|
id: generate-pdf
|
||||||
|
env:
|
||||||
|
GITBOOK_SKIP_PDF: ${{ secrets.GITBOOK_SKIP_PDF }}
|
||||||
|
continue-on-error: ${{ env.GITBOOK_SKIP_PDF != '' }}
|
||||||
|
if: ${{ github.event_name == 'push' }}
|
||||||
|
run: |
|
||||||
|
for i in 1 2 3 4; do gitbook pdf ./ _book/clover.pdf && break || sleep 1; done
|
||||||
|
sudo apt-get -q install ghostscript
|
||||||
|
gs -sDEVICE=pdfwrite -dCompatibilityLevel=1.4 -dPDFSETTINGS=/default -dNOPAUSE -dQUIET -dBATCH -dDetectDuplicateImages -dCompressFonts=true -r150 -sOutputFile=_book/clover_ru_compressed.pdf _book/clover_ru.pdf
|
||||||
|
gs -sDEVICE=pdfwrite -dCompatibilityLevel=1.4 -dPDFSETTINGS=/default -dNOPAUSE -dQUIET -dBATCH -dDetectDuplicateImages -dCompressFonts=true -r150 -sOutputFile=_book/clover_en_compressed.pdf _book/clover_en.pdf
|
||||||
|
rm _book/clover_ru.pdf && mv _book/clover_ru_compressed.pdf _book/clover_ru.pdf
|
||||||
|
rm _book/clover_en.pdf && mv _book/clover_en_compressed.pdf _book/clover_en.pdf
|
||||||
|
ls -lah _book/clover*.pdf
|
||||||
|
echo '::set-output name=GITBOOK_PDF_OK::1'
|
||||||
|
- name: Download older PDFs
|
||||||
|
if: ${{ !steps.generate-pdf.outputs.GITBOOK_PDF_OK }}
|
||||||
|
run: |
|
||||||
|
rm _book/clover*.pdf
|
||||||
|
wget --no-verbose https://clover.coex.tech/clover_ru.pdf -P _book/
|
||||||
|
wget --no-verbose https://clover.coex.tech/clover_en.pdf -P _book/
|
||||||
|
- name: Deploy
|
||||||
|
uses: JamesIves/github-pages-deploy-action@4.1.3
|
||||||
|
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }}
|
||||||
|
with:
|
||||||
|
branch: gh-pages
|
||||||
|
folder: _book
|
||||||
|
clean: true
|
||||||
|
single-commit: true # to avoid multiple copies of large pdf files
|
||||||
18
.github/workflows/editorconfig.yaml
vendored
Normal file
18
.github/workflows/editorconfig.yaml
vendored
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
name: Editorconfig
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ '*' ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ master ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
editorconfig:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: .editorconfig Linter
|
||||||
|
run: |
|
||||||
|
wget --no-verbose https://github.com/okalachev/editorconfig-checker/releases/download/1.2.1-disable-spaces-amount/ec-linux-amd64
|
||||||
|
chmod +x ec-linux-amd64
|
||||||
|
./ec-linux-amd64 -spaces-after-tabs -e "roslib.js|ros3d.js|eventemitter2.js|json-to-pretty-yaml.js|draw.cpp|BinUtils.swift|\.idea|apps/android/app|blockly/|clover_blocks/programs/|highlight/|python.js|Assets.xcassets|test_parser_pass.txt|test_node_failure.txt|aruco_pose/vendor|\.stl|\.dxf|\.dae|\.material"
|
||||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -4,3 +4,9 @@
|
|||||||
node_modules/
|
node_modules/
|
||||||
_book/
|
_book/
|
||||||
package-lock.json
|
package-lock.json
|
||||||
|
clover_blocks/programs/*.*
|
||||||
|
!clover_blocks/programs/examples/*
|
||||||
|
/.vscode/
|
||||||
|
docs/.vuepress/.cache/
|
||||||
|
docs/.vuepress/.temp/
|
||||||
|
docs/.vuepress/dist
|
||||||
|
|||||||
@@ -21,7 +21,9 @@
|
|||||||
"ROS",
|
"ROS",
|
||||||
"ROS Kinetic",
|
"ROS Kinetic",
|
||||||
"ROS Melodic",
|
"ROS Melodic",
|
||||||
|
"ROS Noetic",
|
||||||
"OpenCV",
|
"OpenCV",
|
||||||
|
"OpenVPN",
|
||||||
"Gazebo",
|
"Gazebo",
|
||||||
"GitHub",
|
"GitHub",
|
||||||
"FPV",
|
"FPV",
|
||||||
@@ -106,7 +108,10 @@
|
|||||||
"UDP",
|
"UDP",
|
||||||
"QR",
|
"QR",
|
||||||
"Li-ion",
|
"Li-ion",
|
||||||
"Nvidia"
|
"Nvidia",
|
||||||
|
"VirtualBox",
|
||||||
|
"VMware",
|
||||||
|
"DuoCam"
|
||||||
],
|
],
|
||||||
"code_blocks": false
|
"code_blocks": false
|
||||||
},
|
},
|
||||||
|
|||||||
125
.travis.yml
125
.travis.yml
@@ -1,125 +0,0 @@
|
|||||||
os: linux
|
|
||||||
dist: xenial
|
|
||||||
language: generic
|
|
||||||
services:
|
|
||||||
- docker
|
|
||||||
env:
|
|
||||||
global:
|
|
||||||
- DOCKER="sfalexrog/img-tool:qemu-update"
|
|
||||||
- TARGET_REPO="https://github.com/${TRAVIS_REPO_SLUG}.git"
|
|
||||||
- if [[ -z ${TRAVIS_TAG} ]]; then IMAGE_VERSION="${TRAVIS_COMMIT}}"; else IMAGE_VERSION="${TRAVIS_TAG}"; fi
|
|
||||||
- IMAGE_NAME="$(basename -s '.git' ${TARGET_REPO})_${IMAGE_VERSION}.img"
|
|
||||||
git:
|
|
||||||
depth: 50
|
|
||||||
jobs:
|
|
||||||
fast_finish: true
|
|
||||||
include:
|
|
||||||
- stage: Build
|
|
||||||
name: "Raspberry Pi Image Build"
|
|
||||||
cache:
|
|
||||||
directories:
|
|
||||||
- imgcache
|
|
||||||
before_script:
|
|
||||||
- docker pull ${DOCKER}
|
|
||||||
# Check if there are any cached images, copy them to our "images" directory
|
|
||||||
- if [ -n "$(ls -A imgcache/*.zip)" ]; then mkdir -p images && cp imgcache/*.zip images; fi
|
|
||||||
script:
|
|
||||||
- if [[ -z ${TRAVIS_TAG} && "${TRAVIS_PULL_REQUEST}" != "false" ]]; then
|
|
||||||
echo "Commit range is ${TRAVIS_COMMIT_RANGE}" &&
|
|
||||||
if [ $(git diff --name-only ${TRAVIS_COMMIT_RANGE} | grep -v ^"docs/" | wc -l) -eq 0 ]; then
|
|
||||||
echo " === Docs-only change; skipping build ===" &&
|
|
||||||
export SKIP_BUILD=true;
|
|
||||||
fi;
|
|
||||||
fi
|
|
||||||
- if [ -z ${SKIP_BUILD} ]; then
|
|
||||||
docker run --privileged --rm -v /dev:/dev -v $(pwd):/builder/repo -e TRAVIS_TAG="${TRAVIS_TAG}" ${DOCKER};
|
|
||||||
fi
|
|
||||||
before_cache:
|
|
||||||
- cp images/*.zip imgcache
|
|
||||||
before_deploy:
|
|
||||||
# Set up git user name and tag this commit
|
|
||||||
- git config --local user.name "goldarte"
|
|
||||||
- git config --local user.email "goldartt@gmail.com"
|
|
||||||
- sudo chmod -R 777 *
|
|
||||||
- cd images && zip ${IMAGE_NAME}.zip ${IMAGE_NAME}
|
|
||||||
deploy:
|
|
||||||
provider: releases
|
|
||||||
token: ${GITHUB_OAUTH_TOKEN}
|
|
||||||
file: ${IMAGE_NAME}.zip
|
|
||||||
skip_cleanup: true
|
|
||||||
on:
|
|
||||||
tags: true
|
|
||||||
draft: true
|
|
||||||
name: ${TRAVIS_TAG}
|
|
||||||
- stage: Build
|
|
||||||
name: "Native Kinetic build"
|
|
||||||
env:
|
|
||||||
- NATIVE_DOCKER=ros:kinetic-ros-base
|
|
||||||
before_script:
|
|
||||||
- docker pull ${NATIVE_DOCKER}
|
|
||||||
script:
|
|
||||||
- docker run --rm -v $(pwd):/root/catkin_ws/src/clover ${NATIVE_DOCKER} /root/catkin_ws/src/clover/builder/standalone-install.sh
|
|
||||||
- stage: Build
|
|
||||||
name: "Native Melodic build"
|
|
||||||
env:
|
|
||||||
- NATIVE_DOCKER=ros:melodic-ros-base
|
|
||||||
before_script:
|
|
||||||
- docker pull ${NATIVE_DOCKER}
|
|
||||||
script:
|
|
||||||
- docker run --rm -v $(pwd):/root/catkin_ws/src/clover ${NATIVE_DOCKER} /root/catkin_ws/src/clover/builder/standalone-install.sh
|
|
||||||
- stage: Build
|
|
||||||
name: "Documentation"
|
|
||||||
language: node_js
|
|
||||||
node_js:
|
|
||||||
- "10"
|
|
||||||
before_script:
|
|
||||||
- sudo sh -c "echo ttf-mscorefonts-installer msttcorefonts/accepted-mscorefonts-eula select true | debconf-set-selections"
|
|
||||||
- sudo apt update && sudo apt install -y calibre msttcorefonts
|
|
||||||
- npm install gitbook-cli -g
|
|
||||||
- npm install markdownlint-cli -g
|
|
||||||
- npm install svgexport -g
|
|
||||||
- gitbook -V
|
|
||||||
- markdownlint -V
|
|
||||||
script:
|
|
||||||
- markdownlint docs
|
|
||||||
- ./check_assets_size.py
|
|
||||||
- ./check_unused_assets.py
|
|
||||||
- gitbook install
|
|
||||||
- gitbook build
|
|
||||||
- gitbook pdf ./ _book/clover.pdf
|
|
||||||
deploy:
|
|
||||||
provider: pages
|
|
||||||
local_dir: _book
|
|
||||||
skip_cleanup: true
|
|
||||||
token: ${GITHUB_OAUTH_TOKEN}
|
|
||||||
keep_history: true
|
|
||||||
target_branch: master
|
|
||||||
repo: CopterExpress/clover.coex.tech
|
|
||||||
fqdn: clover.coex.tech
|
|
||||||
verbose: true
|
|
||||||
on:
|
|
||||||
branch: master
|
|
||||||
- stage: Annotate
|
|
||||||
name: Auto-generate changelog
|
|
||||||
language: python
|
|
||||||
python: 3.6
|
|
||||||
install:
|
|
||||||
- pip install GitPython PyGithub
|
|
||||||
script:
|
|
||||||
- PYTHONUNBUFFERED=1 python ./gen_changelog.py
|
|
||||||
- stage: Build
|
|
||||||
name: Editorconfig-lint
|
|
||||||
language: generic
|
|
||||||
before_script:
|
|
||||||
- wget https://github.com/okalachev/editorconfig-checker/releases/download/1.2.1-disable-spaces-amount/ec-linux-amd64
|
|
||||||
- chmod +x ec-linux-amd64
|
|
||||||
script:
|
|
||||||
- ./ec-linux-amd64 -spaces-after-tabs -e "roslib.js|ros3d.js|eventemitter2.js|draw.cpp|BinUtils.swift|\.idea|apps/android/app|Assets.xcassets|test_parser_pass.txt|test_node_failure.txt|aruco_pose/vendor|\.stl|\.dxf|\.dae"
|
|
||||||
stages:
|
|
||||||
- Build
|
|
||||||
- Annotate
|
|
||||||
# More info there
|
|
||||||
# https://github.com/travis-ci/travis-ci/issues/6893
|
|
||||||
# https://docs.travis-ci.com/user/customizing-the-build/
|
|
||||||
# https://docs.travis-ci.com/user/deployment/releases
|
|
||||||
# https://docs.travis-ci.com/user/environment-variables/
|
|
||||||
21
README.md
21
README.md
@@ -1,12 +1,14 @@
|
|||||||
# COEX Clover Drone Kit
|
# clover🍀: create autonomous drones easily
|
||||||
|
|
||||||
<img src="docs/assets/clever4-front-white.png" align="right" width="400px" alt="Clover Drone">
|
<img src="docs/assets/clover42-main-margin.png" align="right" width="400px" alt="COEX Clover Drone">
|
||||||
|
|
||||||
Clover is an educational programmable drone kit consisting of an unassembled quadcopter, open source software and documentation. The kit includes Pixracer-compatible autopilot running PX4 firmware, Raspberry Pi 4 as companion computer, a camera for computer vision navigation as well as additional sensors and peripheral devices.
|
Clover is an open source [ROS](https://www.ros.org)-based framework, providing user-friendly tools to control [PX4](https://px4.io)-powered drones. Clover is available as a ROS package, but is shipped mainly as a preconfigured image for Raspberry Pi. Once you've installed Raspberry Pi on your drone and flashed the image to its microSD card, taking the drone up in the air is a matter of minutes.
|
||||||
|
|
||||||
The main documentation is available [on Gitbook](https://clover.coex.tech/).
|
COEX Clover Drone is an educational programmable drone kit, suited perfectly for running clover software. The kit is shipped unassembled and includes Pixracer-compatible autopilot running PX4 firmware, Raspberry Pi 4 as a companion computer, a camera for computer vision navigation as well as additional sensors and peripheral devices. Batteries included.
|
||||||
|
|
||||||
Official website: <a href="https://coex.tech/clover">coex.tech/clover</a>.
|
The main documentation is available at [https://clover.coex.tech](https://clover.coex.tech/). Official website: [coex.tech/clover](https://coex.tech/clover).
|
||||||
|
|
||||||
|
[__Support us on Kickstarter!__](https://www.kickstarter.com/projects/copterexpress/cloverdrone)
|
||||||
|
|
||||||
## Video compilation
|
## Video compilation
|
||||||
|
|
||||||
@@ -18,12 +20,13 @@ Clover drone is used on a wide range of educational events, including [Copter Ha
|
|||||||
|
|
||||||
Preconfigured image for Raspberry Pi with installed and configured software, ready to fly, is available [in the Releases section](https://github.com/CopterExpress/clover/releases).
|
Preconfigured image for Raspberry Pi with installed and configured software, ready to fly, is available [in the Releases section](https://github.com/CopterExpress/clover/releases).
|
||||||
|
|
||||||
[](https://travis-ci.org/CopterExpress/clover)
|

|
||||||
|

|
||||||
|
|
||||||
Image features:
|
Image features:
|
||||||
|
|
||||||
* Raspbian Buster
|
* Raspbian Buster
|
||||||
* [ROS Melodic](http://wiki.ros.org/melodic)
|
* [ROS Noetic](http://wiki.ros.org/noetic)
|
||||||
* Configured networking
|
* Configured networking
|
||||||
* OpenCV
|
* OpenCV
|
||||||
* [`mavros`](http://wiki.ros.org/mavros)
|
* [`mavros`](http://wiki.ros.org/mavros)
|
||||||
@@ -35,6 +38,10 @@ API description for autonomous flights is available [on GitBook](https://clover.
|
|||||||
|
|
||||||
For manual package installation and running see [`clover` package documentation](clover/README.md).
|
For manual package installation and running see [`clover` package documentation](clover/README.md).
|
||||||
|
|
||||||
|
## Support
|
||||||
|
|
||||||
|
[](https://t.me/COEXHelpdesk)
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
While the Clover platform source code is available under the MIT License, note, that the [documentation](docs/) is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
|
While the Clover platform source code is available under the MIT License, note, that the [documentation](docs/) is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
iOS-приложение для управления Клевером
|
# iOS-приложение для управления Клевером
|
||||||
--------------------------------------
|
|
||||||
|
|
||||||
Для установки зависимостей необходим [CocoaPods](https://cocoapods.org):
|
Для установки зависимостей необходим [CocoaPods](https://cocoapods.org):
|
||||||
|
|
||||||
|
|||||||
8
aruco_pose/CHANGELOG.rst
Normal file
8
aruco_pose/CHANGELOG.rst
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
Changelog for package aruco_pose
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
0.21.1 (2020-11-17)
|
||||||
|
-------------------
|
||||||
|
* First release of aruco_pose package to ROS
|
||||||
|
* Contributors: Alamoris, Alexey Rogachevskiy, Arthur Golubtsov, Ilya Petrov, Oleg Kalachev
|
||||||
@@ -22,13 +22,21 @@ find_package(catkin REQUIRED COMPONENTS
|
|||||||
dynamic_reconfigure
|
dynamic_reconfigure
|
||||||
)
|
)
|
||||||
|
|
||||||
find_package(OpenCV 3 REQUIRED COMPONENTS core imgproc calib3d)
|
# Workaround for OpenCV 3/4 support
|
||||||
|
set(_opencv_version 4)
|
||||||
|
find_package(OpenCV ${_opencv_version} QUIET COMPONENTS core imgproc calib3d)
|
||||||
|
if (NOT OpenCV_FOUND)
|
||||||
|
message(STATUS "Did not find OpenCV 4, searching for OpenCV 3")
|
||||||
|
set(_opencv_version 3)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
find_package(OpenCV ${_opencv_version} REQUIRED COMPONENTS core imgproc calib3d)
|
||||||
if ("${OpenCV_VERSION_MINOR}" LESS "9")
|
if ("${OpenCV_VERSION_MINOR}" LESS "9")
|
||||||
message(STATUS "OpenCV version too low, using vendored ArUco package")
|
message(STATUS "OpenCV version too low, using vendored ArUco package")
|
||||||
include(vendor/VendorOpenCV.cmake)
|
include(vendor/VendorOpenCV.cmake)
|
||||||
else()
|
else()
|
||||||
message(STATUS "Using system OpenCV ArUco package")
|
message(STATUS "Using system OpenCV ArUco package")
|
||||||
find_package(OpenCV 3 REQUIRED COMPONENTS aruco)
|
find_package(OpenCV ${_opencv_version} REQUIRED COMPONENTS aruco)
|
||||||
endif()
|
endif()
|
||||||
message(STATUS "OpenCV include dirs: ${OpenCV_INCLUDE_DIRS}")
|
message(STATUS "OpenCV include dirs: ${OpenCV_INCLUDE_DIRS}")
|
||||||
message(STATUS "OpenCV libraries: ${OpenCV_LIBRARIES}")
|
message(STATUS "OpenCV libraries: ${OpenCV_LIBRARIES}")
|
||||||
@@ -111,7 +119,7 @@ generate_messages(
|
|||||||
|
|
||||||
## Generate dynamic reconfigure parameters in the 'cfg' folder
|
## Generate dynamic reconfigure parameters in the 'cfg' folder
|
||||||
generate_dynamic_reconfigure_options(
|
generate_dynamic_reconfigure_options(
|
||||||
cfg/DetectorParams.cfg
|
cfg/Detector.cfg
|
||||||
)
|
)
|
||||||
|
|
||||||
###################################
|
###################################
|
||||||
@@ -172,6 +180,13 @@ target_link_libraries(aruco_pose
|
|||||||
${OpenCV_LIBRARIES}
|
${OpenCV_LIBRARIES}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Prevent aruco_pose from having undefined symbols
|
||||||
|
set_property(TARGET aruco_pose
|
||||||
|
APPEND
|
||||||
|
PROPERTY LINK_FLAGS
|
||||||
|
-Wl,--no-undefined
|
||||||
|
)
|
||||||
|
|
||||||
#############
|
#############
|
||||||
## Install ##
|
## Install ##
|
||||||
#############
|
#############
|
||||||
@@ -187,11 +202,11 @@ target_link_libraries(aruco_pose
|
|||||||
# )
|
# )
|
||||||
|
|
||||||
## Mark executables and/or libraries for installation
|
## Mark executables and/or libraries for installation
|
||||||
# install(TARGETS ${PROJECT_NAME} ${PROJECT_NAME}_node
|
install(TARGETS ${PROJECT_NAME}
|
||||||
# ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
|
ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
|
||||||
# LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
|
LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
|
||||||
# RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
|
RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
|
||||||
# )
|
)
|
||||||
|
|
||||||
## Mark cpp header files for installation
|
## Mark cpp header files for installation
|
||||||
# install(DIRECTORY include/${PROJECT_NAME}/
|
# install(DIRECTORY include/${PROJECT_NAME}/
|
||||||
@@ -207,6 +222,14 @@ target_link_libraries(aruco_pose
|
|||||||
# DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}
|
# DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}
|
||||||
# )
|
# )
|
||||||
|
|
||||||
|
catkin_install_python(PROGRAMS src/genmap.py
|
||||||
|
DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
|
||||||
|
)
|
||||||
|
|
||||||
|
install(DIRECTORY launch DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION})
|
||||||
|
|
||||||
|
install(DIRECTORY map DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION})
|
||||||
|
|
||||||
#############
|
#############
|
||||||
## Testing ##
|
## Testing ##
|
||||||
#############
|
#############
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ It's recommended to run it within the same nodelet manager with the camera nodel
|
|||||||
|
|
||||||
* `~map` – path to text file with markers list
|
* `~map` – path to text file with markers list
|
||||||
* `~frame_id` – published frame id (default: `aruco_map`)
|
* `~frame_id` – published frame id (default: `aruco_map`)
|
||||||
* `~known_tilt` – debug image width
|
* `~known_tilt` – known tilt (pitch and roll) of markers map as a frame
|
||||||
* `~image_width` – debug image width (default: 2000)
|
* `~image_width` – debug image width (default: 2000)
|
||||||
* `~image_height` – debug image height (default: 2000)
|
* `~image_height` – debug image height (default: 2000)
|
||||||
* `~image_margin` – debug image margin (default: 200)
|
* `~image_margin` – debug image margin (default: 200)
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ p = cv2.aruco.DetectorParameters_create()
|
|||||||
|
|
||||||
gen = ParameterGenerator()
|
gen = ParameterGenerator()
|
||||||
|
|
||||||
|
gen.add("enabled", bool_t, 0, "if detection enabled", True)
|
||||||
|
|
||||||
gen.add("adaptiveThreshConstant", double_t, 0,
|
gen.add("adaptiveThreshConstant", double_t, 0,
|
||||||
"Constant for adaptive thresholding before finding contours",
|
"Constant for adaptive thresholding before finding contours",
|
||||||
p.adaptiveThreshConstant, 0, 100)
|
p.adaptiveThreshConstant, 0, 100)
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0"?>
|
<?xml version="1.0"?>
|
||||||
<package format="2">
|
<package format="2">
|
||||||
<name>aruco_pose</name>
|
<name>aruco_pose</name>
|
||||||
<version>0.0.1</version>
|
<version>0.23.0</version>
|
||||||
<description>Positioning with ArUco markers</description>
|
<description>Positioning with ArUco markers</description>
|
||||||
|
|
||||||
<maintainer email="okalachev@gmail.com">Oleg Kalachev</maintainer>
|
<maintainer email="okalachev@gmail.com">Oleg Kalachev</maintainer>
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ private:
|
|||||||
std::unique_ptr<tf2_ros::Buffer> tf_buffer_;
|
std::unique_ptr<tf2_ros::Buffer> tf_buffer_;
|
||||||
std::unique_ptr<tf2_ros::TransformListener> tf_listener_;
|
std::unique_ptr<tf2_ros::TransformListener> tf_listener_;
|
||||||
std::shared_ptr<dynamic_reconfigure::Server<aruco_pose::DetectorConfig>> dyn_srv_;
|
std::shared_ptr<dynamic_reconfigure::Server<aruco_pose::DetectorConfig>> dyn_srv_;
|
||||||
|
bool enabled_ = true;
|
||||||
cv::Ptr<cv::aruco::Dictionary> dictionary_;
|
cv::Ptr<cv::aruco::Dictionary> dictionary_;
|
||||||
cv::Ptr<cv::aruco::DetectorParameters> parameters_;
|
cv::Ptr<cv::aruco::DetectorParameters> parameters_;
|
||||||
image_transport::Publisher debug_pub_;
|
image_transport::Publisher debug_pub_;
|
||||||
@@ -110,17 +111,14 @@ public:
|
|||||||
image_transport::ImageTransport it(nh_);
|
image_transport::ImageTransport it(nh_);
|
||||||
image_transport::ImageTransport it_priv(nh_priv_);
|
image_transport::ImageTransport it_priv(nh_priv_);
|
||||||
|
|
||||||
map_markers_sub_ = nh_.subscribe("map_markers", 1, &ArucoDetect::mapMarkersCallback, this);
|
dyn_srv_ = std::make_shared<dynamic_reconfigure::Server<aruco_pose::DetectorConfig>>(nh_priv_);
|
||||||
|
dyn_srv_->setCallback(std::bind(&ArucoDetect::paramCallback, this, std::placeholders::_1, std::placeholders::_2));
|
||||||
|
|
||||||
debug_pub_ = it_priv.advertise("debug", 1);
|
debug_pub_ = it_priv.advertise("debug", 1);
|
||||||
markers_pub_ = nh_priv_.advertise<aruco_pose::MarkerArray>("markers", 1);
|
markers_pub_ = nh_priv_.advertise<aruco_pose::MarkerArray>("markers", 1);
|
||||||
vis_markers_pub_ = nh_priv_.advertise<visualization_msgs::MarkerArray>("visualization", 1);
|
vis_markers_pub_ = nh_priv_.advertise<visualization_msgs::MarkerArray>("visualization", 1);
|
||||||
img_sub_ = it.subscribeCamera("image_raw", 1, &ArucoDetect::imageCallback, this);
|
img_sub_ = it.subscribeCamera("image_raw", 1, &ArucoDetect::imageCallback, this);
|
||||||
|
map_markers_sub_ = nh_.subscribe("map_markers", 1, &ArucoDetect::mapMarkersCallback, this);
|
||||||
dyn_srv_ = std::make_shared<dynamic_reconfigure::Server<aruco_pose::DetectorConfig>>(nh_priv_);
|
|
||||||
dynamic_reconfigure::Server<aruco_pose::DetectorConfig>::CallbackType cb;
|
|
||||||
|
|
||||||
cb = std::bind(&ArucoDetect::paramCallback, this, std::placeholders::_1, std::placeholders::_2);
|
|
||||||
dyn_srv_->setCallback(cb);
|
|
||||||
|
|
||||||
NODELET_INFO("ready");
|
NODELET_INFO("ready");
|
||||||
}
|
}
|
||||||
@@ -128,6 +126,8 @@ public:
|
|||||||
private:
|
private:
|
||||||
void imageCallback(const sensor_msgs::ImageConstPtr& msg, const sensor_msgs::CameraInfoConstPtr &cinfo)
|
void imageCallback(const sensor_msgs::ImageConstPtr& msg, const sensor_msgs::CameraInfoConstPtr &cinfo)
|
||||||
{
|
{
|
||||||
|
if (!enabled_) return;
|
||||||
|
|
||||||
Mat image = cv_bridge::toCvShare(msg, "bgr8")->image;
|
Mat image = cv_bridge::toCvShare(msg, "bgr8")->image;
|
||||||
|
|
||||||
vector<int> ids;
|
vector<int> ids;
|
||||||
@@ -356,6 +356,7 @@ private:
|
|||||||
|
|
||||||
void paramCallback(aruco_pose::DetectorConfig &config, uint32_t level)
|
void paramCallback(aruco_pose::DetectorConfig &config, uint32_t level)
|
||||||
{
|
{
|
||||||
|
enabled_ = config.enabled;
|
||||||
parameters_->adaptiveThreshConstant = config.adaptiveThreshConstant;
|
parameters_->adaptiveThreshConstant = config.adaptiveThreshConstant;
|
||||||
parameters_->adaptiveThreshWinSizeMin = config.adaptiveThreshWinSizeMin;
|
parameters_->adaptiveThreshWinSizeMin = config.adaptiveThreshWinSizeMin;
|
||||||
parameters_->adaptiveThreshWinSizeMax = config.adaptiveThreshWinSizeMax;
|
parameters_->adaptiveThreshWinSizeMax = config.adaptiveThreshWinSizeMax;
|
||||||
|
|||||||
@@ -124,6 +124,11 @@ public:
|
|||||||
vis_markers_pub_ = nh_priv_.advertise<visualization_msgs::MarkerArray>("visualization", 1, true);
|
vis_markers_pub_ = nh_priv_.advertise<visualization_msgs::MarkerArray>("visualization", 1, true);
|
||||||
debug_pub_ = it_priv.advertise("debug", 1);
|
debug_pub_ = it_priv.advertise("debug", 1);
|
||||||
|
|
||||||
|
publishMarkersFrames();
|
||||||
|
publishMarkers();
|
||||||
|
publishMapImage();
|
||||||
|
vis_markers_pub_.publish(vis_array_);
|
||||||
|
|
||||||
image_sub_.subscribe(nh_, "image_raw", 1);
|
image_sub_.subscribe(nh_, "image_raw", 1);
|
||||||
info_sub_.subscribe(nh_, "camera_info", 1);
|
info_sub_.subscribe(nh_, "camera_info", 1);
|
||||||
markers_sub_.subscribe(nh_, "markers", 1);
|
markers_sub_.subscribe(nh_, "markers", 1);
|
||||||
@@ -131,11 +136,6 @@ public:
|
|||||||
sync_.reset(new message_filters::Synchronizer<SyncPolicy>(SyncPolicy(10), image_sub_, info_sub_, markers_sub_));
|
sync_.reset(new message_filters::Synchronizer<SyncPolicy>(SyncPolicy(10), image_sub_, info_sub_, markers_sub_));
|
||||||
sync_->registerCallback(boost::bind(&ArucoMap::callback, this, _1, _2, _3));
|
sync_->registerCallback(boost::bind(&ArucoMap::callback, this, _1, _2, _3));
|
||||||
|
|
||||||
publishMarkersFrames();
|
|
||||||
publishMarkers();
|
|
||||||
publishMapImage();
|
|
||||||
vis_markers_pub_.publish(vis_array_);
|
|
||||||
|
|
||||||
NODELET_INFO("ready");
|
NODELET_INFO("ready");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,26 +3,11 @@
|
|||||||
|
|
||||||
#include "draw.h"
|
#include "draw.h"
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
using namespace cv;
|
using namespace cv;
|
||||||
using namespace cv::aruco;
|
using namespace cv::aruco;
|
||||||
|
|
||||||
static void _cvProjectPoints2( const CvMat* object_points, const CvMat* rotation_vector,
|
|
||||||
const CvMat* translation_vector, const CvMat* camera_matrix,
|
|
||||||
const CvMat* distortion_coeffs, CvMat* image_points,
|
|
||||||
CvMat* dpdrot CV_DEFAULT(NULL), CvMat* dpdt CV_DEFAULT(NULL),
|
|
||||||
CvMat* dpdf CV_DEFAULT(NULL), CvMat* dpdc CV_DEFAULT(NULL),
|
|
||||||
CvMat* dpddist CV_DEFAULT(NULL),
|
|
||||||
double aspect_ratio CV_DEFAULT(0));
|
|
||||||
|
|
||||||
static void _projectPoints( InputArray objectPoints,
|
|
||||||
InputArray rvec, InputArray tvec,
|
|
||||||
InputArray cameraMatrix, InputArray distCoeffs,
|
|
||||||
OutputArray imagePoints,
|
|
||||||
OutputArray jacobian = noArray(),
|
|
||||||
double aspectRatio = 0 );
|
|
||||||
|
|
||||||
|
|
||||||
void _drawPlanarBoard(Board *_board, Size outSize, OutputArray _img, int marginSize,
|
void _drawPlanarBoard(Board *_board, Size outSize, OutputArray _img, int marginSize,
|
||||||
int borderBits, bool drawAxis) {
|
int borderBits, bool drawAxis) {
|
||||||
|
|
||||||
@@ -142,35 +127,194 @@ void _drawPlanarBoard(Board *_board, Size outSize, OutputArray _img, int marginS
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Draw a (potentially partially visible) line. */
|
/**
|
||||||
static void linePartial(InputOutputArray image, Point3f p1, Point3f p2, const Scalar& color,
|
* @brief Convert point coordinates from world space to camera space.
|
||||||
int thickness = 1, int lineType = LINE_8, int shift = 0)
|
*
|
||||||
|
* @param points A vector of points in world space.
|
||||||
|
* @param rvec Rotation matrix or Rodrigues rotation vector.
|
||||||
|
* @param tvec Translation vector from world to camera space.
|
||||||
|
*
|
||||||
|
* @return A vector of points in camera space.
|
||||||
|
*/
|
||||||
|
template<typename CvPointType>
|
||||||
|
static std::vector<CvPointType> worldToCamera(const std::vector<CvPointType>& points,
|
||||||
|
const cv::Mat& rvec, const cv::Mat& tvec)
|
||||||
{
|
{
|
||||||
// If both points are behind the screen, don't draw anything
|
// We operate with CV_64F matrices internally to avoid precision loss
|
||||||
if (p1.z <= 0 && p2.z <= 0) {
|
cv::Mat rvec_64f;
|
||||||
return;
|
cv::Mat tvec_64f;
|
||||||
|
rvec.convertTo(rvec_64f, CV_64F);
|
||||||
|
tvec.convertTo(tvec_64f, CV_64F);
|
||||||
|
|
||||||
|
// Convert Rodrigues vector to rotation matrix
|
||||||
|
cv::Mat rmat;
|
||||||
|
if ((rvec_64f.cols == 3 && rvec_64f.rows == 1) ||
|
||||||
|
(rvec_64f.cols == 1 && rvec_64f.rows == 3))
|
||||||
|
{
|
||||||
|
Rodrigues(rvec_64f, rmat);
|
||||||
}
|
}
|
||||||
Point2f p1p{p1.x, p1.y};
|
else
|
||||||
Point2f p2p{p2.x, p2.y};
|
{
|
||||||
// If points are on the different sides of the plane, compute intersection point
|
rmat = rvec_64f.clone();
|
||||||
if (p1.z * p2.z < 0) {
|
|
||||||
// Compute intersection point with the screen
|
|
||||||
// We denote alpha as such:
|
|
||||||
// xi = (1 - alpha) * x1 + alpha * x2
|
|
||||||
// yi = (1 - alpha) * y1 + alpha * y2
|
|
||||||
// zi = (1 - alpha) * z1 + alpha * z2 = 0
|
|
||||||
// Thus, alpha can be expressed as
|
|
||||||
// alpha = z1 / (z1 - z2)
|
|
||||||
float alpha = p1.z / (p1.z - p2.z);
|
|
||||||
Point2f pi{(1 - alpha) * p1.x + alpha * p2.x, (1 - alpha) * p1.y + alpha * p2.y};
|
|
||||||
// Now, if z1 is negative, we draw the line from (xi, yi) to (x2, y2), else we draw from (x1, y1) to (xi, yi)
|
|
||||||
if (p1.z < 0) {
|
|
||||||
p1p = pi;
|
|
||||||
} else {
|
|
||||||
p2p = pi;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
line(image, p1p, p2p, color, thickness, lineType, shift);
|
// Make sure tvec has a size of (3, 1)
|
||||||
|
if (tvec_64f.rows == 1)
|
||||||
|
{
|
||||||
|
tvec_64f = tvec_64f.t();
|
||||||
|
}
|
||||||
|
std::vector<CvPointType> result;
|
||||||
|
result.reserve(points.size());
|
||||||
|
for(const auto& point : points)
|
||||||
|
{
|
||||||
|
// Calculate point coordinates in camera frame
|
||||||
|
// static_casts are here to silence potential narrowing conversion warnings
|
||||||
|
CvPointType camPoint{
|
||||||
|
static_cast<decltype(CvPointType::x)>(point.x * rmat.at<double>(0,0) + point.y * rmat.at<double>(0,1) + point.z * rmat.at<double>(0,2) + tvec_64f.at<double>(0)),
|
||||||
|
static_cast<decltype(CvPointType::y)>(point.x * rmat.at<double>(1,0) + point.y * rmat.at<double>(1,1) + point.z * rmat.at<double>(1,2) + tvec_64f.at<double>(1)),
|
||||||
|
static_cast<decltype(CvPointType::z)>(point.x * rmat.at<double>(2,0) + point.y * rmat.at<double>(2,1) + point.z * rmat.at<double>(2,2) + tvec_64f.at<double>(2))
|
||||||
|
};
|
||||||
|
result.push_back(camPoint);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Project points from camera space to screen space, applying distortion in the process.
|
||||||
|
*
|
||||||
|
* @param points A vector of points in camera space.
|
||||||
|
* @param cameraMatrix OpenCV intrinsic camera matrix.
|
||||||
|
* @param distCoeffs OpenCV distortion model coefficients.
|
||||||
|
*
|
||||||
|
* @return A vector of points in screen space.
|
||||||
|
*/
|
||||||
|
template<typename CvPointType>
|
||||||
|
static std::vector<CvPointType> cameraToScreen(const std::vector<CvPointType>& points,
|
||||||
|
const cv::Mat& cameraMatrix,
|
||||||
|
const cv::Mat& distCoeffs)
|
||||||
|
{
|
||||||
|
// We operate with CV_64F matrices internally to avoid precision loss
|
||||||
|
cv::Mat cm_64f; // camera matrix, CV_64F
|
||||||
|
cv::Mat dc_64f; // distortion coefficients, CV_64F
|
||||||
|
cameraMatrix.convertTo(cm_64f, CV_64F);
|
||||||
|
distCoeffs.convertTo(dc_64f, CV_64F);
|
||||||
|
|
||||||
|
// Make sure distortion vector has a size of (N, 1)
|
||||||
|
if (dc_64f.rows == 1)
|
||||||
|
{
|
||||||
|
dc_64f = dc_64f.t();
|
||||||
|
}
|
||||||
|
|
||||||
|
// We will always use 12 distortion coefficients,
|
||||||
|
// and we can safely pad missing ones with zeroes
|
||||||
|
dc_64f.resize(12, 0.0);
|
||||||
|
|
||||||
|
std::vector<CvPointType> result;
|
||||||
|
result.reserve(points.size());
|
||||||
|
|
||||||
|
for(const auto& point : points)
|
||||||
|
{
|
||||||
|
// Apply perspective projection, preserving initial Z coordinate
|
||||||
|
// Always use double-precision
|
||||||
|
cv::Point3d camPoint{
|
||||||
|
point.x / point.z,
|
||||||
|
point.y / point.z,
|
||||||
|
point.z
|
||||||
|
};
|
||||||
|
|
||||||
|
// Apply distortion
|
||||||
|
// Note that we do not consider tilted sensor distortion
|
||||||
|
// r^2 - distance from the image center squared
|
||||||
|
double r2 = camPoint.x * camPoint.x + camPoint.y * camPoint.y;
|
||||||
|
// r^4 - same, but to the 4th power
|
||||||
|
double r4 = r2 * r2;
|
||||||
|
// r^6 - same, but to the 6th power
|
||||||
|
double r6 = r4 * r2;
|
||||||
|
// tg1 - first tangential shift factor (2 * x * y)
|
||||||
|
double tg1 = 2 * camPoint.x * camPoint.y;
|
||||||
|
// tg2 - second tangential shift factor (r^2 + 2 * x^2)
|
||||||
|
double tg2 = r2 + 2 * camPoint.x * camPoint.x;
|
||||||
|
// tg3 - third tangential shift factor (r^2 + 2 * y^2)
|
||||||
|
double tg3 = r2 + 2 * camPoint.y * camPoint.y;
|
||||||
|
// polynomial distortion factor (numerator)
|
||||||
|
double pndist = 1 + dc_64f.at<double>(0) * r2 + dc_64f.at<double>(1) * r4 + dc_64f.at<double>(4) * r6;
|
||||||
|
// polynomial distortion factror (denominator)
|
||||||
|
double pddist = 1.0 / (1 + dc_64f.at<double>(5) * r2 + dc_64f.at<double>(6) * r4 + dc_64f.at<double>(7) * r6);
|
||||||
|
// Distorted point coordinates (always double-precision)
|
||||||
|
cv::Point3d distortedPoint{
|
||||||
|
camPoint.x * pndist * pddist + dc_64f.at<double>(2) * tg1 + dc_64f.at<double>(3) * tg2 + dc_64f.at<double>(8) * r2 + dc_64f.at<double>(9) * r4,
|
||||||
|
camPoint.y * pndist * pddist + dc_64f.at<double>(2) * tg3 + dc_64f.at<double>(3) * tg1 + dc_64f.at<double>(10) * r2 + dc_64f.at<double>(11) * r4,
|
||||||
|
camPoint.z
|
||||||
|
};
|
||||||
|
|
||||||
|
// Convert to screen space
|
||||||
|
// We use static_cast here to silence potential warnings about narrowing conversions
|
||||||
|
// (we expect that to be the case)
|
||||||
|
CvPointType screenPoint{
|
||||||
|
static_cast<decltype(CvPointType::x)>(distortedPoint.x * cm_64f.at<double>(0, 0) + cm_64f.at<double>(0, 2)),
|
||||||
|
static_cast<decltype(CvPointType::y)>(distortedPoint.y * cm_64f.at<double>(1, 1) + cm_64f.at<double>(1, 2)),
|
||||||
|
static_cast<decltype(CvPointType::z)>(distortedPoint.z)
|
||||||
|
};
|
||||||
|
|
||||||
|
result.push_back(screenPoint);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Clip a line against a clip plane.
|
||||||
|
*
|
||||||
|
* This function "clips" a line (described by two points in *camera space*)
|
||||||
|
* against a clip plane that is `clipPlaneDistance` meters away from the
|
||||||
|
* camera focal point. If both points are further away from the focal point
|
||||||
|
* than `clipPlaneDistance`, they will be returned unmodified. If one of the
|
||||||
|
* points is behind the clipping plane, a point *on* the clipping plane will
|
||||||
|
* be computed and returned as one of the points.
|
||||||
|
*
|
||||||
|
* If none of the points are visible, an empty vector will be returned.
|
||||||
|
*
|
||||||
|
* @param p1 First point on the line, in camera space.
|
||||||
|
* @param p2 Second point on the line, in camera space.
|
||||||
|
* @param clipPlaneDistance Distance from the focal point to the clipping plane.
|
||||||
|
* @return A vector of zero or two points on the clipped line, in camera space.
|
||||||
|
*/
|
||||||
|
static std::vector<Point3f> lineClip(Point3f p1, Point3f p2, float clipPlaneDistance = 0.1f)
|
||||||
|
{
|
||||||
|
// We don't need to compute an intersection if both points are
|
||||||
|
// behind us
|
||||||
|
if (p1.z < clipPlaneDistance && p2.z < clipPlaneDistance)
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
// We don't need to compute an intersection if both points are
|
||||||
|
// in front of us
|
||||||
|
if (p1.z > clipPlaneDistance && p2.z > clipPlaneDistance)
|
||||||
|
{
|
||||||
|
return {p1, p2};
|
||||||
|
}
|
||||||
|
// We don't really want to compute an intersection if both Z coordinates
|
||||||
|
// are sufficiently close to each other
|
||||||
|
if (std::abs(p1.z - p2.z) < 0.0001) // The number here is chosen arbitrarily
|
||||||
|
{
|
||||||
|
return {p1, p2};
|
||||||
|
}
|
||||||
|
// We compute the intersection as such:
|
||||||
|
// zi = (1 - alpha) * p1.z + alpha * p2.z = clipPlaneDistance
|
||||||
|
// alpha = (p1.z - clipPlaneDistance) / (p1.z - p2.z)
|
||||||
|
double alpha = (p1.z - clipPlaneDistance) / (p1.z - p2.z);
|
||||||
|
Point3f clipPlanePoint{
|
||||||
|
static_cast<float>((1 - alpha) * p1.x + alpha * p2.x),
|
||||||
|
static_cast<float>((1 - alpha) * p1.y + alpha * p2.y),
|
||||||
|
clipPlaneDistance
|
||||||
|
};
|
||||||
|
if (p1.z < clipPlaneDistance)
|
||||||
|
{
|
||||||
|
return {clipPlanePoint, p2};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return {p1, clipPlanePoint};
|
||||||
|
}
|
||||||
|
// Unreachable?
|
||||||
}
|
}
|
||||||
|
|
||||||
void _drawAxis(InputOutputArray _image, InputArray _cameraMatrix, InputArray _distCoeffs,
|
void _drawAxis(InputOutputArray _image, InputArray _cameraMatrix, InputArray _distCoeffs,
|
||||||
@@ -186,647 +330,23 @@ void _drawAxis(InputOutputArray _image, InputArray _cameraMatrix, InputArray _di
|
|||||||
axisPoints.push_back(Point3f(length, 0, 0));
|
axisPoints.push_back(Point3f(length, 0, 0));
|
||||||
axisPoints.push_back(Point3f(0, length, 0));
|
axisPoints.push_back(Point3f(0, length, 0));
|
||||||
axisPoints.push_back(Point3f(0, 0, length));
|
axisPoints.push_back(Point3f(0, 0, length));
|
||||||
std::vector<Point3f> imagePointsZ;
|
auto camAxisPoints = worldToCamera(axisPoints, _rvec.getMat(), _tvec.getMat());
|
||||||
_projectPoints(axisPoints, _rvec, _tvec, _cameraMatrix, _distCoeffs, imagePointsZ);
|
auto axisX = cameraToScreen(lineClip(camAxisPoints[0], camAxisPoints[1]), _cameraMatrix.getMat(), _distCoeffs.getMat());
|
||||||
|
auto axisY = cameraToScreen(lineClip(camAxisPoints[0], camAxisPoints[2]), _cameraMatrix.getMat(), _distCoeffs.getMat());
|
||||||
// draw axis lines
|
auto axisZ = cameraToScreen(lineClip(camAxisPoints[0], camAxisPoints[3]), _cameraMatrix.getMat(), _distCoeffs.getMat());
|
||||||
linePartial(_image, imagePointsZ[0], imagePointsZ[1], Scalar(0, 0, 255), 3);
|
if (axisX.size() > 0)
|
||||||
linePartial(_image, imagePointsZ[0], imagePointsZ[2], Scalar(0, 255, 0), 3);
|
|
||||||
linePartial(_image, imagePointsZ[0], imagePointsZ[3], Scalar(255, 0, 0), 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
static CvMat _cvMat(const cv::Mat& m)
|
|
||||||
{
|
|
||||||
CvMat self;
|
|
||||||
CV_DbgAssert(m.dims <= 2);
|
|
||||||
self = cvMat(m.rows, m.dims == 1 ? 1 : m.cols, m.type(), m.data);
|
|
||||||
self.step = (int)m.step[0];
|
|
||||||
self.type = (self.type & ~cv::Mat::CONTINUOUS_FLAG) | (m.flags & cv::Mat::CONTINUOUS_FLAG);
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _projectPoints( InputArray _opoints,
|
|
||||||
InputArray _rvec,
|
|
||||||
InputArray _tvec,
|
|
||||||
InputArray _cameraMatrix,
|
|
||||||
InputArray _distCoeffs,
|
|
||||||
OutputArray _ipoints,
|
|
||||||
OutputArray _jacobian,
|
|
||||||
double aspectRatio )
|
|
||||||
{
|
|
||||||
Mat opoints = _opoints.getMat();
|
|
||||||
int npoints = opoints.checkVector(3), depth = opoints.depth();
|
|
||||||
CV_Assert(npoints >= 0 && (depth == CV_32F || depth == CV_64F));
|
|
||||||
|
|
||||||
CvMat dpdrot, dpdt, dpdf, dpdc, dpddist;
|
|
||||||
CvMat *pdpdrot = 0, *pdpdt = 0, *pdpdf = 0, *pdpdc = 0, *pdpddist = 0;
|
|
||||||
|
|
||||||
CV_Assert(_ipoints.needed());
|
|
||||||
|
|
||||||
_ipoints.create(npoints, 1, CV_MAKETYPE(depth, 3), -1, true);
|
|
||||||
Mat imagePoints = _ipoints.getMat();
|
|
||||||
CvMat c_imagePoints = _cvMat(imagePoints);
|
|
||||||
CvMat c_objectPoints = _cvMat(opoints);
|
|
||||||
Mat cameraMatrix = _cameraMatrix.getMat();
|
|
||||||
|
|
||||||
Mat rvec = _rvec.getMat(), tvec = _tvec.getMat();
|
|
||||||
CvMat c_cameraMatrix = _cvMat(cameraMatrix);
|
|
||||||
CvMat c_rvec = _cvMat(rvec), c_tvec = _cvMat(tvec);
|
|
||||||
|
|
||||||
double dc0buf[5] = {0};
|
|
||||||
Mat dc0(5, 1, CV_64F, dc0buf);
|
|
||||||
Mat distCoeffs = _distCoeffs.getMat();
|
|
||||||
if (distCoeffs.empty())
|
|
||||||
distCoeffs = dc0;
|
|
||||||
CvMat c_distCoeffs = _cvMat(distCoeffs);
|
|
||||||
int ndistCoeffs = distCoeffs.rows + distCoeffs.cols - 1;
|
|
||||||
|
|
||||||
Mat jacobian;
|
|
||||||
if (_jacobian.needed())
|
|
||||||
{
|
{
|
||||||
_jacobian.create(npoints * 2, 3 + 3 + 2 + 2 + ndistCoeffs, CV_64F);
|
line(_image, Point2f{axisX[0].x, axisX[0].y}, Point2f{axisX[1].x, axisX[1].y},
|
||||||
jacobian = _jacobian.getMat();
|
Scalar(0, 0, 255), 3);
|
||||||
pdpdrot = &(dpdrot = _cvMat(jacobian.colRange(0, 3)));
|
}
|
||||||
pdpdt = &(dpdt = _cvMat(jacobian.colRange(3, 6)));
|
if (axisY.size() > 0)
|
||||||
pdpdf = &(dpdf = _cvMat(jacobian.colRange(6, 8)));
|
{
|
||||||
pdpdc = &(dpdc = _cvMat(jacobian.colRange(8, 10)));
|
line(_image, Point2f{axisY[0].x, axisY[0].y}, Point2f{axisY[1].x, axisY[1].y},
|
||||||
pdpddist = &(dpddist = _cvMat(jacobian.colRange(10, 10 + ndistCoeffs)));
|
Scalar(0, 255, 0), 3);
|
||||||
|
}
|
||||||
|
if (axisZ.size() > 0)
|
||||||
|
{
|
||||||
|
line(_image, Point2f{axisZ[0].x, axisZ[0].y}, Point2f{axisZ[1].x, axisZ[1].y},
|
||||||
|
Scalar(255, 0, 0), 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
_cvProjectPoints2(&c_objectPoints, &c_rvec, &c_tvec, &c_cameraMatrix, &c_distCoeffs,
|
|
||||||
&c_imagePoints, pdpdrot, pdpdt, pdpdf, pdpdc, pdpddist, aspectRatio);
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace _detail
|
|
||||||
{
|
|
||||||
template <typename FLOAT>
|
|
||||||
void computeTiltProjectionMatrix(FLOAT tauX,
|
|
||||||
FLOAT tauY,
|
|
||||||
Matx<FLOAT, 3, 3>* matTilt = 0,
|
|
||||||
Matx<FLOAT, 3, 3>* dMatTiltdTauX = 0,
|
|
||||||
Matx<FLOAT, 3, 3>* dMatTiltdTauY = 0,
|
|
||||||
Matx<FLOAT, 3, 3>* invMatTilt = 0)
|
|
||||||
{
|
|
||||||
FLOAT cTauX = cos(tauX);
|
|
||||||
FLOAT sTauX = sin(tauX);
|
|
||||||
FLOAT cTauY = cos(tauY);
|
|
||||||
FLOAT sTauY = sin(tauY);
|
|
||||||
Matx<FLOAT, 3, 3> matRotX = Matx<FLOAT, 3, 3>(1,0,0,0,cTauX,sTauX,0,-sTauX,cTauX);
|
|
||||||
Matx<FLOAT, 3, 3> matRotY = Matx<FLOAT, 3, 3>(cTauY,0,-sTauY,0,1,0,sTauY,0,cTauY);
|
|
||||||
Matx<FLOAT, 3, 3> matRotXY = matRotY * matRotX;
|
|
||||||
Matx<FLOAT, 3, 3> matProjZ = Matx<FLOAT, 3, 3>(matRotXY(2,2),0,-matRotXY(0,2),0,matRotXY(2,2),-matRotXY(1,2),0,0,1);
|
|
||||||
if (matTilt)
|
|
||||||
{
|
|
||||||
// Matrix for trapezoidal distortion of tilted image sensor
|
|
||||||
*matTilt = matProjZ * matRotXY;
|
|
||||||
}
|
|
||||||
if (dMatTiltdTauX)
|
|
||||||
{
|
|
||||||
// Derivative with respect to tauX
|
|
||||||
Matx<FLOAT, 3, 3> dMatRotXYdTauX = matRotY * Matx<FLOAT, 3, 3>(0,0,0,0,-sTauX,cTauX,0,-cTauX,-sTauX);
|
|
||||||
Matx<FLOAT, 3, 3> dMatProjZdTauX = Matx<FLOAT, 3, 3>(dMatRotXYdTauX(2,2),0,-dMatRotXYdTauX(0,2),
|
|
||||||
0,dMatRotXYdTauX(2,2),-dMatRotXYdTauX(1,2),0,0,0);
|
|
||||||
*dMatTiltdTauX = (matProjZ * dMatRotXYdTauX) + (dMatProjZdTauX * matRotXY);
|
|
||||||
}
|
|
||||||
if (dMatTiltdTauY)
|
|
||||||
{
|
|
||||||
// Derivative with respect to tauY
|
|
||||||
Matx<FLOAT, 3, 3> dMatRotXYdTauY = Matx<FLOAT, 3, 3>(-sTauY,0,-cTauY,0,0,0,cTauY,0,-sTauY) * matRotX;
|
|
||||||
Matx<FLOAT, 3, 3> dMatProjZdTauY = Matx<FLOAT, 3, 3>(dMatRotXYdTauY(2,2),0,-dMatRotXYdTauY(0,2),
|
|
||||||
0,dMatRotXYdTauY(2,2),-dMatRotXYdTauY(1,2),0,0,0);
|
|
||||||
*dMatTiltdTauY = (matProjZ * dMatRotXYdTauY) + (dMatProjZdTauY * matRotXY);
|
|
||||||
}
|
|
||||||
if (invMatTilt)
|
|
||||||
{
|
|
||||||
FLOAT inv = 1./matRotXY(2,2);
|
|
||||||
Matx<FLOAT, 3, 3> invMatProjZ = Matx<FLOAT, 3, 3>(inv,0,inv*matRotXY(0,2),0,inv,inv*matRotXY(1,2),0,0,1);
|
|
||||||
*invMatTilt = matRotXY.t()*invMatProjZ;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char* cvDistCoeffErr = "Distortion coefficients must be 1x4, 4x1, 1x5, 5x1, 1x8, 8x1, 1x12, 12x1, 1x14 or 14x1 floating-point vector";
|
|
||||||
|
|
||||||
static void _cvProjectPoints2Internal( const CvMat* objectPoints,
|
|
||||||
const CvMat* r_vec,
|
|
||||||
const CvMat* t_vec,
|
|
||||||
const CvMat* A,
|
|
||||||
const CvMat* distCoeffs,
|
|
||||||
CvMat* imagePoints, CvMat* dpdr CV_DEFAULT(NULL),
|
|
||||||
CvMat* dpdt CV_DEFAULT(NULL), CvMat* dpdf CV_DEFAULT(NULL),
|
|
||||||
CvMat* dpdc CV_DEFAULT(NULL), CvMat* dpdk CV_DEFAULT(NULL),
|
|
||||||
CvMat* dpdo CV_DEFAULT(NULL),
|
|
||||||
double aspectRatio CV_DEFAULT(0) )
|
|
||||||
{
|
|
||||||
Ptr<CvMat> matM, _m;
|
|
||||||
Ptr<CvMat> _dpdr, _dpdt, _dpdc, _dpdf, _dpdk;
|
|
||||||
Ptr<CvMat> _dpdo;
|
|
||||||
|
|
||||||
int i, j, count;
|
|
||||||
int calc_derivatives;
|
|
||||||
const CvPoint3D64f* M;
|
|
||||||
CvPoint3D64f* m;
|
|
||||||
double r[3], R[9], dRdr[27], t[3], a[9], k[14] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0}, fx, fy, cx, cy;
|
|
||||||
Matx33d matTilt = Matx33d::eye();
|
|
||||||
Matx33d dMatTiltdTauX(0,0,0,0,0,0,0,-1,0);
|
|
||||||
Matx33d dMatTiltdTauY(0,0,0,0,0,0,1,0,0);
|
|
||||||
CvMat _r, _t, _a = cvMat( 3, 3, CV_64F, a ), _k;
|
|
||||||
CvMat matR = cvMat( 3, 3, CV_64F, R ), _dRdr = cvMat( 3, 9, CV_64F, dRdr );
|
|
||||||
double *dpdr_p = 0, *dpdt_p = 0, *dpdk_p = 0, *dpdf_p = 0, *dpdc_p = 0;
|
|
||||||
double* dpdo_p = 0;
|
|
||||||
int dpdr_step = 0, dpdt_step = 0, dpdk_step = 0, dpdf_step = 0, dpdc_step = 0;
|
|
||||||
int dpdo_step = 0;
|
|
||||||
bool fixedAspectRatio = aspectRatio > FLT_EPSILON;
|
|
||||||
|
|
||||||
if( !CV_IS_MAT(objectPoints) || !CV_IS_MAT(r_vec) ||
|
|
||||||
!CV_IS_MAT(t_vec) || !CV_IS_MAT(A) ||
|
|
||||||
/*!CV_IS_MAT(distCoeffs) ||*/ !CV_IS_MAT(imagePoints) )
|
|
||||||
CV_Error( CV_StsBadArg, "One of required arguments is not a valid matrix" );
|
|
||||||
|
|
||||||
int total = objectPoints->rows * objectPoints->cols * CV_MAT_CN(objectPoints->type);
|
|
||||||
if(total % 3 != 0)
|
|
||||||
{
|
|
||||||
//we have stopped support of homogeneous coordinates because it cause ambiguity in interpretation of the input data
|
|
||||||
CV_Error( CV_StsBadArg, "Homogeneous coordinates are not supported" );
|
|
||||||
}
|
|
||||||
count = total / 3;
|
|
||||||
|
|
||||||
if( CV_IS_CONT_MAT(objectPoints->type) &&
|
|
||||||
(CV_MAT_DEPTH(objectPoints->type) == CV_32F || CV_MAT_DEPTH(objectPoints->type) == CV_64F)&&
|
|
||||||
((objectPoints->rows == 1 && CV_MAT_CN(objectPoints->type) == 3) ||
|
|
||||||
(objectPoints->rows == count && CV_MAT_CN(objectPoints->type)*objectPoints->cols == 3) ||
|
|
||||||
(objectPoints->rows == 3 && CV_MAT_CN(objectPoints->type) == 1 && objectPoints->cols == count)))
|
|
||||||
{
|
|
||||||
matM.reset(cvCreateMat( objectPoints->rows, objectPoints->cols, CV_MAKETYPE(CV_64F,CV_MAT_CN(objectPoints->type)) ));
|
|
||||||
cvConvert(objectPoints, matM);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// matM = cvCreateMat( 1, count, CV_64FC3 );
|
|
||||||
// cvConvertPointsHomogeneous( objectPoints, matM );
|
|
||||||
CV_Error( CV_StsBadArg, "Homogeneous coordinates are not supported" );
|
|
||||||
}
|
|
||||||
|
|
||||||
if( CV_IS_CONT_MAT(imagePoints->type) &&
|
|
||||||
(CV_MAT_DEPTH(imagePoints->type) == CV_32F || CV_MAT_DEPTH(imagePoints->type) == CV_64F) &&
|
|
||||||
((imagePoints->rows == 1 && CV_MAT_CN(imagePoints->type) == 3) ||
|
|
||||||
(imagePoints->rows == count && CV_MAT_CN(imagePoints->type)*imagePoints->cols == 3) ||
|
|
||||||
(imagePoints->rows == 3 && CV_MAT_CN(imagePoints->type) == 1 && imagePoints->cols == count)))
|
|
||||||
{
|
|
||||||
_m.reset(cvCreateMat( imagePoints->rows, imagePoints->cols, CV_MAKETYPE(CV_64F,CV_MAT_CN(imagePoints->type)) ));
|
|
||||||
cvConvert(imagePoints, _m);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// _m = cvCreateMat( 1, count, CV_64FC2 );
|
|
||||||
CV_Error( CV_StsBadArg, "Homogeneous coordinates are not supported" );
|
|
||||||
}
|
|
||||||
|
|
||||||
M = (CvPoint3D64f*)matM->data.db;
|
|
||||||
m = (CvPoint3D64f*)_m->data.db;
|
|
||||||
|
|
||||||
if( (CV_MAT_DEPTH(r_vec->type) != CV_64F && CV_MAT_DEPTH(r_vec->type) != CV_32F) ||
|
|
||||||
(((r_vec->rows != 1 && r_vec->cols != 1) ||
|
|
||||||
r_vec->rows*r_vec->cols*CV_MAT_CN(r_vec->type) != 3) &&
|
|
||||||
((r_vec->rows != 3 && r_vec->cols != 3) || CV_MAT_CN(r_vec->type) != 1)))
|
|
||||||
CV_Error( CV_StsBadArg, "Rotation must be represented by 1x3 or 3x1 "
|
|
||||||
"floating-point rotation vector, or 3x3 rotation matrix" );
|
|
||||||
|
|
||||||
if( r_vec->rows == 3 && r_vec->cols == 3 )
|
|
||||||
{
|
|
||||||
_r = cvMat( 3, 1, CV_64FC1, r );
|
|
||||||
cvRodrigues2( r_vec, &_r );
|
|
||||||
cvRodrigues2( &_r, &matR, &_dRdr );
|
|
||||||
cvCopy( r_vec, &matR );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_r = cvMat( r_vec->rows, r_vec->cols, CV_MAKETYPE(CV_64F,CV_MAT_CN(r_vec->type)), r );
|
|
||||||
cvConvert( r_vec, &_r );
|
|
||||||
cvRodrigues2( &_r, &matR, &_dRdr );
|
|
||||||
}
|
|
||||||
|
|
||||||
if( (CV_MAT_DEPTH(t_vec->type) != CV_64F && CV_MAT_DEPTH(t_vec->type) != CV_32F) ||
|
|
||||||
(t_vec->rows != 1 && t_vec->cols != 1) ||
|
|
||||||
t_vec->rows*t_vec->cols*CV_MAT_CN(t_vec->type) != 3 )
|
|
||||||
CV_Error( CV_StsBadArg,
|
|
||||||
"Translation vector must be 1x3 or 3x1 floating-point vector" );
|
|
||||||
|
|
||||||
_t = cvMat( t_vec->rows, t_vec->cols, CV_MAKETYPE(CV_64F,CV_MAT_CN(t_vec->type)), t );
|
|
||||||
cvConvert( t_vec, &_t );
|
|
||||||
|
|
||||||
if( (CV_MAT_TYPE(A->type) != CV_64FC1 && CV_MAT_TYPE(A->type) != CV_32FC1) ||
|
|
||||||
A->rows != 3 || A->cols != 3 )
|
|
||||||
CV_Error( CV_StsBadArg, "Instrinsic parameters must be 3x3 floating-point matrix" );
|
|
||||||
|
|
||||||
cvConvert( A, &_a );
|
|
||||||
fx = a[0]; fy = a[4];
|
|
||||||
cx = a[2]; cy = a[5];
|
|
||||||
|
|
||||||
if( fixedAspectRatio )
|
|
||||||
fx = fy*aspectRatio;
|
|
||||||
|
|
||||||
if( distCoeffs )
|
|
||||||
{
|
|
||||||
if( !CV_IS_MAT(distCoeffs) ||
|
|
||||||
(CV_MAT_DEPTH(distCoeffs->type) != CV_64F &&
|
|
||||||
CV_MAT_DEPTH(distCoeffs->type) != CV_32F) ||
|
|
||||||
(distCoeffs->rows != 1 && distCoeffs->cols != 1) ||
|
|
||||||
(distCoeffs->rows*distCoeffs->cols*CV_MAT_CN(distCoeffs->type) != 4 &&
|
|
||||||
distCoeffs->rows*distCoeffs->cols*CV_MAT_CN(distCoeffs->type) != 5 &&
|
|
||||||
distCoeffs->rows*distCoeffs->cols*CV_MAT_CN(distCoeffs->type) != 8 &&
|
|
||||||
distCoeffs->rows*distCoeffs->cols*CV_MAT_CN(distCoeffs->type) != 12 &&
|
|
||||||
distCoeffs->rows*distCoeffs->cols*CV_MAT_CN(distCoeffs->type) != 14) )
|
|
||||||
CV_Error( CV_StsBadArg, cvDistCoeffErr );
|
|
||||||
|
|
||||||
_k = cvMat( distCoeffs->rows, distCoeffs->cols,
|
|
||||||
CV_MAKETYPE(CV_64F,CV_MAT_CN(distCoeffs->type)), k );
|
|
||||||
cvConvert( distCoeffs, &_k );
|
|
||||||
if(k[12] != 0 || k[13] != 0)
|
|
||||||
{
|
|
||||||
_detail::computeTiltProjectionMatrix(k[12], k[13],
|
|
||||||
&matTilt, &dMatTiltdTauX, &dMatTiltdTauY);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if( dpdr )
|
|
||||||
{
|
|
||||||
if( !CV_IS_MAT(dpdr) ||
|
|
||||||
(CV_MAT_TYPE(dpdr->type) != CV_32FC1 &&
|
|
||||||
CV_MAT_TYPE(dpdr->type) != CV_64FC1) ||
|
|
||||||
dpdr->rows != count*2 || dpdr->cols != 3 )
|
|
||||||
CV_Error( CV_StsBadArg, "dp/drot must be 2Nx3 floating-point matrix" );
|
|
||||||
|
|
||||||
if( CV_MAT_TYPE(dpdr->type) == CV_64FC1 )
|
|
||||||
{
|
|
||||||
_dpdr.reset(cvCloneMat(dpdr));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
_dpdr.reset(cvCreateMat( 2*count, 3, CV_64FC1 ));
|
|
||||||
dpdr_p = _dpdr->data.db;
|
|
||||||
dpdr_step = _dpdr->step/sizeof(dpdr_p[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if( dpdt )
|
|
||||||
{
|
|
||||||
if( !CV_IS_MAT(dpdt) ||
|
|
||||||
(CV_MAT_TYPE(dpdt->type) != CV_32FC1 &&
|
|
||||||
CV_MAT_TYPE(dpdt->type) != CV_64FC1) ||
|
|
||||||
dpdt->rows != count*2 || dpdt->cols != 3 )
|
|
||||||
CV_Error( CV_StsBadArg, "dp/dT must be 2Nx3 floating-point matrix" );
|
|
||||||
|
|
||||||
if( CV_MAT_TYPE(dpdt->type) == CV_64FC1 )
|
|
||||||
{
|
|
||||||
_dpdt.reset(cvCloneMat(dpdt));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
_dpdt.reset(cvCreateMat( 2*count, 3, CV_64FC1 ));
|
|
||||||
dpdt_p = _dpdt->data.db;
|
|
||||||
dpdt_step = _dpdt->step/sizeof(dpdt_p[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if( dpdf )
|
|
||||||
{
|
|
||||||
if( !CV_IS_MAT(dpdf) ||
|
|
||||||
(CV_MAT_TYPE(dpdf->type) != CV_32FC1 && CV_MAT_TYPE(dpdf->type) != CV_64FC1) ||
|
|
||||||
dpdf->rows != count*2 || dpdf->cols != 2 )
|
|
||||||
CV_Error( CV_StsBadArg, "dp/df must be 2Nx2 floating-point matrix" );
|
|
||||||
|
|
||||||
if( CV_MAT_TYPE(dpdf->type) == CV_64FC1 )
|
|
||||||
{
|
|
||||||
_dpdf.reset(cvCloneMat(dpdf));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
_dpdf.reset(cvCreateMat( 2*count, 2, CV_64FC1 ));
|
|
||||||
dpdf_p = _dpdf->data.db;
|
|
||||||
dpdf_step = _dpdf->step/sizeof(dpdf_p[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if( dpdc )
|
|
||||||
{
|
|
||||||
if( !CV_IS_MAT(dpdc) ||
|
|
||||||
(CV_MAT_TYPE(dpdc->type) != CV_32FC1 && CV_MAT_TYPE(dpdc->type) != CV_64FC1) ||
|
|
||||||
dpdc->rows != count*2 || dpdc->cols != 2 )
|
|
||||||
CV_Error( CV_StsBadArg, "dp/dc must be 2Nx2 floating-point matrix" );
|
|
||||||
|
|
||||||
if( CV_MAT_TYPE(dpdc->type) == CV_64FC1 )
|
|
||||||
{
|
|
||||||
_dpdc.reset(cvCloneMat(dpdc));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
_dpdc.reset(cvCreateMat( 2*count, 2, CV_64FC1 ));
|
|
||||||
dpdc_p = _dpdc->data.db;
|
|
||||||
dpdc_step = _dpdc->step/sizeof(dpdc_p[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if( dpdk )
|
|
||||||
{
|
|
||||||
if( !CV_IS_MAT(dpdk) ||
|
|
||||||
(CV_MAT_TYPE(dpdk->type) != CV_32FC1 && CV_MAT_TYPE(dpdk->type) != CV_64FC1) ||
|
|
||||||
dpdk->rows != count*2 || (dpdk->cols != 14 && dpdk->cols != 12 && dpdk->cols != 8 && dpdk->cols != 5 && dpdk->cols != 4 && dpdk->cols != 2) )
|
|
||||||
CV_Error( CV_StsBadArg, "dp/df must be 2Nx14, 2Nx12, 2Nx8, 2Nx5, 2Nx4 or 2Nx2 floating-point matrix" );
|
|
||||||
|
|
||||||
if( !distCoeffs )
|
|
||||||
CV_Error( CV_StsNullPtr, "distCoeffs is NULL while dpdk is not" );
|
|
||||||
|
|
||||||
if( CV_MAT_TYPE(dpdk->type) == CV_64FC1 )
|
|
||||||
{
|
|
||||||
_dpdk.reset(cvCloneMat(dpdk));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
_dpdk.reset(cvCreateMat( dpdk->rows, dpdk->cols, CV_64FC1 ));
|
|
||||||
dpdk_p = _dpdk->data.db;
|
|
||||||
dpdk_step = _dpdk->step/sizeof(dpdk_p[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if( dpdo )
|
|
||||||
{
|
|
||||||
if( !CV_IS_MAT( dpdo ) || ( CV_MAT_TYPE( dpdo->type ) != CV_32FC1
|
|
||||||
&& CV_MAT_TYPE( dpdo->type ) != CV_64FC1 )
|
|
||||||
|| dpdo->rows != count * 2 || dpdo->cols != count * 3 )
|
|
||||||
CV_Error( CV_StsBadArg, "dp/do must be 2Nx3N floating-point matrix" );
|
|
||||||
|
|
||||||
if( CV_MAT_TYPE( dpdo->type ) == CV_64FC1 )
|
|
||||||
{
|
|
||||||
_dpdo.reset( cvCloneMat( dpdo ) );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
_dpdo.reset( cvCreateMat( 2 * count, 3 * count, CV_64FC1 ) );
|
|
||||||
cvZero(_dpdo);
|
|
||||||
dpdo_p = _dpdo->data.db;
|
|
||||||
dpdo_step = _dpdo->step / sizeof( dpdo_p[0] );
|
|
||||||
}
|
|
||||||
|
|
||||||
calc_derivatives = dpdr || dpdt || dpdf || dpdc || dpdk || dpdo;
|
|
||||||
|
|
||||||
for( i = 0; i < count; i++ )
|
|
||||||
{
|
|
||||||
double X = M[i].x, Y = M[i].y, Z = M[i].z;
|
|
||||||
double x = R[0]*X + R[1]*Y + R[2]*Z + t[0];
|
|
||||||
double y = R[3]*X + R[4]*Y + R[5]*Z + t[1];
|
|
||||||
double z = R[6]*X + R[7]*Y + R[8]*Z + t[2];
|
|
||||||
double r2, r4, r6, a1, a2, a3, cdist, icdist2;
|
|
||||||
double xd, yd, xd0, yd0, invProj;
|
|
||||||
Vec3d vecTilt;
|
|
||||||
Vec3d dVecTilt;
|
|
||||||
Matx22d dMatTilt;
|
|
||||||
Vec2d dXdYd;
|
|
||||||
|
|
||||||
double z0 = z;
|
|
||||||
z = z ? 1./z : 1;
|
|
||||||
x *= z; y *= z;
|
|
||||||
|
|
||||||
r2 = x*x + y*y;
|
|
||||||
r4 = r2*r2;
|
|
||||||
r6 = r4*r2;
|
|
||||||
a1 = 2*x*y;
|
|
||||||
a2 = r2 + 2*x*x;
|
|
||||||
a3 = r2 + 2*y*y;
|
|
||||||
cdist = 1 + k[0]*r2 + k[1]*r4 + k[4]*r6;
|
|
||||||
icdist2 = 1./(1 + k[5]*r2 + k[6]*r4 + k[7]*r6);
|
|
||||||
xd0 = x*cdist*icdist2 + k[2]*a1 + k[3]*a2 + k[8]*r2+k[9]*r4;
|
|
||||||
yd0 = y*cdist*icdist2 + k[2]*a3 + k[3]*a1 + k[10]*r2+k[11]*r4;
|
|
||||||
|
|
||||||
// additional distortion by projecting onto a tilt plane
|
|
||||||
vecTilt = matTilt*Vec3d(xd0, yd0, 1);
|
|
||||||
invProj = vecTilt(2) ? 1./vecTilt(2) : 1;
|
|
||||||
xd = invProj * vecTilt(0);
|
|
||||||
yd = invProj * vecTilt(1);
|
|
||||||
|
|
||||||
m[i].x = xd*fx + cx;
|
|
||||||
m[i].y = yd*fy + cy;
|
|
||||||
m[i].z = z; // Just put the projected Z coordinate here, we mainly care about the sign
|
|
||||||
|
|
||||||
if( calc_derivatives )
|
|
||||||
{
|
|
||||||
if( dpdc_p )
|
|
||||||
{
|
|
||||||
dpdc_p[0] = 1; dpdc_p[1] = 0; // dp_xdc_x; dp_xdc_y
|
|
||||||
dpdc_p[dpdc_step] = 0;
|
|
||||||
dpdc_p[dpdc_step+1] = 1;
|
|
||||||
dpdc_p += dpdc_step*2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if( dpdf_p )
|
|
||||||
{
|
|
||||||
if( fixedAspectRatio )
|
|
||||||
{
|
|
||||||
dpdf_p[0] = 0; dpdf_p[1] = xd*aspectRatio; // dp_xdf_x; dp_xdf_y
|
|
||||||
dpdf_p[dpdf_step] = 0;
|
|
||||||
dpdf_p[dpdf_step+1] = yd;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
dpdf_p[0] = xd; dpdf_p[1] = 0;
|
|
||||||
dpdf_p[dpdf_step] = 0;
|
|
||||||
dpdf_p[dpdf_step+1] = yd;
|
|
||||||
}
|
|
||||||
dpdf_p += dpdf_step*2;
|
|
||||||
}
|
|
||||||
for (int row = 0; row < 2; ++row)
|
|
||||||
for (int col = 0; col < 2; ++col)
|
|
||||||
dMatTilt(row,col) = matTilt(row,col)*vecTilt(2)
|
|
||||||
- matTilt(2,col)*vecTilt(row);
|
|
||||||
double invProjSquare = (invProj*invProj);
|
|
||||||
dMatTilt *= invProjSquare;
|
|
||||||
if( dpdk_p )
|
|
||||||
{
|
|
||||||
dXdYd = dMatTilt*Vec2d(x*icdist2*r2, y*icdist2*r2);
|
|
||||||
dpdk_p[0] = fx*dXdYd(0);
|
|
||||||
dpdk_p[dpdk_step] = fy*dXdYd(1);
|
|
||||||
dXdYd = dMatTilt*Vec2d(x*icdist2*r4, y*icdist2*r4);
|
|
||||||
dpdk_p[1] = fx*dXdYd(0);
|
|
||||||
dpdk_p[dpdk_step+1] = fy*dXdYd(1);
|
|
||||||
if( _dpdk->cols > 2 )
|
|
||||||
{
|
|
||||||
dXdYd = dMatTilt*Vec2d(a1, a3);
|
|
||||||
dpdk_p[2] = fx*dXdYd(0);
|
|
||||||
dpdk_p[dpdk_step+2] = fy*dXdYd(1);
|
|
||||||
dXdYd = dMatTilt*Vec2d(a2, a1);
|
|
||||||
dpdk_p[3] = fx*dXdYd(0);
|
|
||||||
dpdk_p[dpdk_step+3] = fy*dXdYd(1);
|
|
||||||
if( _dpdk->cols > 4 )
|
|
||||||
{
|
|
||||||
dXdYd = dMatTilt*Vec2d(x*icdist2*r6, y*icdist2*r6);
|
|
||||||
dpdk_p[4] = fx*dXdYd(0);
|
|
||||||
dpdk_p[dpdk_step+4] = fy*dXdYd(1);
|
|
||||||
|
|
||||||
if( _dpdk->cols > 5 )
|
|
||||||
{
|
|
||||||
dXdYd = dMatTilt*Vec2d(
|
|
||||||
x*cdist*(-icdist2)*icdist2*r2, y*cdist*(-icdist2)*icdist2*r2);
|
|
||||||
dpdk_p[5] = fx*dXdYd(0);
|
|
||||||
dpdk_p[dpdk_step+5] = fy*dXdYd(1);
|
|
||||||
dXdYd = dMatTilt*Vec2d(
|
|
||||||
x*cdist*(-icdist2)*icdist2*r4, y*cdist*(-icdist2)*icdist2*r4);
|
|
||||||
dpdk_p[6] = fx*dXdYd(0);
|
|
||||||
dpdk_p[dpdk_step+6] = fy*dXdYd(1);
|
|
||||||
dXdYd = dMatTilt*Vec2d(
|
|
||||||
x*cdist*(-icdist2)*icdist2*r6, y*cdist*(-icdist2)*icdist2*r6);
|
|
||||||
dpdk_p[7] = fx*dXdYd(0);
|
|
||||||
dpdk_p[dpdk_step+7] = fy*dXdYd(1);
|
|
||||||
if( _dpdk->cols > 8 )
|
|
||||||
{
|
|
||||||
dXdYd = dMatTilt*Vec2d(r2, 0);
|
|
||||||
dpdk_p[8] = fx*dXdYd(0); //s1
|
|
||||||
dpdk_p[dpdk_step+8] = fy*dXdYd(1); //s1
|
|
||||||
dXdYd = dMatTilt*Vec2d(r4, 0);
|
|
||||||
dpdk_p[9] = fx*dXdYd(0); //s2
|
|
||||||
dpdk_p[dpdk_step+9] = fy*dXdYd(1); //s2
|
|
||||||
dXdYd = dMatTilt*Vec2d(0, r2);
|
|
||||||
dpdk_p[10] = fx*dXdYd(0);//s3
|
|
||||||
dpdk_p[dpdk_step+10] = fy*dXdYd(1); //s3
|
|
||||||
dXdYd = dMatTilt*Vec2d(0, r4);
|
|
||||||
dpdk_p[11] = fx*dXdYd(0);//s4
|
|
||||||
dpdk_p[dpdk_step+11] = fy*dXdYd(1); //s4
|
|
||||||
if( _dpdk->cols > 12 )
|
|
||||||
{
|
|
||||||
dVecTilt = dMatTiltdTauX * Vec3d(xd0, yd0, 1);
|
|
||||||
dpdk_p[12] = fx * invProjSquare * (
|
|
||||||
dVecTilt(0) * vecTilt(2) - dVecTilt(2) * vecTilt(0));
|
|
||||||
dpdk_p[dpdk_step+12] = fy*invProjSquare * (
|
|
||||||
dVecTilt(1) * vecTilt(2) - dVecTilt(2) * vecTilt(1));
|
|
||||||
dVecTilt = dMatTiltdTauY * Vec3d(xd0, yd0, 1);
|
|
||||||
dpdk_p[13] = fx * invProjSquare * (
|
|
||||||
dVecTilt(0) * vecTilt(2) - dVecTilt(2) * vecTilt(0));
|
|
||||||
dpdk_p[dpdk_step+13] = fy * invProjSquare * (
|
|
||||||
dVecTilt(1) * vecTilt(2) - dVecTilt(2) * vecTilt(1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dpdk_p += dpdk_step*2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if( dpdt_p )
|
|
||||||
{
|
|
||||||
double dxdt[] = { z, 0, -x*z }, dydt[] = { 0, z, -y*z };
|
|
||||||
for( j = 0; j < 3; j++ )
|
|
||||||
{
|
|
||||||
double dr2dt = 2*x*dxdt[j] + 2*y*dydt[j];
|
|
||||||
double dcdist_dt = k[0]*dr2dt + 2*k[1]*r2*dr2dt + 3*k[4]*r4*dr2dt;
|
|
||||||
double dicdist2_dt = -icdist2*icdist2*(k[5]*dr2dt + 2*k[6]*r2*dr2dt + 3*k[7]*r4*dr2dt);
|
|
||||||
double da1dt = 2*(x*dydt[j] + y*dxdt[j]);
|
|
||||||
double dmxdt = (dxdt[j]*cdist*icdist2 + x*dcdist_dt*icdist2 + x*cdist*dicdist2_dt +
|
|
||||||
k[2]*da1dt + k[3]*(dr2dt + 4*x*dxdt[j]) + k[8]*dr2dt + 2*r2*k[9]*dr2dt);
|
|
||||||
double dmydt = (dydt[j]*cdist*icdist2 + y*dcdist_dt*icdist2 + y*cdist*dicdist2_dt +
|
|
||||||
k[2]*(dr2dt + 4*y*dydt[j]) + k[3]*da1dt + k[10]*dr2dt + 2*r2*k[11]*dr2dt);
|
|
||||||
dXdYd = dMatTilt*Vec2d(dmxdt, dmydt);
|
|
||||||
dpdt_p[j] = fx*dXdYd(0);
|
|
||||||
dpdt_p[dpdt_step+j] = fy*dXdYd(1);
|
|
||||||
}
|
|
||||||
dpdt_p += dpdt_step*2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if( dpdr_p )
|
|
||||||
{
|
|
||||||
double dx0dr[] =
|
|
||||||
{
|
|
||||||
X*dRdr[0] + Y*dRdr[1] + Z*dRdr[2],
|
|
||||||
X*dRdr[9] + Y*dRdr[10] + Z*dRdr[11],
|
|
||||||
X*dRdr[18] + Y*dRdr[19] + Z*dRdr[20]
|
|
||||||
};
|
|
||||||
double dy0dr[] =
|
|
||||||
{
|
|
||||||
X*dRdr[3] + Y*dRdr[4] + Z*dRdr[5],
|
|
||||||
X*dRdr[12] + Y*dRdr[13] + Z*dRdr[14],
|
|
||||||
X*dRdr[21] + Y*dRdr[22] + Z*dRdr[23]
|
|
||||||
};
|
|
||||||
double dz0dr[] =
|
|
||||||
{
|
|
||||||
X*dRdr[6] + Y*dRdr[7] + Z*dRdr[8],
|
|
||||||
X*dRdr[15] + Y*dRdr[16] + Z*dRdr[17],
|
|
||||||
X*dRdr[24] + Y*dRdr[25] + Z*dRdr[26]
|
|
||||||
};
|
|
||||||
for( j = 0; j < 3; j++ )
|
|
||||||
{
|
|
||||||
double dxdr = z*(dx0dr[j] - x*dz0dr[j]);
|
|
||||||
double dydr = z*(dy0dr[j] - y*dz0dr[j]);
|
|
||||||
double dr2dr = 2*x*dxdr + 2*y*dydr;
|
|
||||||
double dcdist_dr = (k[0] + 2*k[1]*r2 + 3*k[4]*r4)*dr2dr;
|
|
||||||
double dicdist2_dr = -icdist2*icdist2*(k[5] + 2*k[6]*r2 + 3*k[7]*r4)*dr2dr;
|
|
||||||
double da1dr = 2*(x*dydr + y*dxdr);
|
|
||||||
double dmxdr = (dxdr*cdist*icdist2 + x*dcdist_dr*icdist2 + x*cdist*dicdist2_dr +
|
|
||||||
k[2]*da1dr + k[3]*(dr2dr + 4*x*dxdr) + (k[8] + 2*r2*k[9])*dr2dr);
|
|
||||||
double dmydr = (dydr*cdist*icdist2 + y*dcdist_dr*icdist2 + y*cdist*dicdist2_dr +
|
|
||||||
k[2]*(dr2dr + 4*y*dydr) + k[3]*da1dr + (k[10] + 2*r2*k[11])*dr2dr);
|
|
||||||
dXdYd = dMatTilt*Vec2d(dmxdr, dmydr);
|
|
||||||
dpdr_p[j] = fx*dXdYd(0);
|
|
||||||
dpdr_p[dpdr_step+j] = fy*dXdYd(1);
|
|
||||||
}
|
|
||||||
dpdr_p += dpdr_step*2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if( dpdo_p )
|
|
||||||
{
|
|
||||||
double dxdo[] = { z * ( R[0] - x * z * z0 * R[6] ),
|
|
||||||
z * ( R[1] - x * z * z0 * R[7] ),
|
|
||||||
z * ( R[2] - x * z * z0 * R[8] ) };
|
|
||||||
double dydo[] = { z * ( R[3] - y * z * z0 * R[6] ),
|
|
||||||
z * ( R[4] - y * z * z0 * R[7] ),
|
|
||||||
z * ( R[5] - y * z * z0 * R[8] ) };
|
|
||||||
for( j = 0; j < 3; j++ )
|
|
||||||
{
|
|
||||||
double dr2do = 2 * x * dxdo[j] + 2 * y * dydo[j];
|
|
||||||
double dr4do = 2 * r2 * dr2do;
|
|
||||||
double dr6do = 3 * r4 * dr2do;
|
|
||||||
double da1do = 2 * y * dxdo[j] + 2 * x * dydo[j];
|
|
||||||
double da2do = dr2do + 4 * x * dxdo[j];
|
|
||||||
double da3do = dr2do + 4 * y * dydo[j];
|
|
||||||
double dcdist_do
|
|
||||||
= k[0] * dr2do + k[1] * dr4do + k[4] * dr6do;
|
|
||||||
double dicdist2_do = -icdist2 * icdist2
|
|
||||||
* ( k[5] * dr2do + k[6] * dr4do + k[7] * dr6do );
|
|
||||||
double dxd0_do = cdist * icdist2 * dxdo[j]
|
|
||||||
+ x * icdist2 * dcdist_do + x * cdist * dicdist2_do
|
|
||||||
+ k[2] * da1do + k[3] * da2do + k[8] * dr2do
|
|
||||||
+ k[9] * dr4do;
|
|
||||||
double dyd0_do = cdist * icdist2 * dydo[j]
|
|
||||||
+ y * icdist2 * dcdist_do + y * cdist * dicdist2_do
|
|
||||||
+ k[2] * da3do + k[3] * da1do + k[10] * dr2do
|
|
||||||
+ k[11] * dr4do;
|
|
||||||
dXdYd = dMatTilt * Vec2d( dxd0_do, dyd0_do );
|
|
||||||
dpdo_p[i * 3 + j] = fx * dXdYd( 0 );
|
|
||||||
dpdo_p[dpdo_step + i * 3 + j] = fy * dXdYd( 1 );
|
|
||||||
}
|
|
||||||
dpdo_p += dpdo_step * 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if( _m != imagePoints )
|
|
||||||
cvConvert( _m, imagePoints );
|
|
||||||
|
|
||||||
if( _dpdr != dpdr )
|
|
||||||
cvConvert( _dpdr, dpdr );
|
|
||||||
|
|
||||||
if( _dpdt != dpdt )
|
|
||||||
cvConvert( _dpdt, dpdt );
|
|
||||||
|
|
||||||
if( _dpdf != dpdf )
|
|
||||||
cvConvert( _dpdf, dpdf );
|
|
||||||
|
|
||||||
if( _dpdc != dpdc )
|
|
||||||
cvConvert( _dpdc, dpdc );
|
|
||||||
|
|
||||||
if( _dpdk != dpdk )
|
|
||||||
cvConvert( _dpdk, dpdk );
|
|
||||||
|
|
||||||
if( _dpdo != dpdo )
|
|
||||||
cvConvert( _dpdo, dpdo );
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _cvProjectPoints2( const CvMat* objectPoints,
|
|
||||||
const CvMat* r_vec,
|
|
||||||
const CvMat* t_vec,
|
|
||||||
const CvMat* A,
|
|
||||||
const CvMat* distCoeffs,
|
|
||||||
CvMat* imagePoints, CvMat* dpdr,
|
|
||||||
CvMat* dpdt, CvMat* dpdf,
|
|
||||||
CvMat* dpdc, CvMat* dpdk,
|
|
||||||
double aspectRatio )
|
|
||||||
{
|
|
||||||
_cvProjectPoints2Internal( objectPoints, r_vec, t_vec, A, distCoeffs, imagePoints, dpdr, dpdt,
|
|
||||||
dpdf, dpdc, dpdk, NULL, aspectRatio );
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
Generate map file for aruco_map nodelet.
|
Generate map file for aruco_map nodelet.
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
genmap.py <length> <x> <y> <dist_x> <dist_y> [<first>] [<x0>] [<y0>] [--top-left | --bottom-left]
|
genmap.py <length> <x> <y> <dist_x> <dist_y> [<first>] [<x0>] [<y0>] [--top-left | --bottom-left] [-o <filename>]
|
||||||
genmap.py (-h | --help)
|
genmap.py (-h | --help)
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
@@ -27,13 +27,16 @@ Options:
|
|||||||
<y0> Y coordinate for the first marker [default: 0]
|
<y0> Y coordinate for the first marker [default: 0]
|
||||||
--top-left First marker is on top-left (default)
|
--top-left First marker is on top-left (default)
|
||||||
--bottom-left First marker is on bottom-left
|
--bottom-left First marker is on bottom-left
|
||||||
|
-o <filename> Output map file name in the 'map' subdirectory of aruco_pose package
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
rosrun aruco_pose genmap.py 0.33 2 4 1 1 0 > $(catkin_find aruco_pose map)/test_map.txt
|
rosrun aruco_pose genmap.py 0.33 2 4 1 1 0 -o test_map.txt
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
|
|
||||||
|
import sys
|
||||||
|
from os import path
|
||||||
from docopt import docopt
|
from docopt import docopt
|
||||||
|
|
||||||
|
|
||||||
@@ -49,14 +52,19 @@ dist_x = float(arguments['<dist_x>'])
|
|||||||
dist_y = float(arguments['<dist_y>'])
|
dist_y = float(arguments['<dist_y>'])
|
||||||
bottom_left = arguments['--bottom-left']
|
bottom_left = arguments['--bottom-left']
|
||||||
|
|
||||||
|
if arguments['-o'] is None:
|
||||||
|
output = sys.stdout
|
||||||
|
else:
|
||||||
|
output = open(path.join(path.dirname(__file__), '..', 'map', arguments['-o']), 'w')
|
||||||
|
|
||||||
max_y = y0 + (markers_y - 1) * dist_y
|
max_y = y0 + (markers_y - 1) * dist_y
|
||||||
|
|
||||||
print('# id\tlength\tx\ty\tz\trot_z\trot_y\trot_x')
|
output.write('# id\tlength\tx\ty\tz\trot_z\trot_y\trot_x\n')
|
||||||
for y in range(markers_y):
|
for y in range(markers_y):
|
||||||
for x in range(markers_x):
|
for x in range(markers_x):
|
||||||
pos_x = x0 + x * dist_x
|
pos_x = x0 + x * dist_x
|
||||||
pos_y = y0 + y * dist_y
|
pos_y = y0 + y * dist_y
|
||||||
if not bottom_left:
|
if not bottom_left:
|
||||||
pos_y = max_y - pos_y
|
pos_y = max_y - pos_y
|
||||||
print('{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}'.format(first, length, pos_x, pos_y, 0, 0, 0, 0))
|
output.write('{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\n'.format(first, length, pos_x, pos_y, 0, 0, 0, 0))
|
||||||
first += 1
|
first += 1
|
||||||
|
|||||||
3
aruco_pose/vendor/VendorOpenCV.cmake
vendored
3
aruco_pose/vendor/VendorOpenCV.cmake
vendored
@@ -7,6 +7,7 @@ endif()
|
|||||||
|
|
||||||
message(STATUS "Adding vendored aruco_pose OpenCV module")
|
message(STATUS "Adding vendored aruco_pose OpenCV module")
|
||||||
add_library(_opencv_aruco STATIC
|
add_library(_opencv_aruco STATIC
|
||||||
|
vendor/aruco/src/apriltag_quad_thresh.cpp
|
||||||
vendor/aruco/src/aruco.cpp
|
vendor/aruco/src/aruco.cpp
|
||||||
vendor/aruco/src/charuco.cpp
|
vendor/aruco/src/charuco.cpp
|
||||||
vendor/aruco/src/dictionary.cpp
|
vendor/aruco/src/dictionary.cpp
|
||||||
@@ -23,7 +24,7 @@ target_compile_definitions(_opencv_aruco PRIVATE
|
|||||||
CV_OVERRIDE=override
|
CV_OVERRIDE=override
|
||||||
)
|
)
|
||||||
target_compile_options(_opencv_aruco PRIVATE
|
target_compile_options(_opencv_aruco PRIVATE
|
||||||
-fpic -fPIC
|
-fpic -fPIC -fvisibility=hidden
|
||||||
)
|
)
|
||||||
|
|
||||||
target_include_directories(_opencv_aruco PUBLIC
|
target_include_directories(_opencv_aruco PUBLIC
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ void ptsort_(struct pt *pts, int sz)
|
|||||||
|
|
||||||
// Use stack storage if it's not too big.
|
// Use stack storage if it's not too big.
|
||||||
cv::AutoBuffer<struct pt, 1024> _tmp_stack(sz);
|
cv::AutoBuffer<struct pt, 1024> _tmp_stack(sz);
|
||||||
memcpy(_tmp_stack.data(), pts, sizeof(struct pt) * sz);
|
memcpy(_tmp_stack, pts, sizeof(struct pt) * sz);
|
||||||
|
|
||||||
int asz = sz/2;
|
int asz = sz/2;
|
||||||
int bsz = sz - asz;
|
int bsz = sz - asz;
|
||||||
@@ -470,11 +470,11 @@ int quad_segment_agg(int sz, struct line_fit_pt *lfps, int indices[4]){
|
|||||||
int rvalloc_pos = 0;
|
int rvalloc_pos = 0;
|
||||||
int rvalloc_size = 3*sz;
|
int rvalloc_size = 3*sz;
|
||||||
cv::AutoBuffer<struct remove_vertex, 0> rvalloc_(std::max(1, rvalloc_size));
|
cv::AutoBuffer<struct remove_vertex, 0> rvalloc_(std::max(1, rvalloc_size));
|
||||||
memset(rvalloc_.data(), 0, sizeof(rvalloc_[0]) * rvalloc_.size()); // TODO Add AutoBuffer zero fill
|
memset(rvalloc_, 0, sizeof(rvalloc_[0]) * rvalloc_.size()); // TODO Add AutoBuffer zero fill
|
||||||
struct remove_vertex *rvalloc = rvalloc_.data();
|
struct remove_vertex *rvalloc = rvalloc_;
|
||||||
cv::AutoBuffer<struct segment, 0> segs_(std::max(1, sz)); // TODO Add AutoBuffer zero fill
|
cv::AutoBuffer<struct segment, 0> segs_(std::max(1, sz)); // TODO Add AutoBuffer zero fill
|
||||||
memset(segs_.data(), 0, sizeof(segs_[0]) * segs_.size());
|
memset(segs_, 0, sizeof(segs_[0]) * segs_.size());
|
||||||
struct segment *segs = segs_.data();
|
struct segment *segs = segs_;
|
||||||
|
|
||||||
// populate with initial entries
|
// populate with initial entries
|
||||||
for (int i = 0; i < sz; i++) {
|
for (int i = 0; i < sz; i++) {
|
||||||
@@ -753,8 +753,8 @@ int fit_quad(const Ptr<DetectorParameters> &_params, const Mat im, zarray_t *clu
|
|||||||
// efficiently computed for any contiguous range of indices.
|
// efficiently computed for any contiguous range of indices.
|
||||||
|
|
||||||
cv::AutoBuffer<struct line_fit_pt, 64> lfps_(sz);
|
cv::AutoBuffer<struct line_fit_pt, 64> lfps_(sz);
|
||||||
memset(lfps_.data(), 0, sizeof(lfps_[0]) * lfps_.size()); // TODO Add AutoBuffer zero fill
|
memset(lfps_, 0, sizeof(lfps_[0]) * lfps_.size()); // TODO Add AutoBuffer zero fill
|
||||||
struct line_fit_pt *lfps = lfps_.data();
|
struct line_fit_pt *lfps = lfps_;
|
||||||
|
|
||||||
for (int i = 0; i < sz; i++) {
|
for (int i = 0; i < sz; i++) {
|
||||||
struct pt *p;
|
struct pt *p;
|
||||||
|
|||||||
@@ -9,7 +9,8 @@
|
|||||||
"richquotes@https://github.com/okalachev/gitbook-plugin-richquotes.git",
|
"richquotes@https://github.com/okalachev/gitbook-plugin-richquotes.git",
|
||||||
"yametrika",
|
"yametrika",
|
||||||
"anchors",
|
"anchors",
|
||||||
"validate-links",
|
"collapsible-menu",
|
||||||
|
"validate-links@https://github.com/okalachev/gitbook-plugin-validate-links.git",
|
||||||
"bulk-redirect@https://github.com/okalachev/gitbook-plugin-bulk-redirect.git",
|
"bulk-redirect@https://github.com/okalachev/gitbook-plugin-bulk-redirect.git",
|
||||||
"sitemap@https://github.com/okalachev/plugin-sitemap.git",
|
"sitemap@https://github.com/okalachev/plugin-sitemap.git",
|
||||||
"toolbar@https://github.com/hamishwillee/gitbook-plugin-toolbar.git",
|
"toolbar@https://github.com/hamishwillee/gitbook-plugin-toolbar.git",
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
from distutils.core import setup
|
from distutils.core import setup
|
||||||
|
|
||||||
|
|||||||
@@ -8,5 +8,9 @@ ExecStart=/bin/bash -c ". /home/pi/catkin_ws/devel/setup.sh; \
|
|||||||
ROS_HOSTNAME=`hostname`.local exec stdbuf -o L roslaunch clover clover.launch --wait --screen --skip-log-check \
|
ROS_HOSTNAME=`hostname`.local exec stdbuf -o L roslaunch clover clover.launch --wait --screen --skip-log-check \
|
||||||
2> >(tee /tmp/clover.err)"
|
2> >(tee /tmp/clover.err)"
|
||||||
|
|
||||||
|
ExecStartPre=+rm /var/log/clover.log
|
||||||
|
StandardOutput=file:/var/log/clover.log
|
||||||
|
StandardError=file:/var/log/clover.log
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target
|
WantedBy=multi-user.target
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ echo_stamp "#6 Turn on UART"
|
|||||||
# https://github.com/RPi-Distro/raspi-config/pull/75
|
# https://github.com/RPi-Distro/raspi-config/pull/75
|
||||||
/usr/bin/raspi-config nonint do_serial 1
|
/usr/bin/raspi-config nonint do_serial 1
|
||||||
/usr/bin/raspi-config nonint set_config_var enable_uart 1 /boot/config.txt
|
/usr/bin/raspi-config nonint set_config_var enable_uart 1 /boot/config.txt
|
||||||
/usr/bin/raspi-config nonint set_config_var dtoverlay pi3-disable-bt /boot/config.txt
|
echo dtoverlay=pi3-disable-bt >> /boot/config.txt
|
||||||
systemctl disable hciuart.service
|
systemctl disable hciuart.service
|
||||||
|
|
||||||
# After adding to Raspbian OS
|
# After adding to Raspbian OS
|
||||||
|
|||||||
9
builder/assets/install_gitbook.sh
Executable file
9
builder/assets/install_gitbook.sh
Executable file
@@ -0,0 +1,9 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# GitBook CLI is deprecated, its installation is broken.
|
||||||
|
# This script fixes it until we stop using GitBook.
|
||||||
|
|
||||||
|
export NPM_CONFIG_UNSAFE_PERM=1
|
||||||
|
|
||||||
|
npm install gitbook-cli -g
|
||||||
|
gitbook fetch 3.2.3 && npm i npm@3.10.10 --prefix=~/.gitbook/versions/3.2.3/ # fixing https://travis-ci.org/github/CopterExpress/clover/jobs/766541125#L932
|
||||||
18
builder/assets/noetic-rosdep-clover.yaml
Normal file
18
builder/assets/noetic-rosdep-clover.yaml
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
async_web_server_cpp:
|
||||||
|
debian:
|
||||||
|
buster: [ros-noetic-async-web-server-cpp]
|
||||||
|
led_msgs:
|
||||||
|
debian:
|
||||||
|
buster: [ros-noetic-led-msgs]
|
||||||
|
ros_pytest:
|
||||||
|
debian:
|
||||||
|
buster: [ros-noetic-ros-pytest]
|
||||||
|
tf2_web_republisher:
|
||||||
|
debian:
|
||||||
|
buster: [ros-noetic-tf2-web-republisher]
|
||||||
|
web_video_server:
|
||||||
|
debian:
|
||||||
|
buster: [ros-noetic-web-video-server]
|
||||||
|
ws281x:
|
||||||
|
debian:
|
||||||
|
buster: [ros-noetic-ws281x]
|
||||||
@@ -3,7 +3,7 @@ Description=Launcher for the ROS master, parameter server and rosout logging nod
|
|||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
User=pi
|
User=pi
|
||||||
ExecStart=/bin/sh -c ". /opt/ros/melodic/setup.sh; ROS_HOSTNAME=`hostname`.local exec roscore"
|
ExecStart=/bin/sh -c ". /opt/ros/noetic/setup.sh; ROS_HOSTNAME=`hostname`.local exec roscore"
|
||||||
Restart=on-failure
|
Restart=on-failure
|
||||||
RestartSec=3
|
RestartSec=3
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,8 @@
|
|||||||
|
|
||||||
set -e # Exit immidiately on non-zero result
|
set -e # Exit immidiately on non-zero result
|
||||||
|
|
||||||
SOURCE_IMAGE="https://downloads.raspberrypi.org/raspbian_lite/images/raspbian_lite-2020-02-14/2020-02-13-raspbian-buster-lite.zip"
|
# https://www.raspberrypi.org/software/operating-systems/#raspberry-pi-os-32-bit
|
||||||
|
SOURCE_IMAGE="https://downloads.raspberrypi.org/raspios_lite_armhf/images/raspios_lite_armhf-2021-05-28/2021-05-07-raspios-buster-armhf-lite.zip"
|
||||||
|
|
||||||
export DEBIAN_FRONTEND=${DEBIAN_FRONTEND:='noninteractive'}
|
export DEBIAN_FRONTEND=${DEBIAN_FRONTEND:='noninteractive'}
|
||||||
export LANG=${LANG:='C.UTF-8'}
|
export LANG=${LANG:='C.UTF-8'}
|
||||||
@@ -104,8 +105,6 @@ ${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/butterf
|
|||||||
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/monkey.service' '/lib/systemd/system/'
|
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/monkey.service' '/lib/systemd/system/'
|
||||||
# software install
|
# software install
|
||||||
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} exec ${SCRIPTS_DIR}'/image-software.sh'
|
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} exec ${SCRIPTS_DIR}'/image-software.sh'
|
||||||
# examples
|
|
||||||
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/examples' '/home/pi/'
|
|
||||||
# network setup
|
# network setup
|
||||||
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} exec ${SCRIPTS_DIR}'/image-network.sh'
|
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} exec ${SCRIPTS_DIR}'/image-network.sh'
|
||||||
# avahi setup
|
# avahi setup
|
||||||
@@ -114,15 +113,11 @@ ${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/avahi-s
|
|||||||
# If RPi then use a one thread to build a ROS package on RPi, else use all
|
# If RPi then use a one thread to build a ROS package on RPi, else use all
|
||||||
[[ $(arch) == 'armv7l' ]] && NUMBER_THREADS=1 || NUMBER_THREADS=$(nproc --all)
|
[[ $(arch) == 'armv7l' ]] && NUMBER_THREADS=1 || NUMBER_THREADS=$(nproc --all)
|
||||||
# Clover
|
# Clover
|
||||||
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/clover.service' '/lib/systemd/system/'
|
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/noetic-rosdep-clover.yaml' '/etc/ros/rosdep/'
|
||||||
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/roscore.service' '/lib/systemd/system/'
|
|
||||||
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/melodic-rosdep-clover.yaml' '/etc/ros/rosdep/'
|
|
||||||
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/ros_python_paths' '/etc/sudoers.d/'
|
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/ros_python_paths' '/etc/sudoers.d/'
|
||||||
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/pigpiod.service' '/lib/systemd/system/'
|
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/pigpiod.service' '/lib/systemd/system/'
|
||||||
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/launch.nanorc' '/usr/share/nano/'
|
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/launch.nanorc' '/usr/share/nano/'
|
||||||
# ${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/kinetic-ros-clover.rosinstall' '/home/pi/ros_catkin_ws/'
|
# ${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/kinetic-ros-clover.rosinstall' '/home/pi/ros_catkin_ws/'
|
||||||
# Add PX4 udev rules
|
|
||||||
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${REPO_DIR}'/clover/config/99-px4fmu.rules' '/lib/udev/rules.d/'
|
|
||||||
# Add rename script
|
# Add rename script
|
||||||
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} exec ${SCRIPTS_DIR}'/image-ros.sh' ${REPO_URL} ${IMAGE_VERSION} false false ${NUMBER_THREADS}
|
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} exec ${SCRIPTS_DIR}'/image-ros.sh' ${REPO_URL} ${IMAGE_VERSION} false false ${NUMBER_THREADS}
|
||||||
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} exec ${SCRIPTS_DIR}'/image-validate.sh'
|
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} exec ${SCRIPTS_DIR}'/image-validate.sh'
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
# copies or substantial portions of the Software.
|
# copies or substantial portions of the Software.
|
||||||
#
|
#
|
||||||
|
|
||||||
set -e # Exit immidiately on non-zero result
|
set -ex # exit on error, echo commands
|
||||||
|
|
||||||
REPO=$1
|
REPO=$1
|
||||||
REF=$2
|
REF=$2
|
||||||
@@ -21,6 +21,9 @@ INSTALL_ROS_PACK_SOURCES=$3
|
|||||||
DISCOVER_ROS_PACK=$4
|
DISCOVER_ROS_PACK=$4
|
||||||
NUMBER_THREADS=$5
|
NUMBER_THREADS=$5
|
||||||
|
|
||||||
|
# Current ROS distribution
|
||||||
|
ROS_DISTRO=noetic
|
||||||
|
|
||||||
echo_stamp() {
|
echo_stamp() {
|
||||||
# TEMPLATE: echo_stamp <TEXT> <TYPE>
|
# TEMPLATE: echo_stamp <TEXT> <TYPE>
|
||||||
# TYPE: SUCCESS, ERROR, INFO
|
# TYPE: SUCCESS, ERROR, INFO
|
||||||
@@ -68,7 +71,8 @@ my_travis_retry() {
|
|||||||
# TODO: 'kinetic-rosdep-clover.yaml' should add only if we use our repo?
|
# TODO: 'kinetic-rosdep-clover.yaml' should add only if we use our repo?
|
||||||
echo_stamp "Init rosdep"
|
echo_stamp "Init rosdep"
|
||||||
my_travis_retry rosdep init
|
my_travis_retry rosdep init
|
||||||
echo "yaml file:///etc/ros/rosdep/melodic-rosdep-clover.yaml" >> /etc/ros/rosdep/sources.list.d/20-default.list
|
# FIXME: Re-add this after missing packages are built
|
||||||
|
echo "yaml file:///etc/ros/rosdep/${ROS_DISTRO}-rosdep-clover.yaml" >> /etc/ros/rosdep/sources.list.d/20-default.list
|
||||||
my_travis_retry rosdep update
|
my_travis_retry rosdep update
|
||||||
|
|
||||||
echo_stamp "Populate rosdep for ROS user"
|
echo_stamp "Populate rosdep for ROS user"
|
||||||
@@ -76,20 +80,40 @@ my_travis_retry sudo -u pi rosdep update
|
|||||||
|
|
||||||
export ROS_IP='127.0.0.1' # needed for running tests
|
export ROS_IP='127.0.0.1' # needed for running tests
|
||||||
|
|
||||||
echo_stamp "Reconfiguring Clover repository for simplier unshallowing"
|
# echo_stamp "Reconfiguring Clover repository for simplier unshallowing"
|
||||||
cd /home/pi/catkin_ws/src/clover
|
cd /home/pi/catkin_ws/src/clover
|
||||||
git config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*"
|
git config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*"
|
||||||
|
|
||||||
|
# This is sort of a hack to force "custom" packages to be installed - the ones built by COEX, linked against OpenCV 4.2
|
||||||
|
# I **wish** OpenCV would not be such a mess, but, well, here we are.
|
||||||
|
echo_stamp "Installing OpenCV 4.2-compatible ROS packages"
|
||||||
|
apt install -y --no-install-recommends \
|
||||||
|
ros-${ROS_DISTRO}-compressed-image-transport=1.14.0-0buster \
|
||||||
|
ros-${ROS_DISTRO}-cv-bridge=1.15.0-0buster \
|
||||||
|
ros-${ROS_DISTRO}-cv-camera=0.5.1-0buster \
|
||||||
|
ros-${ROS_DISTRO}-image-publisher=1.15.3-0buster \
|
||||||
|
ros-${ROS_DISTRO}-web-video-server=0.2.1-0buster
|
||||||
|
apt-mark hold \
|
||||||
|
ros-${ROS_DISTRO}-compressed-image-transport \
|
||||||
|
ros-${ROS_DISTRO}-cv-bridge \
|
||||||
|
ros-${ROS_DISTRO}-cv-camera \
|
||||||
|
ros-${ROS_DISTRO}-image-publisher \
|
||||||
|
ros-${ROS_DISTRO}-web-video-server
|
||||||
|
|
||||||
|
echo_stamp "Installing libboost-dev" # https://travis-ci.org/github/CopterExpress/clover/jobs/766318908#L6536
|
||||||
|
my_travis_retry apt-get install -y --no-install-recommends libboost-dev libboost-all-dev
|
||||||
|
|
||||||
echo_stamp "Build and install Clover"
|
echo_stamp "Build and install Clover"
|
||||||
cd /home/pi/catkin_ws
|
cd /home/pi/catkin_ws
|
||||||
# Don't try to install gazebo_ros
|
# Don't try to install gazebo_ros
|
||||||
my_travis_retry rosdep install -y --from-paths src --ignore-src --rosdistro melodic --os=debian:buster \
|
my_travis_retry rosdep install -y --from-paths src --ignore-src --rosdistro ${ROS_DISTRO} --os=debian:buster \
|
||||||
--skip-keys=gazebo_ros --skip-keys=gazebo_plugins
|
--skip-keys=gazebo_ros --skip-keys=gazebo_plugins
|
||||||
my_travis_retry pip install wheel
|
my_travis_retry pip3 install wheel
|
||||||
my_travis_retry pip install -r /home/pi/catkin_ws/src/clover/clover/requirements.txt
|
my_travis_retry pip3 install -r /home/pi/catkin_ws/src/clover/clover/requirements.txt
|
||||||
source /opt/ros/melodic/setup.bash
|
source /opt/ros/${ROS_DISTRO}/setup.bash
|
||||||
# Don't build simulation plugins for actual drone
|
# Don't build simulation plugins for actual drone
|
||||||
catkin_make -j2 -DCMAKE_BUILD_TYPE=Release -DCATKIN_BLACKLIST_PACKAGES=clover_gazebo_plugins
|
catkin_make -j2 -DCMAKE_BUILD_TYPE=RelWithDebInfo
|
||||||
|
source devel/setup.bash
|
||||||
|
|
||||||
echo_stamp "Install clever package (for backwards compatibility)"
|
echo_stamp "Install clever package (for backwards compatibility)"
|
||||||
cd /home/pi/catkin_ws/src/clover/builder/assets/clever
|
cd /home/pi/catkin_ws/src/clover/builder/assets/clever
|
||||||
@@ -98,30 +122,27 @@ rm -rf build # remove build artifacts
|
|||||||
|
|
||||||
echo_stamp "Build Clover documentation"
|
echo_stamp "Build Clover documentation"
|
||||||
cd /home/pi/catkin_ws/src/clover
|
cd /home/pi/catkin_ws/src/clover
|
||||||
NPM_CONFIG_UNSAFE_PERM=true npm install gitbook-cli -g
|
builder/assets/install_gitbook.sh
|
||||||
NPM_CONFIG_UNSAFE_PERM=true gitbook install
|
gitbook install
|
||||||
gitbook build
|
gitbook build
|
||||||
touch node_modules/CATKIN_IGNORE docs/CATKIN_IGNORE _book/CATKIN_IGNORE clover/www/CATKIN_IGNORE apps/CATKIN_IGNORE # ignore documentation files by catkin
|
touch node_modules/CATKIN_IGNORE docs/CATKIN_IGNORE _book/CATKIN_IGNORE clover/www/CATKIN_IGNORE apps/CATKIN_IGNORE # ignore documentation files by catkin
|
||||||
|
|
||||||
echo_stamp "Installing additional ROS packages"
|
echo_stamp "Installing additional ROS packages"
|
||||||
my_travis_retry apt-get install -y --no-install-recommends \
|
my_travis_retry apt-get install -y --no-install-recommends \
|
||||||
ros-melodic-dynamic-reconfigure \
|
ros-${ROS_DISTRO}-dynamic-reconfigure \
|
||||||
ros-melodic-compressed-image-transport \
|
ros-${ROS_DISTRO}-rosbridge-suite \
|
||||||
ros-melodic-rosbridge-suite \
|
ros-${ROS_DISTRO}-rosserial \
|
||||||
ros-melodic-rosserial \
|
ros-${ROS_DISTRO}-usb-cam \
|
||||||
ros-melodic-usb-cam \
|
ros-${ROS_DISTRO}-vl53l1x \
|
||||||
ros-melodic-vl53l1x \
|
ros-${ROS_DISTRO}-ws281x \
|
||||||
ros-melodic-ws281x \
|
ros-${ROS_DISTRO}-rosshow \
|
||||||
ros-melodic-rosshow
|
ros-${ROS_DISTRO}-cmake-modules \
|
||||||
|
ros-${ROS_DISTRO}-image-view
|
||||||
|
|
||||||
# TODO move GeographicLib datasets to Mavros debian package
|
# TODO move GeographicLib datasets to Mavros debian package
|
||||||
echo_stamp "Install GeographicLib datasets (needed for mavros)" \
|
echo_stamp "Install GeographicLib datasets (needed for mavros)" \
|
||||||
&& wget -qO- https://raw.githubusercontent.com/mavlink/mavros/master/mavros/scripts/install_geographiclib_datasets.sh | bash
|
&& wget -qO- https://raw.githubusercontent.com/mavlink/mavros/master/mavros/scripts/install_geographiclib_datasets.sh | bash
|
||||||
|
|
||||||
# FIXME: Buster comes with tornado==5.1.1 but we need tornado==4.2.1 for rosbridge_suite
|
|
||||||
# (note that Python 3 will still have a more recent version)
|
|
||||||
pip install tornado==4.2.1
|
|
||||||
|
|
||||||
echo_stamp "Running tests"
|
echo_stamp "Running tests"
|
||||||
cd /home/pi/catkin_ws
|
cd /home/pi/catkin_ws
|
||||||
# FIXME: Investigate failing tests
|
# FIXME: Investigate failing tests
|
||||||
@@ -130,12 +151,26 @@ catkin_make run_tests #&& catkin_test_results
|
|||||||
echo_stamp "Change permissions for catkin_ws"
|
echo_stamp "Change permissions for catkin_ws"
|
||||||
chown -Rf pi:pi /home/pi/catkin_ws
|
chown -Rf pi:pi /home/pi/catkin_ws
|
||||||
|
|
||||||
|
echo_stamp "Make \$HOME/examples symlink"
|
||||||
|
ln -s "$(catkin_find clover examples --first-only)" /home/pi
|
||||||
|
chown -Rf pi:pi /home/pi/examples
|
||||||
|
|
||||||
|
echo_stamp "Make systemd services symlinks"
|
||||||
|
ln -s /home/pi/catkin_ws/src/clover/builder/assets/clover.service /lib/systemd/system/
|
||||||
|
ln -s /home/pi/catkin_ws/src/clover/builder/assets/roscore.service /lib/systemd/system/
|
||||||
|
# validate
|
||||||
|
[ -f /lib/systemd/system/clover.service ]
|
||||||
|
[ -f /lib/systemd/system/roscore.service ]
|
||||||
|
|
||||||
|
echo_stamp "Make udev rules symlink"
|
||||||
|
ln -s "$(catkin_find clover udev --first-only)"/* /lib/udev/rules.d/
|
||||||
|
|
||||||
echo_stamp "Setup ROS environment"
|
echo_stamp "Setup ROS environment"
|
||||||
cat << EOF >> /home/pi/.bashrc
|
cat << EOF >> /home/pi/.bashrc
|
||||||
LANG='C.UTF-8'
|
LANG='C.UTF-8'
|
||||||
LC_ALL='C.UTF-8'
|
LC_ALL='C.UTF-8'
|
||||||
export ROS_HOSTNAME=\`hostname\`.local
|
export ROS_HOSTNAME=\`hostname\`.local
|
||||||
source /opt/ros/melodic/setup.bash
|
source /opt/ros/${ROS_DISTRO}/setup.bash
|
||||||
source /home/pi/catkin_ws/devel/setup.bash
|
source /home/pi/catkin_ws/devel/setup.bash
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
|||||||
@@ -64,15 +64,14 @@ echo "APT::Acquire::Retries \"3\";" > /etc/apt/apt.conf.d/80-retries
|
|||||||
echo_stamp "Install apt keys & repos"
|
echo_stamp "Install apt keys & repos"
|
||||||
|
|
||||||
# TODO: This STDOUT consist 'OK'
|
# TODO: This STDOUT consist 'OK'
|
||||||
curl http://deb.coex.tech/aptly_repo_signing.key 2> /dev/null | apt-key add -
|
|
||||||
apt-get update \
|
apt-get update \
|
||||||
&& apt-get install --no-install-recommends -y dirmngr > /dev/null \
|
&& apt-get install --no-install-recommends -y dirmngr > /dev/null \
|
||||||
&& apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-key C1CF6E31E6BADE8868B172B4F42ED6FBAB17C654
|
&& apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-key C1CF6E31E6BADE8868B172B4F42ED6FBAB17C654
|
||||||
|
|
||||||
echo "deb http://packages.ros.org/ros/ubuntu buster main" > /etc/apt/sources.list.d/ros-latest.list
|
echo "deb http://packages.ros.org/ros/ubuntu buster main" > /etc/apt/sources.list.d/ros-latest.list
|
||||||
echo "deb http://deb.coex.tech/opencv3 buster main" > /etc/apt/sources.list.d/opencv3.list
|
|
||||||
echo "deb http://deb.coex.tech/rpi-ros-melodic buster main" > /etc/apt/sources.list.d/rpi-ros-melodic.list
|
wget -O - 'http://packages.coex.tech/key.asc' | apt-key add -
|
||||||
echo "deb http://deb.coex.tech/clover buster main" > /etc/apt/sources.list.d/clover.list
|
echo 'deb http://packages.coex.tech buster main' >> /etc/apt/sources.list
|
||||||
|
|
||||||
echo_stamp "Update apt cache"
|
echo_stamp "Update apt cache"
|
||||||
|
|
||||||
@@ -82,6 +81,7 @@ apt-get update
|
|||||||
|
|
||||||
# Let's retry fetching those packages several times, just in case
|
# Let's retry fetching those packages several times, just in case
|
||||||
echo_stamp "Software installing"
|
echo_stamp "Software installing"
|
||||||
|
my_travis_retry apt-get install --no-install-recommends -y cmake-data=3.13.4-1 cmake=3.13.4-1 # FIXME: using older CMake due to https://travis-ci.org/github/CopterExpress/clover/jobs/764367665#L6984
|
||||||
my_travis_retry apt-get install --no-install-recommends -y \
|
my_travis_retry apt-get install --no-install-recommends -y \
|
||||||
unzip \
|
unzip \
|
||||||
zip \
|
zip \
|
||||||
@@ -94,23 +94,22 @@ lsof \
|
|||||||
git \
|
git \
|
||||||
dnsmasq \
|
dnsmasq \
|
||||||
tmux \
|
tmux \
|
||||||
|
tree \
|
||||||
vim \
|
vim \
|
||||||
cmake \
|
|
||||||
libjpeg8 \
|
libjpeg8 \
|
||||||
tcpdump \
|
tcpdump \
|
||||||
ltrace \
|
|
||||||
libpoco-dev \
|
libpoco-dev \
|
||||||
libzbar0 \
|
libzbar0 \
|
||||||
python-rosdep \
|
python3-rosdep \
|
||||||
python-rosinstall-generator \
|
python3-rosinstall-generator \
|
||||||
python-wstool \
|
python3-wstool \
|
||||||
python-rosinstall \
|
python3-rosinstall \
|
||||||
build-essential \
|
build-essential \
|
||||||
libffi-dev \
|
libffi-dev \
|
||||||
monkey \
|
monkey \
|
||||||
pigpio python-pigpio python3-pigpio \
|
pigpio python-pigpio python3-pigpio \
|
||||||
i2c-tools \
|
i2c-tools \
|
||||||
espeak espeak-data python-espeak \
|
espeak espeak-data python-espeak python3-espeak \
|
||||||
ntpdate \
|
ntpdate \
|
||||||
python-dev \
|
python-dev \
|
||||||
python3-dev \
|
python3-dev \
|
||||||
@@ -124,9 +123,10 @@ sed -i "s/updates_available//" /usr/share/byobu/status/status
|
|||||||
|
|
||||||
echo_stamp "Installing pip"
|
echo_stamp "Installing pip"
|
||||||
curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
|
curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
|
||||||
|
curl https://bootstrap.pypa.io/pip/2.7/get-pip.py -o get-pip2.py
|
||||||
python3 get-pip.py
|
python3 get-pip.py
|
||||||
python get-pip.py
|
python get-pip2.py
|
||||||
rm get-pip.py
|
rm get-pip.py get-pip2.py
|
||||||
#my_travis_retry pip install --upgrade pip
|
#my_travis_retry pip install --upgrade pip
|
||||||
#my_travis_retry pip3 install --upgrade pip
|
#my_travis_retry pip3 install --upgrade pip
|
||||||
|
|
||||||
@@ -136,22 +136,26 @@ pip3 --version
|
|||||||
|
|
||||||
echo_stamp "Install and enable Butterfly (web terminal)"
|
echo_stamp "Install and enable Butterfly (web terminal)"
|
||||||
echo_stamp "Workaround for tornado >= 6.0 breaking butterfly"
|
echo_stamp "Workaround for tornado >= 6.0 breaking butterfly"
|
||||||
|
export CRYPTOGRAPHY_DONT_BUILD_RUST=1
|
||||||
|
my_travis_retry pip3 install cryptography==3.4.6 # https://stackoverflow.com/a/68472128/6850197
|
||||||
|
my_travis_retry pip3 install pyOpenSSL==20.0.1
|
||||||
my_travis_retry pip3 install tornado==5.1.1
|
my_travis_retry pip3 install tornado==5.1.1
|
||||||
my_travis_retry pip3 install butterfly
|
my_travis_retry pip3 install butterfly
|
||||||
my_travis_retry pip3 install butterfly[systemd]
|
my_travis_retry pip3 install butterfly[systemd]
|
||||||
systemctl enable butterfly.socket
|
systemctl enable butterfly.socket
|
||||||
|
|
||||||
echo_stamp "Install ws281x library"
|
echo_stamp "Install ws281x library"
|
||||||
my_travis_retry pip install --prefer-binary rpi_ws281x
|
my_travis_retry pip3 install --prefer-binary rpi_ws281x
|
||||||
|
|
||||||
echo_stamp "Setup Monkey"
|
echo_stamp "Setup Monkey"
|
||||||
mv /etc/monkey/sites/default /etc/monkey/sites/default.orig
|
mv /etc/monkey/sites/default /etc/monkey/sites/default.orig
|
||||||
mv /root/monkey /etc/monkey/sites/default
|
mv /root/monkey /etc/monkey/sites/default
|
||||||
|
sed -i 's/SymLink Off/SymLink On/' /etc/monkey/monkey.conf
|
||||||
systemctl enable monkey.service
|
systemctl enable monkey.service
|
||||||
|
|
||||||
echo_stamp "Install Node.js"
|
echo_stamp "Install Node.js"
|
||||||
cd /home/pi
|
cd /home/pi
|
||||||
wget https://nodejs.org/dist/v10.15.0/node-v10.15.0-linux-armv6l.tar.gz
|
wget --no-verbose https://nodejs.org/dist/v10.15.0/node-v10.15.0-linux-armv6l.tar.gz
|
||||||
tar -xzf node-v10.15.0-linux-armv6l.tar.gz
|
tar -xzf node-v10.15.0-linux-armv6l.tar.gz
|
||||||
cp -R node-v10.15.0-linux-armv6l/* /usr/local/
|
cp -R node-v10.15.0-linux-armv6l/* /usr/local/
|
||||||
rm -rf node-v10.15.0-linux-armv6l/
|
rm -rf node-v10.15.0-linux-armv6l/
|
||||||
|
|||||||
@@ -16,16 +16,24 @@ set -ex
|
|||||||
|
|
||||||
echo "Run image tests"
|
echo "Run image tests"
|
||||||
|
|
||||||
export ROS_DISTRO='melodic'
|
export ROS_DISTRO='noetic'
|
||||||
export ROS_IP='127.0.0.1'
|
export ROS_IP='127.0.0.1'
|
||||||
source /opt/ros/melodic/setup.bash
|
source /opt/ros/${ROS_DISTRO}/setup.bash
|
||||||
source /home/pi/catkin_ws/devel/setup.bash
|
source /home/pi/catkin_ws/devel/setup.bash
|
||||||
|
systemctl start roscore
|
||||||
|
|
||||||
cd /home/pi/catkin_ws/src/clover/builder/test/
|
cd /home/pi/catkin_ws/src/clover/builder/test/
|
||||||
./tests.sh
|
./tests.sh
|
||||||
./tests.py
|
./tests.py
|
||||||
./tests_py3.py
|
./tests_py3.py
|
||||||
|
[[ $(./test_qr.py) == "Found QRCODE with data Проверка Unicode with center at x=66.0, y=66.0" ]]
|
||||||
[[ $(./tests_clever.py) == "Warning: clever package is renamed to clover" ]] # test backwards compatibility
|
[[ $(./tests_clever.py) == "Warning: clever package is renamed to clover" ]] # test backwards compatibility
|
||||||
|
|
||||||
|
systemctl stop roscore
|
||||||
|
|
||||||
|
# check documented packages available
|
||||||
|
apt-cache show gst-rtsp-launch
|
||||||
|
apt-cache show openvpn
|
||||||
|
|
||||||
echo "Move /etc/ld.so.preload back to its original position"
|
echo "Move /etc/ld.so.preload back to its original position"
|
||||||
mv /etc/ld.so.preload.disabled-for-build /etc/ld.so.preload
|
mv /etc/ld.so.preload.disabled-for-build /etc/ld.so.preload
|
||||||
|
|||||||
@@ -3,25 +3,45 @@
|
|||||||
# Perform a "standalone install" in a Docker container
|
# Perform a "standalone install" in a Docker container
|
||||||
set -e
|
set -e
|
||||||
# Step 1: Install pip
|
# Step 1: Install pip
|
||||||
apt update
|
apt-key adv --keyserver 'hkp://keyserver.ubuntu.com:80' --recv-key C1CF6E31E6BADE8868B172B4F42ED6FBAB17C654 # https://github.com/osrf/docker_images/issues/535
|
||||||
apt install -y curl
|
apt-get update
|
||||||
curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
|
apt-get install -y curl
|
||||||
python ./get-pip.py
|
if [ "x${ROS_PYTHON_VERSION}" = "x3" ]; then
|
||||||
|
PYTHON=python3
|
||||||
|
curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
|
||||||
|
else
|
||||||
|
PYTHON=python
|
||||||
|
curl https://bootstrap.pypa.io/pip/2.7/get-pip.py -o get-pip.py
|
||||||
|
fi
|
||||||
|
${PYTHON} ./get-pip.py
|
||||||
|
|
||||||
# Step 1.5: Add deb.coex.tech to apt
|
# Step 1.5: Add deb.coex.tech to apt
|
||||||
curl http://deb.coex.tech/aptly_repo_signing.key 2> /dev/null | apt-key add -
|
curl http://deb.coex.tech/aptly_repo_signing.key 2> /dev/null | apt-key add -
|
||||||
echo "deb http://deb.coex.tech/ros xenial main" > /etc/apt/sources.list.d/coex.tech.list
|
echo "deb http://deb.coex.tech/ros xenial main" > /etc/apt/sources.list.d/coex.tech.list
|
||||||
echo "yaml file:///etc/ros/rosdep/coex.yaml" > /etc/ros/rosdep/sources.list.d/99-coex.list
|
echo "yaml file:///etc/ros/rosdep/coex.yaml" > /etc/ros/rosdep/sources.list.d/99-coex.list
|
||||||
|
CODENAME=$(lsb_release -sc)
|
||||||
|
|
||||||
cat <<EOF > /etc/ros/rosdep/coex.yaml
|
cat <<EOF > /etc/ros/rosdep/coex.yaml
|
||||||
led_msgs:
|
led_msgs:
|
||||||
ubuntu:
|
ubuntu:
|
||||||
xenial: ros-kinetic-led-msgs
|
${CODENAME}: [ros-${ROS_DISTRO}-led-msgs]
|
||||||
bionic: ros-melodic-led-msgs
|
async_web_server_cpp:
|
||||||
debian:
|
ubuntu:
|
||||||
stretch: ros-kinetic-led-msgs
|
${CODENAME}: [ros-${ROS_DISTRO}-async-web-server-cpp]
|
||||||
buster: ros-melodic-led-msgs
|
ros_pytest:
|
||||||
|
ubuntu:
|
||||||
|
${CODENAME}: [ros-${ROS_DISTRO}-ros-pytest]
|
||||||
|
tf2_web_republisher:
|
||||||
|
ubuntu:
|
||||||
|
${CODENAME}: [ros-${ROS_DISTRO}-tf2-web-republisher]
|
||||||
|
web_video_server:
|
||||||
|
ubuntu:
|
||||||
|
${CODENAME}: [ros-${ROS_DISTRO}-web-video-server]
|
||||||
|
ws281x:
|
||||||
|
ubuntu:
|
||||||
|
${CODENAME}: [ros-${ROS_DISTRO}-ws281x]
|
||||||
EOF
|
EOF
|
||||||
apt update
|
apt-get update
|
||||||
rosdep update
|
rosdep update
|
||||||
|
|
||||||
# Step 2: Run rosdep to install all dependencies
|
# Step 2: Run rosdep to install all dependencies
|
||||||
@@ -37,7 +57,10 @@ cd /root/catkin_ws
|
|||||||
catkin_make
|
catkin_make
|
||||||
|
|
||||||
# Step 4: Run tests
|
# Step 4: Run tests
|
||||||
pip install --upgrade pytest
|
${PYTHON} -m pip install --upgrade pytest
|
||||||
cd /root/catkin_ws
|
cd /root/catkin_ws
|
||||||
source devel/setup.bash
|
source devel/setup.bash
|
||||||
catkin_make run_tests && catkin_test_results
|
catkin_make run_tests && catkin_test_results
|
||||||
|
|
||||||
|
# Step 5: Install packages
|
||||||
|
catkin_make install
|
||||||
|
|||||||
BIN
builder/test/qr.png
Normal file
BIN
builder/test/qr.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.8 KiB |
42
builder/test/test_qr.py
Executable file
42
builder/test/test_qr.py
Executable file
@@ -0,0 +1,42 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
# Test QG recognition example
|
||||||
|
# Should be synced with the documentation: /docs/en/camera.md, /docs/ru/camera.md
|
||||||
|
# TODO: use real ROS topics
|
||||||
|
|
||||||
|
import rospy
|
||||||
|
from pyzbar import pyzbar
|
||||||
|
from cv_bridge import CvBridge
|
||||||
|
from sensor_msgs.msg import Image
|
||||||
|
|
||||||
|
bridge = CvBridge()
|
||||||
|
|
||||||
|
# rospy.init_node('barcode_test')
|
||||||
|
|
||||||
|
# Image subscriber callback function
|
||||||
|
def image_callback(data):
|
||||||
|
cv_image = bridge.imgmsg_to_cv2(data, 'bgr8') # OpenCV image
|
||||||
|
barcodes = pyzbar.decode(cv_image)
|
||||||
|
for barcode in barcodes:
|
||||||
|
b_data = barcode.data.decode("utf-8")
|
||||||
|
b_type = barcode.type
|
||||||
|
(x, y, w, h) = barcode.rect
|
||||||
|
xc = x + w/2
|
||||||
|
yc = y + h/2
|
||||||
|
print("Found {} with data {} with center at x={}, y={}".format(b_type, b_data, xc, yc))
|
||||||
|
# rospy.signal_shutdown('done')
|
||||||
|
|
||||||
|
# image_sub = rospy.Subscriber('main_camera/image_raw', Image, image_callback, queue_size=1)
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# Publish test image
|
||||||
|
# rospy.sleep(2)
|
||||||
|
|
||||||
|
import cv2
|
||||||
|
img = cv2.imread('qr.png')
|
||||||
|
image_callback(bridge.cv2_to_imgmsg(img, 'bgr8'))
|
||||||
|
|
||||||
|
# image_pub = rospy.Publisher('/main_camera/image_raw', Image, queue_size=1, latch=True)
|
||||||
|
# image_pub.publish(bridge.cv2_to_imgmsg(img, 'bgr8'))
|
||||||
|
|
||||||
|
# rospy.spin()
|
||||||
@@ -1,21 +1,29 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
# validate all required modules installed
|
# validate all required modules installed
|
||||||
|
|
||||||
import rospy
|
import rospy
|
||||||
from geometry_msgs.msg import PoseStamped
|
from geometry_msgs.msg import PoseStamped
|
||||||
|
from sensor_msgs.msg import Range, BatteryState
|
||||||
|
|
||||||
import cv2
|
import cv2
|
||||||
import cv2.aruco
|
import cv2.aruco
|
||||||
|
from sensor_msgs.msg import Image
|
||||||
|
from cv_bridge import CvBridge
|
||||||
|
|
||||||
import numpy
|
import numpy
|
||||||
import mavros
|
import mavros
|
||||||
from mavros_msgs.msg import State, StatusText, ExtendedState
|
from mavros_msgs.msg import State, StatusText, ExtendedState, RCIn, Mavlink
|
||||||
from mavros_msgs.srv import CommandBool, CommandLong, SetMode
|
from mavros_msgs.srv import CommandBool, CommandLong, SetMode
|
||||||
|
|
||||||
from std_srvs.srv import Trigger
|
from std_srvs.srv import Trigger
|
||||||
from clover.srv import GetTelemetry, Navigate, NavigateGlobal, SetPosition, SetVelocity, \
|
from clover.srv import GetTelemetry, Navigate, NavigateGlobal, SetPosition, SetVelocity, \
|
||||||
SetAttitude, SetRates, SetLEDEffect
|
SetAttitude, SetRates, SetLEDEffect
|
||||||
|
from led_msgs.srv import SetLEDs
|
||||||
|
from led_msgs.msg import LEDStateArray, LEDState
|
||||||
|
from aruco_pose.msg import Marker, MarkerArray, Point2D
|
||||||
|
|
||||||
|
import dynamic_reconfigure.client
|
||||||
|
|
||||||
import tf2_ros
|
import tf2_ros
|
||||||
import tf2_geometry_msgs
|
import tf2_geometry_msgs
|
||||||
@@ -25,7 +33,7 @@ import pymavlink
|
|||||||
from pymavlink import mavutil
|
from pymavlink import mavutil
|
||||||
import rpi_ws281x
|
import rpi_ws281x
|
||||||
import pigpio
|
import pigpio
|
||||||
from espeak import espeak
|
# from espeak import espeak
|
||||||
from pyzbar import pyzbar
|
from pyzbar import pyzbar
|
||||||
|
|
||||||
print cv2.getBuildInformation()
|
print(cv2.getBuildInformation())
|
||||||
|
|||||||
@@ -32,8 +32,9 @@ monkey --version
|
|||||||
pigpiod -v
|
pigpiod -v
|
||||||
i2cdetect -V
|
i2cdetect -V
|
||||||
butterfly -h
|
butterfly -h
|
||||||
espeak --version
|
# espeak --version
|
||||||
mjpg_streamer --version
|
mjpg_streamer --version
|
||||||
|
systemctl --version
|
||||||
|
|
||||||
# ros stuff
|
# ros stuff
|
||||||
|
|
||||||
@@ -43,6 +44,8 @@ rosversion aruco_pose
|
|||||||
rosversion vl53l1x
|
rosversion vl53l1x
|
||||||
rosversion mavros
|
rosversion mavros
|
||||||
rosversion mavros_extras
|
rosversion mavros_extras
|
||||||
|
rosversion ws281x
|
||||||
|
rosversion led_msgs
|
||||||
rosversion dynamic_reconfigure
|
rosversion dynamic_reconfigure
|
||||||
rosversion tf2_web_republisher
|
rosversion tf2_web_republisher
|
||||||
rosversion compressed_image_transport
|
rosversion compressed_image_transport
|
||||||
@@ -52,6 +55,12 @@ rosversion usb_cam
|
|||||||
rosversion cv_camera
|
rosversion cv_camera
|
||||||
rosversion web_video_server
|
rosversion web_video_server
|
||||||
rosversion rosshow
|
rosversion rosshow
|
||||||
|
rosversion nodelet
|
||||||
|
rosversion image_view
|
||||||
|
|
||||||
|
# validate some versions
|
||||||
|
[[ $(rosversion cv_camera) == "0.5.1" ]] # patched version with init fix
|
||||||
|
[[ $(rosversion ws281x) == "0.0.12" ]]
|
||||||
|
|
||||||
# validate examples are present
|
# validate examples are present
|
||||||
[[ $(ls /home/pi/examples/*) ]]
|
[[ $(ls /home/pi/examples/*) ]]
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
# test backwards compatibility
|
# test backwards compatibility
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,9 @@ import os
|
|||||||
import sys
|
import sys
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
EXCLUDE = ('clever4-front-white.png', '.DS_Store', 'clever4-front-black-large.png')
|
EXCLUDE = ('clever4-front-white.png', 'clever4-front-white-large.png', '.DS_Store',
|
||||||
|
'clever4-front-black-large.png','clover42-black.png', 'clover42-main-margin.png')
|
||||||
|
|
||||||
code = 0
|
code = 0
|
||||||
|
|
||||||
os.chdir('./docs')
|
os.chdir('./docs')
|
||||||
|
|||||||
8
clover/CHANGELOG.rst
Normal file
8
clover/CHANGELOG.rst
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
Changelog for package clover
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
0.21.1 (2020-11-17)
|
||||||
|
-------------------
|
||||||
|
* First release of clover package to ROS
|
||||||
|
* Contributors: Alexey Rogachevskiy, Arthur Golubtsov, Oleg Kalachev
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
cmake_minimum_required(VERSION 2.8.3)
|
cmake_minimum_required(VERSION 3.0)
|
||||||
project(clover)
|
project(clover)
|
||||||
|
|
||||||
## Compile as C++11, supported in ROS Kinetic and newer
|
## Compile as C++11, supported in ROS Kinetic and newer
|
||||||
@@ -30,7 +30,15 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake")
|
|||||||
|
|
||||||
find_package(GeographicLib REQUIRED)
|
find_package(GeographicLib REQUIRED)
|
||||||
|
|
||||||
find_package(OpenCV 3 REQUIRED
|
# Workaround for OpenCV 3/4 support
|
||||||
|
set(_opencv_version 4)
|
||||||
|
find_package(OpenCV ${_opencv_version} QUIET COMPONENTS calib3d imgproc)
|
||||||
|
if (NOT OpenCV_FOUND)
|
||||||
|
message(STATUS "Did not find OpenCV 4, searching for OpenCV 3")
|
||||||
|
set(_opencv_version 3)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
find_package(OpenCV ${_opencv_version} REQUIRED
|
||||||
COMPONENTS
|
COMPONENTS
|
||||||
calib3d
|
calib3d
|
||||||
imgproc
|
imgproc
|
||||||
@@ -233,12 +241,12 @@ target_link_libraries(${PROJECT_NAME}
|
|||||||
# DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
|
# DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
|
||||||
# )
|
# )
|
||||||
|
|
||||||
## Mark executables and/or libraries for installation
|
# Mark executables and/or libraries for installation
|
||||||
# install(TARGETS ${PROJECT_NAME} ${PROJECT_NAME}_node
|
install(TARGETS simple_offboard clover_rc camera_markers vpe_publisher clover_led shell clover
|
||||||
# ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
|
ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
|
||||||
# LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
|
LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
|
||||||
# RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
|
RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
|
||||||
# )
|
)
|
||||||
|
|
||||||
## Mark cpp header files for installation
|
## Mark cpp header files for installation
|
||||||
# install(DIRECTORY include/${PROJECT_NAME}/
|
# install(DIRECTORY include/${PROJECT_NAME}/
|
||||||
@@ -254,13 +262,25 @@ target_link_libraries(${PROJECT_NAME}
|
|||||||
# DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}
|
# DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}
|
||||||
# )
|
# )
|
||||||
|
|
||||||
|
catkin_install_python(PROGRAMS src/selfcheck.py
|
||||||
|
DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
|
||||||
|
)
|
||||||
|
|
||||||
|
install(DIRECTORY launch DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION})
|
||||||
|
|
||||||
|
install(DIRECTORY examples DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION})
|
||||||
|
|
||||||
|
install(DIRECTORY www DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION})
|
||||||
|
|
||||||
|
# TODO: install www
|
||||||
|
|
||||||
# Only install udev rules when building a Debian package
|
# Only install udev rules when building a Debian package
|
||||||
# FIXME: Other operating systems may have other prefixes
|
# FIXME: Other operating systems may have other prefixes
|
||||||
string(FIND ${CMAKE_INSTALL_PREFIX} "/opt/ros" _PREFIX_INDEX)
|
string(FIND ${CMAKE_INSTALL_PREFIX} "/opt/ros" _PREFIX_INDEX)
|
||||||
if (${_PREFIX_INDEX} EQUAL 0)
|
if (${_PREFIX_INDEX} EQUAL 0)
|
||||||
message(STATUS "Building as a Debian package - adding udev rules as installable files")
|
message(STATUS "Building as a Debian package - adding udev rules as installable files")
|
||||||
install(FILES
|
install(FILES
|
||||||
config/99-px4fmu.rules
|
udev/99-px4fmu.rules
|
||||||
DESTINATION /lib/udev/rules.d
|
DESTINATION /lib/udev/rules.d
|
||||||
)
|
)
|
||||||
else()
|
else()
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ A bundle for autonomous navigation and drone control.
|
|||||||
|
|
||||||
## Manual installation
|
## Manual installation
|
||||||
|
|
||||||
Install ROS Melodic according to the [documentation](http://wiki.ros.org/melodic/Installation), then [create a Catkin workspace](http://wiki.ros.org/catkin/Tutorials/create_a_workspace).
|
Install ROS Noetic according to the [documentation](http://wiki.ros.org/noetic/Installation), then [create a Catkin workspace](http://wiki.ros.org/catkin/Tutorials/create_a_workspace).
|
||||||
|
|
||||||
Clone this repo to directory `~/catkin_ws/src/clover`:
|
Clone this repo to directory `~/catkin_ws/src/clover`:
|
||||||
|
|
||||||
@@ -36,7 +36,7 @@ curl https://raw.githubusercontent.com/mavlink/mavros/master/mavros/scripts/inst
|
|||||||
You may optionally install udev rules to provide `/dev/px4fmu` symlink to your PX4-based flight controller connected over USB. Copy `99-px4fmu.rules` to your `/lib/udev/rules.d` folder:
|
You may optionally install udev rules to provide `/dev/px4fmu` symlink to your PX4-based flight controller connected over USB. Copy `99-px4fmu.rules` to your `/lib/udev/rules.d` folder:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd ~/catkin_ws/src/clover/clover/config
|
cd ~/catkin_ws/src/clover/clover/udev
|
||||||
sudo cp 99-px4fmu.rules /lib/udev/rules.d
|
sudo cp 99-px4fmu.rules /lib/udev/rules.d
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -44,16 +44,12 @@ Alternatively you may change the `fcu_url` property in `mavros.launch` file to p
|
|||||||
|
|
||||||
## Running
|
## Running
|
||||||
|
|
||||||
To start connection to SITL, use:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
roslaunch clover sitl.launch
|
|
||||||
```
|
|
||||||
|
|
||||||
To start connection to the flight controller, use:
|
To start connection to the flight controller, use:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
roslaunch clover clover.launch
|
roslaunch clover clover.launch
|
||||||
```
|
```
|
||||||
|
|
||||||
|
For the simulation information see the [corresponding article](https://clover.coex.tech/en/simulation.html).
|
||||||
|
|
||||||
> Note that the package is configured to connect to `/dev/px4fmu` by default (see [previous section](#manual-installation)). Install udev rules or specify path to your FCU device in `mavros.launch`.
|
> Note that the package is configured to connect to `/dev/px4fmu` by default (see [previous section](#manual-installation)). Install udev rules or specify path to your FCU device in `mavros.launch`.
|
||||||
|
|||||||
@@ -15,17 +15,17 @@ set_attitude = rospy.ServiceProxy('set_attitude', srv.SetAttitude)
|
|||||||
set_rates = rospy.ServiceProxy('set_rates', srv.SetRates)
|
set_rates = rospy.ServiceProxy('set_rates', srv.SetRates)
|
||||||
land = rospy.ServiceProxy('land', Trigger)
|
land = rospy.ServiceProxy('land', Trigger)
|
||||||
|
|
||||||
# Take off and hover 1 m above the ground
|
print('Take off and hover 1 m above the ground')
|
||||||
navigate(x=0, y=0, z=1, frame_id='body', auto_arm=True)
|
navigate(x=0, y=0, z=1, frame_id='body', auto_arm=True)
|
||||||
|
|
||||||
# Wait for 3 seconds
|
# Wait for 5 seconds
|
||||||
rospy.sleep(3)
|
rospy.sleep(5)
|
||||||
|
|
||||||
# Fly forward 1 m
|
print('Fly forward 1 m')
|
||||||
navigate(x=1, y=0, z=0, frame_id='body')
|
navigate(x=1, y=0, z=0, frame_id='body')
|
||||||
|
|
||||||
# Wait for 3 seconds
|
# Wait for 5 seconds
|
||||||
rospy.sleep(3)
|
rospy.sleep(5)
|
||||||
|
|
||||||
# Perform landing
|
print('Perform landing')
|
||||||
land()
|
land()
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
# Information: https://clover.coex.tech/en/aruco.html
|
# Information: https://clover.coex.tech/aruco
|
||||||
|
|
||||||
import rospy
|
import rospy
|
||||||
from clover import srv
|
from clover import srv
|
||||||
@@ -15,23 +15,23 @@ set_attitude = rospy.ServiceProxy('set_attitude', srv.SetAttitude)
|
|||||||
set_rates = rospy.ServiceProxy('set_rates', srv.SetRates)
|
set_rates = rospy.ServiceProxy('set_rates', srv.SetRates)
|
||||||
land = rospy.ServiceProxy('land', Trigger)
|
land = rospy.ServiceProxy('land', Trigger)
|
||||||
|
|
||||||
# Take off and hover 1 m above the ground
|
print('Take off and hover 1 m above the ground')
|
||||||
navigate(x=0, y=0, z=1, frame_id='body', auto_arm=True)
|
navigate(x=0, y=0, z=1, frame_id='body', auto_arm=True)
|
||||||
|
|
||||||
# Wait for 3 seconds
|
# Wait for 5 seconds
|
||||||
rospy.sleep(3)
|
rospy.sleep(5)
|
||||||
|
|
||||||
# Fly 1 meter above ArUco marker 0
|
print('Fly 1 meter above ArUco marker 0')
|
||||||
navigate(x=0, y=0, z=1, frame_id='aruco_0')
|
navigate(x=0, y=0, z=1, frame_id='aruco_0')
|
||||||
|
|
||||||
# Wait for 3 seconds
|
# Wait for 5 seconds
|
||||||
rospy.sleep(3)
|
rospy.sleep(5)
|
||||||
|
|
||||||
# Fly to x=1 y=1 z=1 relative to ArUco markers map
|
print('Fly to x=1 y=1 z=1 relative to ArUco markers map')
|
||||||
navigate(x=1, y=1, z=1, frame_id='aruco_map')
|
navigate(x=1, y=1, z=1, frame_id='aruco_map')
|
||||||
|
|
||||||
# Wait for 3 seconds
|
# Wait for 5 seconds
|
||||||
rospy.sleep(3)
|
rospy.sleep(5)
|
||||||
|
|
||||||
# Perform landing
|
print('Perform landing')
|
||||||
land()
|
land()
|
||||||
11
clover/examples/get_telemetry.py
Normal file
11
clover/examples/get_telemetry.py
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# Information: https://clover.coex.tech/en/simple_offboard.html#gettelemetry
|
||||||
|
|
||||||
|
import rospy
|
||||||
|
from clover import srv
|
||||||
|
|
||||||
|
rospy.init_node('flight')
|
||||||
|
|
||||||
|
get_telemetry = rospy.ServiceProxy('get_telemetry', srv.GetTelemetry)
|
||||||
|
|
||||||
|
# Print drone's state
|
||||||
|
print(get_telemetry())
|
||||||
47
clover/examples/gps.py
Normal file
47
clover/examples/gps.py
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
# Information: https://clover.coex.tech/en/simple_offboard.html#navigateglobal
|
||||||
|
|
||||||
|
import rospy
|
||||||
|
from clover import srv
|
||||||
|
from std_srvs.srv import Trigger
|
||||||
|
import math
|
||||||
|
|
||||||
|
rospy.init_node('flight')
|
||||||
|
|
||||||
|
get_telemetry = rospy.ServiceProxy('get_telemetry', srv.GetTelemetry)
|
||||||
|
navigate = rospy.ServiceProxy('navigate', srv.Navigate)
|
||||||
|
navigate_global = rospy.ServiceProxy('navigate_global', srv.NavigateGlobal)
|
||||||
|
set_position = rospy.ServiceProxy('set_position', srv.SetPosition)
|
||||||
|
set_velocity = rospy.ServiceProxy('set_velocity', srv.SetVelocity)
|
||||||
|
set_attitude = rospy.ServiceProxy('set_attitude', srv.SetAttitude)
|
||||||
|
set_rates = rospy.ServiceProxy('set_rates', srv.SetRates)
|
||||||
|
land = rospy.ServiceProxy('land', Trigger)
|
||||||
|
|
||||||
|
# https://clover.coex.tech/en/snippets.html#wait_arrival
|
||||||
|
def wait_arrival(tolerance=0.2):
|
||||||
|
while not rospy.is_shutdown():
|
||||||
|
telem = get_telemetry(frame_id='navigate_target')
|
||||||
|
if math.sqrt(telem.x ** 2 + telem.y ** 2 + telem.z ** 2) < tolerance:
|
||||||
|
break
|
||||||
|
rospy.sleep(0.2)
|
||||||
|
|
||||||
|
start = get_telemetry()
|
||||||
|
|
||||||
|
if math.isnan(start.lat):
|
||||||
|
raise Exception('No global position, install and configure GPS sensor: https://clover.coex.tech/gps')
|
||||||
|
|
||||||
|
print('Start point global position: lat={}, lon={}'.format(start.lat, start.lon))
|
||||||
|
|
||||||
|
print('Take off 3 meters')
|
||||||
|
navigate(x=0, y=0, z=3, frame_id='body', auto_arm=True)
|
||||||
|
wait_arrival()
|
||||||
|
|
||||||
|
print('Fly 1 arcsecond to the North (approx. 30 meters)')
|
||||||
|
navigate_global(lat=start.lat+1.0/60/60, lon=start.lon, z=start.z+3, yaw=math.inf, speed=5)
|
||||||
|
wait_arrival()
|
||||||
|
|
||||||
|
print('Fly to home position')
|
||||||
|
navigate_global(lat=start.lat, lon=start.lon, z=start.z+3, yaw=math.inf, speed=5)
|
||||||
|
wait_arrival()
|
||||||
|
|
||||||
|
print('Land')
|
||||||
|
land()
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
# Information: https://clover.coex.tech/en/leds.html
|
# Information: https://clover.coex.tech/led
|
||||||
|
|
||||||
import rospy
|
import rospy
|
||||||
from clover.srv import SetLEDEffect
|
from clover.srv import SetLEDEffect
|
||||||
@@ -7,19 +7,25 @@ rospy.init_node('leds')
|
|||||||
|
|
||||||
set_effect = rospy.ServiceProxy('led/set_effect', SetLEDEffect) # define proxy to ROS-service
|
set_effect = rospy.ServiceProxy('led/set_effect', SetLEDEffect) # define proxy to ROS-service
|
||||||
|
|
||||||
|
print('Fill red')
|
||||||
set_effect(r=255, g=0, b=0) # fill strip with red color
|
set_effect(r=255, g=0, b=0) # fill strip with red color
|
||||||
rospy.sleep(2)
|
rospy.sleep(2)
|
||||||
|
|
||||||
|
print('Fill green')
|
||||||
set_effect(r=0, g=100, b=0) # fill strip with green color
|
set_effect(r=0, g=100, b=0) # fill strip with green color
|
||||||
rospy.sleep(2)
|
rospy.sleep(2)
|
||||||
|
|
||||||
|
print('Fade to blue')
|
||||||
set_effect(effect='fade', r=0, g=0, b=255) # fade to blue color
|
set_effect(effect='fade', r=0, g=0, b=255) # fade to blue color
|
||||||
rospy.sleep(2)
|
rospy.sleep(2)
|
||||||
|
|
||||||
|
print('Flash red')
|
||||||
set_effect(effect='flash', r=255, g=0, b=0) # flash twice with red color
|
set_effect(effect='flash', r=255, g=0, b=0) # flash twice with red color
|
||||||
rospy.sleep(5)
|
rospy.sleep(2)
|
||||||
|
|
||||||
|
print('Blink white')
|
||||||
set_effect(effect='blink', r=255, g=255, b=255) # blink with white color
|
set_effect(effect='blink', r=255, g=255, b=255) # blink with white color
|
||||||
rospy.sleep(5)
|
rospy.sleep(5)
|
||||||
|
|
||||||
|
print('Rainbow')
|
||||||
set_effect(effect='rainbow') # show rainbow
|
set_effect(effect='rainbow') # show rainbow
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
# Information: https://clover.coex.tech/en/snippets.html#block-nav
|
# Information: https://clover.coex.tech/en/snippets.html#navigate_wait
|
||||||
|
|
||||||
import math
|
import math
|
||||||
import rospy
|
import rospy
|
||||||
@@ -31,11 +31,11 @@ def navigate_wait(x=0, y=0, z=0, yaw=float('nan'), yaw_rate=0, speed=0.5, \
|
|||||||
return res
|
return res
|
||||||
rospy.sleep(0.2)
|
rospy.sleep(0.2)
|
||||||
|
|
||||||
# Take off 1 meter
|
print('Take off 1 meter')
|
||||||
navigate_wait(z=1, frame_id='body', auto_arm=True)
|
navigate_wait(z=1, frame_id='body', auto_arm=True)
|
||||||
|
|
||||||
# Fly forward 1 m
|
print('Fly forward 1 m')
|
||||||
navigate_wait(x=1, frame_id='body')
|
navigate_wait(x=1, frame_id='body')
|
||||||
|
|
||||||
# Land
|
print('Land')
|
||||||
land()
|
land()
|
||||||
15
clover/examples/subscriber.py
Normal file
15
clover/examples/subscriber.py
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# Information: https://clover.coex.tech/en/laser.html
|
||||||
|
|
||||||
|
import rospy
|
||||||
|
from sensor_msgs.msg import Range
|
||||||
|
|
||||||
|
rospy.init_node('process_rangefinder')
|
||||||
|
|
||||||
|
def range_callback(msg):
|
||||||
|
# Process data from the rangefinder
|
||||||
|
print('Rangefinder distance:', msg.range)
|
||||||
|
|
||||||
|
# Subscribe to laser rangefinder data
|
||||||
|
rospy.Subscriber('rangefinder/range', Range, range_callback)
|
||||||
|
|
||||||
|
rospy.spin()
|
||||||
@@ -2,30 +2,40 @@
|
|||||||
<arg name="aruco_detect" default="true"/>
|
<arg name="aruco_detect" default="true"/>
|
||||||
<arg name="aruco_map" default="false"/>
|
<arg name="aruco_map" default="false"/>
|
||||||
<arg name="aruco_vpe" default="false"/>
|
<arg name="aruco_vpe" default="false"/>
|
||||||
|
<arg name="placement" default="floor"/> <!-- markers placement: floor, ceiling, unknown -->
|
||||||
|
<arg name="length" default="0.22"/> <!-- not-in-map markers length, m -->
|
||||||
|
<arg name="map" default="map.txt"/> <!-- markers map file name -->
|
||||||
|
|
||||||
<!-- For additional help go to https://clover.coex.tech/aruco -->
|
<!-- For additional help go to https://clover.coex.tech/aruco -->
|
||||||
|
|
||||||
|
<arg name="force_init" default="false"/>
|
||||||
|
<arg name="disable" default="false"/> <!-- only force init -->
|
||||||
|
|
||||||
<!-- aruco_detect: detect aruco markers, estimate poses -->
|
<!-- aruco_detect: detect aruco markers, estimate poses -->
|
||||||
<node name="aruco_detect" pkg="nodelet" if="$(arg aruco_detect)" type="nodelet" args="load aruco_pose/aruco_detect nodelet_manager" output="screen" clear_params="true">
|
<node name="aruco_detect" pkg="nodelet" if="$(eval aruco_detect and not disable)" type="nodelet" args="load aruco_pose/aruco_detect main_camera_nodelet_manager" output="screen" clear_params="true" respawn="true">
|
||||||
<remap from="image_raw" to="main_camera/image_raw"/>
|
<remap from="image_raw" to="main_camera/image_raw"/>
|
||||||
<remap from="camera_info" to="main_camera/camera_info"/>
|
<remap from="camera_info" to="main_camera/camera_info"/>
|
||||||
<remap from="map_markers" to="aruco_map/markers" if="$(arg aruco_map)"/>
|
<remap from="map_markers" to="aruco_map/markers"/>
|
||||||
<param name="estimate_poses" value="true"/>
|
<param name="estimate_poses" value="true"/>
|
||||||
<param name="send_tf" value="true"/>
|
<param name="send_tf" value="true"/>
|
||||||
<param name="known_tilt" value="map"/>
|
<param name="known_tilt" value="map" if="$(eval placement == 'floor')"/>
|
||||||
<param name="length" value="0.33"/>
|
<param name="known_tilt" value="map_flipped" if="$(eval placement == 'ceiling')"/>
|
||||||
|
<param name="length" value="$(arg length)"/>
|
||||||
<!-- aruco detector parameters -->
|
<!-- aruco detector parameters -->
|
||||||
<param name="cornerRefinementMethod" value="2"/> <!-- contour refinement -->
|
<param name="cornerRefinementMethod" value="2"/> <!-- contour refinement -->
|
||||||
<param name="minMarkerPerimeterRate" value="0.075"/> <!-- 0.075 for 320x240, 0.0375 for 640x480 -->
|
<param name="minMarkerPerimeterRate" value="0.075"/> <!-- 0.075 for 320x240, 0.0375 for 640x480 -->
|
||||||
|
<!-- length override example: -->
|
||||||
|
<!-- <param name="length_override/3" value="0.1"/> -->
|
||||||
</node>
|
</node>
|
||||||
|
|
||||||
<!-- aruco_map: estimate aruco map pose -->
|
<!-- aruco_map: estimate aruco map pose -->
|
||||||
<node name="aruco_map" pkg="nodelet" type="nodelet" if="$(arg aruco_map)" args="load aruco_pose/aruco_map nodelet_manager" output="screen" clear_params="true">
|
<node name="aruco_map" pkg="nodelet" type="nodelet" if="$(eval aruco_map and not disable)" args="load aruco_pose/aruco_map main_camera_nodelet_manager" output="screen" clear_params="true" respawn="true">
|
||||||
<remap from="image_raw" to="main_camera/image_raw"/>
|
<remap from="image_raw" to="main_camera/image_raw"/>
|
||||||
<remap from="camera_info" to="main_camera/camera_info"/>
|
<remap from="camera_info" to="main_camera/camera_info"/>
|
||||||
<remap from="markers" to="aruco_detect/markers"/>
|
<remap from="markers" to="aruco_detect/markers"/>
|
||||||
<param name="map" value="$(find aruco_pose)/map/map.txt"/>
|
<param name="map" value="$(find aruco_pose)/map/$(arg map)"/>
|
||||||
<param name="known_tilt" value="map"/>
|
<param name="known_tilt" value="map" if="$(eval placement == 'floor')"/>
|
||||||
|
<param name="known_tilt" value="map_flipped" if="$(eval placement == 'ceiling')"/>
|
||||||
<param name="image_axis" value="true"/>
|
<param name="image_axis" value="true"/>
|
||||||
<param name="frame_id" value="aruco_map_detected" if="$(arg aruco_vpe)"/>
|
<param name="frame_id" value="aruco_map_detected" if="$(arg aruco_vpe)"/>
|
||||||
<param name="frame_id" value="aruco_map" unless="$(arg aruco_vpe)"/>
|
<param name="frame_id" value="aruco_map" unless="$(arg aruco_vpe)"/>
|
||||||
@@ -34,11 +44,11 @@
|
|||||||
</node>
|
</node>
|
||||||
|
|
||||||
<!-- vpe publisher from aruco markers -->
|
<!-- vpe publisher from aruco markers -->
|
||||||
<node name="vpe_publisher" pkg="clover" type="vpe_publisher" if="$(arg aruco_vpe)" output="screen" clear_params="true">
|
<node name="vpe_publisher" pkg="clover" type="vpe_publisher" if="$(eval aruco_vpe or force_init)" output="screen" clear_params="true">
|
||||||
<remap from="~pose_cov" to="aruco_map/pose"/>
|
<remap from="~pose_cov" to="aruco_map/pose" if="$(arg aruco_vpe)"/>
|
||||||
<remap from="~vpe" to="mavros/vision_pose/pose"/>
|
<remap from="~vpe" to="mavros/vision_pose/pose"/>
|
||||||
<param name="frame_id" value="aruco_map_detected"/>
|
<param name="frame_id" value="aruco_map_detected" if="$(arg aruco_vpe)"/>
|
||||||
<param name="publish_zero" value="true"/>
|
<param name="force_init" value="$(arg force_init)"/>
|
||||||
<param name="offset_frame_id" value="aruco_map"/>
|
<param name="offset_frame_id" value="aruco_map"/>
|
||||||
</node>
|
</node>
|
||||||
</launch>
|
</launch>
|
||||||
|
|||||||
@@ -10,8 +10,9 @@
|
|||||||
<arg name="aruco" default="false"/>
|
<arg name="aruco" default="false"/>
|
||||||
<arg name="rangefinder_vl53l1x" default="true"/>
|
<arg name="rangefinder_vl53l1x" default="true"/>
|
||||||
<arg name="led" default="true"/>
|
<arg name="led" default="true"/>
|
||||||
<arg name="rc" default="true"/>
|
<arg name="blocks" default="false"/>
|
||||||
<arg name="shell" default="true"/>
|
<arg name="rc" default="false"/>
|
||||||
|
<arg name="force_init" value="true"/> <!-- force estimator to init by publishing zero pose -->
|
||||||
|
|
||||||
<arg name="simulator" default="false"/> <!-- flag that we are operating on a simulated drone -->
|
<arg name="simulator" default="false"/> <!-- flag that we are operating on a simulated drone -->
|
||||||
|
|
||||||
@@ -33,28 +34,24 @@
|
|||||||
</node>
|
</node>
|
||||||
|
|
||||||
<!-- aruco markers -->
|
<!-- aruco markers -->
|
||||||
<include file="$(find clover)/launch/aruco.launch" if="$(arg aruco)"/>
|
<include file="$(find clover)/launch/aruco.launch" if="$(eval aruco or force_init)">
|
||||||
|
<arg name="force_init" value="$(arg force_init)"/>
|
||||||
|
<arg name="disable" value="$(eval not aruco)"/>
|
||||||
|
</include>
|
||||||
|
|
||||||
<!-- optical flow -->
|
<!-- optical flow -->
|
||||||
<node pkg="nodelet" type="nodelet" name="optical_flow" args="load clover/optical_flow nodelet_manager" if="$(arg optical_flow)" clear_params="true" output="screen">
|
<node pkg="nodelet" type="nodelet" name="optical_flow" args="load clover/optical_flow main_camera_nodelet_manager" if="$(arg optical_flow)" clear_params="true" output="screen" respawn="true">
|
||||||
<remap from="image_raw" to="main_camera/image_raw"/>
|
<remap from="image_raw" to="main_camera/image_raw"/>
|
||||||
<remap from="camera_info" to="main_camera/camera_info"/>
|
<remap from="camera_info" to="main_camera/camera_info"/>
|
||||||
<param name="calc_flow_gyro" value="true"/>
|
<param name="calc_flow_gyro" value="true"/>
|
||||||
<param name="roi_rad" value="0.8"/>
|
<param name="roi_rad" value="0.8"/>
|
||||||
</node>
|
</node>
|
||||||
|
|
||||||
<!-- main nodelet manager -->
|
|
||||||
<node pkg="nodelet" type="nodelet" name="nodelet_manager" args="manager" output="screen" clear_params="true">
|
|
||||||
<param name="num_worker_threads" value="2"/>
|
|
||||||
</node>
|
|
||||||
|
|
||||||
<node pkg="tf2_ros" type="static_transform_publisher" name="map_flipped_frame" args="0 0 0 3.1415926 3.1415926 0 map map_flipped"/>
|
<node pkg="tf2_ros" type="static_transform_publisher" name="map_flipped_frame" args="0 0 0 3.1415926 3.1415926 0 map map_flipped"/>
|
||||||
|
|
||||||
<!-- simplified offboard control -->
|
<!-- simplified offboard control -->
|
||||||
<node name="simple_offboard" pkg="clover" type="simple_offboard" output="screen" clear_params="true">
|
<node name="simple_offboard" pkg="clover" type="simple_offboard" output="screen" clear_params="true">
|
||||||
<param name="reference_frames/body" value="map"/>
|
<param name="reference_frames/main_camera_optical" value="map"/>
|
||||||
<param name="reference_frames/base_link" value="map"/>
|
|
||||||
<param name="reference_frames/navigate_target" value="map"/>
|
|
||||||
</node>
|
</node>
|
||||||
|
|
||||||
<!-- main camera -->
|
<!-- main camera -->
|
||||||
@@ -80,15 +77,15 @@
|
|||||||
<arg name="simulator" value="$(arg simulator)"/>
|
<arg name="simulator" value="$(arg simulator)"/>
|
||||||
</include>
|
</include>
|
||||||
|
|
||||||
|
<!-- Clover Blocks -->
|
||||||
|
<node name="clover_blocks" pkg="clover_blocks" type="clover_blocks" output="screen" if="$(arg blocks)"/>
|
||||||
|
|
||||||
<!-- rc backend -->
|
<!-- rc backend -->
|
||||||
<node name="rc" pkg="clover" type="rc" output="screen" if="$(arg rc)" clear_params="true">
|
<node name="rc" pkg="clover" type="rc" output="screen" if="$(arg rc)" clear_params="true">
|
||||||
<!-- Send fake GCS heartbeats. Set to "true" for upstream PX4 -->
|
<!-- Send fake GCS heartbeats. Set to "true" for upstream PX4 -->
|
||||||
<param name="use_fake_gcs" value="false"/>
|
<param name="use_fake_gcs" value="false"/>
|
||||||
</node>
|
</node>
|
||||||
|
|
||||||
<!-- Shell access through ROS service -->
|
|
||||||
<node name="shell" pkg="clover" type="shell" output="screen" if="$(arg shell)"/>
|
|
||||||
|
|
||||||
<!-- Update static directory -->
|
<!-- Update static directory -->
|
||||||
<node pkg="roswww_static" name="roswww_static" type="main.py" clear_params="true">
|
<node pkg="roswww_static" name="roswww_static" type="main.py" clear_params="true">
|
||||||
<param name="default_package" value="clover"/>
|
<param name="default_package" value="clover"/>
|
||||||
|
|||||||
@@ -2,14 +2,17 @@
|
|||||||
<arg name="ws281x" default="true"/>
|
<arg name="ws281x" default="true"/>
|
||||||
<arg name="led_effect" default="true"/>
|
<arg name="led_effect" default="true"/>
|
||||||
<arg name="led_notify" default="true"/>
|
<arg name="led_notify" default="true"/>
|
||||||
|
<arg name="led_count" default="72"/>
|
||||||
|
<arg name="gpio_pin" default="21"/>
|
||||||
|
|
||||||
<arg name="simulator" default="false"/>
|
<arg name="simulator" default="false"/>
|
||||||
|
|
||||||
<!-- For additional help go to https://clover.coex.tech/led -->
|
<!-- For additional help go to https://clover.coex.tech/led -->
|
||||||
|
|
||||||
<!-- ws281x led strip driver -->
|
<!-- ws281x led strip driver -->
|
||||||
<node pkg="ws281x" name="led" type="ws281x_node" clear_params="true" output="screen" if="$(eval ws281x and not simulator)">
|
<node pkg="ws281x" name="led" type="ws281x_node" clear_params="true" output="screen" if="$(eval ws281x and not simulator)">
|
||||||
<param name="led_count" value="58"/>
|
<param name="led_count" value="$(arg led_count)"/>
|
||||||
<param name="gpio_pin" value="21"/>
|
<param name="gpio_pin" value="$(arg gpio_pin)"/>
|
||||||
<param name="brightness" value="64"/>
|
<param name="brightness" value="64"/>
|
||||||
<param name="strip_type" value="WS2811_STRIP_GRB"/>
|
<param name="strip_type" value="WS2811_STRIP_GRB"/>
|
||||||
<param name="target_frequency" value="800000"/>
|
<param name="target_frequency" value="800000"/>
|
||||||
@@ -32,8 +35,8 @@
|
|||||||
altctl: { r: 255, g: 255, b: 40 }
|
altctl: { r: 255, g: 255, b: 40 }
|
||||||
posctl: { r: 50, g: 100, b: 220 }
|
posctl: { r: 50, g: 100, b: 220 }
|
||||||
offboard: { r: 220, g: 20, b: 250 }
|
offboard: { r: 220, g: 20, b: 250 }
|
||||||
low_battery: { threshold: 3.7, effect: blink_fast, r: 255, g: 0, b: 0 }
|
low_battery: { threshold: 3.6, effect: blink_fast, r: 255, g: 0, b: 0 }
|
||||||
error: { effect: flash, r: 255, g: 0, b: 0 }
|
error: { effect: flash, r: 255, g: 0, b: 0, ignore: [ "[lpe] vision position timeout" ]}
|
||||||
</rosparam>
|
</rosparam>
|
||||||
</node>
|
</node>
|
||||||
</launch>
|
</launch>
|
||||||
|
|||||||
@@ -3,21 +3,29 @@
|
|||||||
|
|
||||||
<arg name="direction_z" default="down"/> <!-- direction the camera points: down, up -->
|
<arg name="direction_z" default="down"/> <!-- direction the camera points: down, up -->
|
||||||
<arg name="direction_y" default="backward"/> <!-- direction the camera cable points: backward, forward -->
|
<arg name="direction_y" default="backward"/> <!-- direction the camera cable points: backward, forward -->
|
||||||
|
<arg name="device" default="/dev/video0"/> <!-- v4l2 device -->
|
||||||
<arg name="simulator" default="false"/>
|
<arg name="simulator" default="false"/>
|
||||||
|
|
||||||
<node if="$(eval direction_z == 'down' and direction_y == 'backward')" pkg="tf2_ros" type="static_transform_publisher" name="main_camera_frame" args="0.05 0 -0.07 -1.5707963 0 3.1415926 base_link main_camera_optical"/>
|
<node if="$(eval direction_z == 'down' and direction_y == 'backward')" pkg="tf2_ros" type="static_transform_publisher" name="main_camera_frame" args="0.05 0 -0.07 -1.5707963 0 3.1415926 base_link main_camera_optical"/>
|
||||||
<node if="$(eval direction_z == 'down' and direction_y == 'forward')" pkg="tf2_ros" type="static_transform_publisher" name="main_camera_frame" args="0.05 0 -0.07 1.5707963 0 3.1415926 base_link main_camera_optical"/>
|
<node if="$(eval direction_z == 'down' and direction_y == 'forward')" pkg="tf2_ros" type="static_transform_publisher" name="main_camera_frame" args="0.05 0 -0.07 1.5707963 0 3.1415926 base_link main_camera_optical"/>
|
||||||
<node if="$(eval direction_z == 'up' and direction_y == 'backward')" pkg="tf2_ros" type="static_transform_publisher" name="main_camera_frame" args="0.05 0 0.07 1.5707963 0 0 base_link main_camera_optical"/>
|
<node if="$(eval direction_z == 'up' and direction_y == 'backward')" pkg="tf2_ros" type="static_transform_publisher" name="main_camera_frame" args="0.05 0 0.07 1.5707963 0 0 base_link main_camera_optical"/>
|
||||||
<node if="$(eval direction_z == 'up' and direction_y == 'forward')" pkg="tf2_ros" type="static_transform_publisher" name="main_camera_frame" args="0.05 0 0.07 -1.5707963 0 0 base_link main_camera_optical"/>
|
<node if="$(eval direction_z == 'up' and direction_y == 'forward')" pkg="tf2_ros" type="static_transform_publisher" name="main_camera_frame" args="0.05 0 0.07 -1.5707963 0 0 base_link main_camera_optical"/>
|
||||||
|
<node if="$(eval direction_z == 'forward')" pkg="tf2_ros" type="static_transform_publisher" name="main_camera_frame" args="0.03 0 0.05 -1.5707963 0 -1.5707963 base_link main_camera_optical"/>
|
||||||
|
<node if="$(eval direction_z == 'backward')" pkg="tf2_ros" type="static_transform_publisher" name="main_camera_frame" args="-0.03 0 0.05 1.5707963 0 -1.5707963 base_link main_camera_optical"/>
|
||||||
|
|
||||||
<!-- Template for custom camera orientation -->
|
<!-- Template for custom camera orientation -->
|
||||||
<!-- Camera position and orientation are represented by base_link -> main_camera_optical transform -->
|
<!-- Camera position and orientation are represented by base_link -> main_camera_optical transform -->
|
||||||
<!-- static_transform_publisher arguments: x y z yaw pitch roll frame_id child_frame_id -->
|
<!-- static_transform_publisher arguments: x y z yaw pitch roll frame_id child_frame_id -->
|
||||||
<!-- <node pkg="tf2_ros" type="static_transform_publisher" name="main_camera_frame" args="0.05 0 -0.07 -1.5707963 0 3.1415926 base_link main_camera_optical"/> -->
|
<!-- <node pkg="tf2_ros" type="static_transform_publisher" name="main_camera_frame" args="0.05 0 -0.07 -1.5707963 0 3.1415926 base_link main_camera_optical"/> -->
|
||||||
|
|
||||||
|
<!-- camera nodelet manager -->
|
||||||
|
<node pkg="nodelet" type="nodelet" name="main_camera_nodelet_manager" args="manager" output="screen" clear_params="true" respawn="true">
|
||||||
|
<param name="num_worker_threads" value="2"/>
|
||||||
|
</node>
|
||||||
|
|
||||||
<!-- camera node -->
|
<!-- camera node -->
|
||||||
<node pkg="nodelet" type="nodelet" name="main_camera" args="load cv_camera/CvCameraNodelet nodelet_manager" clear_params="true" unless="$(arg simulator)">
|
<node pkg="nodelet" type="nodelet" name="main_camera" args="load cv_camera/CvCameraNodelet main_camera_nodelet_manager" launch-prefix="rosrun clover waitfile $(arg device)" clear_params="true" unless="$(arg simulator)" respawn="true">
|
||||||
<param name="device_path" value="/dev/video0"/> <!-- v4l2 device -->
|
<param name="device_path" value="$(arg device)"/>
|
||||||
<param name="frame_id" value="main_camera_optical"/>
|
<param name="frame_id" value="main_camera_optical"/>
|
||||||
<param name="camera_info_url" value="file://$(find clover)/camera_info/fisheye_cam.yaml"/>
|
<param name="camera_info_url" value="file://$(find clover)/camera_info/fisheye_cam.yaml"/>
|
||||||
|
|
||||||
|
|||||||
@@ -6,13 +6,16 @@
|
|||||||
<arg name="viz" default="true"/>
|
<arg name="viz" default="true"/>
|
||||||
<arg name="respawn" default="true"/>
|
<arg name="respawn" default="true"/>
|
||||||
<arg name="distance_sensor_remap" default="rangefinder/range"/>
|
<arg name="distance_sensor_remap" default="rangefinder/range"/>
|
||||||
|
<arg name="usb_device" default="/dev/px4fmu"/>
|
||||||
|
<arg name="prefix" default="" unless="$(eval fcu_conn == 'usb')"/>
|
||||||
|
<arg name="prefix" default="rosrun clover waitfile $(arg usb_device)" if="$(eval fcu_conn == 'usb')"/>
|
||||||
|
|
||||||
<node pkg="mavros" type="mavros_node" name="mavros" required="false" clear_params="true" respawn="$(arg respawn)" unless="$(eval fcu_conn == 'none')" respawn_delay="1" output="screen">
|
<node pkg="mavros" type="mavros_node" name="mavros" launch-prefix="$(arg prefix)" required="false" clear_params="true" respawn="$(arg respawn)" unless="$(eval fcu_conn == 'none')" respawn_delay="1" output="screen">
|
||||||
<!-- UART connection -->
|
<!-- UART connection -->
|
||||||
<param name="fcu_url" value="/dev/ttyAMA0:921600" if="$(eval fcu_conn is None or fcu_conn == 'uart')"/>
|
<param name="fcu_url" value="/dev/ttyAMA0:921600" if="$(eval fcu_conn is None or fcu_conn == 'uart')"/>
|
||||||
|
|
||||||
<!-- USB connection -->
|
<!-- USB connection -->
|
||||||
<param name="fcu_url" value="/dev/px4fmu" if="$(eval fcu_conn == 'usb')"/>
|
<param name="fcu_url" value="$(arg usb_device)" if="$(eval fcu_conn == 'usb')"/>
|
||||||
|
|
||||||
<!-- sitl before PX4 1.9.0 -->
|
<!-- sitl before PX4 1.9.0 -->
|
||||||
<param name="fcu_url" value="udp://@$(arg fcu_ip):14557" if="$(eval fcu_conn == 'udp')"/>
|
<param name="fcu_url" value="udp://@$(arg fcu_ip):14557" if="$(eval fcu_conn == 'udp')"/>
|
||||||
@@ -36,7 +39,7 @@
|
|||||||
<rosparam command="load" file="$(find clover)/launch/mavros_config.yaml"/>
|
<rosparam command="load" file="$(find clover)/launch/mavros_config.yaml"/>
|
||||||
|
|
||||||
<!-- remap rangefinder -->
|
<!-- remap rangefinder -->
|
||||||
<remap from="mavros/distance_sensor/rangefinder_sub" to="rangefinder/range"/>
|
<remap from="mavros/distance_sensor/rangefinder_sub" to="$(arg distance_sensor_remap)" if="$(eval bool(distance_sensor_remap))"/>
|
||||||
|
|
||||||
<rosparam param="plugin_whitelist">
|
<rosparam param="plugin_whitelist">
|
||||||
- altitude
|
- altitude
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
# Config file for mavros
|
# Config file for mavros
|
||||||
# Based on https://raw.githubusercontent.com/mavlink/mavros/master/mavros/launch/px4_config.yaml
|
# Based on https://raw.githubusercontent.com/mavlink/mavros/master/mavros/launch/px4_config.yaml
|
||||||
|
|
||||||
startup_px4_usb_quirk: true
|
startup_px4_usb_quirk: false
|
||||||
|
|
||||||
conn:
|
conn:
|
||||||
heartbeat_rate: 1.0 # send hertbeat rate in Hertz
|
heartbeat_rate: 1.0 # send heartbeat rate in Hertz
|
||||||
timeout: 10.0 # hertbeat timeout in seconds
|
timeout: 10.0 # heartbeat timeout in seconds
|
||||||
timesync_rate: 10.0 # TIMESYNC rate in Hertz (feature disabled if 0.0)
|
timesync_rate: 10.0 # TIMESYNC rate in Hertz (feature disabled if 0.0)
|
||||||
system_time_rate: 1.0 # send system time to FCU rate in Hertz (disabled if 0.0)
|
system_time_rate: 1.0 # send system time to FCU rate in Hertz (disabled if 0.0)
|
||||||
|
|
||||||
@@ -13,6 +13,7 @@ time:
|
|||||||
time_ref_source: "fcu" # time_reference source
|
time_ref_source: "fcu" # time_reference source
|
||||||
timesync_mode: MAVLINK
|
timesync_mode: MAVLINK
|
||||||
timesync_avg_alpha: 0.6 # timesync averaging factor
|
timesync_avg_alpha: 0.6 # timesync averaging factor
|
||||||
|
publish_sim_time: false # don't publish /clock
|
||||||
|
|
||||||
global_position:
|
global_position:
|
||||||
frame_id: "map" # origin frame
|
frame_id: "map" # origin frame
|
||||||
@@ -77,6 +78,9 @@ distance_sensor:
|
|||||||
field_of_view: 0.5
|
field_of_view: 0.5
|
||||||
rangefinder_sub:
|
rangefinder_sub:
|
||||||
subscriber: true
|
subscriber: true
|
||||||
|
id: 1
|
||||||
|
orientation: PITCH_270
|
||||||
|
covariance: 1 # cm
|
||||||
|
|
||||||
# fake_gps
|
# fake_gps
|
||||||
fake_gps:
|
fake_gps:
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
<launch>
|
|
||||||
<!-- clover configuration for testing in sitl -->
|
|
||||||
<arg name="ip" default="127.0.0.1"/>
|
|
||||||
<arg name="rosbridge" default="false"/>
|
|
||||||
|
|
||||||
<include file="$(find clover)/launch/clover.launch">
|
|
||||||
<arg name="fcu_conn" value="udp"/>
|
|
||||||
<arg name="fcu_ip" value="$(arg ip)"/>
|
|
||||||
<arg name="gcs_bridge" value="false"/>
|
|
||||||
<arg name="optical_flow" value="false"/>
|
|
||||||
<arg name="web_video_server" default="false"/>
|
|
||||||
<arg name="main_camera" default="false"/>
|
|
||||||
<arg name="rosbridge" value="$(arg rosbridge)"/>
|
|
||||||
<arg name="aruco" default="false"/>
|
|
||||||
<arg name="rangefinder_vl53l1x" default="false"/>
|
|
||||||
<arg name="led" default="false"/>
|
|
||||||
<arg name="rc" default="false"/>
|
|
||||||
</include>
|
|
||||||
</launch>
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0"?>
|
<?xml version="1.0"?>
|
||||||
<package format="2">
|
<package format="3">
|
||||||
<name>clover</name>
|
<name>clover</name>
|
||||||
<version>0.0.1</version>
|
<version>0.23.0</version>
|
||||||
<description>The Clover package</description>
|
<description>The Clover package</description>
|
||||||
|
|
||||||
<maintainer email="okalachev@gmail.com">Oleg Kalachev</maintainer>
|
<maintainer email="okalachev@gmail.com">Oleg Kalachev</maintainer>
|
||||||
@@ -37,7 +37,8 @@
|
|||||||
<depend>rosbridge_server</depend>
|
<depend>rosbridge_server</depend>
|
||||||
<depend>web_video_server</depend>
|
<depend>web_video_server</depend>
|
||||||
<depend>tf2_web_republisher</depend>
|
<depend>tf2_web_republisher</depend>
|
||||||
<depend>python-lxml</depend>
|
<depend condition="$ROS_PYTHON_VERSION == 2">python-lxml</depend>
|
||||||
|
<depend condition="$ROS_PYTHON_VERSION == 3">python3-lxml</depend>
|
||||||
<exec_depend>python-pymavlink</exec_depend>
|
<exec_depend>python-pymavlink</exec_depend>
|
||||||
<!-- Use test_depend for packages you need only for testing: -->
|
<!-- Use test_depend for packages you need only for testing: -->
|
||||||
<!-- <test_depend>gtest</test_depend> -->
|
<!-- <test_depend>gtest</test_depend> -->
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
#include <ros/ros.h>
|
#include <ros/ros.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
#include <boost/algorithm/string.hpp>
|
#include <boost/algorithm/string.hpp>
|
||||||
|
|
||||||
#include <clover/SetLEDEffect.h>
|
#include <clover/SetLEDEffect.h>
|
||||||
@@ -29,6 +30,7 @@ ros::Timer timer;
|
|||||||
ros::Time start_time;
|
ros::Time start_time;
|
||||||
double blink_rate, blink_fast_rate, flash_delay, fade_period, wipe_period, rainbow_period;
|
double blink_rate, blink_fast_rate, flash_delay, fade_period, wipe_period, rainbow_period;
|
||||||
double low_battery_threshold;
|
double low_battery_threshold;
|
||||||
|
std::vector<std::string> error_ignore;
|
||||||
bool blink_state;
|
bool blink_state;
|
||||||
led_msgs::SetLEDs set_leds;
|
led_msgs::SetLEDs set_leds;
|
||||||
led_msgs::LEDStateArray state, start_state;
|
led_msgs::LEDStateArray state, start_state;
|
||||||
@@ -274,6 +276,10 @@ void handleMavrosState(const mavros_msgs::State& msg)
|
|||||||
void handleLog(const rosgraph_msgs::Log& log)
|
void handleLog(const rosgraph_msgs::Log& log)
|
||||||
{
|
{
|
||||||
if (log.level >= rosgraph_msgs::Log::ERROR) {
|
if (log.level >= rosgraph_msgs::Log::ERROR) {
|
||||||
|
// check if ignored
|
||||||
|
for (auto const& str : error_ignore) {
|
||||||
|
if (log.msg.find(str) != std::string::npos) return;
|
||||||
|
}
|
||||||
notify("error");
|
notify("error");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -302,6 +308,7 @@ int main(int argc, char **argv)
|
|||||||
nh_priv.param("rainbow_period", rainbow_period, 5.0);
|
nh_priv.param("rainbow_period", rainbow_period, 5.0);
|
||||||
|
|
||||||
nh_priv.param("notify/low_battery/threshold", low_battery_threshold, 3.7);
|
nh_priv.param("notify/low_battery/threshold", low_battery_threshold, 3.7);
|
||||||
|
nh_priv.param("notify/error/ignore", error_ignore, {});
|
||||||
|
|
||||||
ros::service::waitForService("set_leds"); // cannot work without set_leds service
|
ros::service::waitForService("set_leds"); // cannot work without set_leds service
|
||||||
set_leds_srv = nh.serviceClient<led_msgs::SetLEDs>("set_leds", true);
|
set_leds_srv = nh.serviceClient<led_msgs::SetLEDs>("set_leds", true);
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ private:
|
|||||||
std::unique_ptr<tf2_ros::Buffer> tf_buffer_;
|
std::unique_ptr<tf2_ros::Buffer> tf_buffer_;
|
||||||
std::unique_ptr<tf2_ros::TransformListener> tf_listener_;
|
std::unique_ptr<tf2_ros::TransformListener> tf_listener_;
|
||||||
bool calc_flow_gyro_;
|
bool calc_flow_gyro_;
|
||||||
|
float flow_gyro_default_;
|
||||||
|
|
||||||
void onInit()
|
void onInit()
|
||||||
{
|
{
|
||||||
@@ -69,20 +70,19 @@ private:
|
|||||||
roi_px_ = nh_priv.param("roi", 128);
|
roi_px_ = nh_priv.param("roi", 128);
|
||||||
roi_rad_ = nh_priv.param("roi_rad", 0.0);
|
roi_rad_ = nh_priv.param("roi_rad", 0.0);
|
||||||
calc_flow_gyro_ = nh_priv.param("calc_flow_gyro", false);
|
calc_flow_gyro_ = nh_priv.param("calc_flow_gyro", false);
|
||||||
|
flow_gyro_default_ = nh_priv.param("flow_gyro_default", NAN);
|
||||||
|
|
||||||
img_sub_ = it.subscribeCamera("image_raw", 1, &OpticalFlow::flow, this);
|
|
||||||
img_pub_ = it_priv.advertise("debug", 1);
|
img_pub_ = it_priv.advertise("debug", 1);
|
||||||
flow_pub_ = nh.advertise<mavros_msgs::OpticalFlowRad>("mavros/px4flow/raw/send", 1);
|
flow_pub_ = nh.advertise<mavros_msgs::OpticalFlowRad>("mavros/px4flow/raw/send", 1);
|
||||||
velo_pub_ = nh_priv.advertise<geometry_msgs::TwistStamped>("angular_velocity", 1);
|
velo_pub_ = nh_priv.advertise<geometry_msgs::TwistStamped>("angular_velocity", 1);
|
||||||
shift_pub_ = nh_priv.advertise<geometry_msgs::Vector3Stamped>("shift", 1);
|
shift_pub_ = nh_priv.advertise<geometry_msgs::Vector3Stamped>("shift", 1);
|
||||||
|
|
||||||
flow_.integrated_xgyro = NAN; // no IMU available
|
|
||||||
flow_.integrated_ygyro = NAN;
|
|
||||||
flow_.integrated_zgyro = NAN;
|
|
||||||
flow_.time_delta_distance_us = 0;
|
flow_.time_delta_distance_us = 0;
|
||||||
flow_.distance = -1; // no distance sensor available
|
flow_.distance = -1; // no distance sensor available
|
||||||
flow_.temperature = 0;
|
flow_.temperature = 0;
|
||||||
|
|
||||||
|
img_sub_ = it.subscribeCamera("image_raw", 1, &OpticalFlow::flow, this);
|
||||||
|
|
||||||
NODELET_INFO("Optical Flow initialized");
|
NODELET_INFO("Optical Flow initialized");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,7 +152,7 @@ private:
|
|||||||
cv::Point2d shift = cv::phaseCorrelate(prev_, curr_, hann_, &response);
|
cv::Point2d shift = cv::phaseCorrelate(prev_, curr_, hann_, &response);
|
||||||
|
|
||||||
// Publish raw shift in pixels
|
// Publish raw shift in pixels
|
||||||
static geometry_msgs::Vector3Stamped shift_vec;
|
geometry_msgs::Vector3Stamped shift_vec;
|
||||||
shift_vec.header.stamp = msg->header.stamp;
|
shift_vec.header.stamp = msg->header.stamp;
|
||||||
shift_vec.header.frame_id = msg->header.frame_id;
|
shift_vec.header.frame_id = msg->header.frame_id;
|
||||||
shift_vec.vector.x = shift.x;
|
shift_vec.vector.x = shift.x;
|
||||||
@@ -178,8 +178,8 @@ private:
|
|||||||
double flow_x = atan2(points_undist[0].x, focal_length_x);
|
double flow_x = atan2(points_undist[0].x, focal_length_x);
|
||||||
double flow_y = atan2(points_undist[0].y, focal_length_y);
|
double flow_y = atan2(points_undist[0].y, focal_length_y);
|
||||||
|
|
||||||
// // Convert to FCU frame
|
// Convert to FCU frame
|
||||||
static geometry_msgs::Vector3Stamped flow_camera, flow_fcu;
|
geometry_msgs::Vector3Stamped flow_camera, flow_fcu;
|
||||||
flow_camera.header.frame_id = msg->header.frame_id;
|
flow_camera.header.frame_id = msg->header.frame_id;
|
||||||
flow_camera.header.stamp = msg->header.stamp;
|
flow_camera.header.stamp = msg->header.stamp;
|
||||||
flow_camera.vector.x = flow_y; // +y means counter-clockwise rotation around Y axis
|
flow_camera.vector.x = flow_y; // +y means counter-clockwise rotation around Y axis
|
||||||
@@ -195,18 +195,21 @@ private:
|
|||||||
ros::Duration integration_time = msg->header.stamp - prev_stamp_;
|
ros::Duration integration_time = msg->header.stamp - prev_stamp_;
|
||||||
uint32_t integration_time_us = integration_time.toSec() * 1.0e6;
|
uint32_t integration_time_us = integration_time.toSec() * 1.0e6;
|
||||||
|
|
||||||
|
// Calculate flow gyro
|
||||||
|
flow_.integrated_xgyro = flow_gyro_default_;
|
||||||
|
flow_.integrated_ygyro = flow_gyro_default_;
|
||||||
|
flow_.integrated_zgyro = flow_gyro_default_;
|
||||||
|
|
||||||
if (calc_flow_gyro_) {
|
if (calc_flow_gyro_) {
|
||||||
try {
|
try {
|
||||||
auto flow_gyro_camera = calcFlowGyro(msg->header.frame_id, prev_stamp_, msg->header.stamp);
|
auto flow_gyro_camera = calcFlowGyro(msg->header.frame_id, prev_stamp_, msg->header.stamp);
|
||||||
static geometry_msgs::Vector3Stamped flow_gyro_fcu;
|
geometry_msgs::Vector3Stamped flow_gyro_fcu;
|
||||||
tf_buffer_->transform(flow_gyro_camera, flow_gyro_fcu, fcu_frame_id_);
|
tf_buffer_->transform(flow_gyro_camera, flow_gyro_fcu, fcu_frame_id_);
|
||||||
flow_.integrated_xgyro = flow_gyro_fcu.vector.x;
|
flow_.integrated_xgyro = flow_gyro_fcu.vector.x;
|
||||||
flow_.integrated_ygyro = flow_gyro_fcu.vector.y;
|
flow_.integrated_ygyro = flow_gyro_fcu.vector.y;
|
||||||
flow_.integrated_zgyro = flow_gyro_fcu.vector.z;
|
flow_.integrated_zgyro = flow_gyro_fcu.vector.z;
|
||||||
} catch (const tf2::TransformException& e) {
|
} catch (const tf2::TransformException& e) {
|
||||||
// Invalidate previous frame
|
// Transform not available, keep NANs in flow gyro
|
||||||
prev_.release();
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -218,6 +221,10 @@ private:
|
|||||||
flow_.quality = (uint8_t)(response * 255);
|
flow_.quality = (uint8_t)(response * 255);
|
||||||
flow_pub_.publish(flow_);
|
flow_pub_.publish(flow_);
|
||||||
|
|
||||||
|
prev_ = curr_.clone();
|
||||||
|
prev_stamp_ = msg->header.stamp;
|
||||||
|
|
||||||
|
publish_debug:
|
||||||
// Publish debug image
|
// Publish debug image
|
||||||
if (img_pub_.getNumSubscribers() > 0) {
|
if (img_pub_.getNumSubscribers() > 0) {
|
||||||
// publish debug image
|
// publish debug image
|
||||||
@@ -231,15 +238,12 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Publish estimated angular velocity
|
// Publish estimated angular velocity
|
||||||
static geometry_msgs::TwistStamped velo;
|
geometry_msgs::TwistStamped velo;
|
||||||
velo.header.stamp = msg->header.stamp;
|
velo.header.stamp = msg->header.stamp;
|
||||||
velo.header.frame_id = fcu_frame_id_;
|
velo.header.frame_id = fcu_frame_id_;
|
||||||
velo.twist.angular.x = flow_.integrated_x / integration_time.toSec();
|
velo.twist.angular.x = flow_fcu.vector.x / integration_time.toSec();
|
||||||
velo.twist.angular.y = flow_.integrated_y / integration_time.toSec();
|
velo.twist.angular.y = flow_fcu.vector.y / integration_time.toSec();
|
||||||
velo_pub_.publish(velo);
|
velo_pub_.publish(velo);
|
||||||
|
|
||||||
prev_ = curr_.clone();
|
|
||||||
prev_stamp_ = msg->header.stamp;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ from visualization_msgs.msg import MarkerArray as VisualizationMarkerArray
|
|||||||
import tf.transformations as t
|
import tf.transformations as t
|
||||||
from aruco_pose.msg import MarkerArray
|
from aruco_pose.msg import MarkerArray
|
||||||
from mavros import mavlink
|
from mavros import mavlink
|
||||||
|
import locale
|
||||||
|
|
||||||
|
|
||||||
# TODO: check attitude is present
|
# TODO: check attitude is present
|
||||||
@@ -43,6 +44,10 @@ from mavros import mavlink
|
|||||||
|
|
||||||
rospy.init_node('selfcheck')
|
rospy.init_node('selfcheck')
|
||||||
|
|
||||||
|
os.environ['ROSCONSOLE_FORMAT']='[${severity}]: ${message}'
|
||||||
|
|
||||||
|
# use user's locale to convert numbers, etc
|
||||||
|
locale.setlocale(locale.LC_ALL, '')
|
||||||
|
|
||||||
tf_buffer = tf2_ros.Buffer()
|
tf_buffer = tf2_ros.Buffer()
|
||||||
tf_listener = tf2_ros.TransformListener(tf_buffer)
|
tf_listener = tf2_ros.TransformListener(tf_buffer)
|
||||||
@@ -138,7 +143,7 @@ def mavlink_exec(cmd, timeout=3.0):
|
|||||||
timeout=3,
|
timeout=3,
|
||||||
baudrate=0,
|
baudrate=0,
|
||||||
count=len(cmd),
|
count=len(cmd),
|
||||||
data=map(ord, cmd.ljust(70, '\0')))
|
data=[ord(c) for c in cmd.ljust(70, '\0')])
|
||||||
msg.pack(link)
|
msg.pack(link)
|
||||||
ros_msg = mavlink.convert_to_rosmsg(msg)
|
ros_msg = mavlink.convert_to_rosmsg(msg)
|
||||||
mavlink_pub.publish(ros_msg)
|
mavlink_pub.publish(ros_msg)
|
||||||
@@ -193,24 +198,27 @@ def check_fcu():
|
|||||||
failure('no connection to the FCU (check wiring)')
|
failure('no connection to the FCU (check wiring)')
|
||||||
return
|
return
|
||||||
|
|
||||||
|
clover_tag = re.compile(r'-cl[oe]ver\.\d+$')
|
||||||
|
clover_fw = False
|
||||||
|
|
||||||
# Make sure the console is available to us
|
# Make sure the console is available to us
|
||||||
mavlink_exec('\n')
|
mavlink_exec('\n')
|
||||||
version_str = mavlink_exec('ver all')
|
version_str = mavlink_exec('ver all')
|
||||||
if version_str == '':
|
if version_str == '':
|
||||||
info('no version data available from SITL')
|
info('no version data available from SITL')
|
||||||
|
|
||||||
r = re.compile(r'^FW (git tag|version): (v?\d\.\d\.\d.*)$')
|
for line in version_str.split('\n'):
|
||||||
is_clover_firmware = False
|
if line.startswith('FW version: '):
|
||||||
for ver_line in version_str.split('\n'):
|
info(line[len('FW version: '):])
|
||||||
match = r.search(ver_line)
|
elif line.startswith('FW git tag: '): # only Clover's firmware
|
||||||
if match is not None:
|
tag = line[len('FW git tag: '):]
|
||||||
field, version = match.groups()
|
clover_fw = clover_tag.search(tag)
|
||||||
info('firmware %s: %s' % (field, version))
|
info(tag)
|
||||||
if 'clover' in version or 'clever' in version:
|
elif line.startswith('HW arch: '):
|
||||||
is_clover_firmware = True
|
info(line[len('HW arch: '):])
|
||||||
|
|
||||||
if not is_clover_firmware:
|
if not clover_fw:
|
||||||
failure('not running Clover PX4 firmware, https://clover.coex.tech/firmware')
|
info('not Clover PX4 firmware, check https://clover.coex.tech/firmware')
|
||||||
|
|
||||||
est = get_param('SYS_MC_EST_GROUP')
|
est = get_param('SYS_MC_EST_GROUP')
|
||||||
if est == 1:
|
if est == 1:
|
||||||
@@ -483,6 +491,12 @@ def check_local_position():
|
|||||||
failure('roll is %.2f deg; place copter horizontally or redo level horizon calib',
|
failure('roll is %.2f deg; place copter horizontally or redo level horizon calib',
|
||||||
math.degrees(roll))
|
math.degrees(roll))
|
||||||
|
|
||||||
|
if not tf_buffer.can_transform('base_link', pose.header.frame_id, rospy.get_rostime(), rospy.Duration(0.5)):
|
||||||
|
failure('can\'t transform from %s to base_link (timeout 0.5 s): is TF enabled?', pose.header.frame_id)
|
||||||
|
|
||||||
|
if not tf_buffer.can_transform('body', pose.header.frame_id, rospy.get_rostime(), rospy.Duration(0.5)):
|
||||||
|
failure('can\'t transform from %s to body (timeout 0.5 s)', pose.header.frame_id)
|
||||||
|
|
||||||
except rospy.ROSException:
|
except rospy.ROSException:
|
||||||
failure('no local position')
|
failure('no local position')
|
||||||
|
|
||||||
@@ -609,25 +623,25 @@ def check_rangefinder():
|
|||||||
|
|
||||||
@check('Boot duration')
|
@check('Boot duration')
|
||||||
def check_boot_duration():
|
def check_boot_duration():
|
||||||
output = subprocess.check_output('systemd-analyze')
|
output = subprocess.check_output('systemd-analyze').decode()
|
||||||
r = re.compile(r'([\d\.]+)s\s*$', flags=re.MULTILINE)
|
r = re.compile(r'([\d\.]+)s\s*$', flags=re.MULTILINE)
|
||||||
duration = float(r.search(output).groups()[0])
|
duration = float(r.search(output).groups()[0])
|
||||||
if duration > 15:
|
if duration > 20:
|
||||||
failure('long Raspbian boot duration: %ss (systemd-analyze for analyzing)', duration)
|
failure('long Raspbian boot duration: %ss (systemd-analyze for analyzing)', duration)
|
||||||
|
|
||||||
|
|
||||||
@check('CPU usage')
|
@check('CPU usage')
|
||||||
def check_cpu_usage():
|
def check_cpu_usage():
|
||||||
WHITELIST = 'nodelet',
|
WHITELIST = 'nodelet', 'gzclient', 'gzserver'
|
||||||
CMD = "top -n 1 -b -i | tail -n +8 | awk '{ printf(\"%-8s\\t%-8s\\t%-8s\\n\", $1, $9, $12); }'"
|
CMD = "top -n 1 -b -i | tail -n +8 | awk '{ printf(\"%-8s\\t%-8s\\t%-8s\\n\", $1, $9, $12); }'"
|
||||||
output = subprocess.check_output(CMD, shell=True)
|
output = subprocess.check_output(CMD, shell=True).decode()
|
||||||
processes = output.split('\n')
|
processes = output.split('\n')
|
||||||
for process in processes:
|
for process in processes:
|
||||||
if not process:
|
if not process:
|
||||||
continue
|
continue
|
||||||
pid, cpu, cmd = process.split('\t')
|
pid, cpu, cmd = process.split('\t')
|
||||||
|
|
||||||
if cmd.strip() not in WHITELIST and float(cpu) > 30:
|
if cmd.strip() not in WHITELIST and locale.atof(cpu) > 30:
|
||||||
failure('high CPU usage (%s%%) detected: %s (PID %s)',
|
failure('high CPU usage (%s%%) detected: %s (PID %s)',
|
||||||
cpu.strip(), cmd.strip(), pid.strip())
|
cpu.strip(), cmd.strip(), pid.strip())
|
||||||
|
|
||||||
@@ -636,7 +650,7 @@ def check_cpu_usage():
|
|||||||
def check_clover_service():
|
def check_clover_service():
|
||||||
try:
|
try:
|
||||||
output = subprocess.check_output('systemctl show -p ActiveState --value clover.service'.split(),
|
output = subprocess.check_output('systemctl show -p ActiveState --value clover.service'.split(),
|
||||||
stderr=subprocess.STDOUT)
|
stderr=subprocess.STDOUT).decode()
|
||||||
except subprocess.CalledProcessError as e:
|
except subprocess.CalledProcessError as e:
|
||||||
failure('systemctl returned %s: %s', e.returncode, e.output)
|
failure('systemctl returned %s: %s', e.returncode, e.output)
|
||||||
return
|
return
|
||||||
@@ -646,13 +660,22 @@ def check_clover_service():
|
|||||||
elif 'failed' in output:
|
elif 'failed' in output:
|
||||||
failure('service failed to run, check your launch-files')
|
failure('service failed to run, check your launch-files')
|
||||||
|
|
||||||
r = re.compile(r'^(.*)\[(FATAL|ERROR)\] \[\d+.\d+\]: (.*?)(\x1b(.*))?$')
|
BLACKLIST = 'Unexpected command 520', 'Time jump detected', 'different index:'
|
||||||
|
|
||||||
|
r = re.compile(r'^(.*)\[(FATAL|ERROR| WARN)\] \[\d+.\d+\]: (.*?)(\x1b(.*))?$')
|
||||||
error_count = OrderedDict()
|
error_count = OrderedDict()
|
||||||
try:
|
try:
|
||||||
for line in open('/tmp/clover.err', 'r'):
|
for line in open('/tmp/clover.err', 'r'):
|
||||||
|
skip = False
|
||||||
|
for substr in BLACKLIST:
|
||||||
|
if substr in line:
|
||||||
|
skip = True
|
||||||
|
if skip:
|
||||||
|
continue
|
||||||
|
|
||||||
node_error = r.search(line)
|
node_error = r.search(line)
|
||||||
if node_error:
|
if node_error:
|
||||||
msg = node_error.groups()[1] + ': ' + node_error.groups()[2]
|
msg = node_error.groups()[1].strip() + ': ' + node_error.groups()[2]
|
||||||
if msg in error_count:
|
if msg in error_count:
|
||||||
error_count[msg] += 1
|
error_count[msg] += 1
|
||||||
else:
|
else:
|
||||||
@@ -723,6 +746,14 @@ def check_network():
|
|||||||
|
|
||||||
@check('RPi health')
|
@check('RPi health')
|
||||||
def check_rpi_health():
|
def check_rpi_health():
|
||||||
|
try:
|
||||||
|
import shutil
|
||||||
|
total, used, free = shutil.disk_usage('/')
|
||||||
|
if free < 1024 * 1024 * 1024:
|
||||||
|
failure('disk space is less than 1 GB; consider removing logs (~/.ros/log/)')
|
||||||
|
except Exception as e:
|
||||||
|
info('could not check the disk free space: %s', str(e))
|
||||||
|
|
||||||
# `vcgencmd get_throttled` output codes taken from
|
# `vcgencmd get_throttled` output codes taken from
|
||||||
# https://github.com/raspberrypi/documentation/blob/JamesH65-patch-vcgencmd-vcdbg-docs/raspbian/applications/vcgencmd.md#get_throttled
|
# https://github.com/raspberrypi/documentation/blob/JamesH65-patch-vcgencmd-vcdbg-docs/raspbian/applications/vcgencmd.md#get_throttled
|
||||||
# TODO: support more base platforms?
|
# TODO: support more base platforms?
|
||||||
@@ -751,9 +782,9 @@ def check_rpi_health():
|
|||||||
# <parameter>=<value>
|
# <parameter>=<value>
|
||||||
# In case of `get_throttled`, <value> is a hexadecimal number
|
# In case of `get_throttled`, <value> is a hexadecimal number
|
||||||
# with some of the FLAGs OR'ed together
|
# with some of the FLAGs OR'ed together
|
||||||
output = subprocess.check_output(['vcgencmd', 'get_throttled'])
|
output = subprocess.check_output(['vcgencmd', 'get_throttled']).decode()
|
||||||
except OSError:
|
except OSError:
|
||||||
failure('could not call vcgencmd binary; not a Raspberry Pi?')
|
info('could not call vcgencmd binary; not a Raspberry Pi?')
|
||||||
return
|
return
|
||||||
|
|
||||||
throttle_mask = int(output.split('=')[1], base=16)
|
throttle_mask = int(output.split('=')[1], base=16)
|
||||||
|
|||||||
@@ -36,6 +36,7 @@
|
|||||||
#include <mavros_msgs/Thrust.h>
|
#include <mavros_msgs/Thrust.h>
|
||||||
#include <mavros_msgs/State.h>
|
#include <mavros_msgs/State.h>
|
||||||
#include <mavros_msgs/StatusText.h>
|
#include <mavros_msgs/StatusText.h>
|
||||||
|
#include <mavros_msgs/ManualControl.h>
|
||||||
|
|
||||||
#include <clover/GetTelemetry.h>
|
#include <clover/GetTelemetry.h>
|
||||||
#include <clover/Navigate.h>
|
#include <clover/Navigate.h>
|
||||||
@@ -46,6 +47,7 @@
|
|||||||
#include <clover/SetRates.h>
|
#include <clover/SetRates.h>
|
||||||
|
|
||||||
using std::string;
|
using std::string;
|
||||||
|
using std::isnan;
|
||||||
using namespace geometry_msgs;
|
using namespace geometry_msgs;
|
||||||
using namespace sensor_msgs;
|
using namespace sensor_msgs;
|
||||||
using namespace clover;
|
using namespace clover;
|
||||||
@@ -59,6 +61,7 @@ std::shared_ptr<tf2_ros::TransformBroadcaster> transform_broadcaster;
|
|||||||
std::shared_ptr<tf2_ros::StaticTransformBroadcaster> static_transform_broadcaster;
|
std::shared_ptr<tf2_ros::StaticTransformBroadcaster> static_transform_broadcaster;
|
||||||
|
|
||||||
// Parameters
|
// Parameters
|
||||||
|
string mavros;
|
||||||
string local_frame;
|
string local_frame;
|
||||||
string fcu_frame;
|
string fcu_frame;
|
||||||
ros::Duration transform_timeout;
|
ros::Duration transform_timeout;
|
||||||
@@ -71,9 +74,10 @@ ros::Duration state_timeout;
|
|||||||
ros::Duration velocity_timeout;
|
ros::Duration velocity_timeout;
|
||||||
ros::Duration global_position_timeout;
|
ros::Duration global_position_timeout;
|
||||||
ros::Duration battery_timeout;
|
ros::Duration battery_timeout;
|
||||||
|
ros::Duration manual_control_timeout;
|
||||||
float default_speed;
|
float default_speed;
|
||||||
bool auto_release;
|
bool auto_release;
|
||||||
bool land_only_in_offboard, nav_from_sp;
|
bool land_only_in_offboard, nav_from_sp, check_kill_switch;
|
||||||
std::map<string, string> reference_frames;
|
std::map<string, string> reference_frames;
|
||||||
|
|
||||||
// Publishers
|
// Publishers
|
||||||
@@ -121,6 +125,7 @@ enum { YAW, YAW_RATE, TOWARDS } setpoint_yaw_type;
|
|||||||
// Last received telemetry messages
|
// Last received telemetry messages
|
||||||
mavros_msgs::State state;
|
mavros_msgs::State state;
|
||||||
mavros_msgs::StatusText statustext;
|
mavros_msgs::StatusText statustext;
|
||||||
|
mavros_msgs::ManualControl manual_control;
|
||||||
PoseStamped local_position;
|
PoseStamped local_position;
|
||||||
TwistStamped velocity;
|
TwistStamped velocity;
|
||||||
NavSatFix global_position;
|
NavSatFix global_position;
|
||||||
@@ -177,9 +182,10 @@ inline bool waitTransform(const string& target, const string& source,
|
|||||||
ros::spinOnce();
|
ros::spinOnce();
|
||||||
r.sleep();
|
r.sleep();
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define TIMEOUT(msg, timeout) (ros::Time::now() - msg.header.stamp > timeout)
|
#define TIMEOUT(msg, timeout) (msg.header.stamp.isZero() || (ros::Time::now() - msg.header.stamp > timeout))
|
||||||
|
|
||||||
bool getTelemetry(GetTelemetry::Request& req, GetTelemetry::Response& res)
|
bool getTelemetry(GetTelemetry::Request& req, GetTelemetry::Response& res)
|
||||||
{
|
{
|
||||||
@@ -437,6 +443,10 @@ void publish(const ros::Time stamp)
|
|||||||
|
|
||||||
// publish setpoint frame
|
// publish setpoint frame
|
||||||
if (!setpoint.child_frame_id.empty()) {
|
if (!setpoint.child_frame_id.empty()) {
|
||||||
|
if (setpoint.header.stamp == position_msg.header.stamp) {
|
||||||
|
return; // avoid TF_REPEATED_DATA warnings
|
||||||
|
}
|
||||||
|
|
||||||
setpoint.transform.translation.x = position_msg.pose.position.x;
|
setpoint.transform.translation.x = position_msg.pose.position.x;
|
||||||
setpoint.transform.translation.y = position_msg.pose.position.y;
|
setpoint.transform.translation.y = position_msg.pose.position.y;
|
||||||
setpoint.transform.translation.z = position_msg.pose.position.z;
|
setpoint.transform.translation.z = position_msg.pose.position.z;
|
||||||
@@ -485,6 +495,27 @@ void publishSetpoint(const ros::TimerEvent& event)
|
|||||||
publish(event.current_real);
|
publish(event.current_real);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void checkManualControl()
|
||||||
|
{
|
||||||
|
if (!manual_control_timeout.isZero() && TIMEOUT(manual_control, manual_control_timeout)) {
|
||||||
|
throw std::runtime_error("Manual control timeout, RC is switched off?");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (check_kill_switch) {
|
||||||
|
// switch values: https://github.com/PX4/PX4-Autopilot/blob/c302514a0809b1765fafd13c014d705446ae1113/msg/manual_control_setpoint.msg#L3
|
||||||
|
const uint8_t SWITCH_POS_NONE = 0; // switch is not mapped
|
||||||
|
const uint8_t SWITCH_POS_ON = 1; // switch activated
|
||||||
|
const uint8_t SWITCH_POS_MIDDLE = 2; // middle position
|
||||||
|
const uint8_t SWITCH_POS_OFF = 3; // switch not activated
|
||||||
|
|
||||||
|
const int KILL_SWITCH_BIT = 12; // https://github.com/PX4/Firmware/blob/c302514a0809b1765fafd13c014d705446ae1113/src/modules/mavlink/mavlink_messages.cpp#L3975
|
||||||
|
uint8_t kill_switch = (manual_control.buttons & (0b11 << KILL_SWITCH_BIT)) >> KILL_SWITCH_BIT;
|
||||||
|
|
||||||
|
if (kill_switch == SWITCH_POS_ON)
|
||||||
|
throw std::runtime_error("Kill switch is on");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
inline void checkState()
|
inline void checkState()
|
||||||
{
|
{
|
||||||
if (TIMEOUT(state, state_timeout))
|
if (TIMEOUT(state, state_timeout))
|
||||||
@@ -507,25 +538,88 @@ bool serve(enum setpoint_type_t sp_type, float x, float y, float z, float vx, fl
|
|||||||
if (busy)
|
if (busy)
|
||||||
throw std::runtime_error("Busy");
|
throw std::runtime_error("Busy");
|
||||||
|
|
||||||
ENSURE_FINITE(x);
|
|
||||||
ENSURE_FINITE(y);
|
|
||||||
ENSURE_FINITE(z);
|
|
||||||
ENSURE_FINITE(vx);
|
|
||||||
ENSURE_FINITE(vy);
|
|
||||||
ENSURE_FINITE(vz);
|
|
||||||
ENSURE_FINITE(pitch);
|
|
||||||
ENSURE_FINITE(roll);
|
|
||||||
ENSURE_FINITE(pitch_rate);
|
|
||||||
ENSURE_FINITE(roll_rate);
|
|
||||||
ENSURE_FINITE(lat);
|
|
||||||
ENSURE_FINITE(lon);
|
|
||||||
ENSURE_FINITE(thrust);
|
|
||||||
|
|
||||||
busy = true;
|
busy = true;
|
||||||
|
|
||||||
// Checks
|
// Checks
|
||||||
checkState();
|
checkState();
|
||||||
|
|
||||||
|
if (auto_arm) {
|
||||||
|
checkManualControl();
|
||||||
|
}
|
||||||
|
|
||||||
|
// default frame is local frame
|
||||||
|
if (frame_id.empty())
|
||||||
|
frame_id = local_frame;
|
||||||
|
|
||||||
|
// look up for reference frame
|
||||||
|
auto search = reference_frames.find(frame_id);
|
||||||
|
const string& reference_frame = search == reference_frames.end() ? frame_id : search->second;
|
||||||
|
|
||||||
|
// Serve "partial" commands
|
||||||
|
|
||||||
|
if (!auto_arm && std::isfinite(yaw) &&
|
||||||
|
isnan(x) && isnan(y) && isnan(z) && isnan(vx) && isnan(vy) && isnan(vz) &&
|
||||||
|
isnan(pitch) && isnan(roll) && isnan(thrust) &&
|
||||||
|
isnan(lat) && isnan(lon)) {
|
||||||
|
// change only the yaw
|
||||||
|
if (setpoint_type == POSITION || setpoint_type == NAVIGATE || setpoint_type == NAVIGATE_GLOBAL || setpoint_type == VELOCITY) {
|
||||||
|
if (!waitTransform(setpoint_position.header.frame_id, frame_id, stamp, transform_timeout))
|
||||||
|
throw std::runtime_error("Can't transform from " + frame_id + " to " + setpoint_position.header.frame_id);
|
||||||
|
|
||||||
|
message = "Changing yaw only";
|
||||||
|
|
||||||
|
QuaternionStamped q;
|
||||||
|
q.header.frame_id = frame_id;
|
||||||
|
q.header.stamp = stamp;
|
||||||
|
q.quaternion = tf::createQuaternionMsgFromYaw(yaw); // TODO: pitch=0, roll=0 is not totally correct
|
||||||
|
setpoint_position.pose.orientation = tf_buffer.transform(q, setpoint_position.header.frame_id).quaternion;
|
||||||
|
setpoint_yaw_type = YAW;
|
||||||
|
goto publish_setpoint;
|
||||||
|
} else {
|
||||||
|
throw std::runtime_error("Setting yaw is possible only when position or velocity setpoints active");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!auto_arm && std::isfinite(yaw_rate) &&
|
||||||
|
isnan(x) && isnan(y) && isnan(z) && isnan(vx) && isnan(vy) && isnan(vz) &&
|
||||||
|
isnan(pitch) && isnan(roll) && isnan(yaw) && isnan(thrust) &&
|
||||||
|
isnan(lat) && isnan(lon)) {
|
||||||
|
// change only the yaw rate
|
||||||
|
if (setpoint_type == POSITION || setpoint_type == NAVIGATE || setpoint_type == NAVIGATE_GLOBAL || setpoint_type == VELOCITY) {
|
||||||
|
message = "Changing yaw rate only";
|
||||||
|
|
||||||
|
setpoint_yaw_type = YAW_RATE;
|
||||||
|
setpoint_yaw_rate = yaw_rate;
|
||||||
|
goto publish_setpoint;
|
||||||
|
} else {
|
||||||
|
throw std::runtime_error("Setting yaw rate is possible only when position or velocity setpoints active");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serve normal commands
|
||||||
|
|
||||||
|
if (sp_type == NAVIGATE || sp_type == POSITION) {
|
||||||
|
ENSURE_FINITE(x);
|
||||||
|
ENSURE_FINITE(y);
|
||||||
|
ENSURE_FINITE(z);
|
||||||
|
} else if (sp_type == NAVIGATE_GLOBAL) {
|
||||||
|
ENSURE_FINITE(lat);
|
||||||
|
ENSURE_FINITE(lon);
|
||||||
|
ENSURE_FINITE(z);
|
||||||
|
} else if (sp_type == VELOCITY) {
|
||||||
|
ENSURE_FINITE(vx);
|
||||||
|
ENSURE_FINITE(vy);
|
||||||
|
ENSURE_FINITE(vz);
|
||||||
|
} else if (sp_type == ATTITUDE) {
|
||||||
|
ENSURE_FINITE(pitch);
|
||||||
|
ENSURE_FINITE(roll);
|
||||||
|
ENSURE_FINITE(thrust);
|
||||||
|
} else if (sp_type == RATES) {
|
||||||
|
ENSURE_FINITE(pitch_rate);
|
||||||
|
ENSURE_FINITE(roll_rate);
|
||||||
|
ENSURE_FINITE(thrust);
|
||||||
|
}
|
||||||
|
|
||||||
if (sp_type == NAVIGATE || sp_type == NAVIGATE_GLOBAL) {
|
if (sp_type == NAVIGATE || sp_type == NAVIGATE_GLOBAL) {
|
||||||
if (TIMEOUT(local_position, local_position_timeout))
|
if (TIMEOUT(local_position, local_position_timeout))
|
||||||
throw std::runtime_error("No local position, check settings");
|
throw std::runtime_error("No local position, check settings");
|
||||||
@@ -550,14 +644,6 @@ bool serve(enum setpoint_type_t sp_type, float x, float y, float z, float vx, fl
|
|||||||
throw std::runtime_error("No global position");
|
throw std::runtime_error("No global position");
|
||||||
}
|
}
|
||||||
|
|
||||||
// default frame is local frame
|
|
||||||
if (frame_id.empty())
|
|
||||||
frame_id = local_frame;
|
|
||||||
|
|
||||||
// look up for reference frame
|
|
||||||
auto search = reference_frames.find(frame_id);
|
|
||||||
const string& reference_frame = search == reference_frames.end() ? frame_id : search->second;
|
|
||||||
|
|
||||||
if (sp_type == NAVIGATE || sp_type == NAVIGATE_GLOBAL || sp_type == POSITION || sp_type == VELOCITY || sp_type == ATTITUDE) {
|
if (sp_type == NAVIGATE || sp_type == NAVIGATE_GLOBAL || sp_type == POSITION || sp_type == VELOCITY || sp_type == ATTITUDE) {
|
||||||
// make sure transform from frame_id to reference frame available
|
// make sure transform from frame_id to reference frame available
|
||||||
if (!waitTransform(reference_frame, frame_id, stamp, transform_timeout))
|
if (!waitTransform(reference_frame, frame_id, stamp, transform_timeout))
|
||||||
@@ -625,14 +711,14 @@ bool serve(enum setpoint_type_t sp_type, float x, float y, float z, float vx, fl
|
|||||||
} else {
|
} else {
|
||||||
setpoint_yaw_type = YAW;
|
setpoint_yaw_type = YAW;
|
||||||
setpoint_yaw_rate = 0;
|
setpoint_yaw_rate = 0;
|
||||||
ps.pose.orientation = tf::createQuaternionMsgFromRollPitchYaw(roll, pitch, yaw);
|
ps.pose.orientation = tf::createQuaternionMsgFromYaw(yaw);
|
||||||
}
|
}
|
||||||
|
|
||||||
tf_buffer.transform(ps, setpoint_position, reference_frame);
|
tf_buffer.transform(ps, setpoint_position, reference_frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sp_type == VELOCITY) {
|
if (sp_type == VELOCITY) {
|
||||||
static Vector3Stamped vel;
|
Vector3Stamped vel;
|
||||||
vel.header.frame_id = frame_id;
|
vel.header.frame_id = frame_id;
|
||||||
vel.header.stamp = stamp;
|
vel.header.stamp = stamp;
|
||||||
vel.vector.x = vx;
|
vel.vector.x = vx;
|
||||||
@@ -653,6 +739,7 @@ bool serve(enum setpoint_type_t sp_type, float x, float y, float z, float vx, fl
|
|||||||
|
|
||||||
wait_armed = auto_arm;
|
wait_armed = auto_arm;
|
||||||
|
|
||||||
|
publish_setpoint:
|
||||||
publish(stamp); // calculate initial transformed messages first
|
publish(stamp); // calculate initial transformed messages first
|
||||||
setpoint_timer.start();
|
setpoint_timer.start();
|
||||||
|
|
||||||
@@ -693,27 +780,27 @@ bool serve(enum setpoint_type_t sp_type, float x, float y, float z, float vx, fl
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool navigate(Navigate::Request& req, Navigate::Response& res) {
|
bool navigate(Navigate::Request& req, Navigate::Response& res) {
|
||||||
return serve(NAVIGATE, req.x, req.y, req.z, 0, 0, 0, 0, 0, req.yaw, 0, 0, req.yaw_rate, 0, 0, 0, req.speed, req.frame_id, req.auto_arm, res.success, res.message);
|
return serve(NAVIGATE, req.x, req.y, req.z, NAN, NAN, NAN, NAN, NAN, req.yaw, NAN, NAN, req.yaw_rate, NAN, NAN, NAN, req.speed, req.frame_id, req.auto_arm, res.success, res.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool navigateGlobal(NavigateGlobal::Request& req, NavigateGlobal::Response& res) {
|
bool navigateGlobal(NavigateGlobal::Request& req, NavigateGlobal::Response& res) {
|
||||||
return serve(NAVIGATE_GLOBAL, 0, 0, req.z, 0, 0, 0, 0, 0, req.yaw, 0, 0, req.yaw_rate, req.lat, req.lon, 0, req.speed, req.frame_id, req.auto_arm, res.success, res.message);
|
return serve(NAVIGATE_GLOBAL, NAN, NAN, req.z, NAN, NAN, NAN, NAN, NAN, req.yaw, NAN, NAN, req.yaw_rate, req.lat, req.lon, NAN, req.speed, req.frame_id, req.auto_arm, res.success, res.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool setPosition(SetPosition::Request& req, SetPosition::Response& res) {
|
bool setPosition(SetPosition::Request& req, SetPosition::Response& res) {
|
||||||
return serve(POSITION, req.x, req.y, req.z, 0, 0, 0, 0, 0, req.yaw, 0, 0, req.yaw_rate, 0, 0, 0, 0, req.frame_id, req.auto_arm, res.success, res.message);
|
return serve(POSITION, req.x, req.y, req.z, NAN, NAN, NAN, NAN, NAN, req.yaw, NAN, NAN, req.yaw_rate, NAN, NAN, NAN, NAN, req.frame_id, req.auto_arm, res.success, res.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool setVelocity(SetVelocity::Request& req, SetVelocity::Response& res) {
|
bool setVelocity(SetVelocity::Request& req, SetVelocity::Response& res) {
|
||||||
return serve(VELOCITY, 0, 0, 0, req.vx, req.vy, req.vz, 0, 0, req.yaw, 0, 0, req.yaw_rate, 0, 0, 0, 0, req.frame_id, req.auto_arm, res.success, res.message);
|
return serve(VELOCITY, NAN, NAN, NAN, req.vx, req.vy, req.vz, NAN, NAN, req.yaw, NAN, NAN, req.yaw_rate, NAN, NAN, NAN, NAN, req.frame_id, req.auto_arm, res.success, res.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool setAttitude(SetAttitude::Request& req, SetAttitude::Response& res) {
|
bool setAttitude(SetAttitude::Request& req, SetAttitude::Response& res) {
|
||||||
return serve(ATTITUDE, 0, 0, 0, 0, 0, 0, req.pitch, req.roll, req.yaw, 0, 0, 0, 0, 0, req.thrust, 0, req.frame_id, req.auto_arm, res.success, res.message);
|
return serve(ATTITUDE, NAN, NAN, NAN, NAN, NAN, NAN, req.pitch, req.roll, req.yaw, NAN, NAN, NAN, NAN, NAN, req.thrust, NAN, req.frame_id, req.auto_arm, res.success, res.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool setRates(SetRates::Request& req, SetRates::Response& res) {
|
bool setRates(SetRates::Request& req, SetRates::Response& res) {
|
||||||
return serve(RATES, 0, 0, 0, 0, 0, 0, 0, 0, 0, req.pitch_rate, req.roll_rate, req.yaw_rate, 0, 0, req.thrust, 0, "", req.auto_arm, res.success, res.message);
|
return serve(RATES, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, req.pitch_rate, req.roll_rate, req.yaw_rate, NAN, NAN, req.thrust, NAN, "", req.auto_arm, res.success, res.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool land(std_srvs::Trigger::Request& req, std_srvs::Trigger::Response& res)
|
bool land(std_srvs::Trigger::Request& req, std_srvs::Trigger::Response& res)
|
||||||
@@ -762,6 +849,7 @@ bool land(std_srvs::Trigger::Request& req, std_srvs::Trigger::Response& res)
|
|||||||
busy = false;
|
busy = false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
@@ -774,22 +862,32 @@ int main(int argc, char **argv)
|
|||||||
static_transform_broadcaster = std::make_shared<tf2_ros::StaticTransformBroadcaster>();
|
static_transform_broadcaster = std::make_shared<tf2_ros::StaticTransformBroadcaster>();
|
||||||
|
|
||||||
// Params
|
// Params
|
||||||
nh.param<string>("mavros/local_position/tf/frame_id", local_frame, "map");
|
nh_priv.param("mavros", mavros, string("mavros")); // for case of using multiple connections
|
||||||
nh.param<string>("mavros/local_position/tf/child_frame_id", fcu_frame, "base_link");
|
nh.param<string>(mavros + "/local_position/tf/frame_id", local_frame, "map");
|
||||||
|
nh.param<string>(mavros + "/local_position/tf/child_frame_id", fcu_frame, "base_link");
|
||||||
nh_priv.param("target_frame", target.child_frame_id, string("navigate_target"));
|
nh_priv.param("target_frame", target.child_frame_id, string("navigate_target"));
|
||||||
nh_priv.param("setpoint", setpoint.child_frame_id, string("setpoint"));
|
nh_priv.param("setpoint", setpoint.child_frame_id, string("setpoint"));
|
||||||
nh_priv.param("auto_release", auto_release, true);
|
nh_priv.param("auto_release", auto_release, true);
|
||||||
nh_priv.param("land_only_in_offboard", land_only_in_offboard, true);
|
nh_priv.param("land_only_in_offboard", land_only_in_offboard, true);
|
||||||
nh_priv.param("nav_from_sp", nav_from_sp, true);
|
nh_priv.param("nav_from_sp", nav_from_sp, true);
|
||||||
|
nh_priv.param("check_kill_switch", check_kill_switch, true);
|
||||||
nh_priv.param("default_speed", default_speed, 0.5f);
|
nh_priv.param("default_speed", default_speed, 0.5f);
|
||||||
nh_priv.param<string>("body_frame", body.child_frame_id, "body");
|
nh_priv.param<string>("body_frame", body.child_frame_id, "body");
|
||||||
nh_priv.getParam("reference_frames", reference_frames);
|
nh_priv.getParam("reference_frames", reference_frames);
|
||||||
|
|
||||||
|
// Default reference frames
|
||||||
|
std::map<string, string> default_reference_frames;
|
||||||
|
default_reference_frames[body.child_frame_id] = local_frame;
|
||||||
|
default_reference_frames[fcu_frame] = local_frame;
|
||||||
|
if (!target.child_frame_id.empty()) default_reference_frames[target.child_frame_id] = local_frame;
|
||||||
|
reference_frames.insert(default_reference_frames.begin(), default_reference_frames.end()); // merge defaults
|
||||||
|
|
||||||
state_timeout = ros::Duration(nh_priv.param("state_timeout", 3.0));
|
state_timeout = ros::Duration(nh_priv.param("state_timeout", 3.0));
|
||||||
local_position_timeout = ros::Duration(nh_priv.param("local_position_timeout", 2.0));
|
local_position_timeout = ros::Duration(nh_priv.param("local_position_timeout", 2.0));
|
||||||
velocity_timeout = ros::Duration(nh_priv.param("velocity_timeout", 2.0));
|
velocity_timeout = ros::Duration(nh_priv.param("velocity_timeout", 2.0));
|
||||||
global_position_timeout = ros::Duration(nh_priv.param("global_position_timeout", 10.0));
|
global_position_timeout = ros::Duration(nh_priv.param("global_position_timeout", 10.0));
|
||||||
battery_timeout = ros::Duration(nh_priv.param("battery_timeout", 2.0));
|
battery_timeout = ros::Duration(nh_priv.param("battery_timeout", 2.0));
|
||||||
|
manual_control_timeout = ros::Duration(nh_priv.param("manual_control_timeout", 0.0));
|
||||||
|
|
||||||
transform_timeout = ros::Duration(nh_priv.param("transform_timeout", 0.5));
|
transform_timeout = ros::Duration(nh_priv.param("transform_timeout", 0.5));
|
||||||
telemetry_transform_timeout = ros::Duration(nh_priv.param("telemetry_transform_timeout", 0.5));
|
telemetry_transform_timeout = ros::Duration(nh_priv.param("telemetry_transform_timeout", 0.5));
|
||||||
@@ -798,24 +896,25 @@ int main(int argc, char **argv)
|
|||||||
arming_timeout = ros::Duration(nh_priv.param("arming_timeout", 4.0));
|
arming_timeout = ros::Duration(nh_priv.param("arming_timeout", 4.0));
|
||||||
|
|
||||||
// Service clients
|
// Service clients
|
||||||
arming = nh.serviceClient<mavros_msgs::CommandBool>("mavros/cmd/arming");
|
arming = nh.serviceClient<mavros_msgs::CommandBool>(mavros + "/cmd/arming");
|
||||||
set_mode = nh.serviceClient<mavros_msgs::SetMode>("mavros/set_mode");
|
set_mode = nh.serviceClient<mavros_msgs::SetMode>(mavros + "/set_mode");
|
||||||
|
|
||||||
// Telemetry subscribers
|
// Telemetry subscribers
|
||||||
auto state_sub = nh.subscribe("mavros/state", 1, &handleState);
|
auto state_sub = nh.subscribe(mavros + "/state", 1, &handleState);
|
||||||
auto velocity_sub = nh.subscribe("mavros/local_position/velocity_body", 1, &handleMessage<TwistStamped, velocity>);
|
auto velocity_sub = nh.subscribe(mavros + "/local_position/velocity_body", 1, &handleMessage<TwistStamped, velocity>);
|
||||||
auto global_position_sub = nh.subscribe("mavros/global_position/global", 1, &handleMessage<NavSatFix, global_position>);
|
auto global_position_sub = nh.subscribe(mavros + "/global_position/global", 1, &handleMessage<NavSatFix, global_position>);
|
||||||
auto battery_sub = nh.subscribe("mavros/battery", 1, &handleMessage<BatteryState, battery>);
|
auto battery_sub = nh.subscribe(mavros + "/battery", 1, &handleMessage<BatteryState, battery>);
|
||||||
auto statustext_sub = nh.subscribe("mavros/statustext/recv", 1, &handleMessage<mavros_msgs::StatusText, statustext>);
|
auto statustext_sub = nh.subscribe(mavros + "/statustext/recv", 1, &handleMessage<mavros_msgs::StatusText, statustext>);
|
||||||
auto local_position_sub = nh.subscribe("mavros/local_position/pose", 1, &handleLocalPosition);
|
auto manual_control_sub = nh.subscribe(mavros + "/manual_control/control", 1, &handleMessage<mavros_msgs::ManualControl, manual_control>);
|
||||||
|
auto local_position_sub = nh.subscribe(mavros + "/local_position/pose", 1, &handleLocalPosition);
|
||||||
|
|
||||||
// Setpoint publishers
|
// Setpoint publishers
|
||||||
position_pub = nh.advertise<PoseStamped>("mavros/setpoint_position/local", 1);
|
position_pub = nh.advertise<PoseStamped>(mavros + "/setpoint_position/local", 1);
|
||||||
position_raw_pub = nh.advertise<PositionTarget>("mavros/setpoint_raw/local", 1);
|
position_raw_pub = nh.advertise<PositionTarget>(mavros + "/setpoint_raw/local", 1);
|
||||||
attitude_pub = nh.advertise<PoseStamped>("mavros/setpoint_attitude/attitude", 1);
|
attitude_pub = nh.advertise<PoseStamped>(mavros + "/setpoint_attitude/attitude", 1);
|
||||||
attitude_raw_pub = nh.advertise<AttitudeTarget>("mavros/setpoint_raw/attitude", 1);
|
attitude_raw_pub = nh.advertise<AttitudeTarget>(mavros + "/setpoint_raw/attitude", 1);
|
||||||
rates_pub = nh.advertise<TwistStamped>("mavros/setpoint_attitude/cmd_vel", 1);
|
rates_pub = nh.advertise<TwistStamped>(mavros + "/setpoint_attitude/cmd_vel", 1);
|
||||||
thrust_pub = nh.advertise<Thrust>("mavros/setpoint_attitude/thrust", 1);
|
thrust_pub = nh.advertise<Thrust>(mavros + "/setpoint_attitude/thrust", 1);
|
||||||
|
|
||||||
// Service servers
|
// Service servers
|
||||||
auto gt_serv = nh.advertiseService("get_telemetry", &getTelemetry);
|
auto gt_serv = nh.advertiseService("get_telemetry", &getTelemetry);
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ void publishZero(const ros::TimerEvent& e)
|
|||||||
}
|
}
|
||||||
|
|
||||||
ROS_INFO_THROTTLE(10, "publish zero");
|
ROS_INFO_THROTTLE(10, "publish zero");
|
||||||
static geometry_msgs::PoseStamped zero;
|
geometry_msgs::PoseStamped zero;
|
||||||
zero.header.frame_id = local_frame_id;
|
zero.header.frame_id = local_frame_id;
|
||||||
zero.header.stamp = e.current_real;
|
zero.header.stamp = e.current_real;
|
||||||
zero.pose.orientation.w = 1;
|
zero.pose.orientation.w = 1;
|
||||||
@@ -141,11 +141,11 @@ int main(int argc, char **argv) {
|
|||||||
vpe_pub = nh_priv.advertise<PoseStamped>("vpe", 1);
|
vpe_pub = nh_priv.advertise<PoseStamped>("vpe", 1);
|
||||||
//vpe_cov_pub = nh_priv_.advertise<PoseStamped>("pose_cov_pub", 1);
|
//vpe_cov_pub = nh_priv_.advertise<PoseStamped>("pose_cov_pub", 1);
|
||||||
|
|
||||||
if (nh_priv.param("publish_zero", false)) {
|
if (nh_priv.param("force_init", false) || nh_priv.param("publish_zero", false)) { // publish_zero is old name
|
||||||
// publish zero to initialize the local position
|
// publish zero to initialize the local position
|
||||||
zero_timer = nh.createTimer(ros::Duration(0.1), &publishZero);
|
zero_timer = nh.createTimer(ros::Duration(0.1), &publishZero);
|
||||||
publish_zero_timout = ros::Duration(nh_priv.param("publish_zero_timout", 5.0));
|
publish_zero_timout = ros::Duration(nh_priv.param("force_init_timeout", 5.0));
|
||||||
publish_zero_duration = ros::Duration(nh_priv.param("publish_zero_duration", 5.0));
|
publish_zero_duration = ros::Duration(nh_priv.param("force_init_duration", 5.0));
|
||||||
local_position_sub = nh.subscribe("mavros/local_position/pose", 1, &localPositionCallback);
|
local_position_sub = nh.subscribe("mavros/local_position/pose", 1, &localPositionCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
9
clover/src/waitfile
Executable file
9
clover/src/waitfile
Executable file
@@ -0,0 +1,9 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# $ ./waitfile <file> <command> <args...>
|
||||||
|
# wait until <file> appears and then invoke <command> with <args>
|
||||||
|
|
||||||
|
echo "wait for file $1"
|
||||||
|
while [ ! -e "$1" ]; do sleep 1; done;
|
||||||
|
echo "file $1 appeared"
|
||||||
|
exec "${@:2}"
|
||||||
@@ -26,21 +26,36 @@ def test_simple_offboard_services_available():
|
|||||||
rospy.wait_for_service('land', timeout=5)
|
rospy.wait_for_service('land', timeout=5)
|
||||||
|
|
||||||
def test_web_video_server(node):
|
def test_web_video_server(node):
|
||||||
import urllib2
|
try:
|
||||||
urllib2.urlopen("http://localhost:8080").read()
|
# Python 2
|
||||||
|
import urllib2 as urllib
|
||||||
|
except ModuleNotFoundError:
|
||||||
|
# Python 3
|
||||||
|
import urllib.request as urllib
|
||||||
|
urllib.urlopen("http://localhost:8080").read()
|
||||||
|
|
||||||
def test_shell(node):
|
def test_blocks(node):
|
||||||
execute = rospy.ServiceProxy('exec', srv.Execute)
|
rospy.wait_for_service('clover_blocks/run', timeout=5)
|
||||||
execute.wait_for_service(5)
|
rospy.wait_for_service('clover_blocks/stop', timeout=5)
|
||||||
|
rospy.wait_for_service('clover_blocks/load', timeout=5)
|
||||||
|
rospy.wait_for_service('clover_blocks/store', timeout=5)
|
||||||
|
|
||||||
res = execute(cmd='echo foo')
|
from std_msgs.msg import String
|
||||||
assert res.code == 0
|
from clover_blocks.srv import Run
|
||||||
assert res.output == 'foo\n'
|
|
||||||
|
|
||||||
res = execute(cmd='foo')
|
def wait_print():
|
||||||
assert res.code == 32512
|
try:
|
||||||
assert res.output == ''
|
wait_print.result = rospy.wait_for_message('clover_blocks/print', String, timeout=5).data
|
||||||
|
except Exception as e:
|
||||||
|
wait_print.result = str(e)
|
||||||
|
|
||||||
res = execute(cmd='ls foo')
|
import threading
|
||||||
assert res.code == 512
|
t = threading.Thread(target=wait_print)
|
||||||
assert res.output == ''
|
t.start()
|
||||||
|
rospy.sleep(0.1)
|
||||||
|
|
||||||
|
run = rospy.ServiceProxy('clover_blocks/run', Run)
|
||||||
|
assert run(code='print("test")').success == True
|
||||||
|
|
||||||
|
t.join()
|
||||||
|
assert wait_print.result == 'test'
|
||||||
|
|||||||
@@ -23,10 +23,7 @@
|
|||||||
|
|
||||||
<node pkg="tf2_ros" type="static_transform_publisher" name="map_flipped_frame" args="0 0 0 3.1415926 3.1415926 0 map map_flipped" required="true"/>
|
<node pkg="tf2_ros" type="static_transform_publisher" name="map_flipped_frame" args="0 0 0 3.1415926 3.1415926 0 map map_flipped" required="true"/>
|
||||||
|
|
||||||
<node name="simple_offboard" pkg="clover" type="simple_offboard" required="true" output="screen">
|
<node name="simple_offboard" pkg="clover" type="simple_offboard" required="true" output="screen"/>
|
||||||
<param name="reference_frames/body" value="map"/>
|
|
||||||
<param name="reference_frames/base_link" value="map"/>
|
|
||||||
</node>
|
|
||||||
|
|
||||||
<node name="tf2_web_republisher" pkg="tf2_web_republisher" type="tf2_web_republisher" required="true"/>
|
<node name="tf2_web_republisher" pkg="tf2_web_republisher" type="tf2_web_republisher" required="true"/>
|
||||||
|
|
||||||
@@ -38,6 +35,8 @@
|
|||||||
<rosparam param="notify">startup: { r: 255, g: 255, b: 255 }</rosparam>
|
<rosparam param="notify">startup: { r: 255, g: 255, b: 255 }</rosparam>
|
||||||
</node>
|
</node>
|
||||||
|
|
||||||
|
<node name="clover_blocks" pkg="clover_blocks" type="clover_blocks" output="screen" required="true"/>
|
||||||
|
|
||||||
<param name="test_module" value="$(find clover)/test/basic.py"/>
|
<param name="test_module" value="$(find clover)/test/basic.py"/>
|
||||||
<test test-name="basic_test" pkg="ros_pytest" type="ros_pytest_runner"/>
|
<test test-name="basic_test" pkg="ros_pytest" type="ros_pytest_runner"/>
|
||||||
</launch>
|
</launch>
|
||||||
|
|||||||
@@ -12,4 +12,6 @@ SUBSYSTEM=="tty", ATTRS{idVendor}=="26ac", ATTRS{idProduct}=="0016", ATTRS{produ
|
|||||||
SUBSYSTEM=="tty", ATTRS{idVendor}=="26ac", ATTRS{idProduct}=="0013", ATTRS{product}=="PX4 FMU v4.x PRO", SYMLINK+="px4fmu"
|
SUBSYSTEM=="tty", ATTRS{idVendor}=="26ac", ATTRS{idProduct}=="0013", ATTRS{product}=="PX4 FMU v4.x PRO", SYMLINK+="px4fmu"
|
||||||
# Omnibus
|
# Omnibus
|
||||||
SUBSYSTEM=="tty", ATTRS{idVendor}=="26ac", ATTRS{idProduct}=="0001", ATTRS{product}=="PX4 OmnibusF4SD", SYMLINK+="px4fmu"
|
SUBSYSTEM=="tty", ATTRS{idVendor}=="26ac", ATTRS{idProduct}=="0001", ATTRS{product}=="PX4 OmnibusF4SD", SYMLINK+="px4fmu"
|
||||||
|
# CUAV X7 Pro
|
||||||
|
SUBSYSTEM=="tty", ATTRS{idVendor}=="3163", ATTRS{idProduct}=="004c", ATTRS{product}=="PX4 CUAV X7Pro", SYMLINK+="px4fmu"
|
||||||
|
|
||||||
1
clover/www/clover.log
Symbolic link
1
clover/www/clover.log
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
/var/log/clover.log
|
||||||
1
clover/www/clover_version
Symbolic link
1
clover/www/clover_version
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
/etc/clover_version
|
||||||
23
clover/www/console.html
Normal file
23
clover/www/console.html
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<h1>
|
||||||
|
/var/log/clover.log
|
||||||
|
<a style="font-size: 0.5em; vertical-align: super; font-weight: normal" href="clover.log" download>download</a>
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<pre></pre>
|
||||||
|
|
||||||
|
<script type="module">
|
||||||
|
var pre = document.querySelector('pre');
|
||||||
|
|
||||||
|
fetch('clover.log?' + Math.random()).then(function(response) { // random to forbid caching
|
||||||
|
if (response.status == 404) {
|
||||||
|
pre.innerHTML = '/var/log/clover.log does not exist';
|
||||||
|
return;
|
||||||
|
} else if (response.status !== 200) {
|
||||||
|
pre.innerHTML('Error ' + response.status);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
response.text().then(function(content) {
|
||||||
|
pre.innerHTML = content;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
@@ -1,24 +1,36 @@
|
|||||||
|
<title>Clover Drone Kit Tools</title>
|
||||||
|
|
||||||
<h1>Clover Drone Kit Tools</h1>
|
<h1>Clover Drone Kit Tools</h1>
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="docs">View documentation</a> (snapshot of <a href="https://clover.coex.tech">clover.coex.tech</a>)</li>
|
<li><a href="docs">View documentation</a> (snapshot of <a href="https://clover.coex.tech">clover.coex.tech</a>)</li>
|
||||||
|
<li><a href="topics.html">View topics</a></li>
|
||||||
<li><a href="" id="wvs">View image topics</a> (<code>web_video_server</code>)</li>
|
<li><a href="" id="wvs">View image topics</a> (<code>web_video_server</code>)</li>
|
||||||
<li><a href="" id="butterfly">Open web terminal</a> (<code>Butterfly</code>)</li>
|
<li><a href="" id="butterfly">Open web terminal</a> (<code>Butterfly</code>)</li>
|
||||||
<li><a href="viz.html">View 3D visualization</a> (<code>ros3djs</code>)</li>
|
<li>View <a href="viz.html">View 3D visualization</a>, <a href="aruco_map.html">3D visualization for markers map</a> (<code>ros3djs</code>)</li>
|
||||||
<li><a href="aruco_map.html">3D visualization for markers map</a> (<code>ros3djs</code>)</li>
|
<li><a href="../clover_blocks/">Blocks programming</a> (<code>Blockly</code>)</li>
|
||||||
|
<li><a href="console.html">Clover console</a> (<code>/var/log/clover.log</code>)</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div class="version"></div>
|
<div class="version"></div>
|
||||||
|
|
||||||
<script src="js/roslib.js"></script>
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
document.querySelector("#wvs").href = location.protocol + '//' + location.hostname + ':8080';
|
document.querySelector("#wvs").href = location.protocol + '//' + location.hostname + ':8080';
|
||||||
document.querySelector("#butterfly").href = location.protocol + '//' + location.hostname + ':57575';
|
document.querySelector("#butterfly").href = location.protocol + '//' + location.hostname + ':57575';
|
||||||
|
|
||||||
|
document.querySelector("#butterfly").addEventListener('click', function(e) {
|
||||||
|
if (location.hostname == 'localhost' || location.hostname == '127.0.0.1') {
|
||||||
|
if (!confirm('Please use regular Terminal app on a local machine.\nClick OK to proceed to Butterfly anyway.')) {
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Determine image version
|
// Determine image version
|
||||||
var ros = new ROSLIB.Ros({ url: 'ws://' + location.hostname + ':9090' });
|
fetch('clover_version').then(function(response) {
|
||||||
var exec = new ROSLIB.Service({ ros: ros, name : '/exec', serviceType : 'clover/Execute' });
|
if (response.status !== 200) return;
|
||||||
exec.callService(new ROSLIB.ServiceRequest({ cmd: 'cat /etc/clover_version' }), function(result) {
|
response.text().then(function(text) {
|
||||||
document.querySelector('.version').innerHTML = 'Version: ' + result.output;
|
document.querySelector('.version').innerHTML = 'Version: ' + text;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
236
clover/www/js/json-to-pretty-yaml.js
Normal file
236
clover/www/js/json-to-pretty-yaml.js
Normal file
@@ -0,0 +1,236 @@
|
|||||||
|
// Browserified https://www.npmjs.com/package/json-to-pretty-yaml module
|
||||||
|
|
||||||
|
(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
|
||||||
|
(function() {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var typeOf = require('remedial').typeOf;
|
||||||
|
var trimWhitespace = require('remove-trailing-spaces');
|
||||||
|
|
||||||
|
function stringify(data) {
|
||||||
|
var handlers, indentLevel = '';
|
||||||
|
|
||||||
|
handlers = {
|
||||||
|
"undefined": function() {
|
||||||
|
// objects will not have `undefined` converted to `null`
|
||||||
|
// as this may have unintended consequences
|
||||||
|
// For arrays, however, this behavior seems appropriate
|
||||||
|
return 'null';
|
||||||
|
},
|
||||||
|
"null": function() {
|
||||||
|
return 'null';
|
||||||
|
},
|
||||||
|
"number": function(x) {
|
||||||
|
return x;
|
||||||
|
},
|
||||||
|
"boolean": function(x) {
|
||||||
|
return x ? 'true' : 'false';
|
||||||
|
},
|
||||||
|
"string": function(x) {
|
||||||
|
// to avoid the string "true" being confused with the
|
||||||
|
// the literal `true`, we always wrap strings in quotes
|
||||||
|
return JSON.stringify(x);
|
||||||
|
},
|
||||||
|
"array": function(x) {
|
||||||
|
var output = '';
|
||||||
|
|
||||||
|
if (0 === x.length) {
|
||||||
|
output += '[]';
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
indentLevel = indentLevel.replace(/$/, ' ');
|
||||||
|
x.forEach(function(y, i) {
|
||||||
|
// TODO how should `undefined` be handled?
|
||||||
|
var handler = handlers[typeOf(y)];
|
||||||
|
|
||||||
|
if (!handler) {
|
||||||
|
throw new Error('what the crap: ' + typeOf(y));
|
||||||
|
}
|
||||||
|
|
||||||
|
output += '\n' + indentLevel + '- ' + handler(y, true);
|
||||||
|
|
||||||
|
});
|
||||||
|
indentLevel = indentLevel.replace(/ /, '');
|
||||||
|
|
||||||
|
return output;
|
||||||
|
},
|
||||||
|
"object": function(x, inArray, rootNode) {
|
||||||
|
var output = '';
|
||||||
|
|
||||||
|
if (0 === Object.keys(x).length) {
|
||||||
|
output += '{}';
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!rootNode) {
|
||||||
|
indentLevel = indentLevel.replace(/$/, ' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.keys(x).forEach(function(k, i) {
|
||||||
|
var val = x[k],
|
||||||
|
handler = handlers[typeOf(val)];
|
||||||
|
|
||||||
|
if ('undefined' === typeof val) {
|
||||||
|
// the user should do
|
||||||
|
// delete obj.key
|
||||||
|
// and not
|
||||||
|
// obj.key = undefined
|
||||||
|
// but we'll error on the side of caution
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!handler) {
|
||||||
|
throw new Error('what the crap: ' + typeOf(val));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(inArray && i === 0)) {
|
||||||
|
output += '\n' + indentLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
output += k + ': ' + handler(val);
|
||||||
|
});
|
||||||
|
indentLevel = indentLevel.replace(/ /, '');
|
||||||
|
|
||||||
|
return output;
|
||||||
|
},
|
||||||
|
"function": function() {
|
||||||
|
// TODO this should throw or otherwise be ignored
|
||||||
|
return '[object Function]';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return trimWhitespace(handlers[typeOf(data)](data, true, true) + '\n');
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
window.yamlStringify = stringify;
|
||||||
|
module.exports.stringify = stringify;
|
||||||
|
}());
|
||||||
|
|
||||||
|
},{"remedial":2,"remove-trailing-spaces":3}],2:[function(require,module,exports){
|
||||||
|
/*jslint onevar: true, undef: true, nomen: true, eqeqeq: true, plusplus: true, bitwise: true, regexp: true, newcap: true, immed: true */
|
||||||
|
(function () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var global = Function('return this')()
|
||||||
|
, classes = "Boolean Number String Function Array Date RegExp Object".split(" ")
|
||||||
|
, i
|
||||||
|
, name
|
||||||
|
, class2type = {}
|
||||||
|
;
|
||||||
|
|
||||||
|
for (i in classes) {
|
||||||
|
if (classes.hasOwnProperty(i)) {
|
||||||
|
name = classes[i];
|
||||||
|
class2type["[object " + name + "]"] = name.toLowerCase();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function typeOf(obj) {
|
||||||
|
return (null === obj || undefined === obj) ? String(obj) : class2type[Object.prototype.toString.call(obj)] || "object";
|
||||||
|
}
|
||||||
|
|
||||||
|
function isEmpty(o) {
|
||||||
|
var i, v;
|
||||||
|
if (typeOf(o) === 'object') {
|
||||||
|
for (i in o) { // fails jslint
|
||||||
|
v = o[i];
|
||||||
|
if (v !== undefined && typeOf(v) !== 'function') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!String.prototype.entityify) {
|
||||||
|
String.prototype.entityify = function () {
|
||||||
|
return this.replace(/&/g, "&").replace(/</g,
|
||||||
|
"<").replace(/>/g, ">");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!String.prototype.quote) {
|
||||||
|
String.prototype.quote = function () {
|
||||||
|
var c, i, l = this.length, o = '"';
|
||||||
|
for (i = 0; i < l; i += 1) {
|
||||||
|
c = this.charAt(i);
|
||||||
|
if (c >= ' ') {
|
||||||
|
if (c === '\\' || c === '"') {
|
||||||
|
o += '\\';
|
||||||
|
}
|
||||||
|
o += c;
|
||||||
|
} else {
|
||||||
|
switch (c) {
|
||||||
|
case '\b':
|
||||||
|
o += '\\b';
|
||||||
|
break;
|
||||||
|
case '\f':
|
||||||
|
o += '\\f';
|
||||||
|
break;
|
||||||
|
case '\n':
|
||||||
|
o += '\\n';
|
||||||
|
break;
|
||||||
|
case '\r':
|
||||||
|
o += '\\r';
|
||||||
|
break;
|
||||||
|
case '\t':
|
||||||
|
o += '\\t';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
c = c.charCodeAt();
|
||||||
|
o += '\\u00' + Math.floor(c / 16).toString(16) +
|
||||||
|
(c % 16).toString(16);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return o + '"';
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!String.prototype.supplant) {
|
||||||
|
String.prototype.supplant = function (o) {
|
||||||
|
return this.replace(/{([^{}]*)}/g,
|
||||||
|
function (a, b) {
|
||||||
|
var r = o[b];
|
||||||
|
return typeof r === 'string' || typeof r === 'number' ? r : a;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!String.prototype.trim) {
|
||||||
|
String.prototype.trim = function () {
|
||||||
|
return this.replace(/^\s*(\S*(?:\s+\S+)*)\s*$/, "$1");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// CommonJS / npm / Ender.JS
|
||||||
|
module.exports = {
|
||||||
|
typeOf: typeOf,
|
||||||
|
isEmpty: isEmpty
|
||||||
|
};
|
||||||
|
global.typeOf = global.typeOf || typeOf;
|
||||||
|
global.isEmpty = global.isEmpty || isEmpty;
|
||||||
|
}());
|
||||||
|
|
||||||
|
},{}],3:[function(require,module,exports){
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* removeTrailingSpaces
|
||||||
|
* Remove the trailing spaces from a string.
|
||||||
|
*
|
||||||
|
* @name removeTrailingSpaces
|
||||||
|
* @function
|
||||||
|
* @param {String} input The input string.
|
||||||
|
* @returns {String} The output string.
|
||||||
|
*/
|
||||||
|
|
||||||
|
module.exports = function removeTrailingSpaces(input) {
|
||||||
|
// TODO If possible, use a regex
|
||||||
|
return input.split("\n").map(function (x) {
|
||||||
|
return x.trimRight();
|
||||||
|
}).join("\n");
|
||||||
|
};
|
||||||
|
},{}]},{},[1]);
|
||||||
83
clover/www/js/topics.js
Normal file
83
clover/www/js/topics.js
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
const url = 'ws://' + location.hostname + ':9090';
|
||||||
|
const ros = new ROSLIB.Ros({ url: url });
|
||||||
|
const params = Object.fromEntries(new URLSearchParams(window.location.search).entries());
|
||||||
|
|
||||||
|
ros.on('connection', function () {
|
||||||
|
document.body.classList.add('connected');
|
||||||
|
document.body.classList.remove('closed');
|
||||||
|
init();
|
||||||
|
});
|
||||||
|
|
||||||
|
ros.on('close', function () {
|
||||||
|
document.body.classList.remove('connected');
|
||||||
|
document.body.classList.add('closed');
|
||||||
|
setTimeout(function() {
|
||||||
|
// reconnect
|
||||||
|
ros.connect(url);
|
||||||
|
}, 2000);
|
||||||
|
});
|
||||||
|
|
||||||
|
const title = document.querySelector('h1');
|
||||||
|
const topicsList = document.querySelector('#topics');
|
||||||
|
const topicMessage = document.querySelector('#topic-message');
|
||||||
|
|
||||||
|
function viewTopicsList() {
|
||||||
|
title.innerHTML = 'Topics:';
|
||||||
|
|
||||||
|
ros.getTopics(function(topics) {
|
||||||
|
topicsList.innerHTML = topics.topics.map(function(topic, i) {
|
||||||
|
const type = topics.types[i];
|
||||||
|
if (type == 'sensor_msgs/Image') {
|
||||||
|
let url = `${location.protocol}//${location.hostname}:8080/stream_viewer?topic=${topic}`;
|
||||||
|
return `<li><a href="${url}" class=topic title=${type}>${topic}</a> 🖼</li>`;
|
||||||
|
} else {
|
||||||
|
return `<li><a href="?topic=${topic}" class=topic title=${type}>${topic}</a></li>`;
|
||||||
|
}
|
||||||
|
}).join('');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let rosdistro;
|
||||||
|
|
||||||
|
function viewTopic(topic) {
|
||||||
|
let index = '<a href=topics.html>Topics</a>';
|
||||||
|
title.innerHTML = `${index}: ${topic}`;
|
||||||
|
topicMessage.style.display = 'block';
|
||||||
|
|
||||||
|
ros.getTopicType(topic, function(typeStr) {
|
||||||
|
const [pack, type] = typeStr.split('/');
|
||||||
|
let href = `https://docs.ros.org/en/${rosdistro}/api/${pack}/html/msg/${type}.html`;
|
||||||
|
title.innerHTML = `${index}: ${topic} <a id="topic-type" href=${href} target="_blank">${typeStr}</a>`;
|
||||||
|
});
|
||||||
|
|
||||||
|
new ROSLIB.Topic({ ros: ros, name: topic }).subscribe(function(msg) {
|
||||||
|
document.title = topic;
|
||||||
|
if (mouseDown) return;
|
||||||
|
|
||||||
|
if (msg.header.stamp) {
|
||||||
|
if (params.date || params.offset) {
|
||||||
|
let date = new Date(msg.header.stamp.secs * 1e3 + msg.header.stamp.nsecs * 1e-6);
|
||||||
|
if (params.date) msg.header.date = date.toISOString();
|
||||||
|
if (params.offset) msg.header.offset = (new Date() - date) * 1e-3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
topicMessage.innerHTML = yamlStringify(msg); // JSON.stringify(msg, null, 4);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let mouseDown;
|
||||||
|
|
||||||
|
topicMessage.addEventListener('mousedown', function() { mouseDown = true; });
|
||||||
|
topicMessage.addEventListener('mouseup', function() { mouseDown = false; });
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
if (!params.topic) {
|
||||||
|
viewTopicsList();
|
||||||
|
} else {
|
||||||
|
new ROSLIB.Param({ ros: ros, name: '/rosdistro'}).get(function(value) {
|
||||||
|
rosdistro = value.trim();
|
||||||
|
viewTopic(params.topic);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
28
clover/www/topics.html
Normal file
28
clover/www/topics.html
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<title>ROS topics</title>
|
||||||
|
<script src="js/roslib.js"></script>
|
||||||
|
<link rel="icon" href="data:,"> <!-- make chrome don't request icon -->
|
||||||
|
<script type="module" src="js/topics.js"></script>
|
||||||
|
<script src="js/json-to-pretty-yaml.js"></script>
|
||||||
|
<style>
|
||||||
|
#topics { line-height: 1.2em; }
|
||||||
|
#topic-view {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
#topic-message {
|
||||||
|
display: none;
|
||||||
|
white-space: pre;
|
||||||
|
font-family: monospace;
|
||||||
|
}
|
||||||
|
#topic-type { font-family: monospace; font-size: 0.5em; vertical-align: super; font-weight: normal; }
|
||||||
|
.topic { font-family: monospace; }
|
||||||
|
body.closed { background-color: rgb(207, 207, 207); }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1> </h1>
|
||||||
|
<ul id="topics"></ul>
|
||||||
|
<code id="topic-message">No messages received</code>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
8
clover_blocks/CHANGELOG.rst
Normal file
8
clover_blocks/CHANGELOG.rst
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
Changelog for package clover_blocks
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
0.21.1 (2020-11-17)
|
||||||
|
-------------------
|
||||||
|
* First release of clover_blocks package to ROS
|
||||||
|
* Contributors: Oleg Kalachev
|
||||||
94
clover_blocks/CMakeLists.txt
Normal file
94
clover_blocks/CMakeLists.txt
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
cmake_minimum_required(VERSION 2.8.3)
|
||||||
|
project(clover_blocks)
|
||||||
|
|
||||||
|
find_package(catkin REQUIRED COMPONENTS message_generation)
|
||||||
|
|
||||||
|
add_message_files(
|
||||||
|
FILES
|
||||||
|
Prompt.msg
|
||||||
|
)
|
||||||
|
|
||||||
|
add_service_files(
|
||||||
|
FILES
|
||||||
|
Run.srv
|
||||||
|
Load.srv
|
||||||
|
Store.srv
|
||||||
|
)
|
||||||
|
|
||||||
|
generate_messages(
|
||||||
|
DEPENDENCIES
|
||||||
|
# std_msgs # Or other packages containing msgs
|
||||||
|
)
|
||||||
|
|
||||||
|
catkin_package(
|
||||||
|
# INCLUDE_DIRS include
|
||||||
|
# LIBRARIES roslaunch_editor
|
||||||
|
CATKIN_DEPENDS message_runtime
|
||||||
|
# DEPENDS system_lib
|
||||||
|
)
|
||||||
|
|
||||||
|
#############
|
||||||
|
## Install ##
|
||||||
|
#############
|
||||||
|
|
||||||
|
# all install targets should use catkin DESTINATION variables
|
||||||
|
# See http://ros.org/doc/api/catkin/html/adv_user_guide/variables.html
|
||||||
|
|
||||||
|
## Mark executable scripts (Python etc.) for installation
|
||||||
|
## in contrast to setup.py, you can choose the destination
|
||||||
|
# install(PROGRAMS
|
||||||
|
# scripts/my_python_script
|
||||||
|
# DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
|
||||||
|
# )
|
||||||
|
|
||||||
|
## Mark executables for installation
|
||||||
|
## See http://docs.ros.org/melodic/api/catkin/html/howto/format1/building_executables.html
|
||||||
|
# install(TARGETS ${PROJECT_NAME}_node
|
||||||
|
# RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
|
||||||
|
# )
|
||||||
|
|
||||||
|
## Mark libraries for installation
|
||||||
|
## See http://docs.ros.org/melodic/api/catkin/html/howto/format1/building_libraries.html
|
||||||
|
# install(TARGETS ${PROJECT_NAME}
|
||||||
|
# ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
|
||||||
|
# LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
|
||||||
|
# RUNTIME DESTINATION ${CATKIN_GLOBAL_BIN_DESTINATION}
|
||||||
|
# )
|
||||||
|
|
||||||
|
## Mark cpp header files for installation
|
||||||
|
# install(DIRECTORY include/${PROJECT_NAME}/
|
||||||
|
# DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION}
|
||||||
|
# FILES_MATCHING PATTERN "*.h"
|
||||||
|
# PATTERN ".svn" EXCLUDE
|
||||||
|
# )
|
||||||
|
|
||||||
|
## Mark other files for installation (e.g. launch and bag files, etc.)
|
||||||
|
# install(FILES
|
||||||
|
# # myfile1
|
||||||
|
# # myfile2
|
||||||
|
# DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}
|
||||||
|
# )
|
||||||
|
|
||||||
|
catkin_install_python(PROGRAMS src/clover_blocks
|
||||||
|
DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
|
||||||
|
)
|
||||||
|
|
||||||
|
# TODO: store programs in home directory?
|
||||||
|
install(DIRECTORY programs
|
||||||
|
DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}
|
||||||
|
)
|
||||||
|
|
||||||
|
install(DIRECTORY www DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION})
|
||||||
|
|
||||||
|
#############
|
||||||
|
## Testing ##
|
||||||
|
#############
|
||||||
|
|
||||||
|
## Add gtest based cpp test target and link libraries
|
||||||
|
# catkin_add_gtest(${PROJECT_NAME}-test test/test_roslaunch_editor.cpp)
|
||||||
|
# if(TARGET ${PROJECT_NAME}-test)
|
||||||
|
# target_link_libraries(${PROJECT_NAME}-test ${PROJECT_NAME})
|
||||||
|
# endif()
|
||||||
|
|
||||||
|
## Add folders to be run by python nosetests
|
||||||
|
# catkin_add_nosetests(test)
|
||||||
55
clover_blocks/README.md
Normal file
55
clover_blocks/README.md
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
# clover_blocks
|
||||||
|
|
||||||
|
Blockly programming support for Clover.
|
||||||
|
|
||||||
|
<img src="screenshot.png" width=700>
|
||||||
|
|
||||||
|
See user documentation at the [main Clover documentation site](https://clover.coex.tech/en/blocks.html).
|
||||||
|
|
||||||
|
Internal package documentation is given below.
|
||||||
|
|
||||||
|
## Frontend
|
||||||
|
|
||||||
|
The frontend files are located in [`www`](./www/) subdirectory. The frontend application uses [`roslib.js`](http://wiki.ros.org/roslibjs) library for communicating with backend node and other ROS resources.
|
||||||
|
|
||||||
|
## `clover_blocks` node
|
||||||
|
|
||||||
|
`clover_blocks` is the blocks programming backend, implementing all the services and topics needed for running Blockly-generated Python script.
|
||||||
|
|
||||||
|
### Services
|
||||||
|
|
||||||
|
* `~run` ([*clover_blocks/Run*](srv/Run.srv)) – run Blockly-generated program (in Python).
|
||||||
|
* `~stop` ([*std_srvs/Trigger*](http://docs.ros.org/melodic/api/std_srvs/html/srv/Trigger.html)) – terminate the running program.
|
||||||
|
* `~store` ([*clover_blocks/load*](srv/Store.srv)) – store a user program (to `<package_path>/programs` by default).
|
||||||
|
* `~load` ([*clover_blocks/load*](srv/Load.srv)) – load all the stored programs.
|
||||||
|
|
||||||
|
### Parameters
|
||||||
|
|
||||||
|
* `~programs_dir` (*string*) – directory for user programs.
|
||||||
|
|
||||||
|
Parameters read by frontend:
|
||||||
|
|
||||||
|
* `~navigate_tolerance` (*float*) – distance tolerance in meters, used for navigate-like blocks (default: 0.2).
|
||||||
|
* `~navigate_global_tolerance` (*float*) – distance tolerance for global coordinates navigation (default: 1).
|
||||||
|
* `~yaw_tolerance` (*float*) – yaw angle tolerance in degrees, used in set_yaw block (default: 1).
|
||||||
|
* `~sleep_time` (*float*) – duration of sleep in loop cycles, used for navigate-like blocks (default: 0.2).
|
||||||
|
* `~confirm_run` (*bool*) – enable confirmation to run the program (default: true).
|
||||||
|
|
||||||
|
These parameters also can be set as URL GET-parameters, for example:
|
||||||
|
|
||||||
|
```
|
||||||
|
http://<hostname>/clover_blocks/?navigate_tolerance=0.5&sleep_time=0.1
|
||||||
|
```
|
||||||
|
|
||||||
|
### Topics
|
||||||
|
|
||||||
|
#### Published
|
||||||
|
|
||||||
|
* `~running` ([*std_msgs/Bool*](http://docs.ros.org/melodic/api/std_msgs/html/msg/Bool.html)) – indicates if the program is currently running.
|
||||||
|
* `~block` ([*std_msgs/String*](http://docs.ros.org/melodic/api/std_msgs/html/msg/String.html)) – current executing block (maximum topic rate is limited).
|
||||||
|
* `~error` ([*std_msgs/String*](http://docs.ros.org/melodic/api/std_msgs/html/msg/String.html)) – user program errors and exceptions.
|
||||||
|
* `~prompt` ([*clover_blocks/Prompt*](msg/Prompt.msg)) – user input request (includes random request ID string).
|
||||||
|
|
||||||
|
This topic is published from the frontend side:
|
||||||
|
|
||||||
|
* `~prompt/<request_id>` ([*std_msgs/String*](http://docs.ros.org/melodic/api/std_msgs/html/msg/String.html)) – user input response.
|
||||||
2
clover_blocks/msg/Prompt.msg
Normal file
2
clover_blocks/msg/Prompt.msg
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
string message # message for prompt
|
||||||
|
string id # user response should be published to ~input/<id> topic
|
||||||
48
clover_blocks/package.xml
Normal file
48
clover_blocks/package.xml
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<package format="2">
|
||||||
|
<name>clover_blocks</name>
|
||||||
|
<version>0.23.0</version>
|
||||||
|
<description>Blockly programming support for Clover</description>
|
||||||
|
<maintainer email="okalachev@gmail.com">Oleg Kalachev</maintainer>
|
||||||
|
<license>MIT</license>
|
||||||
|
|
||||||
|
<!-- Url tags are optional, but multiple are allowed, one per tag -->
|
||||||
|
<!-- Optional attribute type can be: website, bugtracker, or repository -->
|
||||||
|
<url type="repository">https://github.com/CopterExpress/clover</url>
|
||||||
|
|
||||||
|
<!-- Author tags are optional, multiple are allowed, one per tag -->
|
||||||
|
<!-- Authors do not have to be maintainers, but could be -->
|
||||||
|
<author email="okalachev@gmail.com">Oleg Kalachev</author>
|
||||||
|
|
||||||
|
<!-- The *depend tags are used to specify dependencies -->
|
||||||
|
<!-- Dependencies can be catkin packages or system dependencies -->
|
||||||
|
<!-- Examples: -->
|
||||||
|
<!-- Use depend as a shortcut for packages that are both build and exec dependencies -->
|
||||||
|
<!-- <depend>roscpp</depend> -->
|
||||||
|
<!-- Note that this is equivalent to the following: -->
|
||||||
|
<!-- <build_depend>roscpp</build_depend> -->
|
||||||
|
<!-- <exec_depend>roscpp</exec_depend> -->
|
||||||
|
<!-- Use build_depend for packages you need at compile time: -->
|
||||||
|
<!-- <build_depend>message_generation</build_depend> -->
|
||||||
|
<!-- Use build_export_depend for packages you need in order to build against this package: -->
|
||||||
|
<!-- <build_export_depend>message_generation</build_export_depend> -->
|
||||||
|
<!-- Use buildtool_depend for build tool packages: -->
|
||||||
|
<!-- <buildtool_depend>catkin</buildtool_depend> -->
|
||||||
|
<!-- Use exec_depend for packages you need at runtime: -->
|
||||||
|
<!-- <exec_depend>message_runtime</exec_depend> -->
|
||||||
|
<!-- Use test_depend for packages you need only for testing: -->
|
||||||
|
<!-- <test_depend>gtest</test_depend> -->
|
||||||
|
<!-- Use doc_depend for packages you need only for building documentation: -->
|
||||||
|
<!-- <doc_depend>doxygen</doc_depend> -->
|
||||||
|
<buildtool_depend>catkin</buildtool_depend>
|
||||||
|
|
||||||
|
<depend>message_generation</depend>
|
||||||
|
<depend>message_runtime</depend>
|
||||||
|
<depend>rospy</depend>
|
||||||
|
|
||||||
|
<!-- The export tag contains other, unspecified, tags -->
|
||||||
|
<export>
|
||||||
|
<!-- Other tools can request additional information be placed here -->
|
||||||
|
|
||||||
|
</export>
|
||||||
|
</package>
|
||||||
73
clover_blocks/programs/examples/aruco-map.xml
Normal file
73
clover_blocks/programs/examples/aruco-map.xml
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
<xml xmlns="https://developers.google.com/blockly/xml">
|
||||||
|
<block type="take_off" id="w4,Hi[}0b.mX68K|hOJv" x="38" y="63">
|
||||||
|
<field name="WAIT">TRUE</field>
|
||||||
|
<value name="ALT">
|
||||||
|
<shadow type="math_number" id="q!Ln!d`jutn*3Gy56B0A">
|
||||||
|
<field name="NUM">1.5</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<next>
|
||||||
|
<block type="controls_repeat_ext" id="fD/HWb]kb!^pYK{UkM;0">
|
||||||
|
<value name="TIMES">
|
||||||
|
<shadow type="math_number" id="%FP0k|kaf~A`0N;M{Oiy">
|
||||||
|
<field name="NUM">10</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<statement name="DO">
|
||||||
|
<block type="navigate" id="qM5ROiq4r:$lq}8U_wol">
|
||||||
|
<field name="FRAME_ID">ARUCO_MAP</field>
|
||||||
|
<field name="WAIT">TRUE</field>
|
||||||
|
<value name="X">
|
||||||
|
<shadow type="math_number" id="=MBV;9zGH8S`Xh57/E;C">
|
||||||
|
<field name="NUM">0</field>
|
||||||
|
</shadow>
|
||||||
|
<block type="text_prompt_ext" id="rUDMP2~yFR!7E`Q]pR/i">
|
||||||
|
<mutation type="NUMBER"></mutation>
|
||||||
|
<field name="TYPE">NUMBER</field>
|
||||||
|
<value name="TEXT">
|
||||||
|
<shadow type="text" id="z+Q3vS?)kxHl?HTcuI?(">
|
||||||
|
<field name="TEXT">Enter X</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
</block>
|
||||||
|
</value>
|
||||||
|
<value name="Y">
|
||||||
|
<shadow type="math_number" id="%$`av8A.w}vNqG-]1GOn">
|
||||||
|
<field name="NUM">0</field>
|
||||||
|
</shadow>
|
||||||
|
<block type="text_prompt_ext" id=".AjhH^Rh*i}+14s)K-:g">
|
||||||
|
<mutation type="NUMBER"></mutation>
|
||||||
|
<field name="TYPE">NUMBER</field>
|
||||||
|
<value name="TEXT">
|
||||||
|
<shadow type="text" id="x#)(kg5PB;0uZA5;{(GT">
|
||||||
|
<field name="TEXT">Enter Y</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
</block>
|
||||||
|
</value>
|
||||||
|
<value name="Z">
|
||||||
|
<shadow type="math_number" id="T,]q+-@V;tCYLy$-(o3}">
|
||||||
|
<field name="NUM">1.5</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<value name="ID">
|
||||||
|
<shadow type="math_number" id="VRo?s#0d)Gl@9fsK@gR=">
|
||||||
|
<field name="NUM">0</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<value name="SPEED">
|
||||||
|
<shadow type="math_number" id="mmZc+d=W})w.AN*B_dSe">
|
||||||
|
<field name="NUM">0.5</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
</block>
|
||||||
|
</statement>
|
||||||
|
<next>
|
||||||
|
<block type="land" id="JB%yj/X~K=8A2=Q*7cRs">
|
||||||
|
<field name="WAIT">TRUE</field>
|
||||||
|
</block>
|
||||||
|
</next>
|
||||||
|
</block>
|
||||||
|
</next>
|
||||||
|
</block>
|
||||||
|
</xml>
|
||||||
64
clover_blocks/programs/examples/aruco-marker.xml
Normal file
64
clover_blocks/programs/examples/aruco-marker.xml
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
<xml xmlns="https://developers.google.com/blockly/xml">
|
||||||
|
<block type="take_off" id="R@=3~Fq;AxFaRO{ZDFUM" x="37" y="63">
|
||||||
|
<field name="WAIT">TRUE</field>
|
||||||
|
<value name="ALT">
|
||||||
|
<shadow type="math_number" id="cf8/,N48N;Fvn@EWB%,B">
|
||||||
|
<field name="NUM">1.5</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<next>
|
||||||
|
<block type="controls_repeat_ext" id="qc$F96[A^%lj,-l[fhX+">
|
||||||
|
<value name="TIMES">
|
||||||
|
<shadow type="math_number" id="v0QgNIpGzMIb`P@8D@}.">
|
||||||
|
<field name="NUM">3</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<statement name="DO">
|
||||||
|
<block type="navigate" id="]kCOMt@NSf$9:N1Fx~dE">
|
||||||
|
<field name="FRAME_ID">ARUCO</field>
|
||||||
|
<field name="WAIT">TRUE</field>
|
||||||
|
<value name="X">
|
||||||
|
<shadow type="math_number" id="}(7P5,{6%^::Gzb;kRaf">
|
||||||
|
<field name="NUM">0</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<value name="Y">
|
||||||
|
<shadow type="math_number" id="hz#sFvY/;IL5IRLQVa.K">
|
||||||
|
<field name="NUM">0</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<value name="Z">
|
||||||
|
<shadow type="math_number" id="n0ULZn64%k.:,l(,D?TZ">
|
||||||
|
<field name="NUM">1</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<value name="ID">
|
||||||
|
<shadow type="math_number" id="P4^twyJo6as#G1}^Dlq8">
|
||||||
|
<field name="NUM">0</field>
|
||||||
|
</shadow>
|
||||||
|
<block type="text_prompt_ext" id="ot;{twL$8`H*Wiafz3D,">
|
||||||
|
<mutation type="NUMBER"></mutation>
|
||||||
|
<field name="TYPE">NUMBER</field>
|
||||||
|
<value name="TEXT">
|
||||||
|
<shadow type="text" id="@P[TyPj[n1*;EcUC?lwF">
|
||||||
|
<field name="TEXT">Enter marker ID</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
</block>
|
||||||
|
</value>
|
||||||
|
<value name="SPEED">
|
||||||
|
<shadow type="math_number" id="WrsUXacZ2w)D]d?:@ec_">
|
||||||
|
<field name="NUM">0.5</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
</block>
|
||||||
|
</statement>
|
||||||
|
<next>
|
||||||
|
<block type="land" id="G5+Fs.6Y4(K9Dw`wjgua">
|
||||||
|
<field name="WAIT">TRUE</field>
|
||||||
|
</block>
|
||||||
|
</next>
|
||||||
|
</block>
|
||||||
|
</next>
|
||||||
|
</block>
|
||||||
|
</xml>
|
||||||
97
clover_blocks/programs/examples/flight-led.xml
Normal file
97
clover_blocks/programs/examples/flight-led.xml
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
<xml xmlns="https://developers.google.com/blockly/xml">
|
||||||
|
<block type="take_off" id="R@=3~Fq;AxFaRO{ZDFUM" x="162" y="63">
|
||||||
|
<field name="WAIT">TRUE</field>
|
||||||
|
<value name="ALT">
|
||||||
|
<shadow type="math_number" id="cf8/,N48N;Fvn@EWB%,B">
|
||||||
|
<field name="NUM">1</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<next>
|
||||||
|
<block type="set_effect" id="5K8qDD0/@Ab!k+ad6IPj">
|
||||||
|
<field name="EFFECT">FILL</field>
|
||||||
|
<value name="COLOR">
|
||||||
|
<shadow type="colour_picker" id="=*Y*L:1DI7y$.RR$6jNQ">
|
||||||
|
<field name="COLOUR">#ff0000</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<next>
|
||||||
|
<block type="navigate" id="kE7TL3+{PI.GiXby~SyO">
|
||||||
|
<field name="FRAME_ID">BODY</field>
|
||||||
|
<field name="WAIT">TRUE</field>
|
||||||
|
<value name="X">
|
||||||
|
<shadow type="math_number" id="W$eQ*Xv/e(*`e%8--W:P">
|
||||||
|
<field name="NUM">1</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<value name="Y">
|
||||||
|
<shadow type="math_number" id="i~r{%5mpU|}FL|9p!5yG">
|
||||||
|
<field name="NUM">0</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<value name="Z">
|
||||||
|
<shadow type="math_number" id="fZ{Cz~^LoWvM#,#;!co_">
|
||||||
|
<field name="NUM">0</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<value name="ID">
|
||||||
|
<shadow type="math_number" id="z$Hj|-?4e3zy@Jx$/THh">
|
||||||
|
<field name="NUM">0</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<value name="SPEED">
|
||||||
|
<shadow type="math_number" id="FjRM:aex=:/l_``^zcO]">
|
||||||
|
<field name="NUM">0.5</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<next>
|
||||||
|
<block type="set_effect" id="Q^DQoW/6G$7#YPlXc}%]">
|
||||||
|
<field name="EFFECT">FILL</field>
|
||||||
|
<value name="COLOR">
|
||||||
|
<shadow type="colour_picker" id="mjOY6]=uMY4$r8^n:n|}">
|
||||||
|
<field name="COLOUR">#3333ff</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<next>
|
||||||
|
<block type="navigate" id=";|j@sXR{9)9@ran7g[;~">
|
||||||
|
<field name="FRAME_ID">BODY</field>
|
||||||
|
<field name="WAIT">TRUE</field>
|
||||||
|
<value name="X">
|
||||||
|
<shadow type="math_number" id="TH(htYn}*/a{[::sFQ[x">
|
||||||
|
<field name="NUM">-1</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<value name="Y">
|
||||||
|
<shadow type="math_number" id="h@`=v?y+Tuh,I)wv?+`f">
|
||||||
|
<field name="NUM">0</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<value name="Z">
|
||||||
|
<shadow type="math_number" id="pzz:U%?m/~~B$-FF!fD?">
|
||||||
|
<field name="NUM">0</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<value name="ID">
|
||||||
|
<shadow type="math_number" id="Ze2Nun;[K{X.1kE0anMF">
|
||||||
|
<field name="NUM">0</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<value name="SPEED">
|
||||||
|
<shadow type="math_number" id="+[A5TSXF:hw4T1M]e~Wu">
|
||||||
|
<field name="NUM">0.5</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<next>
|
||||||
|
<block type="land" id="G5+Fs.6Y4(K9Dw`wjgua">
|
||||||
|
<field name="WAIT">TRUE</field>
|
||||||
|
</block>
|
||||||
|
</next>
|
||||||
|
</block>
|
||||||
|
</next>
|
||||||
|
</block>
|
||||||
|
</next>
|
||||||
|
</block>
|
||||||
|
</next>
|
||||||
|
</block>
|
||||||
|
</next>
|
||||||
|
</block>
|
||||||
|
</xml>
|
||||||
308
clover_blocks/programs/examples/flip.xml
Normal file
308
clover_blocks/programs/examples/flip.xml
Normal file
@@ -0,0 +1,308 @@
|
|||||||
|
<xml xmlns="https://developers.google.com/blockly/xml">
|
||||||
|
<variables>
|
||||||
|
<variable id="MEu_bi$Xk5e(#Q`Yu=k0">start_x</variable>
|
||||||
|
<variable id="GtS.)8^|(~RL*T4.~#VV">start_y</variable>
|
||||||
|
<variable id="jksdQ,]0F47Xan8{_(?+">start_z</variable>
|
||||||
|
</variables>
|
||||||
|
<block type="procedures_defnoreturn" id="Uo{/lI4(1i[O|U^jyh]y" x="188" y="62">
|
||||||
|
<field name="NAME">flip</field>
|
||||||
|
<comment pinned="false" h="80" w="160">Describe this function...</comment>
|
||||||
|
<statement name="STACK">
|
||||||
|
<block type="procedures_callnoreturn" id="G7`l2OBCg~ISh(XsrkRv">
|
||||||
|
<mutation name="memorize position"></mutation>
|
||||||
|
<next>
|
||||||
|
<block type="setpoint" id="8%(,J3=!CEUALDr5gFT+">
|
||||||
|
<field name="TYPE">RATES</field>
|
||||||
|
<field name="FRAME_ID">BODY</field>
|
||||||
|
<comment pinned="false" h="80" w="160">bump up</comment>
|
||||||
|
<value name="VX">
|
||||||
|
<shadow type="math_number" id="!@rHW+cG*U6.Py:.;qc)">
|
||||||
|
<field name="NUM">0</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<value name="VY">
|
||||||
|
<shadow type="math_number" id="n#I~l7$%1m^@Oz+w;k[H">
|
||||||
|
<field name="NUM">0</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<value name="VZ">
|
||||||
|
<shadow type="math_number" id="l0EphD#!^4jX#%UA-rxX">
|
||||||
|
<field name="NUM">0</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<value name="PITCH">
|
||||||
|
<shadow type="math_number" id="Ad.[@07QtZcb;(:^,Kjg">
|
||||||
|
<field name="NUM">0</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<value name="ROLL">
|
||||||
|
<shadow type="math_number" id="50,,4i.p6KR:e3/l|k2i">
|
||||||
|
<field name="NUM">0</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<value name="YAW">
|
||||||
|
<shadow type="math_number" id="(:O}={3*LZ#KDiC@;b[F">
|
||||||
|
<field name="NUM">0</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<value name="THRUST">
|
||||||
|
<shadow type="math_number" id="EC6yU;oHb#y-8?W`B/JC">
|
||||||
|
<field name="NUM">1</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<value name="ID">
|
||||||
|
<shadow type="math_number" id="k.X^bynEZH2dyG1=Y6lr">
|
||||||
|
<field name="NUM">0</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<next>
|
||||||
|
<block type="wait" id="#ga)cR4TsF[9t?;zc1H~">
|
||||||
|
<value name="TIME">
|
||||||
|
<shadow type="math_number" id="zg5fX7[;O,lQt*Txg|tn">
|
||||||
|
<field name="NUM">0.2</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<next>
|
||||||
|
<block type="setpoint" id="j,yf}/lrx*2J}X%^5LG{">
|
||||||
|
<field name="TYPE">RATES</field>
|
||||||
|
<field name="FRAME_ID">BODY</field>
|
||||||
|
<comment pinned="false" h="80" w="160">maximum pitch rate</comment>
|
||||||
|
<value name="VX">
|
||||||
|
<shadow type="math_number" id="^WF12EBPyIXhZdpsj;=D">
|
||||||
|
<field name="NUM">0</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<value name="VY">
|
||||||
|
<shadow type="math_number" id="C/uBsz]ObzwyHxGqzUP_">
|
||||||
|
<field name="NUM">0</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<value name="VZ">
|
||||||
|
<shadow type="math_number" id="4?)O3G/j`UuVTMImLq{T">
|
||||||
|
<field name="NUM">0</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<value name="PITCH">
|
||||||
|
<shadow type="math_number" id="sdCVFTmVkc0v2|*3Fy~h">
|
||||||
|
<field name="NUM">30</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<value name="ROLL">
|
||||||
|
<shadow type="math_number" id="V1o!AV[`!OnhrUP4~$=!">
|
||||||
|
<field name="NUM">0</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<value name="YAW">
|
||||||
|
<shadow type="math_number" id="0e;`*B;ghV*%=ETSvzSM">
|
||||||
|
<field name="NUM">0</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<value name="THRUST">
|
||||||
|
<shadow type="math_number" id="K@VAeOH2bEwc#@7_M!@l">
|
||||||
|
<field name="NUM">0.2</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<value name="ID">
|
||||||
|
<shadow type="math_number" id="c6ycgS!D}bIK8pl8oAv+">
|
||||||
|
<field name="NUM">0</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<next>
|
||||||
|
<block type="controls_whileUntil" id="H_6do.wf6H.E$?5;$uw8">
|
||||||
|
<field name="MODE">WHILE</field>
|
||||||
|
<value name="BOOL">
|
||||||
|
<block type="logic_boolean" id="pu%GH9}wZvUV{RnGk6Se">
|
||||||
|
<field name="BOOL">TRUE</field>
|
||||||
|
</block>
|
||||||
|
</value>
|
||||||
|
<statement name="DO">
|
||||||
|
<block type="controls_if" id="l0w?CSzU]S(K9eL-1(1r">
|
||||||
|
<value name="IF0">
|
||||||
|
<block type="logic_operation" id="gtXXJ7OFs=1BynL@|R{.">
|
||||||
|
<field name="OP">OR</field>
|
||||||
|
<value name="A">
|
||||||
|
<block type="logic_compare" id="2%9ns$Jk(8M[8$~9o(=*">
|
||||||
|
<field name="OP">GT</field>
|
||||||
|
<value name="A">
|
||||||
|
<block type="math_single" id="#gr7h89vpJyc8bnbWU9O">
|
||||||
|
<field name="OP">ABS</field>
|
||||||
|
<value name="NUM">
|
||||||
|
<shadow type="math_number" id="5paq?]87z}}+@~NALY}.">
|
||||||
|
<field name="NUM">9</field>
|
||||||
|
</shadow>
|
||||||
|
<block type="get_attitude" id="awk0-~@[jWFVQ+ZPi=Dc">
|
||||||
|
<field name="FIELD">PITCH</field>
|
||||||
|
</block>
|
||||||
|
</value>
|
||||||
|
</block>
|
||||||
|
</value>
|
||||||
|
<value name="B">
|
||||||
|
<block type="math_number" id=",4p]fd#kwxy`DtCMtraW">
|
||||||
|
<field name="NUM">90</field>
|
||||||
|
</block>
|
||||||
|
</value>
|
||||||
|
</block>
|
||||||
|
</value>
|
||||||
|
<value name="B">
|
||||||
|
<block type="logic_compare" id="?TPG3^?eg4l~CKM,3DfO">
|
||||||
|
<field name="OP">GT</field>
|
||||||
|
<value name="A">
|
||||||
|
<block type="math_single" id="yS*/-+E3TMpZ]%`1j*Up">
|
||||||
|
<field name="OP">ABS</field>
|
||||||
|
<value name="NUM">
|
||||||
|
<shadow type="math_number">
|
||||||
|
<field name="NUM">9</field>
|
||||||
|
</shadow>
|
||||||
|
<block type="get_attitude" id="~E3RS!on]G(?IZ?SVA#J">
|
||||||
|
<field name="FIELD">ROLL</field>
|
||||||
|
</block>
|
||||||
|
</value>
|
||||||
|
</block>
|
||||||
|
</value>
|
||||||
|
<value name="B">
|
||||||
|
<block type="math_number" id="#y??I}u#OmOTK:`!pkNS">
|
||||||
|
<field name="NUM">90</field>
|
||||||
|
</block>
|
||||||
|
</value>
|
||||||
|
</block>
|
||||||
|
</value>
|
||||||
|
</block>
|
||||||
|
</value>
|
||||||
|
<statement name="DO0">
|
||||||
|
<block type="controls_flow_statements" id="X|V=r3BDKw9fv)u5EY3.">
|
||||||
|
<field name="FLOW">BREAK</field>
|
||||||
|
</block>
|
||||||
|
</statement>
|
||||||
|
</block>
|
||||||
|
</statement>
|
||||||
|
<next>
|
||||||
|
<block type="procedures_callnoreturn" id="?;sq{#xJ+]bD`yJ#(]o1">
|
||||||
|
<mutation name="navigate to memorized position"></mutation>
|
||||||
|
</block>
|
||||||
|
</next>
|
||||||
|
</block>
|
||||||
|
</next>
|
||||||
|
</block>
|
||||||
|
</next>
|
||||||
|
</block>
|
||||||
|
</next>
|
||||||
|
</block>
|
||||||
|
</next>
|
||||||
|
</block>
|
||||||
|
</statement>
|
||||||
|
</block>
|
||||||
|
<block type="procedures_defnoreturn" id="!Tv!F*.A}=]dW=C6X@-`" x="538" y="62">
|
||||||
|
<field name="NAME">memorize position</field>
|
||||||
|
<comment pinned="false" h="80" w="160">Describe this function...</comment>
|
||||||
|
<statement name="STACK">
|
||||||
|
<block type="variables_set" id="s,o|q7_A/fSV+c0E)b^k">
|
||||||
|
<field name="VAR" id="MEu_bi$Xk5e(#Q`Yu=k0">start_x</field>
|
||||||
|
<value name="VALUE">
|
||||||
|
<block type="get_position" id="f#,.s%hV^19JJ;G/DO):">
|
||||||
|
<field name="FIELD">X</field>
|
||||||
|
<field name="FRAME_ID">MAP</field>
|
||||||
|
<value name="ID">
|
||||||
|
<shadow type="math_number" id="rJRRE#;#JFg}gY+34LVi">
|
||||||
|
<field name="NUM">0</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
</block>
|
||||||
|
</value>
|
||||||
|
<next>
|
||||||
|
<block type="variables_set" id="E+2Pfn3JOB{r=DQ3f1b`">
|
||||||
|
<field name="VAR" id="GtS.)8^|(~RL*T4.~#VV">start_y</field>
|
||||||
|
<value name="VALUE">
|
||||||
|
<block type="get_position" id="J#y+_prX(DI;j8gFO81H">
|
||||||
|
<field name="FIELD">Y</field>
|
||||||
|
<field name="FRAME_ID">MAP</field>
|
||||||
|
<value name="ID">
|
||||||
|
<shadow type="math_number" id="_bnV`SCa:|Vl#sRlYA@}">
|
||||||
|
<field name="NUM">0</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
</block>
|
||||||
|
</value>
|
||||||
|
<next>
|
||||||
|
<block type="variables_set" id="t*TI?p-h#qC_kC)jM8q0">
|
||||||
|
<field name="VAR" id="jksdQ,]0F47Xan8{_(?+">start_z</field>
|
||||||
|
<value name="VALUE">
|
||||||
|
<block type="get_position" id="Xh#8yLEMxT|bQ}jYvk6/">
|
||||||
|
<field name="FIELD">Z</field>
|
||||||
|
<field name="FRAME_ID">MAP</field>
|
||||||
|
<value name="ID">
|
||||||
|
<shadow type="math_number" id="z3FTv/jt^H7dVYx1`$$C">
|
||||||
|
<field name="NUM">0</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
</block>
|
||||||
|
</value>
|
||||||
|
</block>
|
||||||
|
</next>
|
||||||
|
</block>
|
||||||
|
</next>
|
||||||
|
</block>
|
||||||
|
</statement>
|
||||||
|
</block>
|
||||||
|
<block type="procedures_defnoreturn" id="$p3=p^fBz}k5Pg1]svL," x="563" y="213">
|
||||||
|
<field name="NAME">navigate to memorized position</field>
|
||||||
|
<comment pinned="false" h="80" w="160">Describe this function...</comment>
|
||||||
|
<statement name="STACK">
|
||||||
|
<block type="navigate" id="#7b*(J_t9t)V,Arb)3;R">
|
||||||
|
<field name="FRAME_ID">MAP</field>
|
||||||
|
<field name="WAIT">TRUE</field>
|
||||||
|
<value name="X">
|
||||||
|
<shadow type="math_number">
|
||||||
|
<field name="NUM">0</field>
|
||||||
|
</shadow>
|
||||||
|
<block type="variables_get" id="X#O!NcMac+a9WKL;l7?W">
|
||||||
|
<field name="VAR" id="MEu_bi$Xk5e(#Q`Yu=k0">start_x</field>
|
||||||
|
</block>
|
||||||
|
</value>
|
||||||
|
<value name="Y">
|
||||||
|
<shadow type="math_number">
|
||||||
|
<field name="NUM">0</field>
|
||||||
|
</shadow>
|
||||||
|
<block type="variables_get" id="C(=1.r0+01|e3+3qs%SD">
|
||||||
|
<field name="VAR" id="GtS.)8^|(~RL*T4.~#VV">start_y</field>
|
||||||
|
</block>
|
||||||
|
</value>
|
||||||
|
<value name="Z">
|
||||||
|
<shadow type="math_number">
|
||||||
|
<field name="NUM">0</field>
|
||||||
|
</shadow>
|
||||||
|
<block type="variables_get" id="?v;YVETEO%vUqgmiro^/">
|
||||||
|
<field name="VAR" id="jksdQ,]0F47Xan8{_(?+">start_z</field>
|
||||||
|
</block>
|
||||||
|
</value>
|
||||||
|
<value name="ID">
|
||||||
|
<shadow type="math_number" id="ALt.9P$^_]upK}Q9_M,5">
|
||||||
|
<field name="NUM">0</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<value name="SPEED">
|
||||||
|
<shadow type="math_number" id="ObO|OuF^d,1|Bx{Y`O-R">
|
||||||
|
<field name="NUM">5</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
</block>
|
||||||
|
</statement>
|
||||||
|
</block>
|
||||||
|
<block type="take_off" id="I1oP(b3wChmK?WgPtULz" x="563" y="612">
|
||||||
|
<field name="WAIT">TRUE</field>
|
||||||
|
<value name="ALT">
|
||||||
|
<shadow type="math_number" id="N1EX*JgURuG783yO+B^X">
|
||||||
|
<field name="NUM">2</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<next>
|
||||||
|
<block type="procedures_callnoreturn" id="(Dc3I{36Kt@1KHN7Ef.m">
|
||||||
|
<mutation name="flip"></mutation>
|
||||||
|
<next>
|
||||||
|
<block type="land" id=";c+lM(v6`13$VK`~jZ|5">
|
||||||
|
<field name="WAIT">TRUE</field>
|
||||||
|
</block>
|
||||||
|
</next>
|
||||||
|
</block>
|
||||||
|
</next>
|
||||||
|
</block>
|
||||||
|
</xml>
|
||||||
184
clover_blocks/programs/examples/functions.xml
Normal file
184
clover_blocks/programs/examples/functions.xml
Normal file
@@ -0,0 +1,184 @@
|
|||||||
|
<xml xmlns="https://developers.google.com/blockly/xml">
|
||||||
|
<variables>
|
||||||
|
<variable id="MEu_bi$Xk5e(#Q`Yu=k0">start_x</variable>
|
||||||
|
<variable id="GtS.)8^|(~RL*T4.~#VV">start_y</variable>
|
||||||
|
<variable id="jksdQ,]0F47Xan8{_(?+">start_z</variable>
|
||||||
|
</variables>
|
||||||
|
<block type="procedures_defnoreturn" id="X0i)DAnNpakX]gr;CC0}" x="588" y="88">
|
||||||
|
<field name="NAME">memorize position</field>
|
||||||
|
<comment pinned="false" h="80" w="160">Describe this function...</comment>
|
||||||
|
<statement name="STACK">
|
||||||
|
<block type="variables_set" id="Ma4s0pE-i`P:(-|?c88v">
|
||||||
|
<field name="VAR" id="MEu_bi$Xk5e(#Q`Yu=k0">start_x</field>
|
||||||
|
<value name="VALUE">
|
||||||
|
<block type="get_position" id="~uz[sPf[(s,15#x]3tAw">
|
||||||
|
<field name="FIELD">X</field>
|
||||||
|
<field name="FRAME_ID">MAP</field>
|
||||||
|
<value name="ID">
|
||||||
|
<shadow type="math_number" id="V#rB*5s3W^*q75}gScM(">
|
||||||
|
<field name="NUM">0</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
</block>
|
||||||
|
</value>
|
||||||
|
<next>
|
||||||
|
<block type="variables_set" id="I07,0;W#gn1CNY697O9n">
|
||||||
|
<field name="VAR" id="GtS.)8^|(~RL*T4.~#VV">start_y</field>
|
||||||
|
<value name="VALUE">
|
||||||
|
<block type="get_position" id="z(5A+nNX@tWO[J/X6Hb(">
|
||||||
|
<field name="FIELD">Y</field>
|
||||||
|
<field name="FRAME_ID">MAP</field>
|
||||||
|
<value name="ID">
|
||||||
|
<shadow type="math_number" id="6dM^zy88YqX`3ch{IH{1">
|
||||||
|
<field name="NUM">0</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
</block>
|
||||||
|
</value>
|
||||||
|
<next>
|
||||||
|
<block type="variables_set" id="AGgAbxz?+u_2^h;Xo4oc">
|
||||||
|
<field name="VAR" id="jksdQ,]0F47Xan8{_(?+">start_z</field>
|
||||||
|
<value name="VALUE">
|
||||||
|
<block type="get_position" id="k~cMq+jvE^,Ip*kKUmw]">
|
||||||
|
<field name="FIELD">Z</field>
|
||||||
|
<field name="FRAME_ID">MAP</field>
|
||||||
|
<value name="ID">
|
||||||
|
<shadow type="math_number" id="Q8RU-5,2UG*IfCeZg0cy">
|
||||||
|
<field name="NUM">0</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
</block>
|
||||||
|
</value>
|
||||||
|
</block>
|
||||||
|
</next>
|
||||||
|
</block>
|
||||||
|
</next>
|
||||||
|
</block>
|
||||||
|
</statement>
|
||||||
|
</block>
|
||||||
|
<block type="take_off" id=".0#}q|m$C-GD,.fM/9:/" x="137" y="162">
|
||||||
|
<field name="WAIT">TRUE</field>
|
||||||
|
<value name="ALT">
|
||||||
|
<shadow type="math_number" id="H6G+npH8DS90OnBKP;qy">
|
||||||
|
<field name="NUM">2</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<next>
|
||||||
|
<block type="procedures_callnoreturn" id="_r_hGwEy6uksz}DtrwxW">
|
||||||
|
<mutation name="memorize position"></mutation>
|
||||||
|
<next>
|
||||||
|
<block type="navigate" id="e{_?bCxQ}za#e~~A2GmJ">
|
||||||
|
<field name="FRAME_ID">ARUCO_MAP</field>
|
||||||
|
<field name="WAIT">TRUE</field>
|
||||||
|
<value name="X">
|
||||||
|
<shadow type="math_number" id="}P]lFX9(He:*{Rvp8[.q">
|
||||||
|
<field name="NUM">0</field>
|
||||||
|
</shadow>
|
||||||
|
<block type="text_prompt_ext" id="L:~.WP`isNJ/TBBP75.{">
|
||||||
|
<mutation type="NUMBER"></mutation>
|
||||||
|
<field name="TYPE">NUMBER</field>
|
||||||
|
<value name="TEXT">
|
||||||
|
<shadow type="text" id="v87(;,:4$T8wgbByRv`z">
|
||||||
|
<field name="TEXT">Input X</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
</block>
|
||||||
|
</value>
|
||||||
|
<value name="Y">
|
||||||
|
<shadow type="math_number" id="eMbKtnF{B^P*p7zRD8SX">
|
||||||
|
<field name="NUM">0</field>
|
||||||
|
</shadow>
|
||||||
|
<block type="text_prompt_ext" id="fp[sLcO^N:q}9k6zYwnN">
|
||||||
|
<mutation type="NUMBER"></mutation>
|
||||||
|
<field name="TYPE">NUMBER</field>
|
||||||
|
<value name="TEXT">
|
||||||
|
<shadow type="text" id="2Bqk0Rm@h*Vef,,:0c!+">
|
||||||
|
<field name="TEXT">Input Y</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
</block>
|
||||||
|
</value>
|
||||||
|
<value name="Z">
|
||||||
|
<shadow type="math_number" id="1sLNKuRa}07Ek(eHK;/S">
|
||||||
|
<field name="NUM">2</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<value name="ID">
|
||||||
|
<shadow type="math_number" id="guM,}nHUz^a?_H3(b{FW">
|
||||||
|
<field name="NUM">0</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<value name="SPEED">
|
||||||
|
<shadow type="math_number" id=".EckPx#fD.[:qSk}5/-J">
|
||||||
|
<field name="NUM">0.5</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<next>
|
||||||
|
<block type="text_print" id="d,aouAR0nE80mk6qTAti">
|
||||||
|
<value name="TEXT">
|
||||||
|
<shadow type="text" id="Yr%NvOOvCnrBP8}m]bn`">
|
||||||
|
<field name="TEXT">Point reached. Going back.</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<next>
|
||||||
|
<block type="procedures_callnoreturn" id="B0o:6T%+wpg{O%s27:bH">
|
||||||
|
<mutation name="navigate to memorized position"></mutation>
|
||||||
|
<next>
|
||||||
|
<block type="land" id="~%g;%HDPtF!lqHY}KO@/">
|
||||||
|
<field name="WAIT">TRUE</field>
|
||||||
|
</block>
|
||||||
|
</next>
|
||||||
|
</block>
|
||||||
|
</next>
|
||||||
|
</block>
|
||||||
|
</next>
|
||||||
|
</block>
|
||||||
|
</next>
|
||||||
|
</block>
|
||||||
|
</next>
|
||||||
|
</block>
|
||||||
|
<block type="procedures_defnoreturn" id="POzAJb%[yRgBrEn9K*x{" x="587" y="362">
|
||||||
|
<field name="NAME">navigate to memorized position</field>
|
||||||
|
<comment pinned="false" h="80" w="160">Describe this function...</comment>
|
||||||
|
<statement name="STACK">
|
||||||
|
<block type="navigate" id="VX|`@L*bU=aY`4!l=Pbw">
|
||||||
|
<field name="FRAME_ID">BODY</field>
|
||||||
|
<field name="WAIT">TRUE</field>
|
||||||
|
<value name="X">
|
||||||
|
<shadow type="math_number" id="c$==spjA=Vin//l1(1V@">
|
||||||
|
<field name="NUM">0</field>
|
||||||
|
</shadow>
|
||||||
|
<block type="variables_get" id="LxP$oVs*8[{CH4o^RY._">
|
||||||
|
<field name="VAR" id="MEu_bi$Xk5e(#Q`Yu=k0">start_x</field>
|
||||||
|
</block>
|
||||||
|
</value>
|
||||||
|
<value name="Y">
|
||||||
|
<shadow type="math_number" id="a)/FEG4j=.584f/:2$?H">
|
||||||
|
<field name="NUM">0</field>
|
||||||
|
</shadow>
|
||||||
|
<block type="variables_get" id="_-Ib6#usbp0NS}DZ7OGS">
|
||||||
|
<field name="VAR" id="GtS.)8^|(~RL*T4.~#VV">start_y</field>
|
||||||
|
</block>
|
||||||
|
</value>
|
||||||
|
<value name="Z">
|
||||||
|
<shadow type="math_number" id="q*MDfPe,V]rfhC1pnltq">
|
||||||
|
<field name="NUM">0</field>
|
||||||
|
</shadow>
|
||||||
|
<block type="variables_get" id="VY4xW2Y|=*AH3`s2C@)5">
|
||||||
|
<field name="VAR" id="jksdQ,]0F47Xan8{_(?+">start_z</field>
|
||||||
|
</block>
|
||||||
|
</value>
|
||||||
|
<value name="ID">
|
||||||
|
<shadow type="math_number" id="OiuYE,a3PqUW.vst+Qc$">
|
||||||
|
<field name="NUM">0</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<value name="SPEED">
|
||||||
|
<shadow type="math_number" id="X2Ou?:=kt:`aOq,#Q-)+">
|
||||||
|
<field name="NUM">0.5</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
</block>
|
||||||
|
</statement>
|
||||||
|
</block>
|
||||||
|
</xml>
|
||||||
91
clover_blocks/programs/examples/led-compass.xml
Normal file
91
clover_blocks/programs/examples/led-compass.xml
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
<xml xmlns="https://developers.google.com/blockly/xml">
|
||||||
|
<block type="controls_whileUntil" id="U1it{GcGuSS:=[xiwZr1" x="113" y="113">
|
||||||
|
<field name="MODE">WHILE</field>
|
||||||
|
<value name="BOOL">
|
||||||
|
<block type="logic_boolean" id="]7ZDRwde}/RqjQCX}aVW">
|
||||||
|
<field name="BOOL">TRUE</field>
|
||||||
|
</block>
|
||||||
|
</value>
|
||||||
|
<statement name="DO">
|
||||||
|
<block type="set_effect" id="WI0zqOz/z3].cR/6UWHn">
|
||||||
|
<field name="EFFECT">FILL</field>
|
||||||
|
<value name="COLOR">
|
||||||
|
<shadow type="colour_picker" id="B`6;Xv{s2TFp8Yd=ZpSD">
|
||||||
|
<field name="COLOUR">#000000</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<next>
|
||||||
|
<block type="set_led" id="^Vcs}ki?#ctf7rAchix$">
|
||||||
|
<value name="INDEX">
|
||||||
|
<shadow type="math_number" id="U;VWW$[*LOF7Gf,~?YR7">
|
||||||
|
<field name="NUM">0</field>
|
||||||
|
</shadow>
|
||||||
|
<block type="math_arithmetic" id="AI6PZBd`]_Z%_~4c-%dB">
|
||||||
|
<field name="OP">MULTIPLY</field>
|
||||||
|
<value name="A">
|
||||||
|
<shadow type="math_number" id="|p}X]`SedK3).F/;}NlB">
|
||||||
|
<field name="NUM">1</field>
|
||||||
|
</shadow>
|
||||||
|
<block type="math_arithmetic" id="-haE#:,cg{-J=NZERA;F">
|
||||||
|
<field name="OP">DIVIDE</field>
|
||||||
|
<value name="A">
|
||||||
|
<shadow type="math_number" id="::st;ot}[r]csqceURu*">
|
||||||
|
<field name="NUM">1</field>
|
||||||
|
</shadow>
|
||||||
|
<block type="math_arithmetic" id="a%+LN)F~=Igg+,p02[qo">
|
||||||
|
<field name="OP">ADD</field>
|
||||||
|
<value name="A">
|
||||||
|
<shadow type="math_number" id="*yIGN((y)/^z0:f5:VDw">
|
||||||
|
<field name="NUM">1</field>
|
||||||
|
</shadow>
|
||||||
|
<block type="get_yaw" id="mf%77q30bEqNfc/3`Mtb">
|
||||||
|
<field name="FRAME_ID">MAP</field>
|
||||||
|
<value name="ID">
|
||||||
|
<shadow type="math_number" id="xb32G.N#ip`|^Xv*MOmY">
|
||||||
|
<field name="NUM">0</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
</block>
|
||||||
|
</value>
|
||||||
|
<value name="B">
|
||||||
|
<shadow type="math_number" id="T/fTrm;j2Azgav;gI{ZW">
|
||||||
|
<field name="NUM">180</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
</block>
|
||||||
|
</value>
|
||||||
|
<value name="B">
|
||||||
|
<shadow type="math_number" id="Wo1/ZCeir,u6/.e1H+BB">
|
||||||
|
<field name="NUM">360</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
</block>
|
||||||
|
</value>
|
||||||
|
<value name="B">
|
||||||
|
<shadow type="math_number" id="jENTcXz0C5/=)Xpd!}LL">
|
||||||
|
<field name="NUM">1</field>
|
||||||
|
</shadow>
|
||||||
|
<block type="led_count" id="vM@X8s!xa]v}AaK6PWF5"></block>
|
||||||
|
</value>
|
||||||
|
</block>
|
||||||
|
</value>
|
||||||
|
<value name="COLOR">
|
||||||
|
<shadow type="colour_picker" id="+vw3bff.5c[=_w,Xm^C(">
|
||||||
|
<field name="COLOUR">#3366ff</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<next>
|
||||||
|
<block type="wait" id="DT%f$bn1*1El5zsgUW8Y">
|
||||||
|
<value name="TIME">
|
||||||
|
<shadow type="math_number" id="~Y0hNY[_^#v@aZkE-TH[">
|
||||||
|
<field name="NUM">0.1</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
</block>
|
||||||
|
</next>
|
||||||
|
</block>
|
||||||
|
</next>
|
||||||
|
</block>
|
||||||
|
</statement>
|
||||||
|
</block>
|
||||||
|
</xml>
|
||||||
30
clover_blocks/programs/examples/led.xml
Normal file
30
clover_blocks/programs/examples/led.xml
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
<xml xmlns="https://developers.google.com/blockly/xml">
|
||||||
|
<block type="controls_whileUntil" id="A:_Z_27?K?HlScug%u|5" x="113" y="113">
|
||||||
|
<field name="MODE">WHILE</field>
|
||||||
|
<value name="BOOL">
|
||||||
|
<block type="logic_boolean" id="/rlzePJ6XJcv22J(Q4bs">
|
||||||
|
<field name="BOOL">TRUE</field>
|
||||||
|
</block>
|
||||||
|
</value>
|
||||||
|
<statement name="DO">
|
||||||
|
<block type="set_effect" id="GT~AX(j]r)u^,f_n0agS">
|
||||||
|
<field name="EFFECT">WIPE</field>
|
||||||
|
<value name="COLOR">
|
||||||
|
<shadow type="colour_picker" id="G`I)ZAuIGcnWNyO@N(sH">
|
||||||
|
<field name="COLOUR">#ff0000</field>
|
||||||
|
</shadow>
|
||||||
|
<block type="colour_random" id="}(${|~%[}eJ.QUY?FWi_"></block>
|
||||||
|
</value>
|
||||||
|
<next>
|
||||||
|
<block type="wait" id="Ux(J;l+?5Gq2n^*!jXj^">
|
||||||
|
<value name="TIME">
|
||||||
|
<shadow type="math_number" id=")As9bASkedr9x@M)*)Pf">
|
||||||
|
<field name="NUM">1</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
</block>
|
||||||
|
</next>
|
||||||
|
</block>
|
||||||
|
</statement>
|
||||||
|
</block>
|
||||||
|
</xml>
|
||||||
29
clover_blocks/programs/examples/print-range.xml
Normal file
29
clover_blocks/programs/examples/print-range.xml
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<xml xmlns="https://developers.google.com/blockly/xml">
|
||||||
|
<block type="controls_whileUntil" id="^{D$=!y=ahJx0.^n?Ycr" x="88" y="137">
|
||||||
|
<field name="MODE">WHILE</field>
|
||||||
|
<value name="BOOL">
|
||||||
|
<block type="logic_boolean" id="a;~UtFPf9O3B)#QBJxL6">
|
||||||
|
<field name="BOOL">TRUE</field>
|
||||||
|
</block>
|
||||||
|
</value>
|
||||||
|
<statement name="DO">
|
||||||
|
<block type="text_print" id="doqF#i]/ulE{Z0l/{9`K">
|
||||||
|
<value name="TEXT">
|
||||||
|
<shadow type="text" id="Y$c,rIaw^bNs2wIw%[^(">
|
||||||
|
<field name="TEXT">abc</field>
|
||||||
|
</shadow>
|
||||||
|
<block type="rangefinder_distance" id="Pi+WekHZvZ0p}JY-^CLQ"></block>
|
||||||
|
</value>
|
||||||
|
<next>
|
||||||
|
<block type="wait" id="=$,8TaKwL75Pb,_tsCVU">
|
||||||
|
<value name="TIME">
|
||||||
|
<shadow type="math_number" id="/r+hzH#G0Ru#ES+}MxUl">
|
||||||
|
<field name="NUM">0.5</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
</block>
|
||||||
|
</next>
|
||||||
|
</block>
|
||||||
|
</statement>
|
||||||
|
</block>
|
||||||
|
</xml>
|
||||||
88
clover_blocks/programs/examples/rotation.xml
Normal file
88
clover_blocks/programs/examples/rotation.xml
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
<xml xmlns="https://developers.google.com/blockly/xml">
|
||||||
|
<block type="take_off" id="R@=3~Fq;AxFaRO{ZDFUM" x="162" y="112">
|
||||||
|
<field name="WAIT">TRUE</field>
|
||||||
|
<value name="ALT">
|
||||||
|
<shadow type="math_number" id="cf8/,N48N;Fvn@EWB%,B">
|
||||||
|
<field name="NUM">1</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<next>
|
||||||
|
<block type="navigate" id="[r%uJ2rw#3j.sLSBFf`N">
|
||||||
|
<field name="FRAME_ID">BODY</field>
|
||||||
|
<field name="WAIT">TRUE</field>
|
||||||
|
<value name="X">
|
||||||
|
<shadow type="math_number" id="5;@1vq|;OnlHl#FFDm/}">
|
||||||
|
<field name="NUM">1</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<value name="Y">
|
||||||
|
<shadow type="math_number" id="Jhe8@=gRxwj0oO*Q1F7V">
|
||||||
|
<field name="NUM">0</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<value name="Z">
|
||||||
|
<shadow type="math_number" id="wms+rWijPhnI/K++At;P">
|
||||||
|
<field name="NUM">0</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<value name="ID">
|
||||||
|
<shadow type="math_number" id=".^-h~@m]zg3il)O.9eFy">
|
||||||
|
<field name="NUM">0</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<value name="SPEED">
|
||||||
|
<shadow type="math_number" id="3L3FgAO(:aZp/:V%u=P7">
|
||||||
|
<field name="NUM">0.5</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<next>
|
||||||
|
<block type="set_yaw" id="nE,tYuHO--PmXN:*g@D%">
|
||||||
|
<field name="FRAME_ID">body</field>
|
||||||
|
<field name="WAIT">TRUE</field>
|
||||||
|
<value name="YAW">
|
||||||
|
<shadow type="angle" id="DoJ$Yj+fVT~cj=E%N:4+">
|
||||||
|
<field name="ANGLE">180</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<next>
|
||||||
|
<block type="navigate" id="H6W4@QtAT{_xo0.fO^z4">
|
||||||
|
<field name="FRAME_ID">BODY</field>
|
||||||
|
<field name="WAIT">TRUE</field>
|
||||||
|
<value name="X">
|
||||||
|
<shadow type="math_number" id="|.KN1m1O$/3=C,WG3sx}">
|
||||||
|
<field name="NUM">1</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<value name="Y">
|
||||||
|
<shadow type="math_number" id="oyc7mxrbA/@[_1wtPER^">
|
||||||
|
<field name="NUM">0</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<value name="Z">
|
||||||
|
<shadow type="math_number" id="dh}ac~kErkIIFeIZB,)d">
|
||||||
|
<field name="NUM">0</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<value name="ID">
|
||||||
|
<shadow type="math_number" id="q+{la-yiw61v9BSV-y};">
|
||||||
|
<field name="NUM">0</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<value name="SPEED">
|
||||||
|
<shadow type="math_number" id=";dk@RJGfZ)5w*:m4kp==">
|
||||||
|
<field name="NUM">0.5</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<next>
|
||||||
|
<block type="land" id="53Ssv6^lEHoD?tBO{5Sh">
|
||||||
|
<field name="WAIT">TRUE</field>
|
||||||
|
</block>
|
||||||
|
</next>
|
||||||
|
</block>
|
||||||
|
</next>
|
||||||
|
</block>
|
||||||
|
</next>
|
||||||
|
</block>
|
||||||
|
</next>
|
||||||
|
</block>
|
||||||
|
</xml>
|
||||||
86
clover_blocks/programs/examples/simple-flight.xml
Normal file
86
clover_blocks/programs/examples/simple-flight.xml
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
<xml xmlns="https://developers.google.com/blockly/xml">
|
||||||
|
<block type="take_off" id="R@=3~Fq;AxFaRO{ZDFUM" x="62" y="88">
|
||||||
|
<field name="WAIT">TRUE</field>
|
||||||
|
<value name="ALT">
|
||||||
|
<shadow type="math_number" id="cf8/,N48N;Fvn@EWB%,B">
|
||||||
|
<field name="NUM">1</field>
|
||||||
|
</shadow>
|
||||||
|
<block type="text_prompt_ext" id="#BO!OX5u-GHwsu!KzBT5">
|
||||||
|
<mutation type="NUMBER"></mutation>
|
||||||
|
<field name="TYPE">NUMBER</field>
|
||||||
|
<value name="TEXT">
|
||||||
|
<shadow type="text" id="unay$W?6.WiDB!|[3GN3">
|
||||||
|
<field name="TEXT">Enter flight altitude</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
</block>
|
||||||
|
</value>
|
||||||
|
<next>
|
||||||
|
<block type="navigate" id="BOHP@mRbJamp}]6/yc,n">
|
||||||
|
<field name="FRAME_ID">BODY</field>
|
||||||
|
<field name="WAIT">TRUE</field>
|
||||||
|
<value name="X">
|
||||||
|
<shadow type="math_number" id="hzjq`v(`k6vZN],.8%Hf">
|
||||||
|
<field name="NUM">1</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<value name="Y">
|
||||||
|
<shadow type="math_number" id="FIVk1Hm_+CU8XB~t@?S;">
|
||||||
|
<field name="NUM">0</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<value name="Z">
|
||||||
|
<shadow type="math_number" id="B{LoNO6}MgJ.JeN+8]tR">
|
||||||
|
<field name="NUM">0</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<value name="ID">
|
||||||
|
<shadow type="math_number" id="w!F}PhAG[Gn;5XsIg$XL">
|
||||||
|
<field name="NUM">0</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<value name="SPEED">
|
||||||
|
<shadow type="math_number" id="AjhS9Wf?M`%}A(H_bW9K">
|
||||||
|
<field name="NUM">0.5</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<next>
|
||||||
|
<block type="navigate" id="|Aa[hh]OAUS+b7I?;3VJ">
|
||||||
|
<field name="FRAME_ID">BODY</field>
|
||||||
|
<field name="WAIT">TRUE</field>
|
||||||
|
<value name="X">
|
||||||
|
<shadow type="math_number" id="=n#)78Bd*!iJyzF=dSA*">
|
||||||
|
<field name="NUM">-1</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<value name="Y">
|
||||||
|
<shadow type="math_number" id="YqC+GQF/[G=li}/s_o)q">
|
||||||
|
<field name="NUM">0</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<value name="Z">
|
||||||
|
<shadow type="math_number" id="~xsT!Ug+uMeU5F/Y4;k5">
|
||||||
|
<field name="NUM">0</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<value name="ID">
|
||||||
|
<shadow type="math_number" id="%WRn|02iTTwCG:zB}dSf">
|
||||||
|
<field name="NUM">0</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<value name="SPEED">
|
||||||
|
<shadow type="math_number" id="A#q.M,b1D*,13Ldp.F2w">
|
||||||
|
<field name="NUM">0.5</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<next>
|
||||||
|
<block type="land" id="_U*W_q1)l+l@#^TC%w)w">
|
||||||
|
<field name="WAIT">TRUE</field>
|
||||||
|
</block>
|
||||||
|
</next>
|
||||||
|
</block>
|
||||||
|
</next>
|
||||||
|
</block>
|
||||||
|
</next>
|
||||||
|
</block>
|
||||||
|
</xml>
|
||||||
60
clover_blocks/programs/examples/takeoff-land.xml
Normal file
60
clover_blocks/programs/examples/takeoff-land.xml
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
<xml xmlns="https://developers.google.com/blockly/xml">
|
||||||
|
<block type="text_print" id="2p:*N].+8c=|ZJXCWVn[" x="162" y="88">
|
||||||
|
<value name="TEXT">
|
||||||
|
<shadow type="text" id="9H+7z4A.pZ:Zz)7}H3Y7">
|
||||||
|
<field name="TEXT">Start mission</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<next>
|
||||||
|
<block type="take_off" id="R@=3~Fq;AxFaRO{ZDFUM">
|
||||||
|
<field name="WAIT">TRUE</field>
|
||||||
|
<value name="ALT">
|
||||||
|
<shadow type="math_number" id="cf8/,N48N;Fvn@EWB%,B">
|
||||||
|
<field name="NUM">1</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<next>
|
||||||
|
<block type="text_print" id="|Tdajnf]Rz@ajSW`0Skv">
|
||||||
|
<value name="TEXT">
|
||||||
|
<shadow type="text" id="Wqr:Hf~r2d.A%S7%%4.)">
|
||||||
|
<field name="TEXT">Take off complete, wait</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<next>
|
||||||
|
<block type="wait" id="-]MH9NMT#N-w!5yI1$F)">
|
||||||
|
<value name="TIME">
|
||||||
|
<shadow type="math_number" id="?k~ZUNcrx-iC;LFmarbm">
|
||||||
|
<field name="NUM">3</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<next>
|
||||||
|
<block type="text_print" id="SXJsfN{P13FL~n9;|%P:">
|
||||||
|
<value name="TEXT">
|
||||||
|
<shadow type="text" id="[Z5,f|El:VX5zcRaMtXa">
|
||||||
|
<field name="TEXT">Land</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<next>
|
||||||
|
<block type="land" id="G5+Fs.6Y4(K9Dw`wjgua">
|
||||||
|
<field name="WAIT">TRUE</field>
|
||||||
|
<next>
|
||||||
|
<block type="text_print" id="o?bvXARz^4_U0oXC-#.V">
|
||||||
|
<value name="TEXT">
|
||||||
|
<shadow type="text" id="`tadiS,[OZ`#f:=u.:9~">
|
||||||
|
<field name="TEXT">Mission finished</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
</block>
|
||||||
|
</next>
|
||||||
|
</block>
|
||||||
|
</next>
|
||||||
|
</block>
|
||||||
|
</next>
|
||||||
|
</block>
|
||||||
|
</next>
|
||||||
|
</block>
|
||||||
|
</next>
|
||||||
|
</block>
|
||||||
|
</next>
|
||||||
|
</block>
|
||||||
|
</xml>
|
||||||
137
clover_blocks/programs/examples/variables.xml
Normal file
137
clover_blocks/programs/examples/variables.xml
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
<xml xmlns="https://developers.google.com/blockly/xml">
|
||||||
|
<variables>
|
||||||
|
<variable id="]gc9ItU#$!=G*S._3$n~">start_x</variable>
|
||||||
|
<variable id="D9Oy(29I!!k9+(#zI,4}">start_y</variable>
|
||||||
|
</variables>
|
||||||
|
<block type="take_off" id="X]W2Bs=WFxFsco{Yr`bH" x="87" y="88">
|
||||||
|
<field name="WAIT">TRUE</field>
|
||||||
|
<value name="ALT">
|
||||||
|
<shadow type="math_number" id="{7ezr;C@IT-IJ3ylt?];">
|
||||||
|
<field name="NUM">1.5</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<next>
|
||||||
|
<block type="variables_set" id="IWR_j|7pUxrIGrxGLP#Z">
|
||||||
|
<field name="VAR" id="]gc9ItU#$!=G*S._3$n~">start_x</field>
|
||||||
|
<value name="VALUE">
|
||||||
|
<block type="get_position" id="ze{FI]Vv*E3kZ1Zw6Bmn">
|
||||||
|
<field name="FIELD">X</field>
|
||||||
|
<field name="FRAME_ID">ARUCO_MAP</field>
|
||||||
|
<value name="ID">
|
||||||
|
<shadow type="math_number" id="s~^V?k;dKa(($OG9mAK)">
|
||||||
|
<field name="NUM">0</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
</block>
|
||||||
|
</value>
|
||||||
|
<next>
|
||||||
|
<block type="variables_set" id="s3rE=-6+;f(Lua_uKGY[">
|
||||||
|
<field name="VAR" id="D9Oy(29I!!k9+(#zI,4}">start_y</field>
|
||||||
|
<value name="VALUE">
|
||||||
|
<block type="get_position" id="?t+jp;7Bk#wzoz=DY{sO">
|
||||||
|
<field name="FIELD">Y</field>
|
||||||
|
<field name="FRAME_ID">ARUCO_MAP</field>
|
||||||
|
<value name="ID">
|
||||||
|
<shadow type="math_number" id="KybjD~@_RFIx=8Dzbz4v">
|
||||||
|
<field name="NUM">0</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
</block>
|
||||||
|
</value>
|
||||||
|
<next>
|
||||||
|
<block type="text_print" id="|xj}5M(:.~`Vpq-GKi/0">
|
||||||
|
<value name="TEXT">
|
||||||
|
<shadow type="text" id="{o^2v+y6L^1#[Y8r_sdW">
|
||||||
|
<field name="TEXT">Fly to point 0, 0</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<next>
|
||||||
|
<block type="navigate" id="-8]peWQRtTK$1X0J0w!2">
|
||||||
|
<field name="FRAME_ID">ARUCO_MAP</field>
|
||||||
|
<field name="WAIT">TRUE</field>
|
||||||
|
<value name="X">
|
||||||
|
<shadow type="math_number" id="PT`K-=?U^uL#BXouoi~G">
|
||||||
|
<field name="NUM">0</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<value name="Y">
|
||||||
|
<shadow type="math_number" id="d_1oBZdx#`_kbWf_Qu$g">
|
||||||
|
<field name="NUM">0</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<value name="Z">
|
||||||
|
<shadow type="math_number" id="}Y,E=*pJaG+HOSB*f=aX">
|
||||||
|
<field name="NUM">1.5</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<value name="ID">
|
||||||
|
<shadow type="math_number" id=")/%?zNt|6thYwjowvbk/">
|
||||||
|
<field name="NUM">0</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<value name="SPEED">
|
||||||
|
<shadow type="math_number" id="MH}%@OvXa*FK-Vx63RTd">
|
||||||
|
<field name="NUM">0.5</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<next>
|
||||||
|
<block type="text_print" id="IVsd5hFEwp@V?3f02p},">
|
||||||
|
<value name="TEXT">
|
||||||
|
<shadow type="text" id="0gcgp`mZ,9oDTS[4vWpT">
|
||||||
|
<field name="TEXT">Fly to initial point</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<next>
|
||||||
|
<block type="navigate" id=":;b$YH+6#3h/_$Fa}0w_">
|
||||||
|
<field name="FRAME_ID">ARUCO_MAP</field>
|
||||||
|
<field name="WAIT">TRUE</field>
|
||||||
|
<value name="X">
|
||||||
|
<shadow type="math_number" id=";GL$bb^Yu;vQrhxy(o:S">
|
||||||
|
<field name="NUM">0</field>
|
||||||
|
</shadow>
|
||||||
|
<block type="variables_get" id="tsqWR~saO6UU^*iLmDZ(">
|
||||||
|
<field name="VAR" id="]gc9ItU#$!=G*S._3$n~">start_x</field>
|
||||||
|
</block>
|
||||||
|
</value>
|
||||||
|
<value name="Y">
|
||||||
|
<shadow type="math_number" id="y5|y58G[s-,qnfB/@Xr%">
|
||||||
|
<field name="NUM">0</field>
|
||||||
|
</shadow>
|
||||||
|
<block type="variables_get" id="Hfzk#prb}M2BbEWIAX3k">
|
||||||
|
<field name="VAR" id="D9Oy(29I!!k9+(#zI,4}">start_y</field>
|
||||||
|
</block>
|
||||||
|
</value>
|
||||||
|
<value name="Z">
|
||||||
|
<shadow type="math_number" id="B%NGa`:z=[Kpij2$0p}T">
|
||||||
|
<field name="NUM">1.5</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<value name="ID">
|
||||||
|
<shadow type="math_number" id="KaLkf?/NkD_7q@=J1G|I">
|
||||||
|
<field name="NUM">0</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<value name="SPEED">
|
||||||
|
<shadow type="math_number" id="Agwx#kYhdE}!dY!St:L1">
|
||||||
|
<field name="NUM">0.5</field>
|
||||||
|
</shadow>
|
||||||
|
</value>
|
||||||
|
<next>
|
||||||
|
<block type="land" id="8y]p?)Zm2}*+;0HP]p1$">
|
||||||
|
<field name="WAIT">TRUE</field>
|
||||||
|
</block>
|
||||||
|
</next>
|
||||||
|
</block>
|
||||||
|
</next>
|
||||||
|
</block>
|
||||||
|
</next>
|
||||||
|
</block>
|
||||||
|
</next>
|
||||||
|
</block>
|
||||||
|
</next>
|
||||||
|
</block>
|
||||||
|
</next>
|
||||||
|
</block>
|
||||||
|
</next>
|
||||||
|
</block>
|
||||||
|
</xml>
|
||||||
BIN
clover_blocks/screenshot.png
Normal file
BIN
clover_blocks/screenshot.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 182 KiB |
191
clover_blocks/src/clover_blocks
Executable file
191
clover_blocks/src/clover_blocks
Executable file
@@ -0,0 +1,191 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
# Copyright (C) 2020 Copter Express Technologies
|
||||||
|
#
|
||||||
|
# Author: Oleg Kalachev <okalachev@gmail.com>
|
||||||
|
#
|
||||||
|
# Distributed under MIT License (available at https://opensource.org/licenses/MIT).
|
||||||
|
# The above copyright notice and this permission notice shall be included in all
|
||||||
|
# copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
|
import rospy
|
||||||
|
import os, sys
|
||||||
|
import traceback
|
||||||
|
import threading
|
||||||
|
import re
|
||||||
|
import uuid
|
||||||
|
from std_msgs.msg import Bool, String
|
||||||
|
from std_srvs.srv import Trigger
|
||||||
|
from clover_blocks.msg import Prompt
|
||||||
|
from clover_blocks.srv import Run, Load, Store
|
||||||
|
|
||||||
|
|
||||||
|
rospy.init_node('clover_blocks')
|
||||||
|
|
||||||
|
stop = None
|
||||||
|
block = ''
|
||||||
|
published_block = None
|
||||||
|
running_lock = threading.Lock()
|
||||||
|
|
||||||
|
running_pub = rospy.Publisher('~running', Bool, queue_size=1, latch=True)
|
||||||
|
block_pub = rospy.Publisher('~block', String, queue_size=1, latch=True)
|
||||||
|
print_pub = rospy.Publisher('~print', String, queue_size=10)
|
||||||
|
prompt_pub = rospy.Publisher('~prompt', Prompt, queue_size=10)
|
||||||
|
error_pub = rospy.Publisher('~error', String, queue_size=10)
|
||||||
|
|
||||||
|
|
||||||
|
running_pub.publish(False)
|
||||||
|
|
||||||
|
|
||||||
|
class Stop(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def publish_block(event):
|
||||||
|
global published_block, block
|
||||||
|
if published_block != block:
|
||||||
|
block_pub.publish(block)
|
||||||
|
published_block = block
|
||||||
|
|
||||||
|
|
||||||
|
rospy.Timer(rospy.Duration(rospy.get_param('block_rate', 0.2)), publish_block)
|
||||||
|
|
||||||
|
|
||||||
|
def change_block(_block):
|
||||||
|
global block
|
||||||
|
block = _block
|
||||||
|
if stop: raise Stop
|
||||||
|
|
||||||
|
|
||||||
|
rospy_sleep = rospy.sleep
|
||||||
|
|
||||||
|
|
||||||
|
def sleep(duration):
|
||||||
|
time_start = rospy.get_time()
|
||||||
|
|
||||||
|
if isinstance(duration, rospy.Duration):
|
||||||
|
duration = duration.to_sec()
|
||||||
|
|
||||||
|
time_until = time_start + duration
|
||||||
|
|
||||||
|
while not rospy.is_shutdown():
|
||||||
|
if stop: raise Stop # check stop condition every half-second
|
||||||
|
if (time_until - rospy.get_time()) > 0.5:
|
||||||
|
print('sleep 0.5')
|
||||||
|
rospy_sleep(0.5)
|
||||||
|
else:
|
||||||
|
rospy_sleep(time_until - rospy.get_time())
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
rospy.sleep = sleep
|
||||||
|
rospy.init_node = lambda *args, **kwargs: None
|
||||||
|
|
||||||
|
|
||||||
|
def _print(s):
|
||||||
|
rospy.loginfo(str(s))
|
||||||
|
print_pub.publish(str(s))
|
||||||
|
|
||||||
|
|
||||||
|
def _input(s):
|
||||||
|
rospy.loginfo('Input with message %s', s)
|
||||||
|
prompt_id = str(uuid.uuid4()).replace('-', '')
|
||||||
|
prompt_pub.publish(message=str(s), id=prompt_id)
|
||||||
|
return rospy.wait_for_message('~input/' + prompt_id, String, timeout=30).data;
|
||||||
|
|
||||||
|
|
||||||
|
def run(req):
|
||||||
|
if not running_lock.acquire(False):
|
||||||
|
return {'message': 'Already running'}
|
||||||
|
|
||||||
|
try:
|
||||||
|
rospy.loginfo('Run program')
|
||||||
|
running_pub.publish(True)
|
||||||
|
|
||||||
|
def program_thread():
|
||||||
|
global stop
|
||||||
|
stop = False
|
||||||
|
g = {'rospy': rospy,
|
||||||
|
'_b': change_block,
|
||||||
|
'print': _print,
|
||||||
|
'raw_input': _input}
|
||||||
|
try:
|
||||||
|
exec(req.code, g)
|
||||||
|
except Stop:
|
||||||
|
rospy.loginfo('Program forced to stop')
|
||||||
|
except Exception as e:
|
||||||
|
rospy.logerr(str(e))
|
||||||
|
traceback.print_exc()
|
||||||
|
etype, value, tb = sys.exc_info()
|
||||||
|
fmt = traceback.format_exception(etype, value, tb)
|
||||||
|
fmt.pop(1) # remove 'clover_blocks' file frame
|
||||||
|
exc_info = ''.join(fmt)
|
||||||
|
error_pub.publish(str(e) + '\n\n' + exc_info)
|
||||||
|
|
||||||
|
rospy.loginfo('Program terminated')
|
||||||
|
running_lock.release()
|
||||||
|
running_pub.publish(False)
|
||||||
|
change_block('')
|
||||||
|
|
||||||
|
t = threading.Thread(target=program_thread)
|
||||||
|
t.start()
|
||||||
|
|
||||||
|
return {'success': True}
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
running_lock.release()
|
||||||
|
return {'message': str(e)}
|
||||||
|
|
||||||
|
|
||||||
|
def stop(req):
|
||||||
|
global stop
|
||||||
|
rospy.loginfo('Stop program')
|
||||||
|
stop = True
|
||||||
|
return {'success': True}
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: find dir in installed package
|
||||||
|
programs_path = rospy.get_param('~programs_dir', os.path.dirname(os.path.abspath(__file__)) + '/../programs')
|
||||||
|
|
||||||
|
|
||||||
|
def load(req):
|
||||||
|
res = {'names': [], 'programs': [], 'success': True}
|
||||||
|
try:
|
||||||
|
for currentpath, folders, files in os.walk(programs_path):
|
||||||
|
for f in files:
|
||||||
|
if not f.endswith('.xml'):
|
||||||
|
continue
|
||||||
|
filename = os.path.join(currentpath, f)
|
||||||
|
res['names'].append(os.path.relpath(filename, programs_path))
|
||||||
|
res['programs'].append(open(filename, 'r').read())
|
||||||
|
return res
|
||||||
|
except Exception as e:
|
||||||
|
rospy.logerr(e)
|
||||||
|
return {'names': [], 'programs': [], 'message': str(e)}
|
||||||
|
|
||||||
|
|
||||||
|
name_regexp = re.compile(r'^[a-zA-Z-_.]{0,20}$')
|
||||||
|
|
||||||
|
def store(req):
|
||||||
|
if not name_regexp.match(req.name):
|
||||||
|
return {'message': 'Bad program name'}
|
||||||
|
|
||||||
|
filename = os.path.abspath(os.path.join(programs_path, req.name))
|
||||||
|
try:
|
||||||
|
open(filename, 'w').write(req.program)
|
||||||
|
return {'success': True, 'message': 'Stored to ' + filename}
|
||||||
|
except Exception as e:
|
||||||
|
rospy.logerr(e)
|
||||||
|
return {'names': [], 'programs': [], 'message': str(e)}
|
||||||
|
|
||||||
|
|
||||||
|
rospy.Service('~run', Run, run)
|
||||||
|
rospy.Service('~stop', Trigger, stop)
|
||||||
|
rospy.Service('~load', Load, load)
|
||||||
|
rospy.Service('~store', Store, store)
|
||||||
|
|
||||||
|
|
||||||
|
rospy.loginfo('Ready')
|
||||||
|
rospy.spin()
|
||||||
5
clover_blocks/srv/Load.srv
Normal file
5
clover_blocks/srv/Load.srv
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
bool success
|
||||||
|
string message
|
||||||
|
string[] names
|
||||||
|
string[] programs
|
||||||
4
clover_blocks/srv/Run.srv
Normal file
4
clover_blocks/srv/Run.srv
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
string code # code in Python
|
||||||
|
---
|
||||||
|
bool success
|
||||||
|
string message
|
||||||
5
clover_blocks/srv/Store.srv
Normal file
5
clover_blocks/srv/Store.srv
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
string name
|
||||||
|
string program
|
||||||
|
---
|
||||||
|
bool success
|
||||||
|
string message
|
||||||
202
clover_blocks/www/blockly/LICENSE
Executable file
202
clover_blocks/www/blockly/LICENSE
Executable file
@@ -0,0 +1,202 @@
|
|||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
60
clover_blocks/www/blockly/README.md
Executable file
60
clover_blocks/www/blockly/README.md
Executable file
@@ -0,0 +1,60 @@
|
|||||||
|
# Blockly [](https://travis-ci.org/google/blockly)
|
||||||
|
|
||||||
|
|
||||||
|
Google's Blockly is a web-based, visual programming editor. Users can drag
|
||||||
|
blocks together to build programs. All code is free and open source.
|
||||||
|
|
||||||
|
**The project page is https://developers.google.com/blockly/**
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Blockly has an active [developer forum](https://groups.google.com/forum/#!forum/blockly). Please drop by and say hello. Show us your prototypes early; collectively we have a lot of experience and can offer hints which will save you time. We actively monitor the forums and typically respond to questions within 2 working days.
|
||||||
|
|
||||||
|
Help us focus our development efforts by telling us [what you are doing with
|
||||||
|
Blockly](https://developers.google.com/blockly/registration). The questionnaire only takes
|
||||||
|
a few minutes and will help us better support the Blockly community.
|
||||||
|
|
||||||
|
Cross-browser Testing Platform and Open Source <3 Provided by [Sauce Labs](https://saucelabs.com)
|
||||||
|
|
||||||
|
We support IE11 and test it using [BrowserStack](https://browserstack.com)
|
||||||
|
|
||||||
|
Want to contribute? Great! First, read [our guidelines for contributors](https://developers.google.com/blockly/guides/modify/contributing).
|
||||||
|
|
||||||
|
## Releases
|
||||||
|
|
||||||
|
The next major release will be **June 26th, 2020**.
|
||||||
|
|
||||||
|
We release by pushing the latest code to the master branch, followed by updating our [docs](https://developers.google.com/blockly) and [demo pages](https://blockly-demo.appspot.com). We typically release a new version of Blockly once a quarter (every 3 months). If there are breaking bugs, such as a crash when performing a standard action or a rendering issue that makes Blockly unusable, we will cherry-pick fixes to master between releases to fix them. The [releases page](https://github.com/google/blockly/releases) has a list of all releases.
|
||||||
|
|
||||||
|
Releases are tagged by the release date (YYYYMMDD) with a leading '2.' and a trailing '.0' in case we ever need a major or minor version (such as [2.20190722.1](https://github.com/google/blockly/tree/2.20190722.1)). If you're using npm, you can install the ``blockly`` package on npm:
|
||||||
|
```bash
|
||||||
|
npm install blockly
|
||||||
|
```
|
||||||
|
|
||||||
|
### New APIs
|
||||||
|
|
||||||
|
Once a new API is merged into master it is considered beta until the following release. We generally try to avoid changing an API after it has been merged to master, but sometimes we need to make changes after seeing how an API is used. If an API has been around for at least two releases we'll do our best to avoid breaking it.
|
||||||
|
|
||||||
|
Unreleased APIs may change radically. Anything that is in `develop` but not `master` is subject to change without warning.
|
||||||
|
|
||||||
|
### Branches
|
||||||
|
|
||||||
|
There are two main branches for Blockly.
|
||||||
|
|
||||||
|
**[master](https://github.com/google/blockly)** - This is the (mostly) stable current release of Blockly.
|
||||||
|
|
||||||
|
**[develop](https://github.com/google/blockly/tree/develop)** - This is where most of our work happens. Pull requests should always be made against develop. This branch will generally be usable, but may be less stable than the master branch. Once something is in develop we expect it to merge to master in the next release.
|
||||||
|
|
||||||
|
**other branches:** - Larger changes may have their own branches until they are good enough for people to try out. These will be developed separately until we think they are almost ready for release. These branches typically get merged into develop immediately after a release to allow extra time for testing.
|
||||||
|
|
||||||
|
## Issues and Milestones
|
||||||
|
|
||||||
|
We typically triage all bugs within 2 working days, which includes adding any appropriate labels and assigning it to a milestone. Please keep in mind, we are a small team so even feature requests that everyone agrees on may not be prioritized.
|
||||||
|
|
||||||
|
### Milestones
|
||||||
|
|
||||||
|
**Upcoming release** - The upcoming release milestone is for all bugs we plan on fixing before the next release. This typically has the form of `year_quarter_release` (such as `2019_q2_release`). Some bugs will be added to this release when they are triaged, others may be added closer to a release.
|
||||||
|
|
||||||
|
**Bug Bash Backlog** - These are bugs that we're still prioritizing. They haven't been added to a specific release yet, but we'll consider them for each release depending on relative priority and available time.
|
||||||
|
|
||||||
|
**Icebox** - These are bugs that we do not intend to spend time on. They are either too much work or minor enough that we don't expect them to ever take priority. We are still happy to accept pull requests for these bugs.
|
||||||
1409
clover_blocks/www/blockly/blockly_compressed.js
Executable file
1409
clover_blocks/www/blockly/blockly_compressed.js
Executable file
File diff suppressed because it is too large
Load Diff
1
clover_blocks/www/blockly/blockly_compressed.js.map
Executable file
1
clover_blocks/www/blockly/blockly_compressed.js.map
Executable file
File diff suppressed because one or more lines are too long
182
clover_blocks/www/blockly/blocks_compressed.js
Executable file
182
clover_blocks/www/blockly/blocks_compressed.js
Executable file
@@ -0,0 +1,182 @@
|
|||||||
|
// Do not edit this file; automatically generated by gulp.
|
||||||
|
|
||||||
|
/* eslint-disable */
|
||||||
|
;(function(root, factory) {
|
||||||
|
if (typeof define === 'function' && define.amd) { // AMD
|
||||||
|
define(['./blockly_compressed.js'], factory);
|
||||||
|
} else if (typeof exports === 'object') { // Node.js
|
||||||
|
module.exports = factory(require('./blockly_compressed.js'));
|
||||||
|
} else { // Browser
|
||||||
|
root.Blockly.Blocks = factory(root.Blockly);
|
||||||
|
}
|
||||||
|
}(this, function(Blockly) {
|
||||||
|
'use strict';Blockly.Blocks.colour={};Blockly.Constants={};Blockly.Constants.Colour={};Blockly.Constants.Colour.HUE=20;
|
||||||
|
Blockly.defineBlocksWithJsonArray([{type:"colour_picker",message0:"%1",args0:[{type:"field_colour",name:"COLOUR",colour:"#ff0000"}],output:"Colour",helpUrl:"%{BKY_COLOUR_PICKER_HELPURL}",style:"colour_blocks",tooltip:"%{BKY_COLOUR_PICKER_TOOLTIP}",extensions:["parent_tooltip_when_inline"]},{type:"colour_random",message0:"%{BKY_COLOUR_RANDOM_TITLE}",output:"Colour",helpUrl:"%{BKY_COLOUR_RANDOM_HELPURL}",style:"colour_blocks",tooltip:"%{BKY_COLOUR_RANDOM_TOOLTIP}"},{type:"colour_rgb",message0:"%{BKY_COLOUR_RGB_TITLE} %{BKY_COLOUR_RGB_RED} %1 %{BKY_COLOUR_RGB_GREEN} %2 %{BKY_COLOUR_RGB_BLUE} %3",
|
||||||
|
args0:[{type:"input_value",name:"RED",check:"Number",align:"RIGHT"},{type:"input_value",name:"GREEN",check:"Number",align:"RIGHT"},{type:"input_value",name:"BLUE",check:"Number",align:"RIGHT"}],output:"Colour",helpUrl:"%{BKY_COLOUR_RGB_HELPURL}",style:"colour_blocks",tooltip:"%{BKY_COLOUR_RGB_TOOLTIP}"},{type:"colour_blend",message0:"%{BKY_COLOUR_BLEND_TITLE} %{BKY_COLOUR_BLEND_COLOUR1} %1 %{BKY_COLOUR_BLEND_COLOUR2} %2 %{BKY_COLOUR_BLEND_RATIO} %3",args0:[{type:"input_value",name:"COLOUR1",check:"Colour",
|
||||||
|
align:"RIGHT"},{type:"input_value",name:"COLOUR2",check:"Colour",align:"RIGHT"},{type:"input_value",name:"RATIO",check:"Number",align:"RIGHT"}],output:"Colour",helpUrl:"%{BKY_COLOUR_BLEND_HELPURL}",style:"colour_blocks",tooltip:"%{BKY_COLOUR_BLEND_TOOLTIP}"}]);Blockly.Blocks.lists={};Blockly.Constants.Lists={};Blockly.Constants.Lists.HUE=260;
|
||||||
|
Blockly.defineBlocksWithJsonArray([{type:"lists_create_empty",message0:"%{BKY_LISTS_CREATE_EMPTY_TITLE}",output:"Array",style:"list_blocks",tooltip:"%{BKY_LISTS_CREATE_EMPTY_TOOLTIP}",helpUrl:"%{BKY_LISTS_CREATE_EMPTY_HELPURL}"},{type:"lists_repeat",message0:"%{BKY_LISTS_REPEAT_TITLE}",args0:[{type:"input_value",name:"ITEM"},{type:"input_value",name:"NUM",check:"Number"}],output:"Array",style:"list_blocks",tooltip:"%{BKY_LISTS_REPEAT_TOOLTIP}",helpUrl:"%{BKY_LISTS_REPEAT_HELPURL}"},{type:"lists_reverse",
|
||||||
|
message0:"%{BKY_LISTS_REVERSE_MESSAGE0}",args0:[{type:"input_value",name:"LIST",check:"Array"}],output:"Array",inputsInline:!0,style:"list_blocks",tooltip:"%{BKY_LISTS_REVERSE_TOOLTIP}",helpUrl:"%{BKY_LISTS_REVERSE_HELPURL}"},{type:"lists_isEmpty",message0:"%{BKY_LISTS_ISEMPTY_TITLE}",args0:[{type:"input_value",name:"VALUE",check:["String","Array"]}],output:"Boolean",style:"list_blocks",tooltip:"%{BKY_LISTS_ISEMPTY_TOOLTIP}",helpUrl:"%{BKY_LISTS_ISEMPTY_HELPURL}"},{type:"lists_length",message0:"%{BKY_LISTS_LENGTH_TITLE}",
|
||||||
|
args0:[{type:"input_value",name:"VALUE",check:["String","Array"]}],output:"Number",style:"list_blocks",tooltip:"%{BKY_LISTS_LENGTH_TOOLTIP}",helpUrl:"%{BKY_LISTS_LENGTH_HELPURL}"}]);
|
||||||
|
Blockly.Blocks.lists_create_with={init:function(){this.setHelpUrl(Blockly.Msg.LISTS_CREATE_WITH_HELPURL);this.setStyle("list_blocks");this.itemCount_=3;this.updateShape_();this.setOutput(!0,"Array");this.setMutator(new Blockly.Mutator(["lists_create_with_item"]));this.setTooltip(Blockly.Msg.LISTS_CREATE_WITH_TOOLTIP)},mutationToDom:function(){var a=Blockly.utils.xml.createElement("mutation");a.setAttribute("items",this.itemCount_);return a},domToMutation:function(a){this.itemCount_=parseInt(a.getAttribute("items"),
|
||||||
|
10);this.updateShape_()},decompose:function(a){var b=a.newBlock("lists_create_with_container");b.initSvg();for(var c=b.getInput("STACK").connection,d=0;d<this.itemCount_;d++){var e=a.newBlock("lists_create_with_item");e.initSvg();c.connect(e.previousConnection);c=e.nextConnection}return b},compose:function(a){var b=a.getInputTargetBlock("STACK");for(a=[];b;)a.push(b.valueConnection_),b=b.nextConnection&&b.nextConnection.targetBlock();for(b=0;b<this.itemCount_;b++){var c=this.getInput("ADD"+b).connection.targetConnection;
|
||||||
|
c&&-1==a.indexOf(c)&&c.disconnect()}this.itemCount_=a.length;this.updateShape_();for(b=0;b<this.itemCount_;b++)Blockly.Mutator.reconnect(a[b],this,"ADD"+b)},saveConnections:function(a){a=a.getInputTargetBlock("STACK");for(var b=0;a;){var c=this.getInput("ADD"+b);a.valueConnection_=c&&c.connection.targetConnection;b++;a=a.nextConnection&&a.nextConnection.targetBlock()}},updateShape_:function(){this.itemCount_&&this.getInput("EMPTY")?this.removeInput("EMPTY"):this.itemCount_||this.getInput("EMPTY")||
|
||||||
|
this.appendDummyInput("EMPTY").appendField(Blockly.Msg.LISTS_CREATE_EMPTY_TITLE);for(var a=0;a<this.itemCount_;a++)if(!this.getInput("ADD"+a)){var b=this.appendValueInput("ADD"+a).setAlign(Blockly.ALIGN_RIGHT);0==a&&b.appendField(Blockly.Msg.LISTS_CREATE_WITH_INPUT_WITH)}for(;this.getInput("ADD"+a);)this.removeInput("ADD"+a),a++}};
|
||||||
|
Blockly.Blocks.lists_create_with_container={init:function(){this.setStyle("list_blocks");this.appendDummyInput().appendField(Blockly.Msg.LISTS_CREATE_WITH_CONTAINER_TITLE_ADD);this.appendStatementInput("STACK");this.setTooltip(Blockly.Msg.LISTS_CREATE_WITH_CONTAINER_TOOLTIP);this.contextMenu=!1}};
|
||||||
|
Blockly.Blocks.lists_create_with_item={init:function(){this.setStyle("list_blocks");this.appendDummyInput().appendField(Blockly.Msg.LISTS_CREATE_WITH_ITEM_TITLE);this.setPreviousStatement(!0);this.setNextStatement(!0);this.setTooltip(Blockly.Msg.LISTS_CREATE_WITH_ITEM_TOOLTIP);this.contextMenu=!1}};
|
||||||
|
Blockly.Blocks.lists_indexOf={init:function(){var a=[[Blockly.Msg.LISTS_INDEX_OF_FIRST,"FIRST"],[Blockly.Msg.LISTS_INDEX_OF_LAST,"LAST"]];this.setHelpUrl(Blockly.Msg.LISTS_INDEX_OF_HELPURL);this.setStyle("list_blocks");this.setOutput(!0,"Number");this.appendValueInput("VALUE").setCheck("Array").appendField(Blockly.Msg.LISTS_INDEX_OF_INPUT_IN_LIST);this.appendValueInput("FIND").appendField(new Blockly.FieldDropdown(a),"END");this.setInputsInline(!0);var b=this;this.setTooltip(function(){return Blockly.Msg.LISTS_INDEX_OF_TOOLTIP.replace("%1",
|
||||||
|
b.workspace.options.oneBasedIndex?"0":"-1")})}};
|
||||||
|
Blockly.Blocks.lists_getIndex={init:function(){var a=[[Blockly.Msg.LISTS_GET_INDEX_GET,"GET"],[Blockly.Msg.LISTS_GET_INDEX_GET_REMOVE,"GET_REMOVE"],[Blockly.Msg.LISTS_GET_INDEX_REMOVE,"REMOVE"]];this.WHERE_OPTIONS=[[Blockly.Msg.LISTS_GET_INDEX_FROM_START,"FROM_START"],[Blockly.Msg.LISTS_GET_INDEX_FROM_END,"FROM_END"],[Blockly.Msg.LISTS_GET_INDEX_FIRST,"FIRST"],[Blockly.Msg.LISTS_GET_INDEX_LAST,"LAST"],[Blockly.Msg.LISTS_GET_INDEX_RANDOM,"RANDOM"]];this.setHelpUrl(Blockly.Msg.LISTS_GET_INDEX_HELPURL);this.setStyle("list_blocks");
|
||||||
|
a=new Blockly.FieldDropdown(a,function(a){a="REMOVE"==a;this.getSourceBlock().updateStatement_(a)});this.appendValueInput("VALUE").setCheck("Array").appendField(Blockly.Msg.LISTS_GET_INDEX_INPUT_IN_LIST);this.appendDummyInput().appendField(a,"MODE").appendField("","SPACE");this.appendDummyInput("AT");Blockly.Msg.LISTS_GET_INDEX_TAIL&&this.appendDummyInput("TAIL").appendField(Blockly.Msg.LISTS_GET_INDEX_TAIL);this.setInputsInline(!0);this.setOutput(!0);this.updateAt_(!0);var b=this;this.setTooltip(function(){var a=
|
||||||
|
b.getFieldValue("MODE"),d=b.getFieldValue("WHERE"),e="";switch(a+" "+d){case "GET FROM_START":case "GET FROM_END":e=Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_FROM;break;case "GET FIRST":e=Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_FIRST;break;case "GET LAST":e=Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_LAST;break;case "GET RANDOM":e=Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_RANDOM;break;case "GET_REMOVE FROM_START":case "GET_REMOVE FROM_END":e=Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_FROM;break;case "GET_REMOVE FIRST":e=
|
||||||
|
Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_FIRST;break;case "GET_REMOVE LAST":e=Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_LAST;break;case "GET_REMOVE RANDOM":e=Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_RANDOM;break;case "REMOVE FROM_START":case "REMOVE FROM_END":e=Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_REMOVE_FROM;break;case "REMOVE FIRST":e=Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_REMOVE_FIRST;break;case "REMOVE LAST":e=Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_REMOVE_LAST;break;case "REMOVE RANDOM":e=
|
||||||
|
Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_REMOVE_RANDOM}if("FROM_START"==d||"FROM_END"==d)e+=" "+("FROM_START"==d?Blockly.Msg.LISTS_INDEX_FROM_START_TOOLTIP:Blockly.Msg.LISTS_INDEX_FROM_END_TOOLTIP).replace("%1",b.workspace.options.oneBasedIndex?"#1":"#0");return e})},mutationToDom:function(){var a=Blockly.utils.xml.createElement("mutation");a.setAttribute("statement",!this.outputConnection);var b=this.getInput("AT").type==Blockly.INPUT_VALUE;a.setAttribute("at",b);return a},domToMutation:function(a){var b=
|
||||||
|
"true"==a.getAttribute("statement");this.updateStatement_(b);a="false"!=a.getAttribute("at");this.updateAt_(a)},updateStatement_:function(a){a!=!this.outputConnection&&(this.unplug(!0,!0),a?(this.setOutput(!1),this.setPreviousStatement(!0),this.setNextStatement(!0)):(this.setPreviousStatement(!1),this.setNextStatement(!1),this.setOutput(!0)))},updateAt_:function(a){this.removeInput("AT");this.removeInput("ORDINAL",!0);a?(this.appendValueInput("AT").setCheck("Number"),Blockly.Msg.ORDINAL_NUMBER_SUFFIX&&
|
||||||
|
this.appendDummyInput("ORDINAL").appendField(Blockly.Msg.ORDINAL_NUMBER_SUFFIX)):this.appendDummyInput("AT");var b=new Blockly.FieldDropdown(this.WHERE_OPTIONS,function(b){var c="FROM_START"==b||"FROM_END"==b;if(c!=a){var e=this.getSourceBlock();e.updateAt_(c);e.setFieldValue(b,"WHERE");return null}});this.getInput("AT").appendField(b,"WHERE");Blockly.Msg.LISTS_GET_INDEX_TAIL&&this.moveInputBefore("TAIL",null)}};
|
||||||
|
Blockly.Blocks.lists_setIndex={init:function(){var a=[[Blockly.Msg.LISTS_SET_INDEX_SET,"SET"],[Blockly.Msg.LISTS_SET_INDEX_INSERT,"INSERT"]];this.WHERE_OPTIONS=[[Blockly.Msg.LISTS_GET_INDEX_FROM_START,"FROM_START"],[Blockly.Msg.LISTS_GET_INDEX_FROM_END,"FROM_END"],[Blockly.Msg.LISTS_GET_INDEX_FIRST,"FIRST"],[Blockly.Msg.LISTS_GET_INDEX_LAST,"LAST"],[Blockly.Msg.LISTS_GET_INDEX_RANDOM,"RANDOM"]];this.setHelpUrl(Blockly.Msg.LISTS_SET_INDEX_HELPURL);this.setStyle("list_blocks");this.appendValueInput("LIST").setCheck("Array").appendField(Blockly.Msg.LISTS_SET_INDEX_INPUT_IN_LIST);
|
||||||
|
this.appendDummyInput().appendField(new Blockly.FieldDropdown(a),"MODE").appendField("","SPACE");this.appendDummyInput("AT");this.appendValueInput("TO").appendField(Blockly.Msg.LISTS_SET_INDEX_INPUT_TO);this.setInputsInline(!0);this.setPreviousStatement(!0);this.setNextStatement(!0);this.setTooltip(Blockly.Msg.LISTS_SET_INDEX_TOOLTIP);this.updateAt_(!0);var b=this;this.setTooltip(function(){var a=b.getFieldValue("MODE"),d=b.getFieldValue("WHERE"),e="";switch(a+" "+d){case "SET FROM_START":case "SET FROM_END":e=
|
||||||
|
Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_SET_FROM;break;case "SET FIRST":e=Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_SET_FIRST;break;case "SET LAST":e=Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_SET_LAST;break;case "SET RANDOM":e=Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_SET_RANDOM;break;case "INSERT FROM_START":case "INSERT FROM_END":e=Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_INSERT_FROM;break;case "INSERT FIRST":e=Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_INSERT_FIRST;break;case "INSERT LAST":e=Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_INSERT_LAST;
|
||||||
|
break;case "INSERT RANDOM":e=Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_INSERT_RANDOM}if("FROM_START"==d||"FROM_END"==d)e+=" "+Blockly.Msg.LISTS_INDEX_FROM_START_TOOLTIP.replace("%1",b.workspace.options.oneBasedIndex?"#1":"#0");return e})},mutationToDom:function(){var a=Blockly.utils.xml.createElement("mutation"),b=this.getInput("AT").type==Blockly.INPUT_VALUE;a.setAttribute("at",b);return a},domToMutation:function(a){a="false"!=a.getAttribute("at");this.updateAt_(a)},updateAt_:function(a){this.removeInput("AT");
|
||||||
|
this.removeInput("ORDINAL",!0);a?(this.appendValueInput("AT").setCheck("Number"),Blockly.Msg.ORDINAL_NUMBER_SUFFIX&&this.appendDummyInput("ORDINAL").appendField(Blockly.Msg.ORDINAL_NUMBER_SUFFIX)):this.appendDummyInput("AT");var b=new Blockly.FieldDropdown(this.WHERE_OPTIONS,function(b){var c="FROM_START"==b||"FROM_END"==b;if(c!=a){var e=this.getSourceBlock();e.updateAt_(c);e.setFieldValue(b,"WHERE");return null}});this.moveInputBefore("AT","TO");this.getInput("ORDINAL")&&this.moveInputBefore("ORDINAL",
|
||||||
|
"TO");this.getInput("AT").appendField(b,"WHERE")}};
|
||||||
|
Blockly.Blocks.lists_getSublist={init:function(){this.WHERE_OPTIONS_1=[[Blockly.Msg.LISTS_GET_SUBLIST_START_FROM_START,"FROM_START"],[Blockly.Msg.LISTS_GET_SUBLIST_START_FROM_END,"FROM_END"],[Blockly.Msg.LISTS_GET_SUBLIST_START_FIRST,"FIRST"]];this.WHERE_OPTIONS_2=[[Blockly.Msg.LISTS_GET_SUBLIST_END_FROM_START,"FROM_START"],[Blockly.Msg.LISTS_GET_SUBLIST_END_FROM_END,"FROM_END"],[Blockly.Msg.LISTS_GET_SUBLIST_END_LAST,"LAST"]];this.setHelpUrl(Blockly.Msg.LISTS_GET_SUBLIST_HELPURL);this.setStyle("list_blocks");
|
||||||
|
this.appendValueInput("LIST").setCheck("Array").appendField(Blockly.Msg.LISTS_GET_SUBLIST_INPUT_IN_LIST);this.appendDummyInput("AT1");this.appendDummyInput("AT2");Blockly.Msg.LISTS_GET_SUBLIST_TAIL&&this.appendDummyInput("TAIL").appendField(Blockly.Msg.LISTS_GET_SUBLIST_TAIL);this.setInputsInline(!0);this.setOutput(!0,"Array");this.updateAt_(1,!0);this.updateAt_(2,!0);this.setTooltip(Blockly.Msg.LISTS_GET_SUBLIST_TOOLTIP)},mutationToDom:function(){var a=Blockly.utils.xml.createElement("mutation"),
|
||||||
|
b=this.getInput("AT1").type==Blockly.INPUT_VALUE;a.setAttribute("at1",b);b=this.getInput("AT2").type==Blockly.INPUT_VALUE;a.setAttribute("at2",b);return a},domToMutation:function(a){var b="true"==a.getAttribute("at1");a="true"==a.getAttribute("at2");this.updateAt_(1,b);this.updateAt_(2,a)},updateAt_:function(a,b){this.removeInput("AT"+a);this.removeInput("ORDINAL"+a,!0);b?(this.appendValueInput("AT"+a).setCheck("Number"),Blockly.Msg.ORDINAL_NUMBER_SUFFIX&&this.appendDummyInput("ORDINAL"+a).appendField(Blockly.Msg.ORDINAL_NUMBER_SUFFIX)):
|
||||||
|
this.appendDummyInput("AT"+a);var c=new Blockly.FieldDropdown(this["WHERE_OPTIONS_"+a],function(c){var e="FROM_START"==c||"FROM_END"==c;if(e!=b){var d=this.getSourceBlock();d.updateAt_(a,e);d.setFieldValue(c,"WHERE"+a);return null}});this.getInput("AT"+a).appendField(c,"WHERE"+a);1==a&&(this.moveInputBefore("AT1","AT2"),this.getInput("ORDINAL1")&&this.moveInputBefore("ORDINAL1","AT2"));Blockly.Msg.LISTS_GET_SUBLIST_TAIL&&this.moveInputBefore("TAIL",null)}};
|
||||||
|
Blockly.Blocks.lists_sort={init:function(){this.jsonInit({message0:Blockly.Msg.LISTS_SORT_TITLE,args0:[{type:"field_dropdown",name:"TYPE",options:[[Blockly.Msg.LISTS_SORT_TYPE_NUMERIC,"NUMERIC"],[Blockly.Msg.LISTS_SORT_TYPE_TEXT,"TEXT"],[Blockly.Msg.LISTS_SORT_TYPE_IGNORECASE,"IGNORE_CASE"]]},{type:"field_dropdown",name:"DIRECTION",options:[[Blockly.Msg.LISTS_SORT_ORDER_ASCENDING,"1"],[Blockly.Msg.LISTS_SORT_ORDER_DESCENDING,"-1"]]},{type:"input_value",name:"LIST",check:"Array"}],output:"Array",style:"list_blocks",
|
||||||
|
tooltip:Blockly.Msg.LISTS_SORT_TOOLTIP,helpUrl:Blockly.Msg.LISTS_SORT_HELPURL})}};
|
||||||
|
Blockly.Blocks.lists_split={init:function(){var a=this,b=new Blockly.FieldDropdown([[Blockly.Msg.LISTS_SPLIT_LIST_FROM_TEXT,"SPLIT"],[Blockly.Msg.LISTS_SPLIT_TEXT_FROM_LIST,"JOIN"]],function(b){a.updateType_(b)});this.setHelpUrl(Blockly.Msg.LISTS_SPLIT_HELPURL);this.setStyle("list_blocks");this.appendValueInput("INPUT").setCheck("String").appendField(b,"MODE");this.appendValueInput("DELIM").setCheck("String").appendField(Blockly.Msg.LISTS_SPLIT_WITH_DELIMITER);this.setInputsInline(!0);this.setOutput(!0,
|
||||||
|
"Array");this.setTooltip(function(){var b=a.getFieldValue("MODE");if("SPLIT"==b)return Blockly.Msg.LISTS_SPLIT_TOOLTIP_SPLIT;if("JOIN"==b)return Blockly.Msg.LISTS_SPLIT_TOOLTIP_JOIN;throw Error("Unknown mode: "+b);})},updateType_:function(a){if(this.getFieldValue("MODE")!=a){var b=this.getInput("INPUT").connection;b.setShadowDom(null);var c=b.targetBlock();c&&(b.disconnect(),c.isShadow()?c.dispose():this.bumpNeighbours())}"SPLIT"==a?(this.outputConnection.setCheck("Array"),this.getInput("INPUT").setCheck("String")):
|
||||||
|
(this.outputConnection.setCheck("String"),this.getInput("INPUT").setCheck("Array"))},mutationToDom:function(){var a=Blockly.utils.xml.createElement("mutation");a.setAttribute("mode",this.getFieldValue("MODE"));return a},domToMutation:function(a){this.updateType_(a.getAttribute("mode"))}};Blockly.Blocks.logic={};Blockly.Constants.Logic={};Blockly.Constants.Logic.HUE=210;
|
||||||
|
Blockly.defineBlocksWithJsonArray([{type:"logic_boolean",message0:"%1",args0:[{type:"field_dropdown",name:"BOOL",options:[["%{BKY_LOGIC_BOOLEAN_TRUE}","TRUE"],["%{BKY_LOGIC_BOOLEAN_FALSE}","FALSE"]]}],output:"Boolean",style:"logic_blocks",tooltip:"%{BKY_LOGIC_BOOLEAN_TOOLTIP}",helpUrl:"%{BKY_LOGIC_BOOLEAN_HELPURL}"},{type:"controls_if",message0:"%{BKY_CONTROLS_IF_MSG_IF} %1",args0:[{type:"input_value",name:"IF0",check:"Boolean"}],message1:"%{BKY_CONTROLS_IF_MSG_THEN} %1",args1:[{type:"input_statement",
|
||||||
|
name:"DO0"}],previousStatement:null,nextStatement:null,style:"logic_blocks",helpUrl:"%{BKY_CONTROLS_IF_HELPURL}",mutator:"controls_if_mutator",extensions:["controls_if_tooltip"]},{type:"controls_ifelse",message0:"%{BKY_CONTROLS_IF_MSG_IF} %1",args0:[{type:"input_value",name:"IF0",check:"Boolean"}],message1:"%{BKY_CONTROLS_IF_MSG_THEN} %1",args1:[{type:"input_statement",name:"DO0"}],message2:"%{BKY_CONTROLS_IF_MSG_ELSE} %1",args2:[{type:"input_statement",name:"ELSE"}],previousStatement:null,nextStatement:null,
|
||||||
|
style:"logic_blocks",tooltip:"%{BKYCONTROLS_IF_TOOLTIP_2}",helpUrl:"%{BKY_CONTROLS_IF_HELPURL}",extensions:["controls_if_tooltip"]},{type:"logic_compare",message0:"%1 %2 %3",args0:[{type:"input_value",name:"A"},{type:"field_dropdown",name:"OP",options:[["=","EQ"],["\u2260","NEQ"],["\u200f<","LT"],["\u200f\u2264","LTE"],["\u200f>","GT"],["\u200f\u2265","GTE"]]},{type:"input_value",name:"B"}],inputsInline:!0,output:"Boolean",style:"logic_blocks",helpUrl:"%{BKY_LOGIC_COMPARE_HELPURL}",extensions:["logic_compare",
|
||||||
|
"logic_op_tooltip"]},{type:"logic_operation",message0:"%1 %2 %3",args0:[{type:"input_value",name:"A",check:"Boolean"},{type:"field_dropdown",name:"OP",options:[["%{BKY_LOGIC_OPERATION_AND}","AND"],["%{BKY_LOGIC_OPERATION_OR}","OR"]]},{type:"input_value",name:"B",check:"Boolean"}],inputsInline:!0,output:"Boolean",style:"logic_blocks",helpUrl:"%{BKY_LOGIC_OPERATION_HELPURL}",extensions:["logic_op_tooltip"]},{type:"logic_negate",message0:"%{BKY_LOGIC_NEGATE_TITLE}",args0:[{type:"input_value",name:"BOOL",
|
||||||
|
check:"Boolean"}],output:"Boolean",style:"logic_blocks",tooltip:"%{BKY_LOGIC_NEGATE_TOOLTIP}",helpUrl:"%{BKY_LOGIC_NEGATE_HELPURL}"},{type:"logic_null",message0:"%{BKY_LOGIC_NULL}",output:null,style:"logic_blocks",tooltip:"%{BKY_LOGIC_NULL_TOOLTIP}",helpUrl:"%{BKY_LOGIC_NULL_HELPURL}"},{type:"logic_ternary",message0:"%{BKY_LOGIC_TERNARY_CONDITION} %1",args0:[{type:"input_value",name:"IF",check:"Boolean"}],message1:"%{BKY_LOGIC_TERNARY_IF_TRUE} %1",args1:[{type:"input_value",name:"THEN"}],message2:"%{BKY_LOGIC_TERNARY_IF_FALSE} %1",
|
||||||
|
args2:[{type:"input_value",name:"ELSE"}],output:null,style:"logic_blocks",tooltip:"%{BKY_LOGIC_TERNARY_TOOLTIP}",helpUrl:"%{BKY_LOGIC_TERNARY_HELPURL}",extensions:["logic_ternary"]}]);
|
||||||
|
Blockly.defineBlocksWithJsonArray([{type:"controls_if_if",message0:"%{BKY_CONTROLS_IF_IF_TITLE_IF}",nextStatement:null,enableContextMenu:!1,style:"logic_blocks",tooltip:"%{BKY_CONTROLS_IF_IF_TOOLTIP}"},{type:"controls_if_elseif",message0:"%{BKY_CONTROLS_IF_ELSEIF_TITLE_ELSEIF}",previousStatement:null,nextStatement:null,enableContextMenu:!1,style:"logic_blocks",tooltip:"%{BKY_CONTROLS_IF_ELSEIF_TOOLTIP}"},{type:"controls_if_else",message0:"%{BKY_CONTROLS_IF_ELSE_TITLE_ELSE}",previousStatement:null,
|
||||||
|
enableContextMenu:!1,style:"logic_blocks",tooltip:"%{BKY_CONTROLS_IF_ELSE_TOOLTIP}"}]);Blockly.Constants.Logic.TOOLTIPS_BY_OP={EQ:"%{BKY_LOGIC_COMPARE_TOOLTIP_EQ}",NEQ:"%{BKY_LOGIC_COMPARE_TOOLTIP_NEQ}",LT:"%{BKY_LOGIC_COMPARE_TOOLTIP_LT}",LTE:"%{BKY_LOGIC_COMPARE_TOOLTIP_LTE}",GT:"%{BKY_LOGIC_COMPARE_TOOLTIP_GT}",GTE:"%{BKY_LOGIC_COMPARE_TOOLTIP_GTE}",AND:"%{BKY_LOGIC_OPERATION_TOOLTIP_AND}",OR:"%{BKY_LOGIC_OPERATION_TOOLTIP_OR}"};
|
||||||
|
Blockly.Extensions.register("logic_op_tooltip",Blockly.Extensions.buildTooltipForDropdown("OP",Blockly.Constants.Logic.TOOLTIPS_BY_OP));
|
||||||
|
Blockly.Constants.Logic.CONTROLS_IF_MUTATOR_MIXIN={elseifCount_:0,elseCount_:0,suppressPrefixSuffix:!0,mutationToDom:function(){if(!this.elseifCount_&&!this.elseCount_)return null;var a=Blockly.utils.xml.createElement("mutation");this.elseifCount_&&a.setAttribute("elseif",this.elseifCount_);this.elseCount_&&a.setAttribute("else",1);return a},domToMutation:function(a){this.elseifCount_=parseInt(a.getAttribute("elseif"),10)||0;this.elseCount_=parseInt(a.getAttribute("else"),10)||0;this.rebuildShape_()},
|
||||||
|
decompose:function(a){var b=a.newBlock("controls_if_if");b.initSvg();for(var c=b.nextConnection,d=1;d<=this.elseifCount_;d++){var e=a.newBlock("controls_if_elseif");e.initSvg();c.connect(e.previousConnection);c=e.nextConnection}this.elseCount_&&(a=a.newBlock("controls_if_else"),a.initSvg(),c.connect(a.previousConnection));return b},compose:function(a){a=a.nextConnection.targetBlock();this.elseCount_=this.elseifCount_=0;for(var b=[null],c=[null],d=null;a;){switch(a.type){case "controls_if_elseif":this.elseifCount_++;
|
||||||
|
b.push(a.valueConnection_);c.push(a.statementConnection_);break;case "controls_if_else":this.elseCount_++;d=a.statementConnection_;break;default:throw TypeError("Unknown block type: "+a.type);}a=a.nextConnection&&a.nextConnection.targetBlock()}this.updateShape_();this.reconnectChildBlocks_(b,c,d)},saveConnections:function(a){a=a.nextConnection.targetBlock();for(var b=1;a;){switch(a.type){case "controls_if_elseif":var c=this.getInput("IF"+b),d=this.getInput("DO"+b);a.valueConnection_=c&&c.connection.targetConnection;
|
||||||
|
a.statementConnection_=d&&d.connection.targetConnection;b++;break;case "controls_if_else":d=this.getInput("ELSE");a.statementConnection_=d&&d.connection.targetConnection;break;default:throw TypeError("Unknown block type: "+a.type);}a=a.nextConnection&&a.nextConnection.targetBlock()}},rebuildShape_:function(){var a=[null],b=[null],c=null;this.getInput("ELSE")&&(c=this.getInput("ELSE").connection.targetConnection);for(var d=1;this.getInput("IF"+d);){var e=this.getInput("IF"+d),f=this.getInput("DO"+
|
||||||
|
d);a.push(e.connection.targetConnection);b.push(f.connection.targetConnection);d++}this.updateShape_();this.reconnectChildBlocks_(a,b,c)},updateShape_:function(){this.getInput("ELSE")&&this.removeInput("ELSE");for(var a=1;this.getInput("IF"+a);)this.removeInput("IF"+a),this.removeInput("DO"+a),a++;for(a=1;a<=this.elseifCount_;a++)this.appendValueInput("IF"+a).setCheck("Boolean").appendField(Blockly.Msg.CONTROLS_IF_MSG_ELSEIF),this.appendStatementInput("DO"+a).appendField(Blockly.Msg.CONTROLS_IF_MSG_THEN);
|
||||||
|
this.elseCount_&&this.appendStatementInput("ELSE").appendField(Blockly.Msg.CONTROLS_IF_MSG_ELSE)},reconnectChildBlocks_:function(a,b,c){for(var d=1;d<=this.elseifCount_;d++)Blockly.Mutator.reconnect(a[d],this,"IF"+d),Blockly.Mutator.reconnect(b[d],this,"DO"+d);Blockly.Mutator.reconnect(c,this,"ELSE")}};Blockly.Extensions.registerMutator("controls_if_mutator",Blockly.Constants.Logic.CONTROLS_IF_MUTATOR_MIXIN,null,["controls_if_elseif","controls_if_else"]);
|
||||||
|
Blockly.Constants.Logic.CONTROLS_IF_TOOLTIP_EXTENSION=function(){this.setTooltip(function(){if(this.elseifCount_||this.elseCount_){if(!this.elseifCount_&&this.elseCount_)return Blockly.Msg.CONTROLS_IF_TOOLTIP_2;if(this.elseifCount_&&!this.elseCount_)return Blockly.Msg.CONTROLS_IF_TOOLTIP_3;if(this.elseifCount_&&this.elseCount_)return Blockly.Msg.CONTROLS_IF_TOOLTIP_4}else return Blockly.Msg.CONTROLS_IF_TOOLTIP_1;return""}.bind(this))};Blockly.Extensions.register("controls_if_tooltip",Blockly.Constants.Logic.CONTROLS_IF_TOOLTIP_EXTENSION);
|
||||||
|
Blockly.Constants.Logic.LOGIC_COMPARE_ONCHANGE_MIXIN={onchange:function(a){this.prevBlocks_||(this.prevBlocks_=[null,null]);var b=this.getInputTargetBlock("A"),c=this.getInputTargetBlock("B");b&&c&&!b.outputConnection.checkType(c.outputConnection)&&(Blockly.Events.setGroup(a.group),a=this.prevBlocks_[0],a!==b&&(b.unplug(),!a||a.isDisposed()||a.isShadow()||this.getInput("A").connection.connect(a.outputConnection)),b=this.prevBlocks_[1],b!==c&&(c.unplug(),!b||b.isDisposed()||b.isShadow()||this.getInput("B").connection.connect(b.outputConnection)),
|
||||||
|
this.bumpNeighbours(),Blockly.Events.setGroup(!1));this.prevBlocks_[0]=this.getInputTargetBlock("A");this.prevBlocks_[1]=this.getInputTargetBlock("B")}};Blockly.Constants.Logic.LOGIC_COMPARE_EXTENSION=function(){this.mixin(Blockly.Constants.Logic.LOGIC_COMPARE_ONCHANGE_MIXIN)};Blockly.Extensions.register("logic_compare",Blockly.Constants.Logic.LOGIC_COMPARE_EXTENSION);
|
||||||
|
Blockly.Constants.Logic.LOGIC_TERNARY_ONCHANGE_MIXIN={prevParentConnection_:null,onchange:function(a){var b=this.getInputTargetBlock("THEN"),c=this.getInputTargetBlock("ELSE"),d=this.outputConnection.targetConnection;if((b||c)&&d)for(var e=0;2>e;e++){var f=1==e?b:c;f&&!f.outputConnection.checkType(d)&&(Blockly.Events.setGroup(a.group),d===this.prevParentConnection_?(this.unplug(),d.getSourceBlock().bumpNeighbours()):(f.unplug(),f.bumpNeighbours()),Blockly.Events.setGroup(!1))}this.prevParentConnection_=
|
||||||
|
d}};Blockly.Extensions.registerMixin("logic_ternary",Blockly.Constants.Logic.LOGIC_TERNARY_ONCHANGE_MIXIN);Blockly.Blocks.loops={};Blockly.Constants.Loops={};Blockly.Constants.Loops.HUE=120;
|
||||||
|
Blockly.defineBlocksWithJsonArray([{type:"controls_repeat_ext",message0:"%{BKY_CONTROLS_REPEAT_TITLE}",args0:[{type:"input_value",name:"TIMES",check:"Number"}],message1:"%{BKY_CONTROLS_REPEAT_INPUT_DO} %1",args1:[{type:"input_statement",name:"DO"}],previousStatement:null,nextStatement:null,style:"loop_blocks",tooltip:"%{BKY_CONTROLS_REPEAT_TOOLTIP}",helpUrl:"%{BKY_CONTROLS_REPEAT_HELPURL}"},{type:"controls_repeat",message0:"%{BKY_CONTROLS_REPEAT_TITLE}",args0:[{type:"field_number",name:"TIMES",value:10,
|
||||||
|
min:0,precision:1}],message1:"%{BKY_CONTROLS_REPEAT_INPUT_DO} %1",args1:[{type:"input_statement",name:"DO"}],previousStatement:null,nextStatement:null,style:"loop_blocks",tooltip:"%{BKY_CONTROLS_REPEAT_TOOLTIP}",helpUrl:"%{BKY_CONTROLS_REPEAT_HELPURL}"},{type:"controls_whileUntil",message0:"%1 %2",args0:[{type:"field_dropdown",name:"MODE",options:[["%{BKY_CONTROLS_WHILEUNTIL_OPERATOR_WHILE}","WHILE"],["%{BKY_CONTROLS_WHILEUNTIL_OPERATOR_UNTIL}","UNTIL"]]},{type:"input_value",name:"BOOL",check:"Boolean"}],
|
||||||
|
message1:"%{BKY_CONTROLS_REPEAT_INPUT_DO} %1",args1:[{type:"input_statement",name:"DO"}],previousStatement:null,nextStatement:null,style:"loop_blocks",helpUrl:"%{BKY_CONTROLS_WHILEUNTIL_HELPURL}",extensions:["controls_whileUntil_tooltip"]},{type:"controls_for",message0:"%{BKY_CONTROLS_FOR_TITLE}",args0:[{type:"field_variable",name:"VAR",variable:null},{type:"input_value",name:"FROM",check:"Number",align:"RIGHT"},{type:"input_value",name:"TO",check:"Number",align:"RIGHT"},{type:"input_value",name:"BY",
|
||||||
|
check:"Number",align:"RIGHT"}],message1:"%{BKY_CONTROLS_REPEAT_INPUT_DO} %1",args1:[{type:"input_statement",name:"DO"}],inputsInline:!0,previousStatement:null,nextStatement:null,style:"loop_blocks",helpUrl:"%{BKY_CONTROLS_FOR_HELPURL}",extensions:["contextMenu_newGetVariableBlock","controls_for_tooltip"]},{type:"controls_forEach",message0:"%{BKY_CONTROLS_FOREACH_TITLE}",args0:[{type:"field_variable",name:"VAR",variable:null},{type:"input_value",name:"LIST",check:"Array"}],message1:"%{BKY_CONTROLS_REPEAT_INPUT_DO} %1",
|
||||||
|
args1:[{type:"input_statement",name:"DO"}],previousStatement:null,nextStatement:null,style:"loop_blocks",helpUrl:"%{BKY_CONTROLS_FOREACH_HELPURL}",extensions:["contextMenu_newGetVariableBlock","controls_forEach_tooltip"]},{type:"controls_flow_statements",message0:"%1",args0:[{type:"field_dropdown",name:"FLOW",options:[["%{BKY_CONTROLS_FLOW_STATEMENTS_OPERATOR_BREAK}","BREAK"],["%{BKY_CONTROLS_FLOW_STATEMENTS_OPERATOR_CONTINUE}","CONTINUE"]]}],previousStatement:null,style:"loop_blocks",helpUrl:"%{BKY_CONTROLS_FLOW_STATEMENTS_HELPURL}",
|
||||||
|
extensions:["controls_flow_tooltip","controls_flow_in_loop_check"]}]);Blockly.Constants.Loops.WHILE_UNTIL_TOOLTIPS={WHILE:"%{BKY_CONTROLS_WHILEUNTIL_TOOLTIP_WHILE}",UNTIL:"%{BKY_CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL}"};Blockly.Extensions.register("controls_whileUntil_tooltip",Blockly.Extensions.buildTooltipForDropdown("MODE",Blockly.Constants.Loops.WHILE_UNTIL_TOOLTIPS));Blockly.Constants.Loops.BREAK_CONTINUE_TOOLTIPS={BREAK:"%{BKY_CONTROLS_FLOW_STATEMENTS_TOOLTIP_BREAK}",CONTINUE:"%{BKY_CONTROLS_FLOW_STATEMENTS_TOOLTIP_CONTINUE}"};
|
||||||
|
Blockly.Extensions.register("controls_flow_tooltip",Blockly.Extensions.buildTooltipForDropdown("FLOW",Blockly.Constants.Loops.BREAK_CONTINUE_TOOLTIPS));
|
||||||
|
Blockly.Constants.Loops.CUSTOM_CONTEXT_MENU_CREATE_VARIABLES_GET_MIXIN={customContextMenu:function(a){if(!this.isInFlyout){var b=this.getField("VAR").getVariable(),c=b.name;if(!this.isCollapsed()&&null!=c){var d={enabled:!0};d.text=Blockly.Msg.VARIABLES_SET_CREATE_GET.replace("%1",c);b=Blockly.Variables.generateVariableFieldDom(b);c=Blockly.utils.xml.createElement("block");c.setAttribute("type","variables_get");c.appendChild(b);d.callback=Blockly.ContextMenu.callbackFactory(this,c);a.push(d)}}}};
|
||||||
|
Blockly.Extensions.registerMixin("contextMenu_newGetVariableBlock",Blockly.Constants.Loops.CUSTOM_CONTEXT_MENU_CREATE_VARIABLES_GET_MIXIN);Blockly.Extensions.register("controls_for_tooltip",Blockly.Extensions.buildTooltipWithFieldText("%{BKY_CONTROLS_FOR_TOOLTIP}","VAR"));Blockly.Extensions.register("controls_forEach_tooltip",Blockly.Extensions.buildTooltipWithFieldText("%{BKY_CONTROLS_FOREACH_TOOLTIP}","VAR"));
|
||||||
|
Blockly.Constants.Loops.CONTROL_FLOW_IN_LOOP_CHECK_MIXIN={LOOP_TYPES:["controls_repeat","controls_repeat_ext","controls_forEach","controls_for","controls_whileUntil"],suppressPrefixSuffix:!0,getSurroundLoop:function(a){do{if(-1!=Blockly.Constants.Loops.CONTROL_FLOW_IN_LOOP_CHECK_MIXIN.LOOP_TYPES.indexOf(a.type))return a;a=a.getSurroundParent()}while(a);return null},onchange:function(a){if(this.workspace.isDragging&&!this.workspace.isDragging()&&a.type==Blockly.Events.BLOCK_MOVE&&a.blockId==this.id){var b=
|
||||||
|
Blockly.Constants.Loops.CONTROL_FLOW_IN_LOOP_CHECK_MIXIN.getSurroundLoop(this);this.setWarningText(b?null:Blockly.Msg.CONTROLS_FLOW_STATEMENTS_WARNING);if(!this.isInFlyout){var c=Blockly.Events.getGroup();Blockly.Events.setGroup(a.group);this.setEnabled(b);Blockly.Events.setGroup(c)}}}};Blockly.Extensions.registerMixin("controls_flow_in_loop_check",Blockly.Constants.Loops.CONTROL_FLOW_IN_LOOP_CHECK_MIXIN);Blockly.Blocks.math={};Blockly.Constants.Math={};Blockly.Constants.Math.HUE=230;
|
||||||
|
Blockly.defineBlocksWithJsonArray([{type:"math_number",message0:"%1",args0:[{type:"field_number",name:"NUM",value:0}],output:"Number",helpUrl:"%{BKY_MATH_NUMBER_HELPURL}",style:"math_blocks",tooltip:"%{BKY_MATH_NUMBER_TOOLTIP}",extensions:["parent_tooltip_when_inline"]},{type:"math_arithmetic",message0:"%1 %2 %3",args0:[{type:"input_value",name:"A",check:"Number"},{type:"field_dropdown",name:"OP",options:[["%{BKY_MATH_ADDITION_SYMBOL}","ADD"],["%{BKY_MATH_SUBTRACTION_SYMBOL}","MINUS"],["%{BKY_MATH_MULTIPLICATION_SYMBOL}",
|
||||||
|
"MULTIPLY"],["%{BKY_MATH_DIVISION_SYMBOL}","DIVIDE"],["%{BKY_MATH_POWER_SYMBOL}","POWER"]]},{type:"input_value",name:"B",check:"Number"}],inputsInline:!0,output:"Number",style:"math_blocks",helpUrl:"%{BKY_MATH_ARITHMETIC_HELPURL}",extensions:["math_op_tooltip"]},{type:"math_single",message0:"%1 %2",args0:[{type:"field_dropdown",name:"OP",options:[["%{BKY_MATH_SINGLE_OP_ROOT}","ROOT"],["%{BKY_MATH_SINGLE_OP_ABSOLUTE}","ABS"],["-","NEG"],["ln","LN"],["log10","LOG10"],["e^","EXP"],["10^","POW10"]]},
|
||||||
|
{type:"input_value",name:"NUM",check:"Number"}],output:"Number",style:"math_blocks",helpUrl:"%{BKY_MATH_SINGLE_HELPURL}",extensions:["math_op_tooltip"]},{type:"math_trig",message0:"%1 %2",args0:[{type:"field_dropdown",name:"OP",options:[["%{BKY_MATH_TRIG_SIN}","SIN"],["%{BKY_MATH_TRIG_COS}","COS"],["%{BKY_MATH_TRIG_TAN}","TAN"],["%{BKY_MATH_TRIG_ASIN}","ASIN"],["%{BKY_MATH_TRIG_ACOS}","ACOS"],["%{BKY_MATH_TRIG_ATAN}","ATAN"]]},{type:"input_value",name:"NUM",check:"Number"}],output:"Number",style:"math_blocks",
|
||||||
|
helpUrl:"%{BKY_MATH_TRIG_HELPURL}",extensions:["math_op_tooltip"]},{type:"math_constant",message0:"%1",args0:[{type:"field_dropdown",name:"CONSTANT",options:[["\u03c0","PI"],["e","E"],["\u03c6","GOLDEN_RATIO"],["sqrt(2)","SQRT2"],["sqrt(\u00bd)","SQRT1_2"],["\u221e","INFINITY"]]}],output:"Number",style:"math_blocks",tooltip:"%{BKY_MATH_CONSTANT_TOOLTIP}",helpUrl:"%{BKY_MATH_CONSTANT_HELPURL}"},{type:"math_number_property",message0:"%1 %2",args0:[{type:"input_value",name:"NUMBER_TO_CHECK",check:"Number"},
|
||||||
|
{type:"field_dropdown",name:"PROPERTY",options:[["%{BKY_MATH_IS_EVEN}","EVEN"],["%{BKY_MATH_IS_ODD}","ODD"],["%{BKY_MATH_IS_PRIME}","PRIME"],["%{BKY_MATH_IS_WHOLE}","WHOLE"],["%{BKY_MATH_IS_POSITIVE}","POSITIVE"],["%{BKY_MATH_IS_NEGATIVE}","NEGATIVE"],["%{BKY_MATH_IS_DIVISIBLE_BY}","DIVISIBLE_BY"]]}],inputsInline:!0,output:"Boolean",style:"math_blocks",tooltip:"%{BKY_MATH_IS_TOOLTIP}",mutator:"math_is_divisibleby_mutator"},{type:"math_change",message0:"%{BKY_MATH_CHANGE_TITLE}",args0:[{type:"field_variable",
|
||||||
|
name:"VAR",variable:"%{BKY_MATH_CHANGE_TITLE_ITEM}"},{type:"input_value",name:"DELTA",check:"Number"}],previousStatement:null,nextStatement:null,style:"variable_blocks",helpUrl:"%{BKY_MATH_CHANGE_HELPURL}",extensions:["math_change_tooltip"]},{type:"math_round",message0:"%1 %2",args0:[{type:"field_dropdown",name:"OP",options:[["%{BKY_MATH_ROUND_OPERATOR_ROUND}","ROUND"],["%{BKY_MATH_ROUND_OPERATOR_ROUNDUP}","ROUNDUP"],["%{BKY_MATH_ROUND_OPERATOR_ROUNDDOWN}","ROUNDDOWN"]]},{type:"input_value",name:"NUM",
|
||||||
|
check:"Number"}],output:"Number",style:"math_blocks",helpUrl:"%{BKY_MATH_ROUND_HELPURL}",tooltip:"%{BKY_MATH_ROUND_TOOLTIP}"},{type:"math_on_list",message0:"%1 %2",args0:[{type:"field_dropdown",name:"OP",options:[["%{BKY_MATH_ONLIST_OPERATOR_SUM}","SUM"],["%{BKY_MATH_ONLIST_OPERATOR_MIN}","MIN"],["%{BKY_MATH_ONLIST_OPERATOR_MAX}","MAX"],["%{BKY_MATH_ONLIST_OPERATOR_AVERAGE}","AVERAGE"],["%{BKY_MATH_ONLIST_OPERATOR_MEDIAN}","MEDIAN"],["%{BKY_MATH_ONLIST_OPERATOR_MODE}","MODE"],["%{BKY_MATH_ONLIST_OPERATOR_STD_DEV}",
|
||||||
|
"STD_DEV"],["%{BKY_MATH_ONLIST_OPERATOR_RANDOM}","RANDOM"]]},{type:"input_value",name:"LIST",check:"Array"}],output:"Number",style:"math_blocks",helpUrl:"%{BKY_MATH_ONLIST_HELPURL}",mutator:"math_modes_of_list_mutator",extensions:["math_op_tooltip"]},{type:"math_modulo",message0:"%{BKY_MATH_MODULO_TITLE}",args0:[{type:"input_value",name:"DIVIDEND",check:"Number"},{type:"input_value",name:"DIVISOR",check:"Number"}],inputsInline:!0,output:"Number",style:"math_blocks",tooltip:"%{BKY_MATH_MODULO_TOOLTIP}",
|
||||||
|
helpUrl:"%{BKY_MATH_MODULO_HELPURL}"},{type:"math_constrain",message0:"%{BKY_MATH_CONSTRAIN_TITLE}",args0:[{type:"input_value",name:"VALUE",check:"Number"},{type:"input_value",name:"LOW",check:"Number"},{type:"input_value",name:"HIGH",check:"Number"}],inputsInline:!0,output:"Number",style:"math_blocks",tooltip:"%{BKY_MATH_CONSTRAIN_TOOLTIP}",helpUrl:"%{BKY_MATH_CONSTRAIN_HELPURL}"},{type:"math_random_int",message0:"%{BKY_MATH_RANDOM_INT_TITLE}",args0:[{type:"input_value",name:"FROM",check:"Number"},
|
||||||
|
{type:"input_value",name:"TO",check:"Number"}],inputsInline:!0,output:"Number",style:"math_blocks",tooltip:"%{BKY_MATH_RANDOM_INT_TOOLTIP}",helpUrl:"%{BKY_MATH_RANDOM_INT_HELPURL}"},{type:"math_random_float",message0:"%{BKY_MATH_RANDOM_FLOAT_TITLE_RANDOM}",output:"Number",style:"math_blocks",tooltip:"%{BKY_MATH_RANDOM_FLOAT_TOOLTIP}",helpUrl:"%{BKY_MATH_RANDOM_FLOAT_HELPURL}"},{type:"math_atan2",message0:"%{BKY_MATH_ATAN2_TITLE}",args0:[{type:"input_value",name:"X",check:"Number"},{type:"input_value",
|
||||||
|
name:"Y",check:"Number"}],inputsInline:!0,output:"Number",style:"math_blocks",tooltip:"%{BKY_MATH_ATAN2_TOOLTIP}",helpUrl:"%{BKY_MATH_ATAN2_HELPURL}"}]);
|
||||||
|
Blockly.Constants.Math.TOOLTIPS_BY_OP={ADD:"%{BKY_MATH_ARITHMETIC_TOOLTIP_ADD}",MINUS:"%{BKY_MATH_ARITHMETIC_TOOLTIP_MINUS}",MULTIPLY:"%{BKY_MATH_ARITHMETIC_TOOLTIP_MULTIPLY}",DIVIDE:"%{BKY_MATH_ARITHMETIC_TOOLTIP_DIVIDE}",POWER:"%{BKY_MATH_ARITHMETIC_TOOLTIP_POWER}",ROOT:"%{BKY_MATH_SINGLE_TOOLTIP_ROOT}",ABS:"%{BKY_MATH_SINGLE_TOOLTIP_ABS}",NEG:"%{BKY_MATH_SINGLE_TOOLTIP_NEG}",LN:"%{BKY_MATH_SINGLE_TOOLTIP_LN}",LOG10:"%{BKY_MATH_SINGLE_TOOLTIP_LOG10}",EXP:"%{BKY_MATH_SINGLE_TOOLTIP_EXP}",POW10:"%{BKY_MATH_SINGLE_TOOLTIP_POW10}",
|
||||||
|
SIN:"%{BKY_MATH_TRIG_TOOLTIP_SIN}",COS:"%{BKY_MATH_TRIG_TOOLTIP_COS}",TAN:"%{BKY_MATH_TRIG_TOOLTIP_TAN}",ASIN:"%{BKY_MATH_TRIG_TOOLTIP_ASIN}",ACOS:"%{BKY_MATH_TRIG_TOOLTIP_ACOS}",ATAN:"%{BKY_MATH_TRIG_TOOLTIP_ATAN}",SUM:"%{BKY_MATH_ONLIST_TOOLTIP_SUM}",MIN:"%{BKY_MATH_ONLIST_TOOLTIP_MIN}",MAX:"%{BKY_MATH_ONLIST_TOOLTIP_MAX}",AVERAGE:"%{BKY_MATH_ONLIST_TOOLTIP_AVERAGE}",MEDIAN:"%{BKY_MATH_ONLIST_TOOLTIP_MEDIAN}",MODE:"%{BKY_MATH_ONLIST_TOOLTIP_MODE}",STD_DEV:"%{BKY_MATH_ONLIST_TOOLTIP_STD_DEV}",RANDOM:"%{BKY_MATH_ONLIST_TOOLTIP_RANDOM}"};
|
||||||
|
Blockly.Extensions.register("math_op_tooltip",Blockly.Extensions.buildTooltipForDropdown("OP",Blockly.Constants.Math.TOOLTIPS_BY_OP));
|
||||||
|
Blockly.Constants.Math.IS_DIVISIBLEBY_MUTATOR_MIXIN={mutationToDom:function(){var a=Blockly.utils.xml.createElement("mutation"),b="DIVISIBLE_BY"==this.getFieldValue("PROPERTY");a.setAttribute("divisor_input",b);return a},domToMutation:function(a){a="true"==a.getAttribute("divisor_input");this.updateShape_(a)},updateShape_:function(a){var b=this.getInput("DIVISOR");a?b||this.appendValueInput("DIVISOR").setCheck("Number"):b&&this.removeInput("DIVISOR")}};
|
||||||
|
Blockly.Constants.Math.IS_DIVISIBLE_MUTATOR_EXTENSION=function(){this.getField("PROPERTY").setValidator(function(a){a="DIVISIBLE_BY"==a;this.getSourceBlock().updateShape_(a)})};Blockly.Extensions.registerMutator("math_is_divisibleby_mutator",Blockly.Constants.Math.IS_DIVISIBLEBY_MUTATOR_MIXIN,Blockly.Constants.Math.IS_DIVISIBLE_MUTATOR_EXTENSION);Blockly.Extensions.register("math_change_tooltip",Blockly.Extensions.buildTooltipWithFieldText("%{BKY_MATH_CHANGE_TOOLTIP}","VAR"));
|
||||||
|
Blockly.Constants.Math.LIST_MODES_MUTATOR_MIXIN={updateType_:function(a){"MODE"==a?this.outputConnection.setCheck("Array"):this.outputConnection.setCheck("Number")},mutationToDom:function(){var a=Blockly.utils.xml.createElement("mutation");a.setAttribute("op",this.getFieldValue("OP"));return a},domToMutation:function(a){this.updateType_(a.getAttribute("op"))}};Blockly.Constants.Math.LIST_MODES_MUTATOR_EXTENSION=function(){this.getField("OP").setValidator(function(a){this.updateType_(a)}.bind(this))};
|
||||||
|
Blockly.Extensions.registerMutator("math_modes_of_list_mutator",Blockly.Constants.Math.LIST_MODES_MUTATOR_MIXIN,Blockly.Constants.Math.LIST_MODES_MUTATOR_EXTENSION);Blockly.Blocks.procedures={};
|
||||||
|
Blockly.Blocks.procedures_defnoreturn={init:function(){var a=new Blockly.FieldTextInput("",Blockly.Procedures.rename);a.setSpellcheck(!1);this.appendDummyInput().appendField(Blockly.Msg.PROCEDURES_DEFNORETURN_TITLE).appendField(a,"NAME").appendField("","PARAMS");this.setMutator(new Blockly.Mutator(["procedures_mutatorarg"]));(this.workspace.options.comments||this.workspace.options.parentWorkspace&&this.workspace.options.parentWorkspace.options.comments)&&Blockly.Msg.PROCEDURES_DEFNORETURN_COMMENT&&this.setCommentText(Blockly.Msg.PROCEDURES_DEFNORETURN_COMMENT);
|
||||||
|
this.setStyle("procedure_blocks");this.setTooltip(Blockly.Msg.PROCEDURES_DEFNORETURN_TOOLTIP);this.setHelpUrl(Blockly.Msg.PROCEDURES_DEFNORETURN_HELPURL);this.arguments_=[];this.argumentVarModels_=[];this.setStatements_(!0);this.statementConnection_=null},setStatements_:function(a){this.hasStatements_!==a&&(a?(this.appendStatementInput("STACK").appendField(Blockly.Msg.PROCEDURES_DEFNORETURN_DO),this.getInput("RETURN")&&this.moveInputBefore("STACK","RETURN")):this.removeInput("STACK",!0),this.hasStatements_=
|
||||||
|
a)},updateParams_:function(){var a="";this.arguments_.length&&(a=Blockly.Msg.PROCEDURES_BEFORE_PARAMS+" "+this.arguments_.join(", "));Blockly.Events.disable();try{this.setFieldValue(a,"PARAMS")}finally{Blockly.Events.enable()}},mutationToDom:function(a){var b=Blockly.utils.xml.createElement("mutation");a&&b.setAttribute("name",this.getFieldValue("NAME"));for(var c=0;c<this.argumentVarModels_.length;c++){var d=Blockly.utils.xml.createElement("arg"),e=this.argumentVarModels_[c];d.setAttribute("name",
|
||||||
|
e.name);d.setAttribute("varid",e.getId());a&&this.paramIds_&&d.setAttribute("paramId",this.paramIds_[c]);b.appendChild(d)}this.hasStatements_||b.setAttribute("statements","false");return b},domToMutation:function(a){this.arguments_=[];this.argumentVarModels_=[];for(var b=0,c;c=a.childNodes[b];b++)if("arg"==c.nodeName.toLowerCase()){var d=c.getAttribute("name");c=c.getAttribute("varid")||c.getAttribute("varId");this.arguments_.push(d);c=Blockly.Variables.getOrCreateVariablePackage(this.workspace,c,
|
||||||
|
d,"");null!=c?this.argumentVarModels_.push(c):console.log("Failed to create a variable with name "+d+", ignoring.")}this.updateParams_();Blockly.Procedures.mutateCallers(this);this.setStatements_("false"!==a.getAttribute("statements"))},decompose:function(a){var b=Blockly.utils.xml.createElement("block");b.setAttribute("type","procedures_mutatorcontainer");var c=Blockly.utils.xml.createElement("statement");c.setAttribute("name","STACK");b.appendChild(c);for(var d=0;d<this.arguments_.length;d++){var e=
|
||||||
|
Blockly.utils.xml.createElement("block");e.setAttribute("type","procedures_mutatorarg");var f=Blockly.utils.xml.createElement("field");f.setAttribute("name","NAME");var g=Blockly.utils.xml.createTextNode(this.arguments_[d]);f.appendChild(g);e.appendChild(f);f=Blockly.utils.xml.createElement("next");e.appendChild(f);c.appendChild(e);c=f}a=Blockly.Xml.domToBlock(b,a);"procedures_defreturn"==this.type?a.setFieldValue(this.hasStatements_,"STATEMENTS"):a.removeInput("STATEMENT_INPUT");Blockly.Procedures.mutateCallers(this);
|
||||||
|
return a},compose:function(a){this.arguments_=[];this.paramIds_=[];this.argumentVarModels_=[];for(var b=a.getInputTargetBlock("STACK");b;){var c=b.getFieldValue("NAME");this.arguments_.push(c);c=this.workspace.getVariable(c,"");this.argumentVarModels_.push(c);this.paramIds_.push(b.id);b=b.nextConnection&&b.nextConnection.targetBlock()}this.updateParams_();Blockly.Procedures.mutateCallers(this);a=a.getFieldValue("STATEMENTS");if(null!==a&&(a="TRUE"==a,this.hasStatements_!=a))if(a)this.setStatements_(!0),
|
||||||
|
Blockly.Mutator.reconnect(this.statementConnection_,this,"STACK"),this.statementConnection_=null;else{a=this.getInput("STACK").connection;if(this.statementConnection_=a.targetConnection)a=a.targetBlock(),a.unplug(),a.bumpNeighbours();this.setStatements_(!1)}},getProcedureDef:function(){return[this.getFieldValue("NAME"),this.arguments_,!1]},getVars:function(){return this.arguments_},getVarModels:function(){return this.argumentVarModels_},renameVarById:function(a,b){var c=this.workspace.getVariableById(a);
|
||||||
|
if(""==c.type){c=c.name;b=this.workspace.getVariableById(b);for(var d=!1,e=0;e<this.argumentVarModels_.length;e++)this.argumentVarModels_[e].getId()==a&&(this.arguments_[e]=b.name,this.argumentVarModels_[e]=b,d=!0);d&&(this.displayRenamedVar_(c,b.name),Blockly.Procedures.mutateCallers(this))}},updateVarName:function(a){for(var b=a.name,c=!1,d=0;d<this.argumentVarModels_.length;d++)if(this.argumentVarModels_[d].getId()==a.getId()){var e=this.arguments_[d];this.arguments_[d]=b;c=!0}c&&(this.displayRenamedVar_(e,
|
||||||
|
b),Blockly.Procedures.mutateCallers(this))},displayRenamedVar_:function(a,b){this.updateParams_();if(this.mutator&&this.mutator.isVisible())for(var c=this.mutator.workspace_.getAllBlocks(!1),d=0,e;e=c[d];d++)"procedures_mutatorarg"==e.type&&Blockly.Names.equals(a,e.getFieldValue("NAME"))&&e.setFieldValue(b,"NAME")},customContextMenu:function(a){if(!this.isInFlyout){var b={enabled:!0},c=this.getFieldValue("NAME");b.text=Blockly.Msg.PROCEDURES_CREATE_DO.replace("%1",c);var d=Blockly.utils.xml.createElement("mutation");
|
||||||
|
d.setAttribute("name",c);for(c=0;c<this.arguments_.length;c++){var e=Blockly.utils.xml.createElement("arg");e.setAttribute("name",this.arguments_[c]);d.appendChild(e)}c=Blockly.utils.xml.createElement("block");c.setAttribute("type",this.callType_);c.appendChild(d);b.callback=Blockly.ContextMenu.callbackFactory(this,c);a.push(b);if(!this.isCollapsed())for(c=0;c<this.argumentVarModels_.length;c++)b={enabled:!0},d=this.argumentVarModels_[c],b.text=Blockly.Msg.VARIABLES_SET_CREATE_GET.replace("%1",d.name),
|
||||||
|
d=Blockly.Variables.generateVariableFieldDom(d),e=Blockly.utils.xml.createElement("block"),e.setAttribute("type","variables_get"),e.appendChild(d),b.callback=Blockly.ContextMenu.callbackFactory(this,e),a.push(b)}},callType_:"procedures_callnoreturn"};
|
||||||
|
Blockly.Blocks.procedures_defreturn={init:function(){var a=new Blockly.FieldTextInput("",Blockly.Procedures.rename);a.setSpellcheck(!1);this.appendDummyInput().appendField(Blockly.Msg.PROCEDURES_DEFRETURN_TITLE).appendField(a,"NAME").appendField("","PARAMS");this.appendValueInput("RETURN").setAlign(Blockly.ALIGN_RIGHT).appendField(Blockly.Msg.PROCEDURES_DEFRETURN_RETURN);this.setMutator(new Blockly.Mutator(["procedures_mutatorarg"]));(this.workspace.options.comments||this.workspace.options.parentWorkspace&&
|
||||||
|
this.workspace.options.parentWorkspace.options.comments)&&Blockly.Msg.PROCEDURES_DEFRETURN_COMMENT&&this.setCommentText(Blockly.Msg.PROCEDURES_DEFRETURN_COMMENT);this.setStyle("procedure_blocks");this.setTooltip(Blockly.Msg.PROCEDURES_DEFRETURN_TOOLTIP);this.setHelpUrl(Blockly.Msg.PROCEDURES_DEFRETURN_HELPURL);this.arguments_=[];this.argumentVarModels_=[];this.setStatements_(!0);this.statementConnection_=null},setStatements_:Blockly.Blocks.procedures_defnoreturn.setStatements_,updateParams_:Blockly.Blocks.procedures_defnoreturn.updateParams_,
|
||||||
|
mutationToDom:Blockly.Blocks.procedures_defnoreturn.mutationToDom,domToMutation:Blockly.Blocks.procedures_defnoreturn.domToMutation,decompose:Blockly.Blocks.procedures_defnoreturn.decompose,compose:Blockly.Blocks.procedures_defnoreturn.compose,getProcedureDef:function(){return[this.getFieldValue("NAME"),this.arguments_,!0]},getVars:Blockly.Blocks.procedures_defnoreturn.getVars,getVarModels:Blockly.Blocks.procedures_defnoreturn.getVarModels,renameVarById:Blockly.Blocks.procedures_defnoreturn.renameVarById,
|
||||||
|
updateVarName:Blockly.Blocks.procedures_defnoreturn.updateVarName,displayRenamedVar_:Blockly.Blocks.procedures_defnoreturn.displayRenamedVar_,customContextMenu:Blockly.Blocks.procedures_defnoreturn.customContextMenu,callType_:"procedures_callreturn"};
|
||||||
|
Blockly.Blocks.procedures_mutatorcontainer={init:function(){this.appendDummyInput().appendField(Blockly.Msg.PROCEDURES_MUTATORCONTAINER_TITLE);this.appendStatementInput("STACK");this.appendDummyInput("STATEMENT_INPUT").appendField(Blockly.Msg.PROCEDURES_ALLOW_STATEMENTS).appendField(new Blockly.FieldCheckbox("TRUE"),"STATEMENTS");this.setStyle("procedure_blocks");this.setTooltip(Blockly.Msg.PROCEDURES_MUTATORCONTAINER_TOOLTIP);this.contextMenu=!1}};
|
||||||
|
Blockly.Blocks.procedures_mutatorarg={init:function(){var a=new Blockly.FieldTextInput(Blockly.Procedures.DEFAULT_ARG,this.validator_);a.oldShowEditorFn_=a.showEditor_;a.showEditor_=function(){this.createdVariables_=[];this.oldShowEditorFn_()};this.appendDummyInput().appendField(Blockly.Msg.PROCEDURES_MUTATORARG_TITLE).appendField(a,"NAME");this.setPreviousStatement(!0);this.setNextStatement(!0);this.setStyle("procedure_blocks");this.setTooltip(Blockly.Msg.PROCEDURES_MUTATORARG_TOOLTIP);this.contextMenu=
|
||||||
|
!1;a.onFinishEditing_=this.deleteIntermediateVars_;a.createdVariables_=[];a.onFinishEditing_("x")},validator_:function(a){var b=this.getSourceBlock(),c=Blockly.Mutator.findParentWs(b.workspace);a=a.replace(/[\s\xa0]+/g," ").replace(/^ | $/g,"");if(!a)return null;for(var d=(b.workspace.targetWorkspace||b.workspace).getAllBlocks(!1),e=a.toLowerCase(),f=0;f<d.length;f++)if(d[f].id!=this.getSourceBlock().id){var g=d[f].getFieldValue("NAME");if(g&&g.toLowerCase()==e)return null}if(b.isInFlyout)return a;
|
||||||
|
(b=c.getVariable(a,""))&&b.name!=a&&c.renameVariableById(b.getId(),a);b||(b=c.createVariable(a,""))&&this.createdVariables_&&this.createdVariables_.push(b);return a},deleteIntermediateVars_:function(a){var b=Blockly.Mutator.findParentWs(this.getSourceBlock().workspace);if(b)for(var c=0;c<this.createdVariables_.length;c++){var d=this.createdVariables_[c];d.name!=a&&b.deleteVariableById(d.getId())}}};
|
||||||
|
Blockly.Blocks.procedures_callnoreturn={init:function(){this.appendDummyInput("TOPROW").appendField(this.id,"NAME");this.setPreviousStatement(!0);this.setNextStatement(!0);this.setStyle("procedure_blocks");this.setHelpUrl(Blockly.Msg.PROCEDURES_CALLNORETURN_HELPURL);this.arguments_=[];this.argumentVarModels_=[];this.quarkConnections_={};this.quarkIds_=null;this.previousEnabledState_=!0},getProcedureCall:function(){return this.getFieldValue("NAME")},renameProcedure:function(a,b){Blockly.Names.equals(a,
|
||||||
|
this.getProcedureCall())&&(this.setFieldValue(b,"NAME"),this.setTooltip((this.outputConnection?Blockly.Msg.PROCEDURES_CALLRETURN_TOOLTIP:Blockly.Msg.PROCEDURES_CALLNORETURN_TOOLTIP).replace("%1",b)))},setProcedureParameters_:function(a,b){var c=Blockly.Procedures.getDefinition(this.getProcedureCall(),this.workspace),d=c&&c.mutator&&c.mutator.isVisible();d||(this.quarkConnections_={},this.quarkIds_=null);if(b)if(a.join("\n")==this.arguments_.join("\n"))this.quarkIds_=b;else{if(b.length!=a.length)throw RangeError("paramNames and paramIds must be the same length.");
|
||||||
|
this.setCollapsed(!1);this.quarkIds_||(this.quarkConnections_={},this.quarkIds_=[]);c=this.rendered;this.rendered=!1;for(var e=0;e<this.arguments_.length;e++){var f=this.getInput("ARG"+e);f&&(f=f.connection.targetConnection,this.quarkConnections_[this.quarkIds_[e]]=f,d&&f&&-1==b.indexOf(this.quarkIds_[e])&&(f.disconnect(),f.getSourceBlock().bumpNeighbours()))}this.arguments_=[].concat(a);this.argumentVarModels_=[];for(e=0;e<this.arguments_.length;e++)a=Blockly.Variables.getOrCreateVariablePackage(this.workspace,
|
||||||
|
null,this.arguments_[e],""),this.argumentVarModels_.push(a);this.updateShape_();if(this.quarkIds_=b)for(e=0;e<this.arguments_.length;e++)b=this.quarkIds_[e],b in this.quarkConnections_&&(f=this.quarkConnections_[b],Blockly.Mutator.reconnect(f,this,"ARG"+e)||delete this.quarkConnections_[b]);(this.rendered=c)&&this.render()}},updateShape_:function(){for(var a=0;a<this.arguments_.length;a++){var b=this.getField("ARGNAME"+a);if(b){Blockly.Events.disable();try{b.setValue(this.arguments_[a])}finally{Blockly.Events.enable()}}else b=
|
||||||
|
new Blockly.FieldLabel(this.arguments_[a]),this.appendValueInput("ARG"+a).setAlign(Blockly.ALIGN_RIGHT).appendField(b,"ARGNAME"+a).init()}for(;this.getInput("ARG"+a);)this.removeInput("ARG"+a),a++;if(a=this.getInput("TOPROW"))this.arguments_.length?this.getField("WITH")||(a.appendField(Blockly.Msg.PROCEDURES_CALL_BEFORE_PARAMS,"WITH"),a.init()):this.getField("WITH")&&a.removeField("WITH")},mutationToDom:function(){var a=Blockly.utils.xml.createElement("mutation");a.setAttribute("name",this.getProcedureCall());
|
||||||
|
for(var b=0;b<this.arguments_.length;b++){var c=Blockly.utils.xml.createElement("arg");c.setAttribute("name",this.arguments_[b]);a.appendChild(c)}return a},domToMutation:function(a){var b=a.getAttribute("name");this.renameProcedure(this.getProcedureCall(),b);b=[];for(var c=[],d=0,e;e=a.childNodes[d];d++)"arg"==e.nodeName.toLowerCase()&&(b.push(e.getAttribute("name")),c.push(e.getAttribute("paramId")));this.setProcedureParameters_(b,c)},getVars:function(){return this.arguments_},getVarModels:function(){return this.argumentVarModels_},
|
||||||
|
onchange:function(a){if(this.workspace&&!this.workspace.isFlyout&&a.recordUndo)if(a.type==Blockly.Events.BLOCK_CREATE&&-1!=a.ids.indexOf(this.id)){var b=this.getProcedureCall();b=Blockly.Procedures.getDefinition(b,this.workspace);!b||b.type==this.defType_&&JSON.stringify(b.getVars())==JSON.stringify(this.arguments_)||(b=null);if(!b){Blockly.Events.setGroup(a.group);a=Blockly.utils.xml.createElement("xml");b=Blockly.utils.xml.createElement("block");b.setAttribute("type",this.defType_);var c=this.getRelativeToSurfaceXY(),
|
||||||
|
d=c.y+2*Blockly.SNAP_RADIUS;b.setAttribute("x",c.x+Blockly.SNAP_RADIUS*(this.RTL?-1:1));b.setAttribute("y",d);c=this.mutationToDom();b.appendChild(c);c=Blockly.utils.xml.createElement("field");c.setAttribute("name","NAME");c.appendChild(Blockly.utils.xml.createTextNode(this.getProcedureCall()));b.appendChild(c);a.appendChild(b);Blockly.Xml.domToWorkspace(a,this.workspace);Blockly.Events.setGroup(!1)}}else a.type==Blockly.Events.BLOCK_DELETE?(b=this.getProcedureCall(),b=Blockly.Procedures.getDefinition(b,
|
||||||
|
this.workspace),b||(Blockly.Events.setGroup(a.group),this.dispose(!0),Blockly.Events.setGroup(!1))):a.type==Blockly.Events.CHANGE&&"disabled"==a.element&&(b=this.getProcedureCall(),(b=Blockly.Procedures.getDefinition(b,this.workspace))&&b.id==a.blockId&&((b=Blockly.Events.getGroup())&&console.log("Saw an existing group while responding to a definition change"),Blockly.Events.setGroup(a.group),a.newValue?(this.previousEnabledState_=this.isEnabled(),this.setEnabled(!1)):this.setEnabled(this.previousEnabledState_),
|
||||||
|
Blockly.Events.setGroup(b)))},customContextMenu:function(a){if(this.workspace.isMovable()){var b={enabled:!0};b.text=Blockly.Msg.PROCEDURES_HIGHLIGHT_DEF;var c=this.getProcedureCall(),d=this.workspace;b.callback=function(){var a=Blockly.Procedures.getDefinition(c,d);a&&(d.centerOnBlock(a.id),a.select())};a.push(b)}},defType_:"procedures_defnoreturn"};
|
||||||
|
Blockly.Blocks.procedures_callreturn={init:function(){this.appendDummyInput("TOPROW").appendField("","NAME");this.setOutput(!0);this.setStyle("procedure_blocks");this.setHelpUrl(Blockly.Msg.PROCEDURES_CALLRETURN_HELPURL);this.arguments_=[];this.quarkConnections_={};this.quarkIds_=null;this.previousEnabledState_=!0},getProcedureCall:Blockly.Blocks.procedures_callnoreturn.getProcedureCall,renameProcedure:Blockly.Blocks.procedures_callnoreturn.renameProcedure,setProcedureParameters_:Blockly.Blocks.procedures_callnoreturn.setProcedureParameters_,
|
||||||
|
updateShape_:Blockly.Blocks.procedures_callnoreturn.updateShape_,mutationToDom:Blockly.Blocks.procedures_callnoreturn.mutationToDom,domToMutation:Blockly.Blocks.procedures_callnoreturn.domToMutation,getVars:Blockly.Blocks.procedures_callnoreturn.getVars,getVarModels:Blockly.Blocks.procedures_callnoreturn.getVarModels,onchange:Blockly.Blocks.procedures_callnoreturn.onchange,customContextMenu:Blockly.Blocks.procedures_callnoreturn.customContextMenu,defType_:"procedures_defreturn"};
|
||||||
|
Blockly.Blocks.procedures_ifreturn={init:function(){this.appendValueInput("CONDITION").setCheck("Boolean").appendField(Blockly.Msg.CONTROLS_IF_MSG_IF);this.appendValueInput("VALUE").appendField(Blockly.Msg.PROCEDURES_DEFRETURN_RETURN);this.setInputsInline(!0);this.setPreviousStatement(!0);this.setNextStatement(!0);this.setStyle("procedure_blocks");this.setTooltip(Blockly.Msg.PROCEDURES_IFRETURN_TOOLTIP);this.setHelpUrl(Blockly.Msg.PROCEDURES_IFRETURN_HELPURL);this.hasReturnValue_=!0},mutationToDom:function(){var a=
|
||||||
|
Blockly.utils.xml.createElement("mutation");a.setAttribute("value",Number(this.hasReturnValue_));return a},domToMutation:function(a){this.hasReturnValue_=1==a.getAttribute("value");this.hasReturnValue_||(this.removeInput("VALUE"),this.appendDummyInput("VALUE").appendField(Blockly.Msg.PROCEDURES_DEFRETURN_RETURN))},onchange:function(a){if(this.workspace.isDragging&&!this.workspace.isDragging()){a=!1;var b=this;do{if(-1!=this.FUNCTION_TYPES.indexOf(b.type)){a=!0;break}b=b.getSurroundParent()}while(b);
|
||||||
|
a?("procedures_defnoreturn"==b.type&&this.hasReturnValue_?(this.removeInput("VALUE"),this.appendDummyInput("VALUE").appendField(Blockly.Msg.PROCEDURES_DEFRETURN_RETURN),this.hasReturnValue_=!1):"procedures_defreturn"!=b.type||this.hasReturnValue_||(this.removeInput("VALUE"),this.appendValueInput("VALUE").appendField(Blockly.Msg.PROCEDURES_DEFRETURN_RETURN),this.hasReturnValue_=!0),this.setWarningText(null),this.isInFlyout||this.setEnabled(!0)):(this.setWarningText(Blockly.Msg.PROCEDURES_IFRETURN_WARNING),
|
||||||
|
this.isInFlyout||this.getInheritedDisabled()||this.setEnabled(!1))}},FUNCTION_TYPES:["procedures_defnoreturn","procedures_defreturn"]};Blockly.Blocks.texts={};Blockly.Constants.Text={};Blockly.Constants.Text.HUE=160;
|
||||||
|
Blockly.defineBlocksWithJsonArray([{type:"text",message0:"%1",args0:[{type:"field_input",name:"TEXT",text:""}],output:"String",style:"text_blocks",helpUrl:"%{BKY_TEXT_TEXT_HELPURL}",tooltip:"%{BKY_TEXT_TEXT_TOOLTIP}",extensions:["text_quotes","parent_tooltip_when_inline"]},{type:"text_multiline",message0:"%1 %2",args0:[{type:"field_image",src:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAARCAYAAADpPU2iAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAdhgAAHYYBXaITgQAAABh0RVh0U29mdHdhcmUAcGFpbnQubmV0IDQuMS42/U4J6AAAAP1JREFUOE+Vks0KQUEYhjmRIja4ABtZ2dm5A3t3Ia6AUm7CylYuQRaUhZSlLZJiQbFAyRnPN33y01HOW08z8873zpwzM4F3GWOCruvGIE4/rLaV+Nq1hVGMBqzhqlxgCys4wJA65xnogMHsQ5lujnYHTejBBCK2mE4abjCgMGhNxHgDFWjDSG07kdfVa2pZMf4ZyMAdWmpZMfYOsLiDMYMjlMB+K613QISRhTnITnsYg5yUd0DETmEoMlkFOeIT/A58iyK5E18BuTBfgYXfwNJv4P9/oEBerLylOnRhygmGdPpTTBZAPkde61lbQe4moWUvYUZYLfUNftIY4zwA5X2Z9AYnQrEAAAAASUVORK5CYII=",width:12,
|
||||||
|
height:17,alt:"\u00b6"},{type:"field_multilinetext",name:"TEXT",text:""}],output:"String",style:"text_blocks",helpUrl:"%{BKY_TEXT_TEXT_HELPURL}",tooltip:"%{BKY_TEXT_TEXT_TOOLTIP}",extensions:["parent_tooltip_when_inline"]},{type:"text_join",message0:"",output:"String",style:"text_blocks",helpUrl:"%{BKY_TEXT_JOIN_HELPURL}",tooltip:"%{BKY_TEXT_JOIN_TOOLTIP}",mutator:"text_join_mutator"},{type:"text_create_join_container",message0:"%{BKY_TEXT_CREATE_JOIN_TITLE_JOIN} %1 %2",args0:[{type:"input_dummy"},
|
||||||
|
{type:"input_statement",name:"STACK"}],style:"text_blocks",tooltip:"%{BKY_TEXT_CREATE_JOIN_TOOLTIP}",enableContextMenu:!1},{type:"text_create_join_item",message0:"%{BKY_TEXT_CREATE_JOIN_ITEM_TITLE_ITEM}",previousStatement:null,nextStatement:null,style:"text_blocks",tooltip:"%{BKY_TEXT_CREATE_JOIN_ITEM_TOOLTIP}",enableContextMenu:!1},{type:"text_append",message0:"%{BKY_TEXT_APPEND_TITLE}",args0:[{type:"field_variable",name:"VAR",variable:"%{BKY_TEXT_APPEND_VARIABLE}"},{type:"input_value",name:"TEXT"}],
|
||||||
|
previousStatement:null,nextStatement:null,style:"text_blocks",extensions:["text_append_tooltip"]},{type:"text_length",message0:"%{BKY_TEXT_LENGTH_TITLE}",args0:[{type:"input_value",name:"VALUE",check:["String","Array"]}],output:"Number",style:"text_blocks",tooltip:"%{BKY_TEXT_LENGTH_TOOLTIP}",helpUrl:"%{BKY_TEXT_LENGTH_HELPURL}"},{type:"text_isEmpty",message0:"%{BKY_TEXT_ISEMPTY_TITLE}",args0:[{type:"input_value",name:"VALUE",check:["String","Array"]}],output:"Boolean",style:"text_blocks",tooltip:"%{BKY_TEXT_ISEMPTY_TOOLTIP}",
|
||||||
|
helpUrl:"%{BKY_TEXT_ISEMPTY_HELPURL}"},{type:"text_indexOf",message0:"%{BKY_TEXT_INDEXOF_TITLE}",args0:[{type:"input_value",name:"VALUE",check:"String"},{type:"field_dropdown",name:"END",options:[["%{BKY_TEXT_INDEXOF_OPERATOR_FIRST}","FIRST"],["%{BKY_TEXT_INDEXOF_OPERATOR_LAST}","LAST"]]},{type:"input_value",name:"FIND",check:"String"}],output:"Number",style:"text_blocks",helpUrl:"%{BKY_TEXT_INDEXOF_HELPURL}",inputsInline:!0,extensions:["text_indexOf_tooltip"]},{type:"text_charAt",message0:"%{BKY_TEXT_CHARAT_TITLE}",
|
||||||
|
args0:[{type:"input_value",name:"VALUE",check:"String"},{type:"field_dropdown",name:"WHERE",options:[["%{BKY_TEXT_CHARAT_FROM_START}","FROM_START"],["%{BKY_TEXT_CHARAT_FROM_END}","FROM_END"],["%{BKY_TEXT_CHARAT_FIRST}","FIRST"],["%{BKY_TEXT_CHARAT_LAST}","LAST"],["%{BKY_TEXT_CHARAT_RANDOM}","RANDOM"]]}],output:"String",style:"text_blocks",helpUrl:"%{BKY_TEXT_CHARAT_HELPURL}",inputsInline:!0,mutator:"text_charAt_mutator"}]);
|
||||||
|
Blockly.Blocks.text_getSubstring={init:function(){this.WHERE_OPTIONS_1=[[Blockly.Msg.TEXT_GET_SUBSTRING_START_FROM_START,"FROM_START"],[Blockly.Msg.TEXT_GET_SUBSTRING_START_FROM_END,"FROM_END"],[Blockly.Msg.TEXT_GET_SUBSTRING_START_FIRST,"FIRST"]];this.WHERE_OPTIONS_2=[[Blockly.Msg.TEXT_GET_SUBSTRING_END_FROM_START,"FROM_START"],[Blockly.Msg.TEXT_GET_SUBSTRING_END_FROM_END,"FROM_END"],[Blockly.Msg.TEXT_GET_SUBSTRING_END_LAST,"LAST"]];this.setHelpUrl(Blockly.Msg.TEXT_GET_SUBSTRING_HELPURL);this.setStyle("text_blocks");
|
||||||
|
this.appendValueInput("STRING").setCheck("String").appendField(Blockly.Msg.TEXT_GET_SUBSTRING_INPUT_IN_TEXT);this.appendDummyInput("AT1");this.appendDummyInput("AT2");Blockly.Msg.TEXT_GET_SUBSTRING_TAIL&&this.appendDummyInput("TAIL").appendField(Blockly.Msg.TEXT_GET_SUBSTRING_TAIL);this.setInputsInline(!0);this.setOutput(!0,"String");this.updateAt_(1,!0);this.updateAt_(2,!0);this.setTooltip(Blockly.Msg.TEXT_GET_SUBSTRING_TOOLTIP)},mutationToDom:function(){var a=Blockly.utils.xml.createElement("mutation"),
|
||||||
|
b=this.getInput("AT1").type==Blockly.INPUT_VALUE;a.setAttribute("at1",b);b=this.getInput("AT2").type==Blockly.INPUT_VALUE;a.setAttribute("at2",b);return a},domToMutation:function(a){var b="true"==a.getAttribute("at1");a="true"==a.getAttribute("at2");this.updateAt_(1,b);this.updateAt_(2,a)},updateAt_:function(a,b){this.removeInput("AT"+a);this.removeInput("ORDINAL"+a,!0);b?(this.appendValueInput("AT"+a).setCheck("Number"),Blockly.Msg.ORDINAL_NUMBER_SUFFIX&&this.appendDummyInput("ORDINAL"+a).appendField(Blockly.Msg.ORDINAL_NUMBER_SUFFIX)):
|
||||||
|
this.appendDummyInput("AT"+a);2==a&&Blockly.Msg.TEXT_GET_SUBSTRING_TAIL&&(this.removeInput("TAIL",!0),this.appendDummyInput("TAIL").appendField(Blockly.Msg.TEXT_GET_SUBSTRING_TAIL));var c=new Blockly.FieldDropdown(this["WHERE_OPTIONS_"+a],function(c){var e="FROM_START"==c||"FROM_END"==c;if(e!=b){var d=this.getSourceBlock();d.updateAt_(a,e);d.setFieldValue(c,"WHERE"+a);return null}});this.getInput("AT"+a).appendField(c,"WHERE"+a);1==a&&(this.moveInputBefore("AT1","AT2"),this.getInput("ORDINAL1")&&
|
||||||
|
this.moveInputBefore("ORDINAL1","AT2"))}};Blockly.Blocks.text_changeCase={init:function(){var a=[[Blockly.Msg.TEXT_CHANGECASE_OPERATOR_UPPERCASE,"UPPERCASE"],[Blockly.Msg.TEXT_CHANGECASE_OPERATOR_LOWERCASE,"LOWERCASE"],[Blockly.Msg.TEXT_CHANGECASE_OPERATOR_TITLECASE,"TITLECASE"]];this.setHelpUrl(Blockly.Msg.TEXT_CHANGECASE_HELPURL);this.setStyle("text_blocks");this.appendValueInput("TEXT").setCheck("String").appendField(new Blockly.FieldDropdown(a),"CASE");this.setOutput(!0,"String");this.setTooltip(Blockly.Msg.TEXT_CHANGECASE_TOOLTIP)}};
|
||||||
|
Blockly.Blocks.text_trim={init:function(){var a=[[Blockly.Msg.TEXT_TRIM_OPERATOR_BOTH,"BOTH"],[Blockly.Msg.TEXT_TRIM_OPERATOR_LEFT,"LEFT"],[Blockly.Msg.TEXT_TRIM_OPERATOR_RIGHT,"RIGHT"]];this.setHelpUrl(Blockly.Msg.TEXT_TRIM_HELPURL);this.setStyle("text_blocks");this.appendValueInput("TEXT").setCheck("String").appendField(new Blockly.FieldDropdown(a),"MODE");this.setOutput(!0,"String");this.setTooltip(Blockly.Msg.TEXT_TRIM_TOOLTIP)}};
|
||||||
|
Blockly.Blocks.text_print={init:function(){this.jsonInit({message0:Blockly.Msg.TEXT_PRINT_TITLE,args0:[{type:"input_value",name:"TEXT"}],previousStatement:null,nextStatement:null,style:"text_blocks",tooltip:Blockly.Msg.TEXT_PRINT_TOOLTIP,helpUrl:Blockly.Msg.TEXT_PRINT_HELPURL})}};
|
||||||
|
Blockly.Blocks.text_prompt_ext={init:function(){var a=[[Blockly.Msg.TEXT_PROMPT_TYPE_TEXT,"TEXT"],[Blockly.Msg.TEXT_PROMPT_TYPE_NUMBER,"NUMBER"]];this.setHelpUrl(Blockly.Msg.TEXT_PROMPT_HELPURL);this.setStyle("text_blocks");var b=this;a=new Blockly.FieldDropdown(a,function(a){b.updateType_(a)});this.appendValueInput("TEXT").appendField(a,"TYPE");this.setOutput(!0,"String");this.setTooltip(function(){return"TEXT"==b.getFieldValue("TYPE")?Blockly.Msg.TEXT_PROMPT_TOOLTIP_TEXT:Blockly.Msg.TEXT_PROMPT_TOOLTIP_NUMBER})},
|
||||||
|
updateType_:function(a){this.outputConnection.setCheck("NUMBER"==a?"Number":"String")},mutationToDom:function(){var a=Blockly.utils.xml.createElement("mutation");a.setAttribute("type",this.getFieldValue("TYPE"));return a},domToMutation:function(a){this.updateType_(a.getAttribute("type"))}};
|
||||||
|
Blockly.Blocks.text_prompt={init:function(){this.mixin(Blockly.Constants.Text.QUOTE_IMAGE_MIXIN);var a=[[Blockly.Msg.TEXT_PROMPT_TYPE_TEXT,"TEXT"],[Blockly.Msg.TEXT_PROMPT_TYPE_NUMBER,"NUMBER"]],b=this;this.setHelpUrl(Blockly.Msg.TEXT_PROMPT_HELPURL);this.setStyle("text_blocks");a=new Blockly.FieldDropdown(a,function(a){b.updateType_(a)});this.appendDummyInput().appendField(a,"TYPE").appendField(this.newQuote_(!0)).appendField(new Blockly.FieldTextInput(""),"TEXT").appendField(this.newQuote_(!1));
|
||||||
|
this.setOutput(!0,"String");this.setTooltip(function(){return"TEXT"==b.getFieldValue("TYPE")?Blockly.Msg.TEXT_PROMPT_TOOLTIP_TEXT:Blockly.Msg.TEXT_PROMPT_TOOLTIP_NUMBER})},updateType_:Blockly.Blocks.text_prompt_ext.updateType_,mutationToDom:Blockly.Blocks.text_prompt_ext.mutationToDom,domToMutation:Blockly.Blocks.text_prompt_ext.domToMutation};
|
||||||
|
Blockly.Blocks.text_count={init:function(){this.jsonInit({message0:Blockly.Msg.TEXT_COUNT_MESSAGE0,args0:[{type:"input_value",name:"SUB",check:"String"},{type:"input_value",name:"TEXT",check:"String"}],output:"Number",inputsInline:!0,style:"text_blocks",tooltip:Blockly.Msg.TEXT_COUNT_TOOLTIP,helpUrl:Blockly.Msg.TEXT_COUNT_HELPURL})}};
|
||||||
|
Blockly.Blocks.text_replace={init:function(){this.jsonInit({message0:Blockly.Msg.TEXT_REPLACE_MESSAGE0,args0:[{type:"input_value",name:"FROM",check:"String"},{type:"input_value",name:"TO",check:"String"},{type:"input_value",name:"TEXT",check:"String"}],output:"String",inputsInline:!0,style:"text_blocks",tooltip:Blockly.Msg.TEXT_REPLACE_TOOLTIP,helpUrl:Blockly.Msg.TEXT_REPLACE_HELPURL})}};
|
||||||
|
Blockly.Blocks.text_reverse={init:function(){this.jsonInit({message0:Blockly.Msg.TEXT_REVERSE_MESSAGE0,args0:[{type:"input_value",name:"TEXT",check:"String"}],output:"String",inputsInline:!0,style:"text_blocks",tooltip:Blockly.Msg.TEXT_REVERSE_TOOLTIP,helpUrl:Blockly.Msg.TEXT_REVERSE_HELPURL})}};
|
||||||
|
Blockly.Constants.Text.QUOTE_IMAGE_MIXIN={QUOTE_IMAGE_LEFT_DATAURI:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAKCAQAAAAqJXdxAAAAn0lEQVQI1z3OMa5BURSF4f/cQhAKjUQhuQmFNwGJEUi0RKN5rU7FHKhpjEH3TEMtkdBSCY1EIv8r7nFX9e29V7EBAOvu7RPjwmWGH/VuF8CyN9/OAdvqIXYLvtRaNjx9mMTDyo+NjAN1HNcl9ZQ5oQMM3dgDUqDo1l8DzvwmtZN7mnD+PkmLa+4mhrxVA9fRowBWmVBhFy5gYEjKMfz9AylsaRRgGzvZAAAAAElFTkSuQmCC",QUOTE_IMAGE_RIGHT_DATAURI:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAKCAQAAAAqJXdxAAAAqUlEQVQI1z3KvUpCcRiA8ef9E4JNHhI0aFEacm1o0BsI0Slx8wa8gLauoDnoBhq7DcfWhggONDmJJgqCPA7neJ7p934EOOKOnM8Q7PDElo/4x4lFb2DmuUjcUzS3URnGib9qaPNbuXvBO3sGPHJDRG6fGVdMSeWDP2q99FQdFrz26Gu5Tq7dFMzUvbXy8KXeAj57cOklgA+u1B5AoslLtGIHQMaCVnwDnADZIFIrXsoXrgAAAABJRU5ErkJggg==",
|
||||||
|
QUOTE_IMAGE_WIDTH:12,QUOTE_IMAGE_HEIGHT:12,quoteField_:function(a){for(var b=0,c;c=this.inputList[b];b++)for(var d=0,e;e=c.fieldRow[d];d++)if(a==e.name){c.insertFieldAt(d,this.newQuote_(!0));c.insertFieldAt(d+2,this.newQuote_(!1));return}console.warn('field named "'+a+'" not found in '+this.toDevString())},newQuote_:function(a){a=this.RTL?!a:a;return new Blockly.FieldImage(a?this.QUOTE_IMAGE_LEFT_DATAURI:this.QUOTE_IMAGE_RIGHT_DATAURI,this.QUOTE_IMAGE_WIDTH,this.QUOTE_IMAGE_HEIGHT,a?"\u201c":"\u201d")}};
|
||||||
|
Blockly.Constants.Text.TEXT_QUOTES_EXTENSION=function(){this.mixin(Blockly.Constants.Text.QUOTE_IMAGE_MIXIN);this.quoteField_("TEXT")};
|
||||||
|
Blockly.Constants.Text.TEXT_JOIN_MUTATOR_MIXIN={mutationToDom:function(){var a=Blockly.utils.xml.createElement("mutation");a.setAttribute("items",this.itemCount_);return a},domToMutation:function(a){this.itemCount_=parseInt(a.getAttribute("items"),10);this.updateShape_()},decompose:function(a){var b=a.newBlock("text_create_join_container");b.initSvg();for(var c=b.getInput("STACK").connection,d=0;d<this.itemCount_;d++){var e=a.newBlock("text_create_join_item");e.initSvg();c.connect(e.previousConnection);
|
||||||
|
c=e.nextConnection}return b},compose:function(a){var b=a.getInputTargetBlock("STACK");for(a=[];b;)a.push(b.valueConnection_),b=b.nextConnection&&b.nextConnection.targetBlock();for(b=0;b<this.itemCount_;b++){var c=this.getInput("ADD"+b).connection.targetConnection;c&&-1==a.indexOf(c)&&c.disconnect()}this.itemCount_=a.length;this.updateShape_();for(b=0;b<this.itemCount_;b++)Blockly.Mutator.reconnect(a[b],this,"ADD"+b)},saveConnections:function(a){a=a.getInputTargetBlock("STACK");for(var b=0;a;){var c=
|
||||||
|
this.getInput("ADD"+b);a.valueConnection_=c&&c.connection.targetConnection;b++;a=a.nextConnection&&a.nextConnection.targetBlock()}},updateShape_:function(){this.itemCount_&&this.getInput("EMPTY")?this.removeInput("EMPTY"):this.itemCount_||this.getInput("EMPTY")||this.appendDummyInput("EMPTY").appendField(this.newQuote_(!0)).appendField(this.newQuote_(!1));for(var a=0;a<this.itemCount_;a++)if(!this.getInput("ADD"+a)){var b=this.appendValueInput("ADD"+a).setAlign(Blockly.ALIGN_RIGHT);0==a&&b.appendField(Blockly.Msg.TEXT_JOIN_TITLE_CREATEWITH)}for(;this.getInput("ADD"+
|
||||||
|
a);)this.removeInput("ADD"+a),a++}};Blockly.Constants.Text.TEXT_JOIN_EXTENSION=function(){this.mixin(Blockly.Constants.Text.QUOTE_IMAGE_MIXIN);this.itemCount_=2;this.updateShape_();this.setMutator(new Blockly.Mutator(["text_create_join_item"]))};Blockly.Extensions.register("text_append_tooltip",Blockly.Extensions.buildTooltipWithFieldText("%{BKY_TEXT_APPEND_TOOLTIP}","VAR"));
|
||||||
|
Blockly.Constants.Text.TEXT_INDEXOF_TOOLTIP_EXTENSION=function(){var a=this;this.setTooltip(function(){return Blockly.Msg.TEXT_INDEXOF_TOOLTIP.replace("%1",a.workspace.options.oneBasedIndex?"0":"-1")})};
|
||||||
|
Blockly.Constants.Text.TEXT_CHARAT_MUTATOR_MIXIN={mutationToDom:function(){var a=Blockly.utils.xml.createElement("mutation");a.setAttribute("at",!!this.isAt_);return a},domToMutation:function(a){a="false"!=a.getAttribute("at");this.updateAt_(a)},updateAt_:function(a){this.removeInput("AT",!0);this.removeInput("ORDINAL",!0);a&&(this.appendValueInput("AT").setCheck("Number"),Blockly.Msg.ORDINAL_NUMBER_SUFFIX&&this.appendDummyInput("ORDINAL").appendField(Blockly.Msg.ORDINAL_NUMBER_SUFFIX));Blockly.Msg.TEXT_CHARAT_TAIL&&
|
||||||
|
(this.removeInput("TAIL",!0),this.appendDummyInput("TAIL").appendField(Blockly.Msg.TEXT_CHARAT_TAIL));this.isAt_=a}};
|
||||||
|
Blockly.Constants.Text.TEXT_CHARAT_EXTENSION=function(){this.getField("WHERE").setValidator(function(a){a="FROM_START"==a||"FROM_END"==a;a!=this.isAt_&&this.getSourceBlock().updateAt_(a)});this.updateAt_(!0);var a=this;this.setTooltip(function(){var b=a.getFieldValue("WHERE"),c=Blockly.Msg.TEXT_CHARAT_TOOLTIP;("FROM_START"==b||"FROM_END"==b)&&(b="FROM_START"==b?Blockly.Msg.LISTS_INDEX_FROM_START_TOOLTIP:Blockly.Msg.LISTS_INDEX_FROM_END_TOOLTIP)&&(c+=" "+b.replace("%1",a.workspace.options.oneBasedIndex?
|
||||||
|
"#1":"#0"));return c})};Blockly.Extensions.register("text_indexOf_tooltip",Blockly.Constants.Text.TEXT_INDEXOF_TOOLTIP_EXTENSION);Blockly.Extensions.register("text_quotes",Blockly.Constants.Text.TEXT_QUOTES_EXTENSION);Blockly.Extensions.registerMutator("text_join_mutator",Blockly.Constants.Text.TEXT_JOIN_MUTATOR_MIXIN,Blockly.Constants.Text.TEXT_JOIN_EXTENSION);Blockly.Extensions.registerMutator("text_charAt_mutator",Blockly.Constants.Text.TEXT_CHARAT_MUTATOR_MIXIN,Blockly.Constants.Text.TEXT_CHARAT_EXTENSION);Blockly.Blocks.variables={};Blockly.Constants.Variables={};Blockly.Constants.Variables.HUE=330;
|
||||||
|
Blockly.defineBlocksWithJsonArray([{type:"variables_get",message0:"%1",args0:[{type:"field_variable",name:"VAR",variable:"%{BKY_VARIABLES_DEFAULT_NAME}"}],output:null,style:"variable_blocks",helpUrl:"%{BKY_VARIABLES_GET_HELPURL}",tooltip:"%{BKY_VARIABLES_GET_TOOLTIP}",extensions:["contextMenu_variableSetterGetter"]},{type:"variables_set",message0:"%{BKY_VARIABLES_SET}",args0:[{type:"field_variable",name:"VAR",variable:"%{BKY_VARIABLES_DEFAULT_NAME}"},{type:"input_value",name:"VALUE"}],previousStatement:null,
|
||||||
|
nextStatement:null,style:"variable_blocks",tooltip:"%{BKY_VARIABLES_SET_TOOLTIP}",helpUrl:"%{BKY_VARIABLES_SET_HELPURL}",extensions:["contextMenu_variableSetterGetter"]}]);
|
||||||
|
Blockly.Constants.Variables.CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN={customContextMenu:function(a){if(!this.isInFlyout){if("variables_get"==this.type)var b="variables_set",c=Blockly.Msg.VARIABLES_GET_CREATE_SET;else b="variables_get",c=Blockly.Msg.VARIABLES_SET_CREATE_GET;var d={enabled:0<this.workspace.remainingCapacity()},e=this.getField("VAR").getText();d.text=c.replace("%1",e);c=Blockly.utils.xml.createElement("field");c.setAttribute("name","VAR");c.appendChild(Blockly.utils.xml.createTextNode(e));
|
||||||
|
e=Blockly.utils.xml.createElement("block");e.setAttribute("type",b);e.appendChild(c);d.callback=Blockly.ContextMenu.callbackFactory(this,e);a.push(d)}else if("variables_get"==this.type||"variables_get_reporter"==this.type)b={text:Blockly.Msg.RENAME_VARIABLE,enabled:!0,callback:Blockly.Constants.Variables.RENAME_OPTION_CALLBACK_FACTORY(this)},e=this.getField("VAR").getText(),d={text:Blockly.Msg.DELETE_VARIABLE.replace("%1",e),enabled:!0,callback:Blockly.Constants.Variables.DELETE_OPTION_CALLBACK_FACTORY(this)},
|
||||||
|
a.unshift(b),a.unshift(d)}};Blockly.Constants.Variables.RENAME_OPTION_CALLBACK_FACTORY=function(a){return function(){var b=a.workspace,c=a.getField("VAR").getVariable();Blockly.Variables.renameVariable(b,c)}};Blockly.Constants.Variables.DELETE_OPTION_CALLBACK_FACTORY=function(a){return function(){var b=a.workspace,c=a.getField("VAR").getVariable();b.deleteVariableById(c.getId());b.refreshToolboxSelection()}};Blockly.Extensions.registerMixin("contextMenu_variableSetterGetter",Blockly.Constants.Variables.CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN);Blockly.Constants.VariablesDynamic={};Blockly.Constants.VariablesDynamic.HUE=310;
|
||||||
|
Blockly.defineBlocksWithJsonArray([{type:"variables_get_dynamic",message0:"%1",args0:[{type:"field_variable",name:"VAR",variable:"%{BKY_VARIABLES_DEFAULT_NAME}"}],output:null,style:"variable_dynamic_blocks",helpUrl:"%{BKY_VARIABLES_GET_HELPURL}",tooltip:"%{BKY_VARIABLES_GET_TOOLTIP}",extensions:["contextMenu_variableDynamicSetterGetter"]},{type:"variables_set_dynamic",message0:"%{BKY_VARIABLES_SET}",args0:[{type:"field_variable",name:"VAR",variable:"%{BKY_VARIABLES_DEFAULT_NAME}"},{type:"input_value",
|
||||||
|
name:"VALUE"}],previousStatement:null,nextStatement:null,style:"variable_dynamic_blocks",tooltip:"%{BKY_VARIABLES_SET_TOOLTIP}",helpUrl:"%{BKY_VARIABLES_SET_HELPURL}",extensions:["contextMenu_variableDynamicSetterGetter"]}]);
|
||||||
|
Blockly.Constants.VariablesDynamic.CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN={customContextMenu:function(a){if(!this.isInFlyout){var b=this.getFieldValue("VAR");var c=this.workspace.getVariableById(b).type;if("variables_get_dynamic"==this.type){b="variables_set_dynamic";var d=Blockly.Msg.VARIABLES_GET_CREATE_SET}else b="variables_get_dynamic",d=Blockly.Msg.VARIABLES_SET_CREATE_GET;var e={enabled:0<this.workspace.remainingCapacity()},f=this.getField("VAR").getText();e.text=d.replace("%1",f);
|
||||||
|
d=Blockly.utils.xml.createElement("field");d.setAttribute("name","VAR");d.setAttribute("variabletype",c);d.appendChild(Blockly.utils.xml.createTextNode(f));f=Blockly.utils.xml.createElement("block");f.setAttribute("type",b);f.appendChild(d);e.callback=Blockly.ContextMenu.callbackFactory(this,f);a.push(e)}else if("variables_get_dynamic"==this.type||"variables_get_reporter_dynamic"==this.type)b={text:Blockly.Msg.RENAME_VARIABLE,enabled:!0,callback:Blockly.Constants.Variables.RENAME_OPTION_CALLBACK_FACTORY(this)},
|
||||||
|
f=this.getField("VAR").getText(),e={text:Blockly.Msg.DELETE_VARIABLE.replace("%1",f),enabled:!0,callback:Blockly.Constants.Variables.DELETE_OPTION_CALLBACK_FACTORY(this)},a.unshift(b),a.unshift(e)},onchange:function(a){a=this.getFieldValue("VAR");a=Blockly.Variables.getVariable(this.workspace,a);"variables_get_dynamic"==this.type?this.outputConnection.setCheck(a.type):this.getInput("VALUE").connection.setCheck(a.type)}};
|
||||||
|
Blockly.Constants.VariablesDynamic.RENAME_OPTION_CALLBACK_FACTORY=function(a){return function(){var b=a.workspace,c=a.getField("VAR").getVariable();Blockly.Variables.renameVariable(b,c)}};Blockly.Constants.VariablesDynamic.DELETE_OPTION_CALLBACK_FACTORY=function(a){return function(){var b=a.workspace,c=a.getField("VAR").getVariable();b.deleteVariableById(c.getId());b.refreshToolboxSelection()}};Blockly.Extensions.registerMixin("contextMenu_variableDynamicSetterGetter",Blockly.Constants.VariablesDynamic.CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN);
|
||||||
|
return Blockly.Blocks;
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
//# sourceMappingURL=blocks_compressed.js.map
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user