Compare commits

..

290 Commits

Author SHA1 Message Date
Oleg Kalachev
23c745c17b Try to fix 2021-03-25 18:18:55 +03:00
Oleg Kalachev
c646aa3b43 Install cmake separately 2021-03-25 18:12:59 +03:00
Oleg Kalachev
dbeef76ec0 Try to fix 2021-03-25 18:07:49 +03:00
Oleg Kalachev
91543a9c0e Revert "Add packages.coex.tech"
This reverts commit f25816f2d5.
2021-03-25 18:03:37 +03:00
Oleg Kalachev
f25816f2d5 Add packages.coex.tech 2021-03-25 17:53:50 +03:00
Oleg Kalachev
62e5a20a79 show cmake-data versions 2021-03-25 17:50:42 +03:00
Oleg Kalachev
857e41516a Use cmake 3.13.4-1 2021-03-25 17:19:24 +03:00
Oleg Kalachev
48549de07f Show cmake versions 2021-03-25 17:01:23 +03:00
Oleg Kalachev
1c70499ebd image: roll back cmake to 3.6.2 2021-03-25 16:54:41 +03:00
Oleg Kalachev
6190605312 image: roll back cmake to 3.13.4 2021-03-25 16:44:54 +03:00
Oleg Kalachev
27ae453136 Revert "image: build clover package in 1-threaded"
This reverts commit 9d544a139a.
2021-03-25 16:28:26 +03:00
Oleg Kalachev
9d544a139a image: build clover package in 1-threaded 2021-03-25 15:35:08 +03:00
Oleg Kalachev
bf84a98cb6 image: update Raspberry Pi OS 2021-03-25 15:29:31 +03:00
Oleg Kalachev
9270b6a278 Turn on commands echoing 2021-03-17 18:18:16 +03:00
Oleg Kalachev
8a95789d77 Update pip 2021-03-15 19:18:14 +03:00
Oleg Kalachev
db328ba25e Fix 2021-03-15 16:47:03 +03:00
Oleg Kalachev
afa50add8f CRYPTOGRAPHY_DONT_BUILD_RUST=1 2021-03-15 16:20:30 +03:00
Oleg Kalachev
735b88d498 Try to fix 2021-03-15 16:18:36 +03:00
Oleg Kalachev
8aaaae929e builder: install Rust (needed for cryptography) 2021-03-15 15:11:00 +03:00
Oleg Kalachev
793b614b7b builder: fix getting pip in standalone-install 2021-03-15 14:40:12 +03:00
Oleg Kalachev
62ab5c2357 builder: fix get-pip url for python 2 2021-03-15 14:29:03 +03:00
Oleg Kalachev
181a78e4a9 image: use old pip for Python 2 2021-03-15 14:13:45 +03:00
Oleg Kalachev
c72eb0c027 docs: link to blocks in projects list 2021-03-14 10:27:37 +03:00
Oleg Kalachev
5d99e44c30 docs: restore COEX GPS article 2021-03-12 10:06:34 +03:00
Oleg Kalachev
5eb9b4acbe Revert "docs: add COEX GPS article"
This reverts commit 1dea541df2.
2021-03-12 10:01:04 +03:00
Oleg Kalachev
30ada8f311 Revert "docs: add some text to COEX GPS article"
This reverts commit 50dc17badb.
2021-03-12 09:57:31 +03:00
Oleg Kalachev
e717829945 gitbook: fix retrieving the latest version of the firmware 2021-03-10 11:12:25 +03:00
Oleg Kalachev
50dc17badb docs: add some text to COEX GPS article 2021-03-10 08:03:57 +03:00
Oleg Kalachev
1dea541df2 docs: add COEX GPS article 2021-03-09 17:35:18 +03:00
Oleg Kalachev
d6b950b726 docs: some edits to coex pdb articles 2021-02-27 21:20:23 +03:00
Oleg Kalachev
e2a1d3aaeb docs: minor fix 2021-02-27 01:57:59 +03:00
Oleg Kalachev
165e4d1a61 docs: add COEX PDB page (en/ru) 2021-02-27 01:47:44 +03:00
Oleg Kalachev
4f631300d4 docs: fix sonar example 2021-02-26 15:48:01 +03:00
deadln
e252a1cddc Update package versions (#302)
* Update version in clover_blocks package.xml

* Changed all packages versions to 0.21.1
2021-02-21 22:20:43 +03:00
deadln
25dd17c286 Add packages changelogs (#301)
* Generated changelogs

* Cleared aruco_pose changelog

* Cleared clover changelog

* Cleared clover_blocks changelog

* Cleared clover_description changelog

* Cleared clover_simulation changelog

* Cleared roswww_static changelog

* Fixed typo in roswww_static changelog

* Simplify changelogs

Co-authored-by: Oleg Kalachev <okalachev@gmail.com>
2021-02-19 23:37:36 +03:00
timkondratiev
b9395e3d18 simulator: Add default ArUco map based on "cmit.txt" (#300)
* Add ArUco map model

* Add ArUco map into the new .world file and set this world as the default.
2021-02-17 14:48:18 +03:00
timkondratiev
a32dd7dcdd clover_simulation: fix XML formatting in generated world (#299)
Changed the way of adding an XML element into the .world file to fix the formatting issue.
2021-01-27 16:01:59 +03:00
MatveyBarabanshchikov
1e12e34070 docs: add agriculture.md article (rus/eng) (#297)
* add agriculture.md

* add photos for agriculture.md

* Update agriculture.md

Add photos and fixed code.

* Update agriculture.md

Changed the name and the content of the article.

* Add agriculture.md at SUMMARY.md

* Update agriculture.md

Fixed indentation errors.

* Update agriculture.md

Fixed text bugs.

* Create the English version of agriculture.md

* Update agriculture.md

Fixed English.

* Add agriculture.md to SUMMARY.md (eng)

* Delete field.png

* Delete field2.png

* Update pictures for agriculture.md

* Update links to images in agriculture.md

* Update links to images in agriculture.md

* Delete field.jpg

* Delete field2.jpg

* Add update photo at agriculture.md

* Update ugriculture.md

* Update agriculture.md

* Update agriculture.md

* Update agriculture.md

Fixed text bugs.

* Update ugriculture.md

Fixed text bugs.

* Update agriculture.md

Fixed text bugs.

* Update agriculture.md

Updated the article according to the recommendations.

* Update agriculture.md

Updated the article according to the recommendations.
2021-01-22 04:16:08 +03:00
Oleg Kalachev
3ff675d794 docs: add clover 4.2 dimension drawings 2021-01-12 22:00:17 +03:00
Alamoris
bb3e4befe5 docs: add Zerotire VPN article (#295)
* docs: Added article about configuring zerotire VPN

* docs: Add paragraph about macos VPN connection

* docs: Fix md exeptions

* dosc: Fix orthography

* docs: Add eng version, some fix

* docs: Fix

* docs: Small fix

* docs: Add paragraph about QGC connection

* docs: Add images and fix md

* Update docs/en/zerotire_vpn.md

* Update docs/ru/zerotire_vpn.md

Co-authored-by: Oleg Kalachev <okalachev@gmail.com>
2020-12-28 21:22:38 +03:00
Alamoris
fe71007ebd docs: add images for article about mechanical grip (#294) 2020-12-18 13:34:23 +03:00
Oleg Kalachev
68cec159f7 simulation: add type:=jmavsim to simulator.launch, remove sitl.launch, some adjustments 2020-12-08 16:28:57 +03:00
Oleg Kalachev
4e8127f690 docs: add vertical markers article to the summary 2020-12-08 15:28:29 +03:00
Alamoris
8f78f2b6e4 docs: add translations of articles about grippers and assembling an FPV set (#288)
* docs: Add translations and fix rusian articles

* docs: Fix md and style

* docs: Fix summary and dupont naming

* docs: Fix summary tabbing
2020-12-08 14:17:20 +03:00
Oleg Kalachev
c8163cd38b docs: add translation of the wall_aruco article and fix the Russian version (#289) 2020-12-08 01:13:35 +03:00
Oleg Kalachev
7831992d6a Edit 2020-12-08 01:12:25 +03:00
Volga
873befdba9 docs: Added translation of the wall_aruco article and fix russian version 2020-12-08 00:55:50 +03:00
Oleg Kalachev
c3cbc305c3 docs: add clarification on set_rates rotation directions 2020-12-05 04:27:47 +03:00
Oleg Kalachev
b71e802a2e docs: replace Pixracer with COEX pix on the main page 2020-12-05 04:07:38 +03:00
Ralf Seidel
3c5f2c958e docs: add heading for "Brushless motors" (#287) 2020-12-04 00:19:00 +03:00
Oleg Kalachev
267993aec4 Add title to the main clover page 2020-12-03 00:24:15 +03:00
Oleg Kalachev
86dd42c3b3 docs: fix main illustration (en) 2020-12-02 04:06:00 +03:00
Oleg Kalachev
9d338d843b docs: fix 2020-11-30 22:15:38 +03:00
Oleg Kalachev
3e100bee91 docs: add note on Pixhawk firmware to setup article 2020-11-30 22:15:32 +03:00
Oleg Kalachev
8a29b9a37a Fix checking unused assets 2020-11-30 00:55:40 +03:00
Oleg Kalachev
2e80a06db1 docs: add main clover image for black background 2020-11-29 23:03:10 +03:00
Oleg Kalachev
0003985c3b docs: fix typos 2020-11-28 01:37:41 +03:00
Oleg Kalachev
f250916ede docs: fix more images in coex pix article 2020-11-28 01:33:27 +03:00
Oleg Kalachev
ee2944a1d3 docs: fix images in coex_pix article 2020-11-27 17:57:47 +03:00
Oleg Kalachev
a088524468 docs: change /led redirect to English version, shortened links in examples 2020-11-27 03:06:57 +03:00
Oleg Kalachev
215fe237ca docs: fix check_unused_assets 2020-11-26 22:54:12 +03:00
Oleg Kalachev
8c1b5c19d0 readme: update main illustration 2020-11-26 20:39:57 +03:00
Oleg Kalachev
779dfb3f4f docs: update the main illustration 2020-11-26 20:36:24 +03:00
Oleg Kalachev
23d503adc5 docs: correct mode and kill switch channels 2020-11-26 19:56:04 +03:00
Oleg Kalachev
0350ecbff7 docs: decrease pix-sd.png size 2020-11-26 18:18:01 +03:00
Oleg Kalachev
12bed337dc docs: add video review of the simulator 2020-11-26 18:18:01 +03:00
Volga
6a1b609ccd docs: Update COEX Pix rev. 1.2 images 2020-11-25 11:16:38 +03:00
Oleg Kalachev
3d5c51a42e aruco_detect: add 'enabled' dynamic param 2020-11-24 22:34:12 +03:00
Oleg Kalachev
3702ed0c86 docs: reduce images sizes in setup section 2020-11-24 15:03:23 +03:00
Oleg Kalachev
741abadb54 docs: add big leg model 2020-11-23 20:55:41 +03:00
Oleg Kalachev
c6dc732867 docs: remove trailing whitespace 2020-11-23 20:45:32 +03:00
Oleg Kalachev
ba76e51966 docs: fix blocks page address 2020-11-21 13:38:21 +03:00
Oleg Kalachev
7951f0e2ba docs: minor fix 2020-11-20 17:55:06 +03:00
Alamoris
cd58c03c0f docs: assembling grips and fpv (ru) (#283)
* docs: Add article about magnetic and mechanical grippers

* docs: Add and fix article about install fpv camera

* fpv: orthography, punctuation, and style

* magnetic_grip: orthography and punctuation

* mechanical_grip: orthography, punctuation, and style

* Fix lists style

* docs: Added magnetic grip assembly images

* docs: Add translate draft

* docs: Delete article translations

* docs: Fix incorrect image name

* docs: Add images about assembling FPV

* docs: Fix images size

* Optimize images

* Fixes

Co-authored-by: Oleg Kalachev <okalachev@gmail.com>
2020-11-20 15:12:20 +03:00
Oleg Kalachev
ce6b2530c4 image: don’t install espeak to reduce size 2020-11-17 15:13:56 +03:00
Oleg Kalachev
14e4af76aa image: change clone depth to 1 2020-11-17 15:12:59 +03:00
Konstantin Petrykin
f3f1557b0b docs: add COEX Duocam docs (ru) (#285)
* docs: add COEX Duocam docs

* Orthography and punctuation

* docs: change gitup_seek image resolution and extension

* docs: add missing duocam-mavlink article

* docs: fix linter problems in duocam-mavlink article

* docs: add a link to doucam-mavlink in SUMMARY

* Decrease the size of gitup_seek image

* Make duocam_mavlink a subsection of duocam section

* Edit duocam articles

Co-authored-by: Oleg Kalachev <okalachev@gmail.com>
2020-11-16 13:29:28 +03:00
Oleg Kalachev
18d410db24 docs: update setup section 2020-11-14 17:56:50 +03:00
Volga
207dc88579 docs: Update coex pix images 2020-11-13 12:26:23 +03:00
Oleg Kalachev
58f6ac4b39 simple_offboard: fix checking kill switch state 2020-11-12 06:43:46 +03:00
Oleg Kalachev
688e4f0ca9 docs: add link to blocks into programming intro article 2020-11-10 06:16:12 +03:00
Oleg Kalachev
7cbe823700 blocks: add set_duty_cycle block 2020-11-10 06:06:44 +03:00
Oleg Kalachev
df681e0a79 docs: add gpio info to block article 2020-11-10 06:06:20 +03:00
Oleg Kalachev
8aad2fc363 blocks: fix units in set_servo tooltip 2020-11-10 04:10:41 +03:00
Oleg Kalachev
3c8dd14c9d docs: update clover versions images 2020-11-10 01:22:37 +03:00
Volga
3a20bc3212 docs: Fix broken image link 2020-11-05 01:20:56 +03:00
Volga
1105cd8750 docs: Add clover 4 sphere guard image 2020-11-04 23:53:21 +03:00
Alamoris
43d7e7c70b docs: add article on assembling clover 4.2 WS (en) (#282)
* docs: Add translation of the article about assembling clover 4_2 WS

* Minor fix

* docs: Add new renders and update article

Co-authored-by: Oleg Kalachev <okalachev@gmail.com>
2020-11-04 01:34:44 +03:00
Oleg Kalachev
7a1e885df1 blocks: add led_count block 2020-11-04 00:45:34 +03:00
Oleg Kalachev
9a9c2d5c9f blocks: fix gpio blocks indentation 2020-11-03 23:23:40 +03:00
Alamoris
eaeb146878 docs: add article about assembling sphere guard (#284)
* docs: Add article about assembling sphere guard

* docs: Add article into summary

* docs: Fix broken images

* Shorten the article name

Co-authored-by: Oleg Kalachev <okalachev@gmail.com>
2020-11-03 17:56:57 +03:00
Volga
2452be05ff docs: Update assembly images 2020-11-03 17:13:40 +03:00
Oleg Kalachev
a4336a39c9 blocks: change default z to 1 in aruco-marker example 2020-11-03 16:58:26 +03:00
Volga
9cdf7dea41 docs: Update assembliy renders in article aboutt clover WS 2020-11-03 16:03:37 +03:00
Volga
b90dc3c020 docs: Update article about coex pix 2020-11-03 13:27:07 +03:00
Oleg Kalachev
91252d8d50 image: decrease git clone depth 2020-10-31 22:31:21 +03:00
Oleg Kalachev
c4b94390e9 image: increase compression level more 2020-10-31 18:11:10 +03:00
Oleg Kalachev
1b4167365e image: increase compression level 2020-10-31 16:48:01 +03:00
Oleg Kalachev
01ec592abb docs: remove unused assets 2020-10-28 17:07:39 +03:00
Volga
e2e2e04381 docs: Fix image link 2020-10-28 11:44:45 +03:00
Andrei Korigodski
27e0189cf5 readme: remove table with logos 2020-10-27 20:35:54 +03:00
Andrei Korigodskii
e3d89cbc4c readme: change title and update description
Co-authored-by: Oleg Kalachev <okalachev@gmail.com>
2020-10-27 20:13:11 +03:00
Oleg Kalachev
a0ac85e0d3 led: change default number of leds to 72 2020-10-27 19:52:02 +03:00
Oleg Kalachev
83e5911110 docs: add pid tuning stand idea to projects 2020-10-27 19:38:29 +03:00
Oleg Kalachev
05d634d2d3 docs: make download link to vm more notable 2020-10-27 19:38:29 +03:00
Oleg Kalachev
4967d651bd docs: add default username/password info to the simulator vm article 2020-10-27 19:38:29 +03:00
Volga
91f948d3f4 docs: Fix orthography 2020-10-27 17:07:59 +03:00
Volga
ebf55244f4 docs: Update renders in the article about clover 4_2 WS 2020-10-27 17:01:53 +03:00
Oleg Kalachev
5b6d08e25d blocks: fix set_leds with color-typed argument 2020-10-25 19:20:45 +03:00
Oleg Kalachev
8036214406 Merge branch 'master' of github.com:CopterExpress/clever 2020-10-24 21:53:06 +03:00
Oleg Kalachev
5d3c8c89cb builder: make pi an owner of examples files 2020-10-24 21:52:54 +03:00
Oleg Kalachev
2075fa52ef examples: make leds.py more verbose 2020-10-24 21:52:34 +03:00
Andrei Korigodski
b0e1e1ffae docs: fix translation 2020-10-24 09:46:21 +03:00
Oleg Kalachev
4482f973db docs: editing 2020-10-23 13:08:09 +03:00
Oleg Kalachev
b1c7ee6b66 docs: editing 2020-10-23 12:35:41 +03:00
Oleg Kalachev
ff9e669352 docs: minor fixes 2020-10-23 12:23:53 +03:00
Oleg Kalachev
6c8291749f simple_offboard: correctly check manual control timeout, separate it from kill switch check 2020-10-22 19:12:51 +03:00
Alamoris
039d2438cd docs: paragraph about changes in Coex pix version 1.2 (#281)
* docs: add description about coex pix 1.2

* docs: Add new revision 1.2 schemes

* docs: More changes added
2020-10-22 11:09:40 +03:00
Oleg Kalachev
048a605f2f simple_offboard: detect killswitch when auto_arm is set (#280)
* simple_offboard: subscribe to manual control

* simple_offboard: read check_kill_switch parameter

* simple_offboard: check kill switch status

* Fixes
2020-10-20 09:58:32 +03:00
Oleg Kalachev
0e0d4fe5cc led.launch: add led_count and gpio_pin arguments (#279)
* led.launch: add led_count and gpio_pin arguments

* docs: new args in led.launch
2020-10-19 20:58:31 +03:00
Oleg Kalachev
37bbd7522c Merge branch 'master' of github.com:CopterExpress/clover 2020-10-19 16:23:37 +03:00
Oleg Kalachev
f206b2ea18 docs: update projects list 2020-10-19 15:55:07 +03:00
Oleg Kalachev
ae7f3ccfbc docs: new cad models (#277)
* docs: add new cad models for milling

* docs: Сompleted new model table

* docs: orthography

Co-authored-by: Volga <gonzalez1139@gmail.com>
2020-10-17 07:44:37 +03:00
Oleg Kalachev
da28c61f1e main_camera.launch: add forward and backward camera shortcuts 2020-10-15 18:40:17 +03:00
Oleg Kalachev
b7ce588b07 blocks: add yaw_tolerance parameter 2020-10-13 15:22:13 +03:00
Oleg Kalachev
f663a62c1e blocks: add confirm_run parameter for disabling running confirmation 2020-10-13 15:14:41 +03:00
Oleg Kalachev
5b370ee96b blocks: treat cancel in prompt as empty string 2020-10-13 15:06:26 +03:00
Oleg Kalachev
e0512c209e Add code headers 2020-10-13 01:15:56 +03:00
Oleg Kalachev
7385f673de blocks: remove unused code 2020-10-13 01:12:30 +03:00
Oleg Kalachev
5629b0a9b3 led: change default low battery threshold to 3.6 2020-10-08 21:06:29 +03:00
Alamoris
9de0034fb9 docs: add article on assembling Clover 4.2 ws (#275)
* docs: add article about soldering clever

* Rename soldering 4.2 to 4.2 WorldSkills

* Change 4.2 article title

* Add link to 4.2 WS to assembly page

* Edit orthography in 4.2 WS assembly

* Edit orthography in 4.2 assembly

* Edit orthography in 4.2 assemble (en)

* Edit ponctuation in 4.2 WS assemble article

* Edit punctuation in 4.2 assemble article

* assemble 4.2 WS: remove unnecessary emphases + editing

* assemble 4.2: remove unnecessary emphases + editing

* Edit assemble 4.2, remove unnecessary emphases (en)

* Make COEX Pix a link in assemble 4.2 article

* Make COEX Pix a link in assemble 4.2 WS article

Co-authored-by: Oleg Kalachev <okalachev@gmail.com>
2020-10-08 20:45:03 +03:00
Oleg Kalachev
ae29fe00d3 docs: fix typo in css 2020-10-08 06:23:42 +03:00
Oleg Kalachev
7295c6c040 docs: make images in assembly articles links 2020-10-08 05:31:18 +03:00
Oleg Kalachev
11bdf2d3da docs: fix link in assembly articles 2020-10-08 05:10:01 +03:00
Alamoris
89ccca3c30 docs: instructions for connecting to LogMeIn VPN (#276)
* docs: add article about connection to vpn

* docs: fix

* docs: add instruction for connecting using Windows

* Edit vpn_hamachi article

* docs: update hamachi_vpn article

Co-authored-by: Oleg Kalachev <okalachev@gmail.com>
2020-10-07 18:39:06 +03:00
Oleg Kalachev
50218a8822 docs: add COEX Pix mention in firmware flashing article 2020-10-07 03:53:14 +03:00
Oleg Kalachev
11d5da5db2 docs: minor change 2020-10-07 00:18:02 +03:00
Oleg Kalachev
757129782e docs: add simulator screenshot 2020-10-07 00:17:28 +03:00
Oleg Kalachev
f7c1f4330d docs: add link to cad-models article to assembly section 2020-10-06 04:18:22 +03:00
Oleg Kalachev
b36292cdb8 blocks: add print-range example 2020-10-06 01:40:07 +03:00
Oleg Kalachev
a9f6fe329b docs: minor summary change 2020-10-06 01:38:19 +03:00
Oleg Kalachev
f89cc92736 docs: rename glossary articles back to glossary.md 2020-10-06 01:36:17 +03:00
Oleg Kalachev
60755fa1b5 gitbook: enable collapsible-menu (#270)
* gitbook: enable collapsible-menu

* Collapse main menu (en)

* Update events and supplementary materials articles

* Fix collapsed items of main menu

* Add Copter Hack 2021 article
2020-10-06 01:05:53 +03:00
Oleg Kalachev
792352d072 blocks: add flip example 2020-10-01 22:20:37 +03:00
Oleg Kalachev
2f6d9639c1 blocks: fix setpoint block 2020-10-01 22:20:26 +03:00
Oleg Kalachev
d52175bb30 docs: css cicle class 2020-10-01 05:10:26 +03:00
Oleg Kalachev
be6e894b80 docs: add led events table 2020-10-01 05:09:00 +03:00
Oleg Kalachev
2f6125ce54 Implement block programming (using Blockly) (#272)
* Clover Blockly: add first blocks set

* Adjust Blockly settings

* Fix get_position output type

* Add screenshot

* Rename readme.md to README.md

* Resize screenshot

* Add package.xml

* Little change

* Fixes

* Add python_compressed to blockly

* Implement some of the Clover blocks in Python

* Make Python indentation 4 spaces

* Fixes to Python blocks implementation

* Implement set_velocity block in Python

* Implement wait_arrival block in Python

* Fix indentation in Python implementation of blocks

* Fix

* Fix land_wait template

* Set reserved words in Python

* Change default frame_id to aruco_map in get_position block

* Fix

* Move blocks definitions to blocks.js

* Get rid of missing favicon error

* Simplify navigate

* Rearrange layout, add tabs

* Generate Python code

* Small style change

* -console.log

* Code style

* Use modules

* Move modules to the header

* Correct order for ROS definitions + generating "backend" code

* Fix rangefinder_distance block

* simple_offboard: commands to change only yaw and yaw rate

* Implement set_yaw block

* Start working on Blockly documentation

* Implement print block with a topic

* Unneeded code

* Little fixes

* Fix indentation

* Fixes

* Fix wait_arival, get_distance

* Implement running Blockly programs, implement prompt block, fixes

* Add land button

* Little change

* Fix reserved words + little fixes

* +x for main.py

* Simplify run button

* Auto-save and load workspace

* Make land button work

* Handle exceptions

* Minor change

* Add help URL for blocks

* Fix

* Implement arrived block

* Mark blockly and highlight.js as linguist-vendored

* Add forgotten CMakeLists.txt

* Add wait checkbox to set_yaw block

* Disable run button when disconnected

* Add message and service files

* Add some comments

* Add tooltip to some blocks

* Implement GPIO blocks

* Don’t latch print message to prevent duplication

* Prevent duplication prompts

* Add ROS init code to backend code anyways

* Make GPIO blocks color a constant

* Minor fix

* More correctly update blocks on input value changes

* Minor fixes

* Remove unneeded readonly attribute

* Add marker ID shadow blocks to toolbox

* Add lacking reserved words

* Fix frame id generation for complex marker id expressions

* Consider frame_id in set_yaw block

* Shorten ros module import

* Implement stop service

* Disable and enable run button correctly

* Don’t print KeyboardInterrupt exceptions

* Put notifications to notifications element

* Add 'running' mark

* Disable signal in backend python code

* Sleep a little bit to let rospy initialize publishers

* Remove accidental code

* Make ROS namespace and private namespace constants

* editorconfig-lint: don’t check Blockly code

* Use private namespace constant in Python generator

* Implement ~running topic to display current program status more robustly

* Make navigate tolerance and sleep time constants

* Make set_leds and and set_effect services proxies persistent

* Replace a number with constant

* Limit ~block topic publishing rate
Otherwise messages get queued making the frontend to freeze

* Improve internal documentation

* Append 'map' to frames list

* Return degrees in get_attitude block

* Move getting yaw in a separate block

* Improve block tooltips

* Add some more files to editorconfig-lint excludes

* Add get_yaw block to toolbox

* Implement get_time block

* Implement ~store and ~load services for storing user programs

* Set auto_arm only in take_off block

* Minor CSS fixes

* Make 'Python' tab textarea-like

* Implement saving and loading programs

* Adjust styles

* Retrieve only .xml files in load service

* Forgotten code

* Documentation on store and load services

* Add some examples

* Add blocks programming arg to launch file

* Update docs

* Add package’s dependencies

* Add dependency

* Add title to select

* Fix syntax

* Minor fix in docs

* Add forgotten roslib.js

* Run user program in the same process

* Use print function for print block in Python 2

* Add variables example

* Fix url

* Add functions example

* Fix set_servo block

* Fix gpio_read block

* Update blocks screenshot

* Update docs

* Update docs

* Fix set_effect block

* Minor fix in example

* Add setpoint block, remove set_velocity from toolbox

* Remove unused modules

* Unused variable

* Add English article skeleton

* Clarify backend node link error

* Remove unused variable

* Update documentation

* Fix link to documentation

* Add Blockly logo

* Update English article

* Add Blocks programming link to the main page

* Minor change

* Add catkin_install_python to CMakeLists.txt

* Make navigate tolerance and sleep time configurable

* Add minor todo

* Add blockly examples directory to editorconfig-lint excludes

* Rename main node to clover_blocks

* Add a warning to the old blocks programming article

* Fix editorconfig-lint exclude
2020-09-30 17:07:03 +03:00
Volga
c6a238c671 docs: fixed gpio port assignment 2020-09-10 14:46:58 +03:00
Volga
28851f39ad docs: add notice 2020-09-10 13:56:57 +03:00
Oleg Kalachev
4a2fbf3e22 docs: minor fixes 2020-09-08 17:01:13 +03:00
Oleg Kalachev
c3b549df0d docs: little titles fix 2020-09-03 01:17:25 +03:00
Oleg Kalachev
21d922beb2 simple_offboard: set main_camera_optical reference frame to map 2020-09-02 16:40:14 +03:00
Alamoris
88ef00e043 docs: translate flight articles (#269)
* docs: Add translation of the article about flight exercises

* docs: Add translation of the article about flight

* docs: Added articles about flying to summary

* docs: fix

* Update docs/en/flight.md

Co-authored-by: Oleg Kalachev <okalachev@gmail.com>

* docs: fix

* Fix

* Remove unnecessary image borders

* Edit flight article

* Edit flight articles

* Fix

Co-authored-by: Oleg Kalachev <okalachev@gmail.com>
2020-08-27 08:41:24 +03:00
Oleg Kalachev
1b85a9cdf2 docs: simplify laser rangefinder code sample 2020-08-26 18:25:58 +03:00
Oleg Kalachev
d5a79babad docs: add links to demo js gcs sources 2020-08-26 18:14:10 +03:00
alamoris
4d80e6f6cf docs: Small fixes 2020-08-25 19:05:44 +03:00
Oleg Kalachev
8ce4de191b docs: little fix 2020-08-25 17:32:13 +03:00
Oleg Kalachev
ecaab1650f docs: edit snippets articles 2020-08-21 22:46:39 +03:00
Oleg Kalachev
a62107132d docs: add wait_arrival snippet 2020-08-21 22:08:07 +03:00
Oleg Kalachev
423490304b docs: add link to Clover blocks development 2020-08-21 19:37:18 +03:00
Oleg Kalachev
ee9c956fc7 docs: add cloud simulator to projects 2020-08-21 19:28:31 +03:00
oponfil
14e49a4f7b docs: update projects article (#268)
* Update projects.md

Скорректировал статус по существующим проектам и добавил новые проекты.

* docs: edit projects article

Co-authored-by: Oleg Kalachev <okalachev@gmail.com>
2020-08-21 19:21:29 +03:00
Oleg Kalachev
d4d25c61a2 docs: rework and simplify navigate_wait snippet, move it on top 2020-08-21 18:16:25 +03:00
Oleg Kalachev
106209d79b docs: add links to Python tutorials 2020-08-20 20:28:55 +03:00
Oleg Kalachev
333cf9655f docs: remove trailing whitespace 2020-08-20 20:09:32 +03:00
Oleg Kalachev
2ddf831842 docs: remove trailing whitespace 2020-08-20 20:07:35 +03:00
Oleg Kalachev
68f23babcc docs: more reasonable title 2020-08-20 20:06:44 +03:00
Alexey Rogachevskiy
4f0a099152 travis: Remove changelog generation 2020-08-13 22:53:44 +03:00
Alamoris
acfb858598 docs: add section about manual flight (#267)
* docs: Add article about flight

* docs: add flight lessons

* Update docs/ru/flight.md

Co-authored-by: Alexey Rogachevskiy <sfalexrog@gmail.com>

* Update docs/ru/flight.md

Co-authored-by: Alexey Rogachevskiy <sfalexrog@gmail.com>

* Update docs/ru/flight.md

Co-authored-by: Alexey Rogachevskiy <sfalexrog@gmail.com>

* Update docs/ru/flight.md

Co-authored-by: Alexey Rogachevskiy <sfalexrog@gmail.com>

* Update docs/ru/flight.md

Co-authored-by: Alexey Rogachevskiy <sfalexrog@gmail.com>

* Update docs/ru/flight.md

Co-authored-by: Alexey Rogachevskiy <sfalexrog@gmail.com>

* Update docs/ru/flight.md

Co-authored-by: Alexey Rogachevskiy <sfalexrog@gmail.com>

* docs: Updates

* Edit flight article

* Move flight exercises to separate article

* sfalexrog edits

* docs: add exercise

* docs: update exercises

* docs: Some fixes

* Edit flight exercises article

* docs: Resize and change images

* Reduce images sizes more

Co-authored-by: Alexey Rogachevskiy <sfalexrog@gmail.com>
Co-authored-by: Oleg Kalachev <okalachev@gmail.com>
2020-08-12 04:45:45 +03:00
Alexey Rogachevskiy
c0f15fc1e6 clover.world: Shift parquet_plane down
This will make placing thin objects easier.
2020-08-11 23:26:58 +03:00
Alexey Rogachevskiy
e94e552da3 travis: Show compressed image size
Squashed commit of the following:

commit 3460fec25e
Author: Alexey Rogachevskiy <sfalexrog@gmail.com>
Date:   Mon Aug 10 00:19:05 2020 +0300

    travis: Use short commit notation

commit fa44e4b42f
Author: Alexey Rogachevskiy <sfalexrog@gmail.com>
Date:   Sun Aug 9 01:00:56 2020 +0300

    travis: Fix IMAGE_VERSION initialization

commit 4bc985a7f4
Author: Alexey Rogachevskiy <sfalexrog@gmail.com>
Date:   Sat Aug 8 21:26:06 2020 +0300

    travis: Generate zip file after successful build

commit c2bfa07a36
Author: Alexey Rogachevskiy <sfalexrog@gmail.com>
Date:   Fri Aug 7 17:36:56 2020 +0300

    travis: Fix parsing errors

commit 6ba27ef15d
Author: Alexey Rogachevskiy <sfalexrog@gmail.com>
Date:   Fri Aug 7 17:28:48 2020 +0300

    travis: Output compressed image size
2020-08-10 13:47:17 +03:00
Oleg Kalachev
6b5e3464f1 Remove info clover.service as it's only for Raspbian 2020-08-06 15:24:44 +03:00
Oleg Kalachev
768c0be5a5 Change running section in clover's readme 2020-08-06 14:56:49 +03:00
Oleg Kalachev
892c6f853b docs: add note on how to reset Clover package using git (ru, en) 2020-08-05 14:03:08 +03:00
Alexey Rogachevskiy
8b690900b9 simple_offboard: Ensure quaternion initialization 2020-08-04 14:51:40 +03:00
Alexey Rogachevskiy
17c210919d simple_offboard: relax position_msg timestamp update rules (#264)
* simple_offboard: Relax position_msg timestamp update rules

* Little code style change

Co-authored-by: Oleg Kalachev <okalachev@gmail.com>
2020-08-04 13:19:58 +03:00
Alexey Rogachevskiy
ebeb3f58d6 docs/simulation_usage: Performance suggestions (#262) 2020-07-31 16:25:44 +03:00
Alexey Rogachevskiy
3b19346a44 docs: Add note about Gazebo in SITL articles (en/ru) 2020-07-30 17:31:56 +03:00
Dmitrii Okoneshnikov
32d27f3f66 Tried translating simulator articles (#259)
* Translation + fix broken links

* Fix broken links

* Changes

* Fixed some typos

* Fixed header

* docs/simulation_native: Stylistic changes, fix typo (ru)

* docs: Add old simulation_vm article (ru)

* docs: Update SUMMARY.md (ru)

* docs/simulation_native: Use main branch for simulation (ru)

* Fixed some stuff

* Update docs/ru/simulation_native.md

Co-authored-by: Ilya Petrov <38784273+copterspace@users.noreply.github.com>

* Fixed some stuff

* Removed extra spaces

Co-authored-by: Oleg Kalachev <okalachev@gmail.com>

* Fixed typo

Co-authored-by: Oleg Kalachev <okalachev@gmail.com>

* Fixed typo

Co-authored-by: Oleg Kalachev <okalachev@gmail.com>

* Update docs/ru/simulation_vm.md

Co-authored-by: Oleg Kalachev <okalachev@gmail.com>

* Removed extra spaces

Co-authored-by: Oleg Kalachev <okalachev@gmail.com>

* Fixed typo

Co-authored-by: Alexey Rogachevskiy <sfalexrog@gmail.com>
Co-authored-by: Ilya Petrov <38784273+copterspace@users.noreply.github.com>
Co-authored-by: Oleg Kalachev <okalachev@gmail.com>
2020-07-29 13:08:19 +03:00
Alexey Rogachevskiy
585af026d4 docs/simulation_native: Use main branch for simulation (en) 2020-07-29 00:27:59 +03:00
Dmitrii Okoneshnikov
b52f702723 Fixed broken links (#258) 2020-07-27 17:06:24 +03:00
Dmitrii Okoneshnikov
3e4f1ab8a9 Fixed broken links (#257) 2020-07-27 17:05:43 +03:00
Dmitrii Okoneshnikov
513f4f419a Fixed broken link (#256) 2020-07-27 13:20:51 +03:00
Dmitrii Okoneshnikov
6db5602a5a Fix for errors occurred while running catkin_make (#255)
* Fix errors occurred while running catkin_make

* docs: Fix formatting, add reason for patching

Co-authored-by: Alexey Rogachevskiy <sfalexrog@gmail.com>
2020-07-27 12:50:14 +03:00
alamoris
fb17a3f926 docs: fix bolts size 2020-07-22 15:16:15 +03:00
Oleg Kalachev
70e1675f9a docs: fix 2020-07-22 15:09:42 +03:00
Alexey Rogachevskiy
e03eaa51c4 Add official Clover simulation config (#254)
* clover_description: Add preliminary configs/models

* clover_description: Use proper models for the drone

* clover_description: Be more specific about spawn arguments

* clover_description: Tweak parameters a bit, add collision boxes

* travis: Add .dae files to the list of ignored by eclint

* Add clover_simulation package

* clover: Add Gazebo plugin sources

* builder: Ignore clover_gazebo_plugins for actual drone

* clover_gazebo_plugins: Expose include directories for plugins

This should fix building the unit tests

* clover_gazebo_plugins: Remove dependency on gazebo_ros

This should prevent RPi image failing to build.

* travis, gitattributes: Mark clover_gazebo_plugins as vendored, stop checks

* clover_simulation: Minor package.xml fix

* clover_description: Add IMU joint preservation

Oh, Gazebo, you are ever so very helpful, it's hard to put my appreciation into words! If not for your helpful model simplification, I wouldn't have spent two hours looking through the plugin sources, the urdf sources, trying lots of
different options for the joints and links, and finally getting an answer from GazeboOverflow or however you've named your Q&A site. How wonderful it is to have an issue that makes you tear your hair out just because you know
what's better for me!

* clover_simulation: Add the bare necessities to run a simulation

* clover_gazebo_plugins: Prevent gazebo from trying to download models

For some reason the models are no longer available, so Gazebo just spends some time waiting for a timeout.

* clover_gazebo_plugins: Update Gazebo model database URI

* clover_simulation: Add script to find and launch PX4

* clover_simulation: Fix launch file

* clover_description: Add missing plugins

* simulation: Re-enable gazebo_ros dependencies

This will force rosdep to try to install gazebo_ros on the drone,
but this can be counteracted by --skip-keys rosdep option.
This does not look reliable, but I could not come up with a better
solution.

* builder: Be more resilient about apt-get errors

* builder: Remove reference to resolve_rosdep

* clover_description: Update Clover model, change xacro description

Previous xacro description file was not performing too well, so I went with
a more sensible route and started changing iris.xacro to use our Clover model.

* clover_description: Bring back constants.xacro

* clover_description: Prevent lumping for camera link/joint

* clover_description: Move near clipping plane further away

* clover_description: Allow setting width/height for rpi_cam

* clover_description: Add a Clover model with a camera

* clover_description: Remove whitespaces

* clover_description: Add drone+camera spawning .launch file

* clover_simulation: Add gazebo_ros here as well

* clover_simulation: Spawn drone with camera by default

* clover_simulation: Allow specifying data path for px4

* clover_simulation: Add startup scripts from px4

Big TODO: Clean them up eventually

* clover_simulation: Use local data files

* clover_simulation: Launch clover services by default

* clover_description: Depend on gazebo_plugins as well

libgazebo_ros_camera is in gazebo_plugins, so we need that package.

* clover_description: Fix camera_sensor description

* clover_description: Fix typo in package.xml

* clover_simulation: rename sim_gazebo.launch to simulator.launch

* clover_simulation: Don't look for ROMFS in px4_source_path

We provide our own, no reason to fail if we can't find the originals.

* clover_simulation: Remove extra CMakeLists.txt

* clover_description: Use xacro: namespace for xacro macros

* clover_description: Fix package.xml formatting

* clover_description: Better camera defaults

* clover_description: Add distance sensor

* clover_description: Add leg colliders

* clover_simulation: Actually forward vehicle name

* clover_description: Revert adding additional colliders

Unfortunately, this breaks physics too much

* clover_description: Tweak drone physics, make it more bouncy

* clover_description: Don't spawn the drone inside the floor

* clover_description: Set rangefinder min range outside drone collider

* clover_simulation: Set default flow parameters for Clover

* clover_description: Update Clover 4 model

* clover_simulation: rename sim_gazebo.launch to simulator.launch (#233)

* clover_simulation: Add workaround for Gazebo crashes in VMware

* clover_simulation: Ignore .git for now

* clover: Add "simulated" argument

* clover_simulation: Start Gazebo early

* clover_gazebo_plugins: Remove unused files

* clover_description: Allow turning sensors on and off

* clover_description: Fix rangefinder creation

* Remove unneeded stuff and use PX4 from catkin workspace

* Remove clover_gazebo_plugins

* Rename arg simulated to simulator

* clover: Change target names to avoid clashing with PX4

* Fix

* clover_simulation: Re-add deleted comments

* Add loop model

* loop.material: use tabs instead of spaces

* loop model: don’t rotate by yaw

* loop.material: turn on alpha_blend

* Rename model loop to loop_line

* Add parquet plane model

* loop_line: fix description

* Set alpha_blend for loop_solid material

* Add square line model

* Add CATKIN_IGNORE to models directory

* Add LED strip Gazebo model

* Add hardcoded URDF LED strip

* clover_description: Add LED xarco model

clover_simulation: Implement LED visual plugin and controller

* clover_simulation: Make led plugin less chatty

* clover_simulation: Depend on led_msgs

This should allow the packages to be built in the proper order.

* clover_simulation: Support building against Kinetic

* clover_simulation: Don't build plugins if Gazebo is not installed

* clover_description: Get rid of "constants" file

* clover_description: Add README

* clover_simulation: Add README

* clover_simulation: Make parquet thicker

Otherwise the rangefinder beam goes right through it.

* docs: Start working on simulation articles

* docs: Start working on the simulation overview (en)

* Add launch-file for PX4 v1.8.2

* clover_simulation: Disable GPS, use EKF2 by default

Ideally we should be using LPE, but it is broken in PX4 1.10, and our need for a somewhat working simulator is higher than for a completely correct one.

* clover_simulation: Add experimental throttling camera

* clover_simulation: Add note about throttling camera

* clover_description: Remove unused file

* clover_simulation: Link against CameraPlugin

* clover_description: Add option to use throttling_camera

* Add clover.world

* clover_description: Add calculated inertial parameters

* simulator: change default world to clover.world

* clover_simulation: Start working on ArUco generation script

Port over aruco_gazebo_gen, add more options.
Does not modify the world right now.

* clover_simulation: Make LED plugin less chatty

* clover_description: Be more ROS-like in script naming

* clover_simulation: Implement model insertion to the world

* clover_simulation: Allow specifying output model dir

* clover_description: Don't use throttling camera by default

throttling camera is still a work-in-progress, there's no reason to
enable it by default.

* clover_simulation: Use proper script name in CMakeLists

This is what typically happens when I'm rushed.

* docs: Add instructions for VM setup (en)

* clover_simulation: Remove extra spaces

* docs: Describe simulation usage (en)

* clover_simulation: Remove led_strip

* docs/assets: Crunch sim image a bit

* clover: Bump VL53L1X version

For some reason, 0.0.2 is not installable on x86.

* docs/simulation: Fix capitalization

* Remove remnants of clover_gazebo_plugins

* Remove unneeded Clover 3 model

* Remove empty.world and asphalt_plane model

* Remove unused LED strip model

* Reduce images size

* Shortened simulator related urls

Co-authored-by: Oleg Kalachev <okalachev@gmail.com>
2020-07-22 09:17:56 +03:00
Oleg Kalachev
b346af92ae docs: add missing z parameter 2020-07-21 23:23:52 +03:00
Oleg Kalachev
cb2cf48f39 docs: add possible projects list (ru) 2020-07-21 10:33:41 +03:00
Alexey Rogachevskiy
ebec850010 docs: add contributed models for Jetson Nano (#250)
* docs: Add contributed models for Jetson Nano

* Add Vyacheslav Buzov’s contact link

Co-authored-by: Oleg Kalachev <okalachev@gmail.com>
2020-07-18 17:37:02 +03:00
Vasily Yuryev
5a8db188f6 docs: add English version of Innopolis Open 2020 L22_AERO team article (#252) 2020-07-17 21:41:07 +03:00
Vasily Yuryev
4376bbb723 docs: add Innopolis Open 2020 L22_AERO team article (#251)
* L22_AERO docs

* remove html code

* docs: edit Innopolis Open 2020 (L22_AERO) article

Co-authored-by: Oleg Kalachev <okalachev@gmail.com>
2020-07-17 19:25:01 +03:00
alamoris
1d694e1a15 docs: Delete m3x6 image 2020-07-16 16:31:26 +03:00
alamoris
54de7fca3a docs: Fix clover 4.2 equipment list 2020-07-16 16:21:51 +03:00
alamoris
32f5584082 docs: Fix text formatting 2020-07-14 21:57:51 +03:00
alamoris
d7c0fb33ff docs: Add hint about FC rotation 2020-07-14 19:27:38 +03:00
alamoris
d16e7bf155 docs: delete not used images 2020-07-14 15:02:06 +03:00
alamoris
26bd1e2d8f docs: Rework type size table 2020-07-13 22:05:31 +03:00
alamoris
e12577cf0e docs: Fix md typo 2020-07-13 13:49:59 +03:00
alamoris
12544a69af docs: add notification about ir sensors article 2020-07-10 20:45:16 +03:00
alamoris
f471280bef docs: Hidden articles about working with ir sensors 2020-07-10 17:33:08 +03:00
alamoris
2ff9887ac4 docs: fix broken image link 2020-07-09 22:24:49 +03:00
alamoris
2ecfb28a5d docs: Rename assembly clover 4 article 2020-07-09 20:43:51 +03:00
Alamoris
091483226f Update assembling clover 4 (#243)
* docs: Rewrite article  article about assembling clever 4

* docs: Update

* docs: Update clever 4 assemble

* docs: remove old assembly instructions images

* docs: Renamed and returned  assembly instruction about clover 4.0, some fixes

* docs: Add english version of the article, some fixes

* docs: resize assemble images

* docs: fix assets check errors

* docs: fix image size error
2020-07-09 19:39:45 +03:00
Oleg Kalachev
af16414c77 docs: fix links to PDF 2020-06-30 23:01:20 +03:00
Oleg Kalachev
696214e717 docs: add link to PDF docs 2020-06-30 22:44:55 +03:00
Alexey Rogachevskiy
15fd156ce0 travis: Install mscorefonts 2020-06-30 22:31:51 +03:00
Alexey Rogachevskiy
82bfb961a4 travis: Fix travis formatting 2020-06-30 22:04:59 +03:00
Alexey Rogachevskiy
c50637c60f travis: Generate pdf in _book 2020-06-30 22:04:59 +03:00
Alexey Rogachevskiy
a26f0f41e7 docs: Fix capitalization 2020-06-30 22:04:59 +03:00
Alexey Rogachevskiy
28690491c2 travis: Build gitbook pdf 2020-06-30 22:04:59 +03:00
Oleg Kalachev
ca1323faf3 Merge pull request #244 from goldarte/genmap-update
genmap.py: Add x0 and y0 shift for markers coordinates
2020-06-25 13:09:06 +03:00
Arthur Golubtsov
b713b49f3f genmap.py: Add x0 and y0 shift for markers coordinates 2020-06-25 00:31:56 +03:00
Alexey Rogachevskiy
62a77519e6 docs: Remove notice about compressed topics
Not relevant since web_video_server 0.2.1+.
2020-06-15 18:07:50 +03:00
Arthur Golubtsov
e1bf8aade5 docs: assemble Clover 4.1 version (#232)
* docs: Add russian instruction for building intermediate frame for Clover 4

* Update SUMMARY.md

* docs: Fix typos

* docs: Rename intermediate to 4.1

* Fix links, remove unnecessary asset

* Fix header

* Move article header down

* Small fixes

* Fix link to clover 4.0 frame
2020-06-15 17:00:20 +03:00
Oleg Kalachev
0172d6e892 docs: simplify leds examples 2020-06-13 02:11:45 +03:00
Oleg Kalachev
bcb7351a90 roswww_static: infrastructure for web-based Clover plugins (#230)
* Package for generating static web sites for ROS

* rosstatic: add CMakeLists.txt

* rosstatic: utilize rospkg, store static directory in ROS_HOME

* rosstatic: default_package param

* rosstatic: fix URLs in docs

* clover.launch: make clover the default package for www

* Unused import

* Rename rosstatic to roswww_static

* Fixes
2020-06-13 02:08:00 +03:00
Oleg Kalachev
c71a46ce9d Put CATKIN_IGNORE file to some directories 2020-06-11 16:13:07 +03:00
Oleg Kalachev
91dd7799ef image: remove unneeded ROS_DISTRO settings from .bashrc 2020-06-10 23:25:57 +03:00
Oleg Kalachev
3682e253a7 selfcheck.py: don’t fall when ROS_HOSTNAME is not set 2020-06-10 22:40:49 +03:00
Alexey Rogachevskiy
66ecbb4d09 docs: Update ROS repository keys (en/ru) 2020-06-10 14:51:13 +03:00
Alexey Rogachevskiy
f041b6125b Add Avahi services broadcasting (#231)
* Builder: Add Avahi services broadcasting

* avahi-services: Remove http.service

* builder: Expose sftp-ssh instead of just ssh, fix build
2020-06-03 22:35:48 +03:00
Oleg Kalachev
a4f2bab3d7 docs: fix incorrect order of focused and unfocused camera image 2020-06-03 08:15:35 +03:00
Oleg Kalachev
56bcfa5c87 Enable back publishing documentation 2020-06-02 11:54:08 +03:00
Alexey Rogachevskiy
998796045c aruco_pose nodelet cleanup (#239)
* aruco_pose: Unhardcode contour refinement

Besides, this was basically a no-op anyway, since dynamic parameters
overwrote that anyway.

* aruco_pose: Late-construct objects that use ROS

* aruco_map: Don't create/store node handle

* aruco_pose: Don't assume dist_coeffs size

* aruco_pose: more const == more better

* aruco_pose: Be more obvious about changing variables

* aruco_pose: Fix building for Kinetic

* aruco_pose: Remove global add_definitions
2020-05-30 01:59:51 +03:00
Alexey Rogachevskiy
c5e954b56a optical_flow: Use functional-style parameter fetching 2020-05-30 01:58:14 +03:00
Alexey Rogachevskiy
2814fea9cd optical_flow: Pass nodelet callback queue to TransformListener 2020-05-30 01:58:14 +03:00
Alexey Rogachevskiy
b85326c02a optical_flow: Use cv::Mat(std::vector, bool) ctor for dist_coeffs_ 2020-05-30 01:58:14 +03:00
Alexey Rogachevskiy
98d5d50607 aruco_pose: Prevent OpenCV from crashing (#238)
* aruco_pose: Add tests that crash OpenCV

* aruco_pose: Don't try to interpolate single points
2020-05-30 01:57:14 +03:00
Alexey Rogachevskiy
69c46786de builder: Set apt retries to 3
This should lower the number of builds that failed due to
repositories being unstable
2020-05-29 21:25:58 +03:00
Oleg Kalachev
abb495275b docs: translate robocross-2019 article 2020-05-26 06:54:42 +03:00
Oleg Kalachev
044d6c6d33 docs: switch lpe and ekf2 settings in aruco map navigation articles 2020-05-21 21:01:16 +03:00
Alexey Rogachevskiy
22d5a356b6 clover: Update ros3djs, THREE.js 2020-05-18 16:25:56 +03:00
Alexey Rogachevskiy
c7828557ca standalone_install: Fail on error 2020-05-16 15:49:38 +03:00
Alexey Rogachevskiy
514c0f1b65 Flysky FS-A8S article (#229)
* docs: Add FS-A8S article draft

* docs: Fix image links

* docs/flysky_a8s: Make images appear smaller

* docs: Add animated images

* docs/flysky_a8s: Proofreading

* docs/flysky_a8s: Sync up header to summary entry

* docs/flysky_a8s: Add Flysky FS-A8S article (en)

* docs/flysky_a8s: More proofreading
2020-05-12 12:35:05 +03:00
Oleg Kalachev
6a79b8292a docs: little fix 2020-05-08 17:02:36 +03:00
Oleg Kalachev
10b6661266 docs: add images for ROS javascript article 2020-05-08 16:54:10 +03:00
Oleg Kalachev
7f2cb1c63e docs: add using ROS with javascript article 2020-05-08 16:51:15 +03:00
Oleg Kalachev
1d48c79c52 docs: rename package and service to clover 2020-05-07 19:43:25 +03:00
Oleg Kalachev
ad46a0918c Temporarily disable documentation upload 2020-05-07 19:03:51 +03:00
Alexey Rogachevskiy
9487522992 clover: Use saner min marker perimeter rate 2020-05-07 18:07:11 +03:00
Oleg Kalachev
80b35d3b90 Change camera calibration name to main_camera_optical 2020-05-06 19:49:45 +03:00
VeneraDal
12e292c9d7 docs: add Russian and English trainer mode article (#219)
* Upload trainer_mode.md

Add an English version of the file

* Update trainer mode article

* Update SUMMARY.md

For adding the trainer mode article

* Update SUMMARY.md

For adding trainer mode article

* docs: edit trainer mode articles

* docs: move trainer mode article in summary

* docs: fix

* Update SUMMARY.md

Co-authored-by: Oleg Kalachev <okalachev@gmail.com>
2020-05-06 19:41:17 +03:00
Alexey Rogachevskiy
9b28e9cad2 travis: Resolve some issues with validation 2020-05-06 16:39:24 +03:00
Alexey Rogachevskiy
d57ab82f38 docs: Fix up according to MD037 2020-05-06 15:03:39 +03:00
Oleg Kalachev
c8da639eab Merge pull request #227 from goldarte/target-system-id
Add fcu_sys_id argument to clover.launch
2020-05-06 13:30:46 +03:00
Alexey Rogachevskiy
c7e7edec70 builder: Enable ROS services after first boot (#208)
Merging this into master, this should not break anything.
2020-05-06 13:24:53 +03:00
Oleg Kalachev
72869fcf2b Merge pull request #216 from CopterExpress/new-camera-calib
Average camera calibration
2020-05-01 01:04:04 +03:00
Oleg Kalachev
387d2c2341 Update documentation links 2020-05-01 00:39:38 +03:00
Oleg Kalachev
a665caeea3 docs: small fixes 2020-04-30 20:04:20 +03:00
Oleg Kalachev
3079d2a3e1 docs: typos 2020-04-30 20:02:31 +03:00
Alexey Rogachevskiy
7d5bdf4f22 docs/migrate20: Remove extra space character 2020-04-30 19:35:51 +03:00
Oleg Kalachev
2a3efa2908 docs: reflect camera frame configuration changes 2020-04-30 05:03:29 +03:00
Oleg Kalachev
ddee29a0e8 docs: English version on 0.20 image transition article + add to summary 2020-04-30 04:19:09 +03:00
Oleg Kalachev
8596be07c6 docs: add article on migration to v0.20 2020-04-30 04:02:52 +03:00
Oleg Kalachev
a480ebe80a Continue renaming to Clover 2020-04-30 03:41:14 +03:00
Oleg Kalachev
77ca50b901 docs: change rpi version on main page 2020-04-30 01:39:56 +03:00
Oleg Kalachev
ead9b904fa docs: small fix 2020-04-29 05:05:22 +03:00
Oleg Kalachev
90956ecd44 docs: English version of new camera calibration article 2020-04-29 04:44:52 +03:00
Oleg Kalachev
f070c60e14 docs: add example of wait_for_message for rangefinder 2020-04-28 05:53:45 +03:00
Arthur Golubtsov
68edf07f6e Add fcu_sys_id argument to clover.launch and mavros.launch to set up target_system_id parameter in mavros 2020-04-27 15:36:08 +03:00
Oleg Kalachev
7f161b1ad7 Fix Travis badge 2020-04-26 08:00:33 +03:00
Oleg Kalachev
a41a432ef3 Fixes 2020-04-23 21:20:29 +03:00
Oleg Kalachev
2b896b06d9 Move manual installation and running to clover/readme.md 2020-04-23 21:19:46 +03:00
Oleg Kalachev
5070cafbfb Update main readme 2020-04-23 20:54:15 +03:00
Oleg Kalachev
9c0af7285c docs: decrease video size in clever-show article 2020-04-20 18:25:06 +03:00
Arthur Golubtsov
c67d937842 docs: Add article about clever-show (#226)
* docs: Add article about clever-show

* docs: Fix mistake in sentence in en clever-show article

* docs: Add dots to clever-show article

* docs: resolve conflict

Co-authored-by: Oleg Kalachev <okalachev@gmail.com>
2020-04-20 13:30:20 +03:00
Oleg Kalachev
b79d87242f docs: add p4df2 team article in NTI olympics 2020 2020-04-18 15:42:55 +03:00
Alamoris
4d0ddcb319 docs: fix .html link typo 2020-04-18 15:12:09 +03:00
Oleg Kalachev
3ff4ee6c4c docs: add instructions on easy way of sending documentation updates 2020-04-18 14:36:15 +03:00
Oleg Kalachev
90049182cf image: add navigate_wait example 2020-04-09 15:44:48 +03:00
Oleg Kalachev
33f4601fdc docs: add example on retrieving one camera frame 2020-04-09 15:32:45 +03:00
Arthur Golubtsov
2a62891d60 Install pyzbar to image (#225)
* Install pyzbar to image

I suggest installing pyzbar to RPi image for making barcodes scanning easier during different competitions.

* pyzbar: Add libzbar0 install

* pyzbar: Add simple tests

* pyzbar: Update docs
2020-04-09 04:16:05 +03:00
Alamoris
b043737e91 docs: An article on how to configure an image for flying on wall markers (#221)
* docs: Add a draft of an article about flying using wall markers

* docs: Add paragraph about setting up launch files

* docs: Fix typos and add some links

* Small logic fixes

* docs: fix

* docs: Add description setting for earlier version

* docs: Fix sed string

* docs: Add article about wall aruco in summary
2020-04-08 15:13:32 +03:00
Oleg Kalachev
c61a0485ff docs: fix 2020-04-07 17:35:07 +03:00
Arthur Golubtsov
f1539177eb docs: Update install instruction for qr code scan 2020-04-07 17:25:11 +03:00
Arthur Golubtsov
6cbbb5580e docs (en): Update qr code scan instruction and script 2020-04-07 17:06:05 +03:00
Arthur Golubtsov
c2d22ae12a docs: fix codestyle in camera.md 2020-04-07 16:58:20 +03:00
Arthur Golubtsov
7160d804cd docs (ru): Update qr code scan instruction and script 2020-04-07 16:47:03 +03:00
Oleg Kalachev
3ac51baf7c docs: small fix
It’s better not to nest the throttled image topic as this way consumers would subscribe to appropriate camera_info topic automatically
2020-04-07 02:58:08 +03:00
Alamoris
48cc82001d docs: Fix typo 2020-04-03 21:15:04 +03:00
Alamoris
43eae885c6 docs: Add small mounting deck model 2020-04-03 21:13:14 +03:00
Alexey Rogachevskiy
2bb29ff389 clover: Add required OpenCV libraries 2020-03-31 23:56:04 +03:00
Oleg Kalachev
3811cbff3e examples: fix markers example link 2020-03-19 13:10:05 +03:00
Oleg Kalachev
2d49f58fb8 image: add markers flight example 2020-03-19 13:09:31 +03:00
Oleg Kalachev
bbcf75b806 docs: add gyro calibration snippet 2020-03-18 21:52:19 +03:00
Oleg Kalachev
3e79c25147 Camera info resolution matching camera resolution is not necessary with auto rescaling 2020-02-20 19:16:23 +03:00
Oleg Kalachev
2f69ad3f43 Keep only one calibration file 2020-02-13 23:17:52 +03:00
Oleg Kalachev
b08ad5a618 Camera calibration: set principal point strictly to the center 2020-02-13 23:14:05 +03:00
871 changed files with 240314 additions and 1845 deletions

2
.gitattributes vendored
View File

@@ -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

2
.gitignore vendored
View File

@@ -4,3 +4,5 @@
node_modules/ node_modules/
_book/ _book/
package-lock.json package-lock.json
clover_blocks/programs/*.*
!clover_blocks/programs/examples/*

View File

@@ -22,6 +22,7 @@
"ROS Kinetic", "ROS Kinetic",
"ROS Melodic", "ROS Melodic",
"OpenCV", "OpenCV",
"OpenVPN",
"Gazebo", "Gazebo",
"GitHub", "GitHub",
"FPV", "FPV",
@@ -106,7 +107,9 @@
"UDP", "UDP",
"QR", "QR",
"Li-ion", "Li-ion",
"Nvidia" "Nvidia",
"VirtualBox",
"VMware"
], ],
"code_blocks": false "code_blocks": false
}, },

View File

@@ -1,4 +1,5 @@
sudo: required os: linux
dist: xenial
language: generic language: generic
services: services:
- docker - docker
@@ -6,10 +7,10 @@ env:
global: global:
- DOCKER="sfalexrog/img-tool:qemu-update" - DOCKER="sfalexrog/img-tool:qemu-update"
- TARGET_REPO="https://github.com/${TRAVIS_REPO_SLUG}.git" - 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_VERSION=${TRAVIS_TAG:-${TRAVIS_COMMIT:0:7}}
- IMAGE_NAME="$(basename -s '.git' ${TARGET_REPO})_${IMAGE_VERSION}.img" - IMAGE_NAME="$(basename -s '.git' ${TARGET_REPO})_${IMAGE_VERSION}.img"
git: git:
depth: 50 depth: 1
jobs: jobs:
fast_finish: true fast_finish: true
include: include:
@@ -35,15 +36,16 @@ jobs:
fi fi
before_cache: before_cache:
- cp images/*.zip imgcache - cp images/*.zip imgcache
after_success:
- sudo chmod -R 777 *
- cd images && zip -9 ${IMAGE_NAME}.zip ${IMAGE_NAME} && stat --printf="Compressed image size:%s\n" ${IMAGE_NAME}.zip
before_deploy: before_deploy:
# Set up git user name and tag this commit # Set up git user name and tag this commit
- git config --local user.name "goldarte" - git config --local user.name "goldarte"
- git config --local user.email "goldartt@gmail.com" - git config --local user.email "goldartt@gmail.com"
- sudo chmod -R 777 *
- cd images && zip ${IMAGE_NAME}.zip ${IMAGE_NAME}
deploy: deploy:
provider: releases provider: releases
api_key: ${GITHUB_OAUTH_TOKEN} token: ${GITHUB_OAUTH_TOKEN}
file: ${IMAGE_NAME}.zip file: ${IMAGE_NAME}.zip
skip_cleanup: true skip_cleanup: true
on: on:
@@ -72,8 +74,11 @@ jobs:
node_js: node_js:
- "10" - "10"
before_script: 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 gitbook-cli -g
- npm install markdownlint-cli -g - npm install markdownlint-cli -g
- npm install svgexport -g
- gitbook -V - gitbook -V
- markdownlint -V - markdownlint -V
script: script:
@@ -82,26 +87,19 @@ jobs:
- ./check_unused_assets.py - ./check_unused_assets.py
- gitbook install - gitbook install
- gitbook build - gitbook build
- gitbook pdf ./ _book/clover.pdf
deploy: deploy:
provider: pages provider: pages
local-dir: _book local_dir: _book
skip-cleanup: true skip_cleanup: true
github-token: ${GITHUB_OAUTH_TOKEN} token: ${GITHUB_OAUTH_TOKEN}
keep-history: true keep_history: true
target-branch: master target_branch: master
repo: CopterExpress/clever.coex.tech repo: CopterExpress/clover.coex.tech
fqdn: clever.coex.tech fqdn: clover.coex.tech
verbose: true verbose: true
on: on:
branch: master 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 - stage: Build
name: Editorconfig-lint name: Editorconfig-lint
language: generic language: generic
@@ -109,10 +107,9 @@ jobs:
- wget https://github.com/okalachev/editorconfig-checker/releases/download/1.2.1-disable-spaces-amount/ec-linux-amd64 - wget https://github.com/okalachev/editorconfig-checker/releases/download/1.2.1-disable-spaces-amount/ec-linux-amd64
- chmod +x ec-linux-amd64 - chmod +x ec-linux-amd64
script: 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" - ./ec-linux-amd64 -spaces-after-tabs -e "roslib.js|ros3d.js|eventemitter2.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"
stages: stages:
- Build - Build
- Annotate
# More info there # More info there
# https://github.com/travis-ci/travis-ci/issues/6893 # https://github.com/travis-ci/travis-ci/issues/6893
# https://docs.travis-ci.com/user/customizing-the-build/ # https://docs.travis-ci.com/user/customizing-the-build/

107
README.md
View File

@@ -1,103 +1,42 @@
# CLEVER # clover🍀: create autonomous drones easily
<img src="docs/assets/clever4-front-white.png" align="right" width="400px" alt="CLEVER drone"> <img src="docs/assets/clover42-main.png" align="right" width="400px" alt="COEX Clover Drone">
CLEVER (Russian: *"Клевер"*, meaning *"Clover"*) is an educational programmable drone kit consisting of an unassembled quadcopter, open source software and documentation. The kit includes Pixhawk/Pixracer autopilot running PX4 firmware, Raspberry Pi 3 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.
Copter Express has implemented a large number of different autonomous drone projects using exactly the same platform: [automated pizza delivery](https://www.youtube.com/watch?v=hmkAoZOtF58) in Samara and Kazan, coffee delivery in Skolkovo Innovation Center, [autonomous quadcopter with charging station](https://www.youtube.com/watch?v=RjX6nUqw1mI) for site monitoring and security, winning drones on [Robocross-2016](https://www.youtube.com/watch?v=dGbDaz_VmYU) and [Robocross-2017](https://youtu.be/AQnd2CRczbQ) competitions and many others. 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.
**The main documentation is available [on Gitbook](https://clever.coex.tech/).** The main documentation is available at [https://clover.coex.tech](https://clover.coex.tech/). Official website: [coex.tech/clover](https://coex.tech/clover).
Use it to learn how to assemble, configure, pilot and program autonomous CLEVER drone. [__Support us on Kickstarter!__](https://www.kickstarter.com/projects/copterexpress/cloverdrone)
## Video compilation
[![Clover Drone Kit autonomy compilation](http://img.youtube.com/vi/u3omgsYC4Fk/hqdefault.jpg)](https://youtu.be/u3omgsYC4Fk)
Clover drone is used on a wide range of educational events, including [Copter Hack](https://www.youtube.com/watch?v=xgXheg3TTs4), WorldSkills Drone Operation competition, [Autonomous Vehicles Track of NTI Olympics 20162020](https://www.youtube.com/watch?v=E1_ehvJRKxg), Quadro Hack 2019 (National University of Science and Technology MISiS), Russian Robot Olympiad (autonomous flights), and others.
## Raspberry Pi image ## Raspberry Pi image
**Preconfigured image for Raspberry Pi 3 with installed and configured software, ready to fly, is available [in the Releases section](https://github.com/CopterExpress/clever/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).
[![Build Status](https://travis-ci.org/CopterExpress/clever.svg?branch=master)](https://travis-ci.org/CopterExpress/clever) [![Build Status](https://travis-ci.org/CopterExpress/clover.svg?branch=master)](https://travis-ci.org/CopterExpress/clover)
Image includes: Image features:
* Raspbian Buster * Raspbian Buster
* ROS Melodic * [ROS Melodic](http://wiki.ros.org/melodic)
* Configured networking * Configured networking
* OpenCV * OpenCV
* mavros * [`mavros`](http://wiki.ros.org/mavros)
* Periphery drivers (`pigpiod`, `rpi_ws281x`, etc) * Periphery drivers for ROS ([GPIO](https://clover.coex.tech/en/gpio.html), [LED strip](https://clover.coex.tech/en/leds.html), etc)
* CLEVER software bundle for autonomous drone control * `aruco_pose` package for marker-assisted navigation
* `clover` package for autonomous drone control
API description (in Russian) for autonomous flights is available [on GitBook](https://clever.coex.tech/simple_offboard.html). API description for autonomous flights is available [on GitBook](https://clover.coex.tech/en/simple_offboard.html).
## Manual installation For manual package installation and running see [`clover` package documentation](clover/README.md).
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).
Clone this repo to directory `~/catkin_ws/src/clever`:
```bash
cd ~/catkin_ws/src
git clone https://github.com/CopterExpress/clever.git clever
```
All the required ROS packages (including `mavros` and `opencv`) can be installed using `rosdep`:
```bash
cd ~/catkin_ws/
rosdep install -y --from-paths src --ignore-src
```
Build ROS packages (on memory constrained platforms you might be going to need to use `-j1` key):
```bash
cd ~/catkin_ws
catkin_make -j1
```
To complete `mavros` install you'll need to install `geographiclib` datasets:
```bash
curl https://raw.githubusercontent.com/mavlink/mavros/master/mavros/scripts/install_geographiclib_datasets.sh | sudo bash
```
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
cd ~/catkin_ws/src/clever/clever/config
sudo cp 99-px4fmu.rules /lib/udev/rules.d
```
Alternatively you may change the `fcu_url` property in `mavros.launch` file to point to your flight controller device.
## Running
Enable systemd service `roscore` (if not running):
```bash
sudo systemctl enable /home/<username>/catkin_ws/src/clever/builder/assets/roscore.service
sudo systemctl start roscore
```
To start connection to SITL, use:
```bash
roslaunch clever sitl.launch
```
To start connection to the flight controller, use:
```bash
roslaunch clever clever.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`.
Also, you can enable and start the systemd service:
```bash
sudo systemctl enable /home/<username>/catkin_ws/src/clever/deploy/clever.service
sudo systemctl start clever
```
## License ## License
While the Clever 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.

0
apps/CATKIN_IGNORE Normal file
View File

8
aruco_pose/CHANGELOG.rst Normal file
View 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

View File

@@ -1,8 +1,6 @@
cmake_minimum_required(VERSION 3.0) cmake_minimum_required(VERSION 3.0)
project(aruco_pose) project(aruco_pose)
add_definitions(-std=c++11 -Wall -g)
## Compile as C++11, supported in ROS Kinetic and newer ## Compile as C++11, supported in ROS Kinetic and newer
add_compile_options(-std=c++11) add_compile_options(-std=c++11)
@@ -25,7 +23,7 @@ find_package(catkin REQUIRED COMPONENTS
) )
find_package(OpenCV 3 REQUIRED COMPONENTS core imgproc calib3d) find_package(OpenCV 3 REQUIRED COMPONENTS core imgproc calib3d)
if ("${OpenCV_VERSION_MINOR}" LESS "3") 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()
@@ -229,4 +227,5 @@ if (CATKIN_ENABLE_TESTING)
add_rostest(test/test_parser_empty_map.test) add_rostest(test/test_parser_empty_map.test)
add_rostest(test/test_node_failure.test) add_rostest(test/test_node_failure.test)
add_rostest(test/largemap.test) add_rostest(test/largemap.test)
add_rostest(test/crash_opencv.test)
endif() endif()

View File

@@ -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)

View File

@@ -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.21.1</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>

View File

@@ -58,11 +58,11 @@ using cv::Mat;
class ArucoDetect : public nodelet::Nodelet { class ArucoDetect : public nodelet::Nodelet {
private: private:
ros::NodeHandle nh_, nh_priv_; std::unique_ptr<tf2_ros::TransformBroadcaster> br_;
tf2_ros::TransformBroadcaster br_; std::unique_ptr<tf2_ros::Buffer> tf_buffer_;
tf2_ros::Buffer tf_buffer_; std::unique_ptr<tf2_ros::TransformListener> tf_listener_;
tf2_ros::TransformListener tf_listener_{tf_buffer_};
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_;
@@ -81,30 +81,32 @@ private:
public: public:
virtual void onInit() virtual void onInit()
{ {
nh_ = getNodeHandle(); ros::NodeHandle& nh_ = getNodeHandle();
nh_priv_ = getPrivateNodeHandle(); ros::NodeHandle& nh_priv_ = getPrivateNodeHandle();
br_.reset(new tf2_ros::TransformBroadcaster());
tf_buffer_.reset(new tf2_ros::Buffer());
tf_listener_.reset(new tf2_ros::TransformListener(*tf_buffer_, nh_));
int dictionary; int dictionary;
nh_priv_.param("dictionary", dictionary, 2); dictionary = nh_priv_.param("dictionary", 2);
nh_priv_.param("estimate_poses", estimate_poses_, true); estimate_poses_ = nh_priv_.param("estimate_poses", true);
nh_priv_.param("send_tf", send_tf_, true); send_tf_ = nh_priv_.param("send_tf", true);
if (estimate_poses_ && !nh_priv_.getParam("length", length_)) { if (estimate_poses_ && !nh_priv_.getParam("length", length_)) {
NODELET_FATAL("can't estimate marker's poses as ~length parameter is not defined"); NODELET_FATAL("can't estimate marker's poses as ~length parameter is not defined");
ros::shutdown(); ros::shutdown();
} }
readLengthOverride(); readLengthOverride(nh_priv_);
nh_priv_.param<std::string>("known_tilt", known_tilt_, ""); known_tilt_ = nh_priv_.param<std::string>("known_tilt", "");
nh_priv_.param("auto_flip", auto_flip_, false); auto_flip_ = nh_priv_.param("auto_flip", false);
nh_priv_.param<std::string>("frame_id_prefix", frame_id_prefix_, "aruco_"); frame_id_prefix_ = nh_priv_.param<std::string>("frame_id_prefix", "aruco_");
camera_matrix_ = cv::Mat::zeros(3, 3, CV_64F); camera_matrix_ = cv::Mat::zeros(3, 3, CV_64F);
dist_coeffs_ = cv::Mat::zeros(8, 1, CV_64F);
dictionary_ = cv::aruco::getPredefinedDictionary(static_cast<cv::aruco::PREDEFINED_DICTIONARY_NAME>(dictionary)); dictionary_ = cv::aruco::getPredefinedDictionary(static_cast<cv::aruco::PREDEFINED_DICTIONARY_NAME>(dictionary));
parameters_ = cv::aruco::DetectorParameters::create(); parameters_ = cv::aruco::DetectorParameters::create();
parameters_->cornerRefinementMethod = cv::aruco::CORNER_REFINE_SUBPIX;
image_transport::ImageTransport it(nh_); image_transport::ImageTransport it(nh_);
image_transport::ImageTransport it_priv(nh_priv_); image_transport::ImageTransport it_priv(nh_priv_);
@@ -127,6 +129,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;
@@ -170,8 +174,8 @@ private:
if (!known_tilt_.empty()) { if (!known_tilt_.empty()) {
try { try {
snap_to = tf_buffer_.lookupTransform(msg->header.frame_id, known_tilt_, snap_to = tf_buffer_->lookupTransform(msg->header.frame_id, known_tilt_,
msg->header.stamp, ros::Duration(0.02)); msg->header.stamp, ros::Duration(0.02));
} catch (const tf2::TransformException& e) { } catch (const tf2::TransformException& e) {
NODELET_WARN_THROTTLE(5, "can't snap: %s", e.what()); NODELET_WARN_THROTTLE(5, "can't snap: %s", e.what());
} }
@@ -205,7 +209,7 @@ private:
if (map_markers_ids_.find(ids[i]) == map_markers_ids_.end()) { if (map_markers_ids_.find(ids[i]) == map_markers_ids_.end()) {
transform.transform.rotation = marker.pose.orientation; transform.transform.rotation = marker.pose.orientation;
fillTranslation(transform.transform.translation, tvecs[i]); fillTranslation(transform.transform.translation, tvecs[i]);
br_.sendTransform(transform); br_->sendTransform(transform);
} }
} }
} }
@@ -326,10 +330,10 @@ private:
return frame_id_prefix_ + std::to_string(id); return frame_id_prefix_ + std::to_string(id);
} }
void readLengthOverride() void readLengthOverride(ros::NodeHandle& nh)
{ {
std::map<std::string, double> length_override; std::map<std::string, double> length_override;
nh_priv_.getParam("length_override", length_override); nh.getParam("length_override", length_override);
for (auto const& item : length_override) { for (auto const& item : length_override) {
length_override_[std::stoi(item.first)] = item.second; length_override_[std::stoi(item.first)] = item.second;
} }
@@ -355,6 +359,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;

View File

@@ -58,7 +58,6 @@ typedef message_filters::sync_policies::ExactTime<Image, CameraInfo, MarkerArray
class ArucoMap : public nodelet::Nodelet { class ArucoMap : public nodelet::Nodelet {
private: private:
ros::NodeHandle nh_, nh_priv_;
ros::Publisher img_pub_, pose_pub_, markers_pub_, vis_markers_pub_; ros::Publisher img_pub_, pose_pub_, markers_pub_, vis_markers_pub_;
image_transport::Publisher debug_pub_; image_transport::Publisher debug_pub_;
message_filters::Subscriber<Image> image_sub_; message_filters::Subscriber<Image> image_sub_;
@@ -83,8 +82,8 @@ private:
public: public:
virtual void onInit() virtual void onInit()
{ {
nh_ = getNodeHandle(); ros::NodeHandle &nh_ = getNodeHandle();
nh_priv_ = getPrivateNodeHandle(); ros::NodeHandle &nh_priv_ = getPrivateNodeHandle();
image_transport::ImageTransport it_priv(nh_priv_); image_transport::ImageTransport it_priv(nh_priv_);
@@ -96,19 +95,18 @@ public:
board_->dictionary = cv::aruco::getPredefinedDictionary( board_->dictionary = cv::aruco::getPredefinedDictionary(
static_cast<cv::aruco::PREDEFINED_DICTIONARY_NAME>(nh_priv_.param("dictionary", 2))); static_cast<cv::aruco::PREDEFINED_DICTIONARY_NAME>(nh_priv_.param("dictionary", 2)));
camera_matrix_ = cv::Mat::zeros(3, 3, CV_64F); camera_matrix_ = cv::Mat::zeros(3, 3, CV_64F);
dist_coeffs_ = cv::Mat::zeros(8, 1, CV_64F);
std::string type, map; std::string type, map;
nh_priv_.param<std::string>("type", type, "map"); type = nh_priv_.param<std::string>("type", "map");
nh_priv_.param<std::string>("frame_id", transform_.child_frame_id, "aruco_map"); transform_.child_frame_id = nh_priv_.param<std::string>("frame_id", "aruco_map");
nh_priv_.param<std::string>("known_tilt", known_tilt_, ""); known_tilt_ = nh_priv_.param<std::string>("known_tilt", "");
nh_priv_.param("auto_flip", auto_flip_, false); auto_flip_ = nh_priv_.param("auto_flip", false);
nh_priv_.param("image_width", image_width_, 2000); image_width_ = nh_priv_.param("image_width" , 2000);
nh_priv_.param("image_height", image_height_, 2000); image_height_ = nh_priv_.param("image_height", 2000);
nh_priv_.param("image_margin", image_margin_, 200); image_margin_ = nh_priv_.param("image_margin", 200);
nh_priv_.param("image_axis", image_axis_, true); image_axis_ = nh_priv_.param("image_axis", true);
nh_priv_.param<std::string>("markers/frame_id", markers_parent_frame_, transform_.child_frame_id); markers_parent_frame_ = nh_priv_.param<std::string>("markers/frame_id", transform_.child_frame_id);
nh_priv_.param<std::string>("markers/child_frame_id_prefix", markers_frame_, ""); markers_frame_ = nh_priv_.param<std::string>("markers/child_frame_id_prefix", "");
// createStripLine(); // createStripLine();
@@ -116,7 +114,7 @@ public:
param(nh_priv_, "map", map); param(nh_priv_, "map", map);
loadMap(map); loadMap(map);
} else if (type == "gridboard") { } else if (type == "gridboard") {
createGridBoard(); createGridBoard(nh_priv_);
} else { } else {
NODELET_FATAL("unknown type: %s", type.c_str()); NODELET_FATAL("unknown type: %s", type.c_str());
ros::shutdown(); ros::shutdown();
@@ -331,7 +329,7 @@ publish_debug:
NODELET_INFO("loading %s complete (%d markers)", filename.c_str(), static_cast<int>(board_->ids.size())); NODELET_INFO("loading %s complete (%d markers)", filename.c_str(), static_cast<int>(board_->ids.size()));
} }
void createGridBoard() void createGridBoard(ros::NodeHandle& nh)
{ {
NODELET_INFO("generate gridboard"); NODELET_INFO("generate gridboard");
NODELET_WARN("gridboard maps are deprecated"); NODELET_WARN("gridboard maps are deprecated");
@@ -339,15 +337,15 @@ publish_debug:
int markers_x, markers_y, first_marker; int markers_x, markers_y, first_marker;
double markers_side, markers_sep_x, markers_sep_y; double markers_side, markers_sep_x, markers_sep_y;
std::vector<int> marker_ids; std::vector<int> marker_ids;
nh_priv_.param<int>("markers_x", markers_x, 10); markers_x = nh.param("markers_x", 10);
nh_priv_.param<int>("markers_y", markers_y, 10); markers_y = nh.param("markers_y", 10);
nh_priv_.param<int>("first_marker", first_marker, 0); first_marker = nh.param("first_marker", 0);
param(nh_priv_, "markers_side", markers_side); param(nh, "markers_side", markers_side);
param(nh_priv_, "markers_sep_x", markers_sep_x); param(nh, "markers_sep_x", markers_sep_x);
param(nh_priv_, "markers_sep_y", markers_sep_y); param(nh, "markers_sep_y", markers_sep_y);
if (nh_priv_.getParam("marker_ids", marker_ids)) { if (nh.getParam("marker_ids", marker_ids)) {
if ((unsigned int)(markers_x * markers_y) != marker_ids.size()) { if ((unsigned int)(markers_x * markers_y) != marker_ids.size()) {
NODELET_FATAL("~marker_ids length should be equal to ~markers_x * ~markers_y"); NODELET_FATAL("~marker_ids length should be equal to ~markers_x * ~markers_y");
ros::shutdown(); ros::shutdown();
@@ -394,7 +392,7 @@ publish_debug:
int num_markers = board_->dictionary->bytesList.rows; int num_markers = board_->dictionary->bytesList.rows;
if (num_markers <= id) { if (num_markers <= id) {
NODELET_ERROR("Marker id %d is not in dictionary; current dictionary contains %d markers. " NODELET_ERROR("Marker id %d is not in dictionary; current dictionary contains %d markers. "
"Please see https://github.com/CopterExpress/clever/blob/master/aruco_pose/README.md#parameters for details", "Please see https://github.com/CopterExpress/clover/blob/master/aruco_pose/README.md#parameters for details",
id, num_markers); id, num_markers);
return; return;
} }

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python3 #!/usr/bin/env python
# Copyright (C) 2018 Copter Express Technologies # Copyright (C) 2018 Copter Express Technologies
# #
@@ -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>] [--top-left | --bottom-left] genmap.py <length> <x> <y> <dist_x> <dist_y> [<first>] [<x0>] [<y0>] [--top-left | --bottom-left]
genmap.py (-h | --help) genmap.py (-h | --help)
Options: Options:
@@ -23,6 +23,8 @@ Options:
<dist_x> Distance between markers along X axis <dist_x> Distance between markers along X axis
<dist_y> Distance between markers along Y axis <dist_y> Distance between markers along Y axis
<first> First marker ID [default: 0] <first> First marker ID [default: 0]
<x0> X 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
@@ -39,20 +41,22 @@ arguments = docopt(__doc__)
length = float(arguments['<length>']) length = float(arguments['<length>'])
first = int(arguments['<first>'] if arguments['<first>'] is not None else 0) first = int(arguments['<first>'] if arguments['<first>'] is not None else 0)
x0 = float(arguments['<x0>'] if arguments['<x0>'] is not None else 0)
y0 = float(arguments['<y0>'] if arguments['<y0>'] is not None else 0)
markers_x = int(arguments['<x>']) markers_x = int(arguments['<x>'])
markers_y = int(arguments['<y>']) markers_y = int(arguments['<y>'])
dist_x = float(arguments['<dist_x>']) 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']
max_y = (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') print('# id\tlength\tx\ty\tz\trot_z\trot_y\trot_x')
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 = x * dist_x pos_x = x0 + x * dist_x
pos_y = 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{}\t'.format(first, length, pos_x, pos_y, 0, 0, 0, 0)) print('{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}'.format(first, length, pos_x, pos_y, 0, 0, 0, 0))
first += 1 first += 1

View File

@@ -35,9 +35,7 @@ static void parseCameraInfo(const sensor_msgs::CameraInfoConstPtr& cinfo, cv::Ma
for (unsigned int i = 0; i < 3; ++i) for (unsigned int i = 0; i < 3; ++i)
for (unsigned int j = 0; j < 3; ++j) for (unsigned int j = 0; j < 3; ++j)
matrix.at<double>(i, j) = cinfo->K[3 * i + j]; matrix.at<double>(i, j) = cinfo->K[3 * i + j];
dist = cv::Mat(cinfo->D, true);
for (unsigned int k = 0; k < cinfo->D.size(); k++)
dist.at<double>(k) = cinfo->D[k];
} }
inline void rotatePoint(cv::Point3f& p, cv::Point3f origin, float angle) inline void rotatePoint(cv::Point3f& p, cv::Point3f origin, float angle)

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 165 KiB

View File

@@ -0,0 +1,18 @@
import rospy
import pytest
from visualization_msgs.msg import MarkerArray as VisMarkerArray
@pytest.fixture
def node():
return rospy.init_node('aruco_pose_opencv_crash', anonymous=True)
def test_opencv_crashes_img01(node):
rospy.wait_for_message('aruco_detect_01/visualization', VisMarkerArray, timeout=5)
def test_opencv_crashes_img02(node):
rospy.wait_for_message('aruco_detect_02/visualization', VisMarkerArray, timeout=5)
def test_opencv_crashes_img03(node):
rospy.wait_for_message('aruco_detect_03/visualization', VisMarkerArray, timeout=5)

View File

@@ -0,0 +1,51 @@
<launch>
<arg name="corner_method" default="2"/>
<node pkg="image_publisher" type="image_publisher" name="imgpub_01" args="$(find aruco_pose)/test/crash_image_01.png">
<param name="frame_id" value="main_camera_optical"/>
<param name="publish_rate" value="10"/>
<param name="camera_info_url" value="file://$(find aruco_pose)/test/camera_info.yaml" />
</node>
<node pkg="image_publisher" type="image_publisher" name="imgpub_02" args="$(find aruco_pose)/test/crash_image_02.png">
<param name="frame_id" value="main_camera_optical"/>
<param name="publish_rate" value="10"/>
<param name="camera_info_url" value="file://$(find aruco_pose)/test/camera_info.yaml" />
</node>
<node pkg="image_publisher" type="image_publisher" name="imgpub_03" args="$(find aruco_pose)/test/crash_image_03.png">
<param name="frame_id" value="main_camera_optical"/>
<param name="publish_rate" value="10"/>
<param name="camera_info_url" value="file://$(find aruco_pose)/test/camera_info.yaml" />
</node>
<node pkg="nodelet" type="nodelet" name="nodelet_manager_01" args="manager"/>
<node pkg="nodelet" clear_params="true" type="nodelet" name="aruco_detect_01" args="load aruco_pose/aruco_detect nodelet_manager_01">
<remap from="image_raw" to="imgpub_01/image_raw"/>
<remap from="camera_info" to="imgpub_01/camera_info"/>
<param name="length" value="0.33"/>
<param name="cornerRefinementMethod" value="$(arg corner_method)"/>
</node>
<node pkg="nodelet" type="nodelet" name="nodelet_manager_02" args="manager"/>
<node pkg="nodelet" clear_params="true" type="nodelet" name="aruco_detect_02" args="load aruco_pose/aruco_detect nodelet_manager_02">
<remap from="image_raw" to="imgpub_02/image_raw"/>
<remap from="camera_info" to="imgpub_02/camera_info"/>
<param name="length" value="0.33"/>
<param name="cornerRefinementMethod" value="$(arg corner_method)"/>
</node>
<node pkg="nodelet" type="nodelet" name="nodelet_manager_03" args="manager"/>
<node pkg="nodelet" clear_params="true" type="nodelet" name="aruco_detect_03" args="load aruco_pose/aruco_detect nodelet_manager_03">
<remap from="image_raw" to="imgpub_03/image_raw"/>
<remap from="camera_info" to="imgpub_03/camera_info"/>
<param name="length" value="0.33"/>
<param name="cornerRefinementMethod" value="$(arg corner_method)"/>
</node>
<param name="test_module" value="$(find aruco_pose)/test/crash_opencv.py"/>
<test test-name="crash_opencv" pkg="ros_pytest" type="ros_pytest_runner"/>
</launch>

View File

@@ -1,19 +1,26 @@
#!/usr/bin/env python
import sys
import unittest
import json
import rospy import rospy
import pytest import rostest
from sensor_msgs.msg import Image from sensor_msgs.msg import Image
from visualization_msgs.msg import MarkerArray as VisMarkerArray from visualization_msgs.msg import MarkerArray as VisMarkerArray
@pytest.fixture
def node():
return rospy.init_node('test_aruco_largemap', anonymous=True)
def test_large_map_image(node): class TestArucoPose(unittest.TestCase):
img = rospy.wait_for_message('aruco_map/image', Image, timeout=5) def setUp(self):
assert img.width == 2000 rospy.init_node('test_aruco_largemap', anonymous=True)
assert img.height == 2000
assert img.encoding in ('mono8', 'rgb8')
def test_large_map_visualization(node): def test_map_image(self):
vis = rospy.wait_for_message('aruco_map/visualization', VisMarkerArray, timeout=5) img = rospy.wait_for_message('aruco_map/image', Image, timeout=5)
assert len(vis.markers) == 11 self.assertEqual(img.width, 2000)
self.assertEqual(img.height, 2000)
self.assertIn(img.encoding, ('mono8', 'rgb8'))
def test_map_visualization(self):
vis = rospy.wait_for_message('aruco_map/visualization', VisMarkerArray, timeout=5)
rostest.rosrun('aruco_pose', 'test_aruco_largemap', TestArucoPose, sys.argv)

View File

@@ -9,6 +9,5 @@
<param name="map" value="$(find aruco_pose)/test/largemap.txt"/> <param name="map" value="$(find aruco_pose)/test/largemap.txt"/>
</node> </node>
<param name="test_module" value="$(find aruco_pose)/test/largemap.py"/> <test test-name="test_aruco_pose_largemap" pkg="aruco_pose" type="largemap.py"/>
<test test-name="test_node_pass" pkg="ros_pytest" type="ros_pytest_runner"/>
</launch> </launch>

View File

@@ -924,6 +924,8 @@ static void _refineCandidateLines(std::vector<Point>& nContours, std::vector<Poi
// calculate the line :: who passes through the grouped points // calculate the line :: who passes through the grouped points
Point3f lines[4]; Point3f lines[4];
for(int i=0; i<4; i++){ for(int i=0; i<4; i++){
// Don't try to "interpolate" single points
if (cntPts[i].size() < 2) return;
lines[i]=_interpolate2Dline(cntPts[i]); lines[i]=_interpolate2Dline(cntPts[i]);
} }

View File

@@ -1,5 +1,5 @@
{ {
"title": "Clever", "title": "Clover",
"description": "Конструктор квадрокоптера «Клевер»", "description": "Конструктор квадрокоптера «Клевер»",
"author": "Copter Express", "author": "Copter Express",
"language": "en", "language": "en",
@@ -9,6 +9,7 @@
"richquotes@https://github.com/okalachev/gitbook-plugin-richquotes.git", "richquotes@https://github.com/okalachev/gitbook-plugin-richquotes.git",
"yametrika", "yametrika",
"anchors", "anchors",
"collapsible-menu",
"validate-links", "validate-links",
"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",
@@ -28,7 +29,7 @@
"blank": true "blank": true
}, },
"sitemap": { "sitemap": {
"hostname": "https://clever.coex.tech" "hostname": "https://clover.coex.tech"
}, },
"toolbar": { "toolbar": {
"buttons": "buttons":
@@ -37,19 +38,19 @@
"label": "Edit page on github", "label": "Edit page on github",
"icon": "fa fa-pencil-square-o", "icon": "fa fa-pencil-square-o",
"position" : "left", "position" : "left",
"url": "https://github.com/CopterExpress/clever/edit/master/docs/{{filepath_lang}}" "url": "https://github.com/CopterExpress/clover/edit/master/docs/{{filepath_lang}}"
}, },
{ {
"label": "GitHub", "label": "GitHub",
"icon": "fa fa-github", "icon": "fa fa-github",
"position" : "left", "position" : "left",
"url": "https://github.com/CopterExpress/clever" "url": "https://github.com/CopterExpress/clover"
} }
] ]
}, },
"addcssjs": { "addcssjs": {
"css": ["../clever.css"], "css": ["../clover.css"],
"js": ["../clever.js"] "js": ["../clover.js"]
}, },
"language-picker": { "language-picker": {
"languages": [["ru", "Russian"], ["en", "English"]] "languages": [["ru", "Russian"], ["en", "English"]]

View File

@@ -0,0 +1,34 @@
<?xml version="1.0" standalone='no'?><!--*-nxml-*-->
<!DOCTYPE service-group SYSTEM "avahi-service.dtd">
<!--
This file is part of avahi.
avahi is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
avahi is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with avahi; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307 USA.
-->
<!-- See avahi.service(5) for more information about this configuration file -->
<service-group>
<name replace-wildcards="yes">%h</name>
<service>
<type>_sftp-ssh._tcp</type>
<port>22</port>
</service>
</service-group>

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python3 #!/usr/bin/env python
from distutils.core import setup from distutils.core import setup

View File

@@ -1,4 +1,4 @@
# Information: https://clever.coex.tech/en/programming.html # Information: https://clover.coex.tech/programming
import rospy import rospy
from clover import srv from clover import srv
@@ -15,7 +15,7 @@ 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)
# Takeoff and hover 1 m above the ground # 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 3 seconds

View File

@@ -0,0 +1,37 @@
# Information: https://clover.coex.tech/aruco
import rospy
from clover import srv
from std_srvs.srv import Trigger
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)
# Take off and hover 1 m above the ground
navigate(x=0, y=0, z=1, frame_id='body', auto_arm=True)
# Wait for 3 seconds
rospy.sleep(3)
# Fly 1 meter above ArUco marker 0
navigate(x=0, y=0, z=1, frame_id='aruco_0')
# Wait for 3 seconds
rospy.sleep(3)
# Fly to x=1 y=1 z=1 relative to ArUco markers map
navigate(x=1, y=1, z=1, frame_id='aruco_map')
# Wait for 3 seconds
rospy.sleep(3)
# Perform landing
land()

View File

@@ -1,4 +1,4 @@
# Information: https://clever.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

View File

@@ -0,0 +1,41 @@
# Information: https://clover.coex.tech/en/snippets.html#navigate_wait
import math
import rospy
from clover import srv
from std_srvs.srv import Trigger
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)
def navigate_wait(x=0, y=0, z=0, yaw=float('nan'), yaw_rate=0, speed=0.5, \
frame_id='body', tolerance=0.2, auto_arm=False):
res = navigate(x=x, y=y, z=z, yaw=yaw, yaw_rate=yaw_rate, speed=speed, \
frame_id=frame_id, auto_arm=auto_arm)
if not res.success:
return res
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:
return res
rospy.sleep(0.2)
# Take off 1 meter
navigate_wait(z=1, frame_id='body', auto_arm=True)
# Fly forward 1 m
navigate_wait(x=1, frame_id='body')
# Land
land()

View File

@@ -62,6 +62,10 @@ hostnamectl set-hostname $NEW_HOSTNAME
sed -i 's/127\.0\.1\.1.*/127.0.1.1\t'${NEW_HOSTNAME}' '${NEW_HOSTNAME}'.local/g' /etc/hosts sed -i 's/127\.0\.1\.1.*/127.0.1.1\t'${NEW_HOSTNAME}' '${NEW_HOSTNAME}'.local/g' /etc/hosts
# .local (mdns) hostname added to make it accesable when wlan and ethernet interfaces are down # .local (mdns) hostname added to make it accesable when wlan and ethernet interfaces are down
echo_stamp "Enable ROS services"
systemctl enable roscore
systemctl enable clover
echo_stamp "Harware setup" echo_stamp "Harware setup"
/root/hardware_setup.sh /root/hardware_setup.sh

View File

@@ -20,7 +20,7 @@
# Example: # Example:
# DocumentRoot /home/krypton/htdocs # DocumentRoot /home/krypton/htdocs
DocumentRoot /home/pi/catkin_ws/src/clover/clover/www DocumentRoot /home/pi/.ros/www
# Redirect: # Redirect:
# --------- # ---------

View File

@@ -1,9 +0,0 @@
python3-wxgtk:
debian:
buster: [python3-wxgtk4.0]
python3-serial:
debian:
buster: [python3-serial]
python3-requests:
debian:
buster: [python3-requests]

View File

@@ -15,7 +15,7 @@
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" SOURCE_IMAGE="https://downloads.raspberrypi.org/raspios_lite_armhf/images/raspios_lite_armhf-2021-01-12/2021-01-11-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'}
@@ -105,9 +105,11 @@ ${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/monkey.
# 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 # examples
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/examples' '/home/pi/' ${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/examples' '/home/pi/' # TODO: symlink?
# 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
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/avahi-services/sftp-ssh.service' '/etc/avahi/services'
# 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)
@@ -115,7 +117,6 @@ ${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} exec ${SCRIPTS_DIR}'/image-network.
${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/clover.service' '/lib/systemd/system/'
${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/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/melodic-rosdep-clover.yaml' '/etc/ros/rosdep/'
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/python3.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/'

View File

@@ -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 immidiately on non-zero result
REPO=$1 REPO=$1
REF=$2 REF=$2
@@ -68,27 +68,13 @@ 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/30-clover.list echo "yaml file:///etc/ros/rosdep/melodic-rosdep-clover.yaml" >> /etc/ros/rosdep/sources.list.d/20-default.list
echo "yaml file:///etc/ros/rosdep/python3.yaml" > /etc/ros/rosdep/sources.list.d/40-python3.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"
my_travis_retry sudo -u pi rosdep update my_travis_retry sudo -u pi rosdep update
resolve_rosdep() {
# TEMPLATE: resolve_rosdep <CATKIN_PATH> <ROS_DISTRO> <OS_DISTRO> <OS_VERSION>
CATKIN_PATH=$1
ROS_DISTRO='melodic'
OS_DISTRO='debian'
OS_VERSION='buster'
echo_stamp "Installing dependencies apps with rosdep in ${CATKIN_PATH}"
cd ${CATKIN_PATH}
my_travis_retry rosdep install -y --from-paths src --ignore-src --rosdistro ${ROS_DISTRO} --os=${OS_DISTRO}:${OS_VERSION}
}
export ROS_IP='127.0.0.1' # needed for running tests export ROS_IP='127.0.0.1' # needed for running tests
export ROS_PYTHON_VERSION=3
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
@@ -96,15 +82,14 @@ git config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*"
echo_stamp "Build and install Clover" echo_stamp "Build and install Clover"
cd /home/pi/catkin_ws cd /home/pi/catkin_ws
resolve_rosdep $(pwd) # Don't try to install gazebo_ros
my_travis_retry pip3 install wheel my_travis_retry rosdep install -y --from-paths src --ignore-src --rosdistro melodic --os=debian:buster \
my_travis_retry pip3 install -r /home/pi/catkin_ws/src/clover/clover/requirements.txt --skip-keys=gazebo_ros --skip-keys=gazebo_plugins
my_travis_retry pip install wheel
my_travis_retry pip install -r /home/pi/catkin_ws/src/clover/clover/requirements.txt
source /opt/ros/melodic/setup.bash source /opt/ros/melodic/setup.bash
catkin_make -j2 -DCMAKE_BUILD_TYPE=Release # Don't build simulation plugins for actual drone
catkin_make -j2 -DCMAKE_BUILD_TYPE=Release -DCATKIN_BLACKLIST_PACKAGES=clover_gazebo_plugins
echo_stamp "Enable ROS services"
systemctl enable roscore
systemctl enable clover
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
@@ -119,7 +104,7 @@ 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"
apt-get install -y --no-install-recommends \ my_travis_retry apt-get install -y --no-install-recommends \
ros-melodic-dynamic-reconfigure \ ros-melodic-dynamic-reconfigure \
ros-melodic-compressed-image-transport \ ros-melodic-compressed-image-transport \
ros-melodic-rosbridge-suite \ ros-melodic-rosbridge-suite \
@@ -135,7 +120,7 @@ echo_stamp "Install GeographicLib datasets (needed for mavros)" \
# FIXME: Buster comes with tornado==5.1.1 but we need tornado==4.2.1 for rosbridge_suite # 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) # (note that Python 3 will still have a more recent version)
pip3 install tornado==4.2.1 pip install tornado==4.2.1
echo_stamp "Running tests" echo_stamp "Running tests"
cd /home/pi/catkin_ws cd /home/pi/catkin_ws
@@ -145,11 +130,13 @@ 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 "Change permissions for examples"
chown -Rf pi:pi /home/pi/examples
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'
ROS_DISTRO='melodic'
export ROS_HOSTNAME=\`hostname\`.local export ROS_HOSTNAME=\`hostname\`.local
source /opt/ros/melodic/setup.bash source /opt/ros/melodic/setup.bash
source /home/pi/catkin_ws/devel/setup.bash source /home/pi/catkin_ws/devel/setup.bash

View File

@@ -57,6 +57,10 @@ my_travis_retry() {
return $result return $result
} }
echo_stamp "Increase apt retries"
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'
@@ -67,7 +71,7 @@ apt-get update \
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/opencv3 buster main" > /etc/apt/sources.list.d/opencv3.list
echo "deb http://deb.coex.tech/melodic-py3 buster main" > /etc/apt/sources.list.d/rpi-ros-melodic.list echo "deb http://deb.coex.tech/rpi-ros-melodic buster main" > /etc/apt/sources.list.d/rpi-ros-melodic.list
echo "deb http://deb.coex.tech/clover buster main" > /etc/apt/sources.list.d/clover.list echo "deb http://deb.coex.tech/clover buster main" > /etc/apt/sources.list.d/clover.list
echo_stamp "Update apt cache" echo_stamp "Update apt cache"
@@ -76,8 +80,12 @@ echo_stamp "Update apt cache"
apt-get update apt-get update
# && apt upgrade -y # && apt upgrade -y
# Let's retry fetching those packages several times, just in case
echo_stamp "Software installing" echo_stamp "Software installing"
apt-get install --no-install-recommends -y \ apt-cache policy cmake
apt-cache policy cmake-data
my_travis_retry apt-get install --no-install-recommends -y cmake-data=3.13.4-1 cmake=3.13.4-1
my_travis_retry apt-get install --no-install-recommends -y \
unzip \ unzip \
zip \ zip \
ipython \ ipython \
@@ -90,30 +98,26 @@ git \
dnsmasq \ dnsmasq \
tmux \ tmux \
vim \ vim \
cmake \
libjpeg8 \ libjpeg8 \
tcpdump \ tcpdump \
ltrace \ ltrace \
libpoco-dev \ libpoco-dev \
python3-rosdep \ libzbar0 \
python3-rosinstall-generator \ python-rosdep \
python3-wstool \ python-rosinstall-generator \
python3-rosinstall \ python-wstool \
python-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 python3-espeak \
ntpdate \ ntpdate \
python-dev \ python-dev \
python3-dev \ python3-dev \
python3-venv \ python-systemd \
python3-systemd \
mjpg-streamer \ mjpg-streamer \
python3-opencv \ python3-opencv
&& echo_stamp "Everything was installed!" "SUCCESS" \
|| (echo_stamp "Some packages wasn't installed!" "ERROR"; exit 1)
# Deny byobu to check available updates # Deny byobu to check available updates
sed -i "s/updates_available//" /usr/share/byobu/status/status sed -i "s/updates_available//" /usr/share/byobu/status/status
@@ -121,10 +125,11 @@ 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
echo_stamp "Make sure both pip and pip3 are installed" echo_stamp "Make sure both pip and pip3 are installed"
@@ -133,14 +138,14 @@ 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"
my_travis_retry pip3 install tornado==4.2.1 export CRYPTOGRAPHY_DONT_BUILD_RUST=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 pip2 install --prefer-binary rpi_ws281x 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
@@ -156,9 +161,13 @@ rm -rf node-v10.15.0-linux-armv6l/
rm node-v10.15.0-linux-armv6l.tar.gz rm node-v10.15.0-linux-armv6l.tar.gz
echo_stamp "Installing ptvsd" echo_stamp "Installing ptvsd"
my_travis_retry pip2 install ptvsd my_travis_retry pip install ptvsd
my_travis_retry pip3 install ptvsd my_travis_retry pip3 install ptvsd
echo_stamp "Installing pyzbar"
my_travis_retry pip install pyzbar
my_travis_retry pip3 install pyzbar
echo_stamp "Add .vimrc" echo_stamp "Add .vimrc"
cat << EOF > /home/pi/.vimrc cat << EOF > /home/pi/.vimrc
set mouse-=a set mouse-=a

View File

@@ -18,15 +18,13 @@ echo "Run image tests"
export ROS_DISTRO='melodic' export ROS_DISTRO='melodic'
export ROS_IP='127.0.0.1' export ROS_IP='127.0.0.1'
export ROS_PYTHON_VERSION=3
source /opt/ros/melodic/setup.bash source /opt/ros/melodic/setup.bash
source /home/pi/catkin_ws/devel/setup.bash source /home/pi/catkin_ws/devel/setup.bash
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
# Disable Python 2 tests for image - we're dropping support, right? ./tests_py3.py
# ./tests_py2.py
[[ $(./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
echo "Move /etc/ld.so.preload back to its original position" echo "Move /etc/ld.so.preload back to its original position"

View File

@@ -1,11 +1,11 @@
#!/bin/bash #!/bin/bash
# Perform a "standalone install" in a Docker container # Perform a "standalone install" in a Docker container
set -e
# Step 1: Install pip # Step 1: Install pip
apt update apt update
apt install -y curl apt install -y curl
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-pip.py
python ./get-pip.py python ./get-pip.py
# Step 1.5: Add deb.coex.tech to apt # Step 1.5: Add deb.coex.tech to apt

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python3 #!/usr/bin/env python
# validate all required modules installed # validate all required modules installed
@@ -17,8 +17,6 @@ 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
get_telemetry = rospy.ServiceProxy('get_telemetry', GetTelemetry)
import tf2_ros import tf2_ros
import tf2_geometry_msgs import tf2_geometry_msgs
@@ -27,6 +25,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
print(cv2.getBuildInformation()) print cv2.getBuildInformation()

View File

@@ -32,7 +32,7 @@ monkey --version
pigpiod -v pigpiod -v
i2cdetect -V i2cdetect -V
butterfly -h butterfly -h
espeak --version # espeak --version
mjpg_streamer --version mjpg_streamer --version
# ros stuff # ros stuff

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python3 #!/usr/bin/env python
# test backwards compatibility # test backwards compatibility

View File

@@ -1,7 +0,0 @@
#!/usr/bin/env python
# Make sure our Python 2 software is installed
import cv2
print(cv2.getBuildInformation())

8
builder/test/tests_py3.py Executable file
View File

@@ -0,0 +1,8 @@
#!/usr/bin/env python3
# Make sure our Python 3 software is installed
import cv2
from pyzbar import pyzbar
print(cv2.getBuildInformation())

View File

@@ -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')
code = 0 code = 0
os.chdir('./docs') os.chdir('./docs')

8
clover/CHANGELOG.rst Normal file
View 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

View File

@@ -30,6 +30,12 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake")
find_package(GeographicLib REQUIRED) find_package(GeographicLib REQUIRED)
find_package(OpenCV 3 REQUIRED
COMPONENTS
calib3d
imgproc
)
## System dependencies are found with CMake's conventions ## System dependencies are found with CMake's conventions
# find_package(Boost REQUIRED COMPONENTS system) # find_package(Boost REQUIRED COMPONENTS system)
@@ -160,13 +166,14 @@ add_library(${PROJECT_NAME}
## The recommended prefix ensures that target names across packages don't collide ## The recommended prefix ensures that target names across packages don't collide
add_executable(simple_offboard src/simple_offboard.cpp) add_executable(simple_offboard src/simple_offboard.cpp)
add_executable(rc src/rc.cpp) # PX4 already has rc and led targets, so we prefix ours with clover_
add_executable(clover_rc src/rc.cpp)
add_executable(camera_markers src/camera_markers.cpp) add_executable(camera_markers src/camera_markers.cpp)
add_executable(vpe_publisher src/vpe_publisher.cpp) add_executable(vpe_publisher src/vpe_publisher.cpp)
add_executable(led src/led.cpp) add_executable(clover_led src/led.cpp)
add_executable(shell src/shell.cpp) add_executable(shell src/shell.cpp)
@@ -175,19 +182,24 @@ target_link_libraries(simple_offboard
${GeographicLib_LIBRARIES} ${GeographicLib_LIBRARIES}
) )
target_link_libraries(rc ${catkin_LIBRARIES}) # Don't change actual binary names
set_target_properties(clover_rc PROPERTIES OUTPUT_NAME rc)
set_target_properties(clover_led PROPERTIES OUTPUT_NAME led)
target_link_libraries(clover_rc ${catkin_LIBRARIES})
target_link_libraries(camera_markers ${catkin_LIBRARIES}) target_link_libraries(camera_markers ${catkin_LIBRARIES})
target_link_libraries(vpe_publisher ${catkin_LIBRARIES}) target_link_libraries(vpe_publisher ${catkin_LIBRARIES})
target_link_libraries(led ${catkin_LIBRARIES}) target_link_libraries(clover_led ${catkin_LIBRARIES})
target_link_libraries(shell ${catkin_LIBRARIES}) target_link_libraries(shell ${catkin_LIBRARIES})
add_dependencies(simple_offboard ${PROJECT_NAME}_generate_messages_cpp) add_dependencies(simple_offboard ${PROJECT_NAME}_generate_messages_cpp)
add_dependencies(led ${PROJECT_NAME}_generate_messages_cpp) add_dependencies(clover_led ${PROJECT_NAME}_generate_messages_cpp)
add_dependencies(shell ${PROJECT_NAME}_generate_messages_cpp) add_dependencies(shell ${PROJECT_NAME}_generate_messages_cpp)
@@ -204,6 +216,7 @@ add_dependencies(shell ${PROJECT_NAME}_generate_messages_cpp)
## Specify libraries to link a library or executable target against ## Specify libraries to link a library or executable target against
target_link_libraries(${PROJECT_NAME} target_link_libraries(${PROJECT_NAME}
${catkin_LIBRARIES} ${catkin_LIBRARIES}
${OpenCV_LIBRARIES}
) )
############# #############

55
clover/README.md Normal file
View File

@@ -0,0 +1,55 @@
# `clover` ROS package
A bundle for autonomous navigation and drone control.
## 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).
Clone this repo to directory `~/catkin_ws/src/clover`:
```bash
cd ~/catkin_ws/src
git clone https://github.com/CopterExpress/clover.git clover
```
All the required ROS packages (including `mavros` and `opencv`) can be installed using `rosdep`:
```bash
cd ~/catkin_ws/
rosdep install -y --from-paths src --ignore-src
```
Build ROS packages (on memory constrained platforms you might be going to need to use `-j1` key):
```bash
cd ~/catkin_ws
catkin_make -j1
```
To complete `mavros` install you'll need to install `geographiclib` datasets:
```bash
curl https://raw.githubusercontent.com/mavlink/mavros/master/mavros/scripts/install_geographiclib_datasets.sh | sudo bash
```
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
cd ~/catkin_ws/src/clover/clover/config
sudo cp 99-px4fmu.rules /lib/udev/rules.d
```
Alternatively you may change the `fcu_url` property in `mavros.launch` file to point to your flight controller device.
## Running
To start connection to the flight controller, use:
```bash
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`.

View File

@@ -1,17 +1,17 @@
image_width: 640 image_width: 640
image_height: 480 image_height: 480
distortion_model: plumb_bob distortion_model: plumb_bob
camera_name: raspicam camera_name: main_camera_optical
camera_matrix: camera_matrix:
rows: 3 rows: 3
cols: 3 cols: 3
data: data:
- 332.47884746146343 - 332.47884746146343
- 0. - 0.
- 324.38022493658536 - 320.0
- 0. - 0.
- 333.1761847948052 - 333.1761847948052
- 219.6445547142857 - 240.0
- 0. - 0.
- 0. - 0.
- 1. - 1.

View File

@@ -1,45 +0,0 @@
image_width: 320
image_height: 240
distortion_model: plumb_bob
camera_name: raspicam
camera_matrix:
rows: 3
cols: 3
data:
- 166.23942373073172
- 0.
- 162.19011246829268
- 0.
- 166.5880923974026
- 109.82227735714285
- 0.
- 0.
- 1.
distortion_coefficients:
rows: 1
cols: 8
data: [ 2.15356885e-01, -1.17472846e-01, -3.06197672e-04,
-1.09444025e-04, -4.53657258e-03, 5.73090623e-01,
-1.27574577e-01, -2.86125589e-02, 0.00000000e+00,
0.00000000e+00, 0.00000000e+00, 0.00000000e+00,
0.00000000e+00, 0.00000000e+00]
rectification_matrix:
rows: 3
cols: 3
data: [1, 0, 0, 0, 1, 0, 0, 0, 1]
projection_matrix:
rows: 3
cols: 4
data:
- 166.23942373073172
- 0.
- 162.19011246829268
- 0.
- 0.
- 166.5880923974026
- 109.82227735714285
- 0.
- 0.
- 0.
- 1.
- 0.

View File

@@ -3,18 +3,20 @@
<arg name="aruco_map" default="false"/> <arg name="aruco_map" default="false"/>
<arg name="aruco_vpe" default="false"/> <arg name="aruco_vpe" default="false"/>
<!-- For additional help go to https://clever.coex.tech/aruco --> <!-- For additional help go to https://clover.coex.tech/aruco -->
<!-- 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="$(arg aruco_detect)" type="nodelet" args="load aruco_pose/aruco_detect nodelet_manager" output="screen" clear_params="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" if="$(arg aruco_map)"/>
<param name="cornerRefinementMethod" value="2"/>
<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"/>
<param name="length" value="0.33"/> <param name="length" value="0.33"/>
<!-- aruco detector parameters -->
<param name="cornerRefinementMethod" value="2"/> <!-- contour refinement -->
<param name="minMarkerPerimeterRate" value="0.075"/> <!-- 0.075 for 320x240, 0.0375 for 640x480 -->
</node> </node>
<!-- aruco_map: estimate aruco map pose --> <!-- aruco_map: estimate aruco map pose -->

View File

@@ -1,6 +1,7 @@
<launch> <launch>
<arg name="fcu_conn" default="usb"/> <arg name="fcu_conn" default="usb"/>
<arg name="fcu_ip" default="127.0.0.1"/> <arg name="fcu_ip" default="127.0.0.1"/>
<arg name="fcu_sys_id" default="1"/>
<arg name="gcs_bridge" default="tcp"/> <arg name="gcs_bridge" default="tcp"/>
<arg name="web_video_server" default="true"/> <arg name="web_video_server" default="true"/>
<arg name="rosbridge" default="true"/> <arg name="rosbridge" default="true"/>
@@ -9,9 +10,12 @@
<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="blocks" default="false"/>
<arg name="rc" default="true"/> <arg name="rc" default="true"/>
<arg name="shell" default="true"/> <arg name="shell" default="true"/>
<arg name="simulator" default="false"/> <!-- flag that we are operating on a simulated drone -->
<!-- log formatting --> <!-- log formatting -->
<env name="ROSCONSOLE_FORMAT" value="[${severity}] [${time}]: ${logger}: ${message}"/> <env name="ROSCONSOLE_FORMAT" value="[${severity}] [${time}]: ${logger}: ${message}"/>
@@ -19,6 +23,7 @@
<include file="$(find clover)/launch/mavros.launch"> <include file="$(find clover)/launch/mavros.launch">
<arg name="fcu_conn" value="$(arg fcu_conn)"/> <arg name="fcu_conn" value="$(arg fcu_conn)"/>
<arg name="fcu_ip" value="$(arg fcu_ip)"/> <arg name="fcu_ip" value="$(arg fcu_ip)"/>
<arg name="fcu_sys_id" value="$(arg fcu_sys_id)"/>
<arg name="gcs_bridge" value="$(arg gcs_bridge)"/> <arg name="gcs_bridge" value="$(arg gcs_bridge)"/>
</include> </include>
@@ -51,10 +56,13 @@
<param name="reference_frames/body" value="map"/> <param name="reference_frames/body" value="map"/>
<param name="reference_frames/base_link" value="map"/> <param name="reference_frames/base_link" value="map"/>
<param name="reference_frames/navigate_target" value="map"/> <param name="reference_frames/navigate_target" value="map"/>
<param name="reference_frames/main_camera_optical" value="map"/>
</node> </node>
<!-- main camera --> <!-- main camera -->
<include file="$(find clover)/launch/main_camera.launch" if="$(arg main_camera)"/> <include file="$(find clover)/launch/main_camera.launch" if="$(arg main_camera)">
<arg name="simulator" value="$(arg simulator)"/>
</include>
<!-- rosbridge --> <!-- rosbridge -->
<include file="$(find rosbridge_server)/launch/rosbridge_websocket.launch" if="$(eval rosbridge or rc)"/> <include file="$(find rosbridge_server)/launch/rosbridge_websocket.launch" if="$(eval rosbridge or rc)"/>
@@ -63,14 +71,19 @@
<node name="tf2_web_republisher" pkg="tf2_web_republisher" type="tf2_web_republisher" output="screen" if="$(arg rosbridge)"/> <node name="tf2_web_republisher" pkg="tf2_web_republisher" type="tf2_web_republisher" output="screen" if="$(arg rosbridge)"/>
<!-- vl53l1x ToF rangefinder --> <!-- vl53l1x ToF rangefinder -->
<node name="rangefinder" pkg="vl53l1x" type="vl53l1x_node" output="screen" if="$(arg rangefinder_vl53l1x)"> <node name="rangefinder" pkg="vl53l1x" type="vl53l1x_node" output="screen" if="$(eval rangefinder_vl53l1x and not simulator)">
<param name="frame_id" value="rangefinder"/> <param name="frame_id" value="rangefinder"/>
<param name="min_signal" value="0.4"/> <param name="min_signal" value="0.4"/>
<param name="pass_statuses" type="yaml" value="[0, 6, 7, 11]"/> <param name="pass_statuses" type="yaml" value="[0, 6, 7, 11]"/>
</node> </node>
<!-- led strip --> <!-- led strip -->
<include file="$(find clover)/launch/led.launch" if="$(arg led)"/> <include file="$(find clover)/launch/led.launch" if="$(arg led)">
<arg name="simulator" value="$(arg simulator)"/>
</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">
@@ -80,4 +93,9 @@
<!-- Shell access through ROS service --> <!-- Shell access through ROS service -->
<node name="shell" pkg="clover" type="shell" output="screen" if="$(arg shell)"/> <node name="shell" pkg="clover" type="shell" output="screen" if="$(arg shell)"/>
<!-- Update static directory -->
<node pkg="roswww_static" name="roswww_static" type="main.py" clear_params="true">
<param name="default_package" value="clover"/>
</node>
</launch> </launch>

View File

@@ -2,13 +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"/>
<!-- 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="$(arg ws281x)"> <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"/>
@@ -31,7 +35,7 @@
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 }
</rosparam> </rosparam>
</node> </node>

View File

@@ -1,13 +1,16 @@
<launch> <launch>
<!-- article about camera setup: https://clever.coex.tech/camera_frame --> <!-- article about camera setup: https://clover.coex.tech/camera_setup -->
<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="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 -->
@@ -15,17 +18,17 @@
<!-- <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 node --> <!-- camera node -->
<node pkg="nodelet" type="nodelet" name="main_camera" args="load cv_camera/CvCameraNodelet nodelet_manager" clear_params="true"> <node pkg="nodelet" type="nodelet" name="main_camera" args="load cv_camera/CvCameraNodelet nodelet_manager" clear_params="true" unless="$(arg simulator)">
<param name="device_path" value="/dev/video0"/> <!-- v4l2 device --> <param name="device_path" value="/dev/video0"/> <!-- v4l2 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_320.yaml"/> <param name="camera_info_url" value="file://$(find clover)/camera_info/fisheye_cam.yaml"/>
<param name="rate" value="100"/> <!-- poll rate --> <param name="rate" value="100"/> <!-- poll rate -->
<param name="cv_cap_prop_fps" value="40"/> <!-- camera FPS --> <param name="cv_cap_prop_fps" value="40"/> <!-- camera FPS -->
<param name="capture_delay" value="0.02"/> <!-- approximate delay on frame retrieving --> <param name="capture_delay" value="0.02"/> <!-- approximate delay on frame retrieving -->
<param name="rescale_camera_info" value="true"/> <!-- automatically rescale camera calibration info --> <param name="rescale_camera_info" value="true"/> <!-- automatically rescale camera calibration info -->
<!-- camera resolution, NOTE: camera_info file should match it --> <!-- camera resolution -->
<param name="image_width" value="320"/> <param name="image_width" value="320"/>
<param name="image_height" value="240"/> <param name="image_height" value="240"/>
</node> </node>

View File

@@ -1,6 +1,7 @@
<launch> <launch>
<arg name="fcu_conn" default="usb"/> <!-- options: usb, uart, tcp, udp, sitl --> <arg name="fcu_conn" default="usb"/> <!-- options: usb, uart, tcp, udp, sitl -->
<arg name="fcu_ip" default="127.0.0.1"/> <arg name="fcu_ip" default="127.0.0.1"/>
<arg name="fcu_sys_id" default="1"/>
<arg name="gcs_bridge" default="tcp"/> <arg name="gcs_bridge" default="tcp"/>
<arg name="viz" default="true"/> <arg name="viz" default="true"/>
<arg name="respawn" default="true"/> <arg name="respawn" default="true"/>
@@ -19,6 +20,9 @@
<!-- sitl since PX4 1.9.0 --> <!-- sitl since PX4 1.9.0 -->
<param name="fcu_url" value="udp://@$(arg fcu_ip):14580" if="$(eval fcu_conn == 'sitl')"/> <param name="fcu_url" value="udp://@$(arg fcu_ip):14580" if="$(eval fcu_conn == 'sitl')"/>
<!-- set target_system_id -->
<param name="target_system_id" value="$(arg fcu_sys_id)" />
<!-- gcs bridge --> <!-- gcs bridge -->
<param name="gcs_url" value="tcp-l://0.0.0.0:5760" if="$(eval gcs_bridge == 'tcp')"/> <param name="gcs_url" value="tcp-l://0.0.0.0:5760" if="$(eval gcs_bridge == 'tcp')"/>
<param name="gcs_url" value="udp://0.0.0.0:14550@14550" if="$(eval gcs_bridge == 'udp')"/> <param name="gcs_url" value="udp://0.0.0.0:14550@14550" if="$(eval gcs_bridge == 'udp')"/>

View File

@@ -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>

View File

@@ -1,13 +1,13 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<package format="3"> <package format="2">
<name>clover</name> <name>clover</name>
<version>0.0.1</version> <version>0.21.1</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>
<license>MIT</license> <license>MIT</license>
<url type="website">https://clever.coex.tech/</url> <url type="website">https://clover.coex.tech/</url>
<author email="okalachev@gmail.com">Oleg Kalachev</author> <author email="okalachev@gmail.com">Oleg Kalachev</author>
<author email="urpylka@gmail.com">Artem Smirnov</author> <author email="urpylka@gmail.com">Artem Smirnov</author>
@@ -37,10 +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 condition="$ROS_PYTHON_VERSION == 2">python-lxml</depend> <depend>python-lxml</depend>
<depend condition="$ROS_PYTHON_VERSION == 3">python3-lxml</depend> <exec_depend>python-pymavlink</exec_depend>
<exec_depend condition="$ROS_PYTHON_VERSION == 2">python-pymavlink</exec_depend>
<exec_depend condition="$ROS_PYTHON_VERSION == 3">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> -->

View File

@@ -1,5 +1,5 @@
flask==1.1.1 flask==1.1.1
docopt==0.6.2 docopt==0.6.2
geopy==1.20.0 geopy==1.11.0
smbus2==0.3.0 smbus2==0.3.0
VL53L1X==0.0.4 VL53L1X==0.0.5

View File

@@ -34,9 +34,7 @@ class OpticalFlow : public nodelet::Nodelet
{ {
public: public:
OpticalFlow(): OpticalFlow():
camera_matrix_(3, 3, CV_64F), camera_matrix_(3, 3, CV_64F)
dist_coeffs_(8, 1, CV_64F),
tf_listener_(tf_buffer_)
{} {}
private: private:
@@ -52,8 +50,8 @@ private:
Mat hann_; Mat hann_;
Mat prev_, curr_; Mat prev_, curr_;
Mat camera_matrix_, dist_coeffs_; Mat camera_matrix_, dist_coeffs_;
tf2_ros::Buffer tf_buffer_; std::unique_ptr<tf2_ros::Buffer> tf_buffer_;
tf2_ros::TransformListener tf_listener_; std::unique_ptr<tf2_ros::TransformListener> tf_listener_;
bool calc_flow_gyro_; bool calc_flow_gyro_;
void onInit() void onInit()
@@ -63,11 +61,14 @@ private:
image_transport::ImageTransport it(nh); image_transport::ImageTransport it(nh);
image_transport::ImageTransport it_priv(nh_priv); image_transport::ImageTransport it_priv(nh_priv);
nh.param<std::string>("mavros/local_position/tf/frame_id", local_frame_id_, "map"); tf_buffer_.reset(new tf2_ros::Buffer());
nh.param<std::string>("mavros/local_position/tf/child_frame_id", fcu_frame_id_, "base_link"); tf_listener_.reset(new tf2_ros::TransformListener(*tf_buffer_, nh));
nh_priv.param("roi", roi_px_, 128);
nh_priv.param("roi_rad", roi_rad_, 0.0); local_frame_id_ = nh.param<std::string>("mavros/local_position/tf/frame_id", "map");
nh_priv.param("calc_flow_gyro", calc_flow_gyro_, false); fcu_frame_id_ = nh.param<std::string>("mavros/local_position/tf/child_frame_id", "base_link");
roi_px_ = nh_priv.param("roi", 128);
roi_rad_ = nh_priv.param("roi_rad", 0.0);
calc_flow_gyro_ = nh_priv.param("calc_flow_gyro", false);
img_sub_ = it.subscribeCamera("image_raw", 1, &OpticalFlow::flow, this); img_sub_ = it.subscribeCamera("image_raw", 1, &OpticalFlow::flow, this);
img_pub_ = it_priv.advertise("debug", 1); img_pub_ = it_priv.advertise("debug", 1);
@@ -91,9 +92,7 @@ private:
camera_matrix_.at<double>(i, j) = cinfo->K[3 * i + j]; camera_matrix_.at<double>(i, j) = cinfo->K[3 * i + j];
} }
} }
for (int k = 0; k < cinfo->D.size(); k++) { dist_coeffs_ = cv::Mat(cinfo->D, true);
dist_coeffs_.at<double>(k) = cinfo->D[k];
}
} }
void drawFlow(Mat& frame, double x, double y, double quality) const void drawFlow(Mat& frame, double x, double y, double quality) const
@@ -186,7 +185,7 @@ private:
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
flow_camera.vector.y = -flow_x; // +x means clockwise rotation around X axis flow_camera.vector.y = -flow_x; // +x means clockwise rotation around X axis
try { try {
tf_buffer_.transform(flow_camera, flow_fcu, fcu_frame_id_); tf_buffer_->transform(flow_camera, flow_fcu, fcu_frame_id_);
} catch (const tf2::TransformException& e) { } catch (const tf2::TransformException& e) {
// transform is not available yet // transform is not available yet
return; return;
@@ -200,7 +199,7 @@ private:
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; static 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;
@@ -247,8 +246,8 @@ private:
geometry_msgs::Vector3Stamped calcFlowGyro(const std::string& frame_id, const ros::Time& prev, const ros::Time& curr) geometry_msgs::Vector3Stamped calcFlowGyro(const std::string& frame_id, const ros::Time& prev, const ros::Time& curr)
{ {
tf2::Quaternion prev_rot, curr_rot; tf2::Quaternion prev_rot, curr_rot;
tf2::fromMsg(tf_buffer_.lookupTransform(frame_id, local_frame_id_, prev).transform.rotation, prev_rot); tf2::fromMsg(tf_buffer_->lookupTransform(frame_id, local_frame_id_, prev).transform.rotation, prev_rot);
tf2::fromMsg(tf_buffer_.lookupTransform(frame_id, local_frame_id_, curr, ros::Duration(0.1)).transform.rotation, curr_rot); tf2::fromMsg(tf_buffer_->lookupTransform(frame_id, local_frame_id_, curr, ros::Duration(0.1)).transform.rotation, curr_rot);
geometry_msgs::Vector3Stamped flow; geometry_msgs::Vector3Stamped flow;
flow.header.frame_id = frame_id; flow.header.frame_id = frame_id;

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python3 #!/usr/bin/env python
# coding=utf-8 # coding=utf-8
# Copyright (C) 2018 Copter Express Technologies # Copyright (C) 2018 Copter Express Technologies
@@ -138,7 +138,7 @@ def mavlink_exec(cmd, timeout=3.0):
timeout=3, timeout=3,
baudrate=0, baudrate=0,
count=len(cmd), count=len(cmd),
data=[ord(c) for c in cmd.ljust(70, '\0')]) data=map(ord, 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)
@@ -210,7 +210,7 @@ def check_fcu():
is_clover_firmware = True is_clover_firmware = True
if not is_clover_firmware: if not is_clover_firmware:
failure('not running Clover PX4 firmware, https://clever.coex.tech/firmware') failure('not running Clover PX4 firmware, 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:
@@ -250,11 +250,11 @@ def check_fcu():
try: try:
battery = rospy.wait_for_message('mavros/battery', BatteryState, timeout=3) battery = rospy.wait_for_message('mavros/battery', BatteryState, timeout=3)
if not battery.cell_voltage: if not battery.cell_voltage:
failure('cell voltage is not available, https://clever.coex.tech/power') failure('cell voltage is not available, https://clover.coex.tech/power')
else: else:
cell = battery.cell_voltage[0] cell = battery.cell_voltage[0]
if cell > 4.3 or cell < 3.0: if cell > 4.3 or cell < 3.0:
failure('incorrect cell voltage: %.2f V, https://clever.coex.tech/power', cell) failure('incorrect cell voltage: %.2f V, https://clover.coex.tech/power', cell)
elif cell < 3.7: elif cell < 3.7:
failure('critically low cell voltage: %.2f V, recharge battery', cell) failure('critically low cell voltage: %.2f V, recharge battery', cell)
except rospy.ROSException: except rospy.ROSException:
@@ -609,7 +609,7 @@ def check_rangefinder():
@check('Boot duration') @check('Boot duration')
def check_boot_duration(): def check_boot_duration():
output = subprocess.check_output('systemd-analyze').decode() output = subprocess.check_output('systemd-analyze')
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 > 15:
@@ -620,7 +620,7 @@ def check_boot_duration():
def check_cpu_usage(): def check_cpu_usage():
WHITELIST = 'nodelet', WHITELIST = 'nodelet',
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).decode() output = subprocess.check_output(CMD, shell=True)
processes = output.split('\n') processes = output.split('\n')
for process in processes: for process in processes:
if not process: if not process:
@@ -636,7 +636,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).decode() stderr=subprocess.STDOUT)
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
@@ -701,7 +701,7 @@ def check_preflight_status():
@check('Network') @check('Network')
def check_network(): def check_network():
ros_hostname = os.environ.get('ROS_HOSTNAME').strip() ros_hostname = os.environ.get('ROS_HOSTNAME', '').strip()
if not ros_hostname: if not ros_hostname:
failure('no ROS_HOSTNAME is set') failure('no ROS_HOSTNAME is set')
@@ -718,7 +718,7 @@ def check_network():
if ros_hostname in parts: if ros_hostname in parts:
break break
else: else:
failure('not found %s in /etc/hosts, ROS will malfunction if network interfaces are down, https://clever.coex.tech/hostname', ros_hostname) failure('not found %s in /etc/hosts, ROS will malfunction if network interfaces are down, https://clover.coex.tech/hostname', ros_hostname)
@check('RPi health') @check('RPi health')
@@ -751,7 +751,7 @@ 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']).decode() output = subprocess.check_output(['vcgencmd', 'get_throttled'])
except OSError: except OSError:
failure('could not call vcgencmd binary; not a Raspberry Pi?') failure('could not call vcgencmd binary; not a Raspberry Pi?')
return return

View File

@@ -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;
@@ -71,9 +73,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 +124,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;
@@ -417,8 +421,9 @@ void publish(const ros::Time stamp)
} }
if (setpoint_type == POSITION || setpoint_type == NAVIGATE || setpoint_type == NAVIGATE_GLOBAL) { if (setpoint_type == POSITION || setpoint_type == NAVIGATE || setpoint_type == NAVIGATE_GLOBAL) {
position_msg.header.stamp = stamp;
if (setpoint_yaw_type == YAW || setpoint_yaw_type == TOWARDS) { if (setpoint_yaw_type == YAW || setpoint_yaw_type == TOWARDS) {
position_msg.header.stamp = stamp;
position_pub.publish(position_msg); position_pub.publish(position_msg);
} else { } else {
@@ -484,13 +489,34 @@ 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))
throw std::runtime_error("State timeout, check mavros settings"); throw std::runtime_error("State timeout, check mavros settings");
if (!state.connected) if (!state.connected)
throw std::runtime_error("No connection to FCU, https://clever.coex.tech/connection"); throw std::runtime_error("No connection to FCU, https://clover.coex.tech/connection");
} }
#define ENSURE_FINITE(var) { if (!std::isfinite(var)) throw std::runtime_error(#var " argument cannot be NaN or Inf"); } #define ENSURE_FINITE(var) { if (!std::isfinite(var)) throw std::runtime_error(#var " argument cannot be NaN or Inf"); }
@@ -506,25 +532,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");
@@ -549,14 +638,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))
@@ -605,12 +686,13 @@ bool serve(enum setpoint_type_t sp_type, float x, float y, float z, float vx, fl
if (sp_type == POSITION || sp_type == NAVIGATE || sp_type == NAVIGATE_GLOBAL || sp_type == VELOCITY || sp_type == ATTITUDE) { if (sp_type == POSITION || sp_type == NAVIGATE || sp_type == NAVIGATE_GLOBAL || sp_type == VELOCITY || sp_type == ATTITUDE) {
// destination point and/or yaw // destination point and/or yaw
static PoseStamped ps; PoseStamped ps;
ps.header.frame_id = frame_id; ps.header.frame_id = frame_id;
ps.header.stamp = stamp; ps.header.stamp = stamp;
ps.pose.position.x = x; ps.pose.position.x = x;
ps.pose.position.y = y; ps.pose.position.y = y;
ps.pose.position.z = z; ps.pose.position.z = z;
ps.pose.orientation.w = 1.0; // Ensure quaternion is always valid
if (std::isnan(yaw)) { if (std::isnan(yaw)) {
setpoint_yaw_type = YAW_RATE; setpoint_yaw_type = YAW_RATE;
@@ -623,7 +705,7 @@ 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);
@@ -651,6 +733,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();
@@ -691,27 +774,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)
@@ -779,6 +862,7 @@ int main(int argc, char **argv)
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);
@@ -788,6 +872,7 @@ int main(int argc, char **argv)
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));
@@ -805,6 +890,7 @@ int main(int argc, char **argv)
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 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); auto local_position_sub = nh.subscribe("mavros/local_position/pose", 1, &handleLocalPosition);
// Setpoint publishers // Setpoint publishers

View File

@@ -1,3 +1,4 @@
#!/usr/bin/env python
import rospy import rospy
import pytest import pytest
from mavros_msgs.msg import State from mavros_msgs.msg import State

0
clover/www/CATKIN_IGNORE Normal file
View File

View File

@@ -1,19 +1,22 @@
<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://clever.coex.tech">clever.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="" 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><a href="viz.html">View 3D visualization</a> (<code>ros3djs</code>)</li>
<li><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>
</ul> </ul>
<div class="version"></div> <div class="version"></div>
<script src="js/roslib.js"></script> <script src="js/roslib.js"></script>
<script type="text/javascript"> <script type="text/javascript">
document.querySelector("#wvs").href = location.origin + ':8080'; document.querySelector("#wvs").href = location.protocol + '//' + location.hostname + ':8080';
document.querySelector("#butterfly").href = location.origin + ':57575'; document.querySelector("#butterfly").href = location.protocol + '//' + location.hostname + ':57575';
// Determine image version // Determine image version
var ros = new ROSLIB.Ros({ url: 'ws://' + location.hostname + ':9090' }); var ros = new ROSLIB.Ros({ url: 'ws://' + location.hostname + ':9090' });

View 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

View File

@@ -0,0 +1,87 @@
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}
)
#############
## 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)

54
clover_blocks/README.md Normal file
View File

@@ -0,0 +1,54 @@
# 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 [`roblib.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).
* `~yaw_tolerance` (*float*) yaw angle tolerance in degrees, used in set_yaw block (default: 20).
* `~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.

View 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
View File

@@ -0,0 +1,48 @@
<?xml version="1.0"?>
<package format="2">
<name>clover_blocks</name>
<version>0.21.1</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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 182 KiB

184
clover_blocks/src/clover_blocks Executable file
View File

@@ -0,0 +1,184 @@
#!/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
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 in g
except Stop:
rospy.loginfo('Program forced to stop')
except Exception as e:
rospy.logerr(str(e))
error_pub.publish(str(e))
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}
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()

View File

@@ -0,0 +1,5 @@
---
bool success
string message
string[] names
string[] programs

View File

@@ -0,0 +1,4 @@
string code # code in Python
---
bool success
string message

View File

@@ -0,0 +1,5 @@
string name
string program
---
bool success
string message

202
clover_blocks/www/blockly/LICENSE Executable file
View 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.

View File

@@ -0,0 +1,60 @@
# Blockly [![Build Status]( https://travis-ci.org/google/blockly.svg?branch=master)](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/**
![](https://developers.google.com/blockly/images/sample.png)
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.

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View 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

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 B

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More