Compare commits

..

114 Commits

Author SHA1 Message Date
Oleg Kalachev
fbc9c22cb3 Try not to install espeak to reduce size 2020-11-17 11:32:45 +03:00
Oleg Kalachev
32da94aeae image: decrease git clone depth 2020-11-14 12:59:52 +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
382 changed files with 152605 additions and 1446 deletions

2
.gitattributes vendored
View File

@@ -4,3 +4,5 @@ eventemitter2.js linguist-vendored
ros3d.js linguist-vendored
three.min.js linguist-vendored
aruco_pose/vendor/* linguist-vendored
blockly/* linguist-vendored
highlight/* linguist-vendored

2
.gitignore vendored
View File

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

View File

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

View File

@@ -7,10 +7,10 @@ env:
global:
- DOCKER="sfalexrog/img-tool:qemu-update"
- TARGET_REPO="https://github.com/${TRAVIS_REPO_SLUG}.git"
- if [[ -z ${TRAVIS_TAG} ]]; then IMAGE_VERSION="${TRAVIS_COMMIT}}"; else IMAGE_VERSION="${TRAVIS_TAG}"; fi
- IMAGE_VERSION=${TRAVIS_TAG:-${TRAVIS_COMMIT:0:7}}
- IMAGE_NAME="$(basename -s '.git' ${TARGET_REPO})_${IMAGE_VERSION}.img"
git:
depth: 50
depth: 1
jobs:
fast_finish: true
include:
@@ -36,12 +36,13 @@ jobs:
fi
before_cache:
- 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:
# Set up git user name and tag this commit
- git config --local user.name "goldarte"
- git config --local user.email "goldartt@gmail.com"
- sudo chmod -R 777 *
- cd images && zip ${IMAGE_NAME}.zip ${IMAGE_NAME}
deploy:
provider: releases
token: ${GITHUB_OAUTH_TOKEN}
@@ -99,14 +100,6 @@ jobs:
verbose: true
on:
branch: master
- stage: Annotate
name: Auto-generate changelog
language: python
python: 3.6
install:
- pip install GitPython PyGithub
script:
- PYTHONUNBUFFERED=1 python ./gen_changelog.py
- stage: Build
name: Editorconfig-lint
language: generic
@@ -114,10 +107,9 @@ jobs:
- wget https://github.com/okalachev/editorconfig-checker/releases/download/1.2.1-disable-spaces-amount/ec-linux-amd64
- chmod +x ec-linux-amd64
script:
- ./ec-linux-amd64 -spaces-after-tabs -e "roslib.js|ros3d.js|eventemitter2.js|draw.cpp|BinUtils.swift|\.idea|apps/android/app|Assets.xcassets|test_parser_pass.txt|test_node_failure.txt|aruco_pose/vendor|\.stl|\.dxf"
- ./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:
- Build
- Annotate
# More info there
# https://github.com/travis-ci/travis-ci/issues/6893
# https://docs.travis-ci.com/user/customizing-the-build/

View File

@@ -1,12 +1,14 @@
# COEX Clover Drone Kit
# clover🍀: create autonomous drones easily
<img src="docs/assets/clever4-front-white.png" align="right" width="400px" alt="Clover Drone">
<img src="docs/assets/clever4-front-white.png" align="right" width="400px" alt="COEX Clover Drone">
Clover is an educational programmable drone kit consisting of an unassembled quadcopter, open source software and documentation. The kit includes Pixracer-compatible autopilot running PX4 firmware, Raspberry Pi 4 as companion computer, a camera for computer vision navigation as well as additional sensors and peripheral devices.
Clover is an open source [ROS](https://www.ros.org)-based framework, providing user-friendly tools to control [PX4](https://px4.io)-powered drones. Clover is available as a ROS package, but is shipped mainly as a preconfigured image for Raspberry Pi. Once you've installed Raspberry Pi on your drone and flashed the image to its microSD card, taking the drone up in the air is a matter of minutes.
The main documentation is available [on Gitbook](https://clover.coex.tech/).
COEX Clover Drone is an educational programmable drone kit, suited perfectly for running clover software. The kit is shipped unassembled and includes Pixracer-compatible autopilot running PX4 firmware, Raspberry Pi 4 as a companion computer, a camera for computer vision navigation as well as additional sensors and peripheral devices. Batteries included.
Official website: <a href="https://coex.tech/clover">coex.tech/clover</a>.
The main documentation is available at [https://clover.coex.tech](https://clover.coex.tech/). Official website: [coex.tech/clover](https://coex.tech/clover).
[__Support us on Kickstarter!__](https://www.kickstarter.com/projects/copterexpress/cloverdrone)
## Video compilation

View File

@@ -9,6 +9,7 @@
"richquotes@https://github.com/okalachev/gitbook-plugin-richquotes.git",
"yametrika",
"anchors",
"collapsible-menu",
"validate-links",
"bulk-redirect@https://github.com/okalachev/gitbook-plugin-bulk-redirect.git",
"sitemap@https://github.com/okalachev/plugin-sitemap.git",

View File

@@ -7,19 +7,25 @@ rospy.init_node('leds')
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
rospy.sleep(2)
print('Fill green')
set_effect(r=0, g=100, b=0) # fill strip with green color
rospy.sleep(2)
print('Fade to blue')
set_effect(effect='fade', r=0, g=0, b=255) # fade to blue color
rospy.sleep(2)
print('Flash red')
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
rospy.sleep(5)
print('Rainbow')
set_effect(effect='rainbow') # show rainbow

View File

@@ -1,4 +1,4 @@
# Information: https://clover.coex.tech/en/snippets.html#block-nav
# Information: https://clover.coex.tech/en/snippets.html#navigate_wait
import math
import rospy

View File

@@ -105,7 +105,7 @@ ${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/monkey.
# software install
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} exec ${SCRIPTS_DIR}'/image-software.sh'
# examples
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/examples' '/home/pi/'
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/examples' '/home/pi/' # TODO: symlink?
# network setup
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} exec ${SCRIPTS_DIR}'/image-network.sh'
# avahi setup

View File

@@ -74,18 +74,6 @@ my_travis_retry rosdep update
echo_stamp "Populate rosdep for ROS user"
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
echo_stamp "Reconfiguring Clover repository for simplier unshallowing"
@@ -94,11 +82,14 @@ git config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*"
echo_stamp "Build and install Clover"
cd /home/pi/catkin_ws
resolve_rosdep $(pwd)
# Don't try to install gazebo_ros
my_travis_retry rosdep install -y --from-paths src --ignore-src --rosdistro melodic --os=debian:buster \
--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
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 "Install clever package (for backwards compatibility)"
cd /home/pi/catkin_ws/src/clover/builder/assets/clever
@@ -113,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
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-compressed-image-transport \
ros-melodic-rosbridge-suite \
@@ -139,6 +130,9 @@ catkin_make run_tests #&& catkin_test_results
echo_stamp "Change permissions for 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"
cat << EOF >> /home/pi/.bashrc
LANG='C.UTF-8'

View File

@@ -80,8 +80,9 @@ echo_stamp "Update apt cache"
apt-get update
# && apt upgrade -y
# Let's retry fetching those packages several times, just in case
echo_stamp "Software installing"
apt-get install --no-install-recommends -y \
my_travis_retry apt-get install --no-install-recommends -y \
unzip \
zip \
ipython \
@@ -109,15 +110,12 @@ libffi-dev \
monkey \
pigpio python-pigpio python3-pigpio \
i2c-tools \
espeak espeak-data python-espeak \
ntpdate \
python-dev \
python3-dev \
python-systemd \
mjpg-streamer \
python3-opencv \
&& echo_stamp "Everything was installed!" "SUCCESS" \
|| (echo_stamp "Some packages wasn't installed!" "ERROR"; exit 1)
python3-opencv
# Deny byobu to check available updates
sed -i "s/updates_available//" /usr/share/byobu/status/status

View File

@@ -25,7 +25,7 @@ import pymavlink
from pymavlink import mavutil
import rpi_ws281x
import pigpio
from espeak import espeak
# from espeak import espeak
from pyzbar import pyzbar
print cv2.getBuildInformation()

View File

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

View File

@@ -166,13 +166,14 @@ add_library(${PROJECT_NAME}
## The recommended prefix ensures that target names across packages don't collide
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(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)
@@ -181,19 +182,24 @@ target_link_libraries(simple_offboard
${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(vpe_publisher ${catkin_LIBRARIES})
target_link_libraries(led ${catkin_LIBRARIES})
target_link_libraries(clover_led ${catkin_LIBRARIES})
target_link_libraries(shell ${catkin_LIBRARIES})
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)

View File

@@ -44,13 +44,6 @@ Alternatively you may change the `fcu_url` property in `mavros.launch` file to p
## Running
Enable systemd service `roscore` (if not running):
```bash
sudo systemctl enable /home/<username>/catkin_ws/src/clover/builder/assets/roscore.service
sudo systemctl start roscore
```
To start connection to SITL, use:
```bash
@@ -64,10 +57,3 @@ roslaunch clover clover.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/clover/deploy/clover.service
sudo systemctl start clover
```

View File

@@ -1,10 +1,7 @@
<launch>
<arg name="aruco_detect" default="false"/> <!-- markers recognition enabled -->
<arg name="aruco_map" default="false"/> <!-- markers map recognition enabled -->
<arg name="aruco_vpe" default="false"/> <!-- markers map positioning enabled -->
<arg name="placement" default="floor"/> <!-- markers placement: floor, ceiling, unknown -->
<arg name="length" default="0.33"/> <!-- not-in-map markers length, m -->
<arg name="map" default="map.txt"/> <!-- markers map file name -->
<arg name="aruco_detect" default="true"/>
<arg name="aruco_map" default="false"/>
<arg name="aruco_vpe" default="false"/>
<!-- For additional help go to https://clover.coex.tech/aruco -->
@@ -15,9 +12,8 @@
<remap from="map_markers" to="aruco_map/markers" if="$(arg aruco_map)"/>
<param name="estimate_poses" value="true"/>
<param name="send_tf" value="true"/>
<param name="known_tilt" value="map" if="$(eval placement == 'floor')"/>
<param name="known_tilt" value="map_flipped" if="$(eval position == 'ceiling')"/>
<param name="length" value="$(arg length)"/>
<param name="known_tilt" value="map"/>
<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 -->
@@ -28,9 +24,8 @@
<remap from="image_raw" to="main_camera/image_raw"/>
<remap from="camera_info" to="main_camera/camera_info"/>
<remap from="markers" to="aruco_detect/markers"/>
<param name="map" value="$(find aruco_pose)/map/$(arg map)"/>
<param name="known_tilt" value="map" if="$(eval placement == 'floor')"/>
<param name="known_tilt" value="map_flipped" if="$(eval placement == 'ceiling')"/>
<param name="map" value="$(find aruco_pose)/map/map.txt"/>
<param name="known_tilt" value="map"/>
<param name="image_axis" value="true"/>
<param name="frame_id" value="aruco_map_detected" if="$(arg aruco_vpe)"/>
<param name="frame_id" value="aruco_map" unless="$(arg aruco_vpe)"/>

View File

@@ -1,17 +1,21 @@
<launch>
<arg name="fcu_conn" default="usb"/> <!-- FCU connection type: usb, uart, tcp, udp, sitl -->
<arg name="fcu_ip" default="127.0.0.1"/> <!-- FCU IP adddress (if using TCP/UDP) -->
<arg name="fcu_sys_id" default="1"/> <!-- MAVLink system ID, noeditor -->
<arg name="gcs_bridge" default="tcp"/> <!-- GCS bridge type: tcp, udp, udp-b, udp-pb -->
<arg name="web_video_server" default="true"/> <!-- web video server enabled -->
<arg name="rosbridge" default="true"/> <!-- rosbridge_suite enabled, noeditor -->
<arg name="main_camera" default="true"/> <!-- main camera enabled -->
<arg name="optical_flow" default="true"/> <!-- optical flow enabled -->
<arg name="rangefinder_vl53l1x" default="true"/> <!-- VL53l1X rangefinder enabled -->
<arg name="led" default="true"/> <!-- LED strip driver enabled -->
<arg name="rc" default="true"/> <!-- support for mobile RC enabled -->
<arg name="fcu_conn" default="usb"/>
<arg name="fcu_ip" default="127.0.0.1"/>
<arg name="fcu_sys_id" default="1"/>
<arg name="gcs_bridge" default="tcp"/>
<arg name="web_video_server" default="true"/>
<arg name="rosbridge" default="true"/>
<arg name="main_camera" default="true"/>
<arg name="optical_flow" default="true"/>
<arg name="aruco" default="false"/>
<arg name="rangefinder_vl53l1x" default="true"/>
<arg name="led" default="true"/>
<arg name="blocks" default="false"/>
<arg name="rc" default="true"/>
<arg name="shell" default="true"/>
<arg name="simulator" default="false"/> <!-- flag that we are operating on a simulated drone -->
<!-- log formatting -->
<env name="ROSCONSOLE_FORMAT" value="[${severity}] [${time}]: ${logger}: ${message}"/>
@@ -30,7 +34,7 @@
</node>
<!-- aruco markers -->
<include file="$(find clover)/launch/aruco.launch"/>
<include file="$(find clover)/launch/aruco.launch" if="$(arg aruco)"/>
<!-- optical flow -->
<node pkg="nodelet" type="nodelet" name="optical_flow" args="load clover/optical_flow nodelet_manager" if="$(arg optical_flow)" clear_params="true" output="screen">
@@ -52,10 +56,13 @@
<param name="reference_frames/body" value="map"/>
<param name="reference_frames/base_link" value="map"/>
<param name="reference_frames/navigate_target" value="map"/>
<param name="reference_frames/main_camera_optical" value="map"/>
</node>
<!-- 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 -->
<include file="$(find rosbridge_server)/launch/rosbridge_websocket.launch" if="$(eval rosbridge or rc)"/>
@@ -64,14 +71,19 @@
<node name="tf2_web_republisher" pkg="tf2_web_republisher" type="tf2_web_republisher" output="screen" if="$(arg rosbridge)"/>
<!-- 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="min_signal" value="0.4"/>
<param name="pass_statuses" type="yaml" value="[0, 6, 7, 11]"/>
</node>
<!-- 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 -->
<node name="rc" pkg="clover" type="rc" output="screen" if="$(arg rc)" clear_params="true">
@@ -86,11 +98,4 @@
<node pkg="roswww_static" name="roswww_static" type="main.py" clear_params="true">
<param name="default_package" value="clover"/>
</node>
<!-- roslaunch editor parameters -->
<group ns="roslaunch_editor">
<param name="items" type="yaml" value="[clover/clover.launch, clover/main_camera.launch, clover/aruco.launch, clover/led.launch]"/>
<param name="apply_command" value="sudo systemctl restart clover"/>
<param name="hide_uncommented" value="true"/>
</group>
</launch>

View File

@@ -1,14 +1,18 @@
<launch>
<arg name="ws281x" default="true"/> <!-- LED strip driver enabled -->
<arg name="led_effect" default="true"/> <!-- LED effect API enabled -->
<arg name="led_notify" default="all"/> <!-- LED strip notifications: all, battery, none -->
<arg name="ws281x" default="true"/>
<arg name="led_effect" 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 -->
<!-- ws281x led strip driver -->
<node pkg="ws281x" name="led" type="ws281x_node" clear_params="true" output="screen" if="$(arg ws281x)">
<param name="led_count" value="58"/>
<param name="gpio_pin" value="21"/>
<node pkg="ws281x" name="led" type="ws281x_node" clear_params="true" output="screen" if="$(eval ws281x and not simulator)">
<param name="led_count" value="$(arg led_count)"/>
<param name="gpio_pin" value="$(arg gpio_pin)"/>
<param name="brightness" value="64"/>
<param name="strip_type" value="WS2811_STRIP_GRB"/>
<param name="target_frequency" value="800000"/>
@@ -22,7 +26,7 @@
<param name="fade_period" value="0.5"/>
<param name="rainbow_period" value="5"/>
<!-- events effects table -->
<rosparam param="notify" if="$(eval led_notify == 'all')">
<rosparam param="notify" if="$(arg led_notify)">
startup: { r: 255, g: 255, b: 255 }
connected: { effect: rainbow }
disconnected: { effect: blink, r: 255, g: 50, b: 50 }
@@ -31,11 +35,8 @@
altctl: { r: 255, g: 255, b: 40 }
posctl: { r: 50, g: 100, b: 220 }
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 }
</rosparam>
<rosparam param="notify" if="$(eval led_notify == 'battery')">
low_battery: { threshold: 3.7, effect: blink_fast, r: 255, g: 0, b: 0 }
</rosparam>
</node>
</launch>

View File

@@ -3,11 +3,14 @@
<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="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 == '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 == '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 -->
<!-- Camera position and orientation are represented by base_link -> main_camera_optical transform -->
@@ -15,7 +18,7 @@
<!-- <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 -->
<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="frame_id" value="main_camera_optical"/>
<param name="camera_info_url" value="file://$(find clover)/camera_info/fisheye_cam.yaml"/>

View File

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

View File

@@ -36,6 +36,7 @@
#include <mavros_msgs/Thrust.h>
#include <mavros_msgs/State.h>
#include <mavros_msgs/StatusText.h>
#include <mavros_msgs/ManualControl.h>
#include <clover/GetTelemetry.h>
#include <clover/Navigate.h>
@@ -46,6 +47,7 @@
#include <clover/SetRates.h>
using std::string;
using std::isnan;
using namespace geometry_msgs;
using namespace sensor_msgs;
using namespace clover;
@@ -71,9 +73,10 @@ ros::Duration state_timeout;
ros::Duration velocity_timeout;
ros::Duration global_position_timeout;
ros::Duration battery_timeout;
ros::Duration manual_control_timeout;
float default_speed;
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;
// Publishers
@@ -121,6 +124,7 @@ enum { YAW, YAW_RATE, TOWARDS } setpoint_yaw_type;
// Last received telemetry messages
mavros_msgs::State state;
mavros_msgs::StatusText statustext;
mavros_msgs::ManualControl manual_control;
PoseStamped local_position;
TwistStamped velocity;
NavSatFix global_position;
@@ -417,8 +421,9 @@ void publish(const ros::Time stamp)
}
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) {
position_msg.header.stamp = stamp;
position_pub.publish(position_msg);
} else {
@@ -484,6 +489,27 @@ void publishSetpoint(const ros::TimerEvent& event)
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()
{
if (TIMEOUT(state, state_timeout))
@@ -506,25 +532,88 @@ bool serve(enum setpoint_type_t sp_type, float x, float y, float z, float vx, fl
if (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;
// Checks
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 (TIMEOUT(local_position, local_position_timeout))
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");
}
// 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) {
// make sure transform from frame_id to reference frame available
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) {
// destination point and/or yaw
static PoseStamped ps;
PoseStamped ps;
ps.header.frame_id = frame_id;
ps.header.stamp = stamp;
ps.pose.position.x = x;
ps.pose.position.y = y;
ps.pose.position.z = z;
ps.pose.orientation.w = 1.0; // Ensure quaternion is always valid
if (std::isnan(yaw)) {
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 {
setpoint_yaw_type = YAW;
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);
@@ -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;
publish_setpoint:
publish(stamp); // calculate initial transformed messages first
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) {
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) {
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) {
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) {
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) {
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) {
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)
@@ -779,6 +862,7 @@ int main(int argc, char **argv)
nh_priv.param("auto_release", auto_release, 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("check_kill_switch", check_kill_switch, true);
nh_priv.param("default_speed", default_speed, 0.5f);
nh_priv.param<string>("body_frame", body.child_frame_id, "body");
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));
global_position_timeout = ros::Duration(nh_priv.param("global_position_timeout", 10.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));
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 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 manual_control_sub = nh.subscribe("mavros/manual_control/control", 1, &handleMessage<mavros_msgs::ManualControl, manual_control>);
auto local_position_sub = nh.subscribe("mavros/local_position/pose", 1, &handleLocalPosition);
// Setpoint publishers

View File

@@ -6,6 +6,7 @@
<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="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>
<div class="version"></div>

View File

@@ -1,17 +1,18 @@
cmake_minimum_required(VERSION 2.8.3)
project(roslaunch_editor)
project(clover_blocks)
find_package(catkin REQUIRED COMPONENTS message_generation)
add_message_files(
FILES
LaunchFile.msg
Prompt.msg
)
add_service_files(
FILES
ReadLaunchFiles.srv
WriteLaunchFiles.srv
Run.srv
Load.srv
Store.srv
)
generate_messages(
@@ -22,7 +23,7 @@ generate_messages(
catkin_package(
# INCLUDE_DIRS include
# LIBRARIES roslaunch_editor
# CATKIN_DEPENDS other_catkin_pkg
CATKIN_DEPENDS message_runtime
# DEPENDS system_lib
)
@@ -68,6 +69,10 @@ catkin_package(
# DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}
# )
catkin_install_python(PROGRAMS src/clover_blocks
DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)
#############
## Testing ##
#############

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

View File

@@ -1,8 +1,8 @@
<?xml version="1.0"?>
<package format="2">
<name>roslaunch_editor</name>
<name>clover_blocks</name>
<version>0.0.0</version>
<description>Web based roslaunch files editor</description>
<description>Blockly programming support for Clover</description>
<maintainer email="okalachev@gmail.com">Oleg Kalachev</maintainer>
<license>MIT</license>
@@ -35,7 +35,10 @@
<!-- 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>

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.

View File

@@ -0,0 +1 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" width="12.71" height="8.79" viewBox="0 0 12.71 8.79"><title>dropdown-arrow</title><g opacity="0.1"><path d="M12.71,2.44A2.41,2.41,0,0,1,12,4.16L8.08,8.08a2.45,2.45,0,0,1-3.45,0L0.72,4.16A2.42,2.42,0,0,1,0,2.44,2.48,2.48,0,0,1,.71.71C1,0.47,1.43,0,6.36,0S11.75,0.46,12,.71A2.44,2.44,0,0,1,12.71,2.44Z" fill="#231f20"/></g><path d="M6.36,7.79a1.43,1.43,0,0,1-1-.42L1.42,3.45a1.44,1.44,0,0,1,0-2c0.56-.56,9.31-0.56,9.87,0a1.44,1.44,0,0,1,0,2L7.37,7.37A1.43,1.43,0,0,1,6.36,7.79Z" fill="#fff"/></svg>

After

Width:  |  Height:  |  Size: 569 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 326 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 766 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 198 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1010 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 771 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 738 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@@ -0,0 +1,74 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="96px" height="124px">
<style type="text/css">
#background {
fill: none;
}
.arrows {
fill: #000;
stroke: none;
}
.selected>.arrows {
fill: #fff;
}
.checkmark {
fill: #000;
font-family: sans-serif;
font-size: 10pt;
text-anchor: middle;
}
.trash {
fill: #888;
}
.zoom {
fill: none;
stroke: #888;
stroke-width: 2;
stroke-linecap: round;
}
.zoom>.center {
fill: #888;
stroke-width: 0;
}
</style>
<rect id="background" width="96" height="124" x="0" y="0" />
<g>
<path class="arrows" d="M 13,1.5 13,14.5 1.74,8 z" />
<path class="arrows" d="M 17.5,3 30.5,3 24,14.26 z" />
<path class="arrows" d="M 35,1.5 35,14.5 46.26,8 z" />
</g>
<g class="selected" transform="translate(0, 16)">
<path class="arrows" d="M 13,1.5 13,14.5 1.74,8 z" />
<path class="arrows" d="M 17.5,3 30.5,3 24,14.26 z" />
<path class="arrows" d="M 35,1.5 35,14.5 46.26,8 z" />
</g>
<text class="checkmark" x="55.5" y="28">&#10003;</text>
<g class="trash">
<path d="M 2,41 v 6 h 42 v -6 h -10.5 l -3,-3 h -15 l -3,3 z" />
<rect width="36" height="20" x="5" y="50" />
<rect width="36" height="42" x="5" y="50" rx="4" ry="4" />
</g>
<g class="zoom">
<circle r="11.5" cx="16" cy="108" />
<circle r="4.3" cx="16" cy="108" class="center" />
<path d="m 28,108 h3" />
<path d="m 1,108 h3" />
<path d="m 16,120 v3" />
<path d="m 16,93 v3" />
</g>
<g class="zoom">
<circle r="15" cx="48" cy="108" />
<path d="m 48,101.6 v12.8" />
<path d="m 41.6,108 h12.8" />
</g>
<g class="zoom">
<circle r="15" cx="80" cy="108" />
<path d="m 73.6,108 h12.8" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -0,0 +1,434 @@
// This file was automatically generated. Do not modify.
'use strict';
Blockly.Msg["ADD_COMMENT"] = "Add Comment";
Blockly.Msg["CANNOT_DELETE_VARIABLE_PROCEDURE"] = "Can't delete the variable '%1' because it's part of the definition of the function '%2'";
Blockly.Msg["CHANGE_VALUE_TITLE"] = "Change value:";
Blockly.Msg["CLEAN_UP"] = "Clean up Blocks";
Blockly.Msg["COLLAPSED_WARNINGS_WARNING"] = "Collapsed blocks contain warnings.";
Blockly.Msg["COLLAPSE_ALL"] = "Collapse Blocks";
Blockly.Msg["COLLAPSE_BLOCK"] = "Collapse Block";
Blockly.Msg["COLOUR_BLEND_COLOUR1"] = "colour 1";
Blockly.Msg["COLOUR_BLEND_COLOUR2"] = "colour 2";
Blockly.Msg["COLOUR_BLEND_HELPURL"] = "https://meyerweb.com/eric/tools/color-blend/#:::rgbp";
Blockly.Msg["COLOUR_BLEND_RATIO"] = "ratio";
Blockly.Msg["COLOUR_BLEND_TITLE"] = "blend";
Blockly.Msg["COLOUR_BLEND_TOOLTIP"] = "Blends two colours together with a given ratio (0.0 - 1.0).";
Blockly.Msg["COLOUR_PICKER_HELPURL"] = "https://en.wikipedia.org/wiki/Color";
Blockly.Msg["COLOUR_PICKER_TOOLTIP"] = "Choose a colour from the palette.";
Blockly.Msg["COLOUR_RANDOM_HELPURL"] = "http://randomcolour.com";
Blockly.Msg["COLOUR_RANDOM_TITLE"] = "random colour";
Blockly.Msg["COLOUR_RANDOM_TOOLTIP"] = "Choose a colour at random.";
Blockly.Msg["COLOUR_RGB_BLUE"] = "blue";
Blockly.Msg["COLOUR_RGB_GREEN"] = "green";
Blockly.Msg["COLOUR_RGB_HELPURL"] = "https://www.december.com/html/spec/colorpercompact.html";
Blockly.Msg["COLOUR_RGB_RED"] = "red";
Blockly.Msg["COLOUR_RGB_TITLE"] = "colour with";
Blockly.Msg["COLOUR_RGB_TOOLTIP"] = "Create a colour with the specified amount of red, green, and blue. All values must be between 0 and 100.";
Blockly.Msg["CONTROLS_FLOW_STATEMENTS_HELPURL"] = "https://github.com/google/blockly/wiki/Loops#loop-termination-blocks";
Blockly.Msg["CONTROLS_FLOW_STATEMENTS_OPERATOR_BREAK"] = "break out of loop";
Blockly.Msg["CONTROLS_FLOW_STATEMENTS_OPERATOR_CONTINUE"] = "continue with next iteration of loop";
Blockly.Msg["CONTROLS_FLOW_STATEMENTS_TOOLTIP_BREAK"] = "Break out of the containing loop.";
Blockly.Msg["CONTROLS_FLOW_STATEMENTS_TOOLTIP_CONTINUE"] = "Skip the rest of this loop, and continue with the next iteration.";
Blockly.Msg["CONTROLS_FLOW_STATEMENTS_WARNING"] = "Warning: This block may only be used within a loop.";
Blockly.Msg["CONTROLS_FOREACH_HELPURL"] = "https://github.com/google/blockly/wiki/Loops#for-each";
Blockly.Msg["CONTROLS_FOREACH_TITLE"] = "for each item %1 in list %2";
Blockly.Msg["CONTROLS_FOREACH_TOOLTIP"] = "For each item in a list, set the variable '%1' to the item, and then do some statements.";
Blockly.Msg["CONTROLS_FOR_HELPURL"] = "https://github.com/google/blockly/wiki/Loops#count-with";
Blockly.Msg["CONTROLS_FOR_TITLE"] = "count with %1 from %2 to %3 by %4";
Blockly.Msg["CONTROLS_FOR_TOOLTIP"] = "Have the variable '%1' take on the values from the start number to the end number, counting by the specified interval, and do the specified blocks.";
Blockly.Msg["CONTROLS_IF_ELSEIF_TOOLTIP"] = "Add a condition to the if block.";
Blockly.Msg["CONTROLS_IF_ELSE_TOOLTIP"] = "Add a final, catch-all condition to the if block.";
Blockly.Msg["CONTROLS_IF_HELPURL"] = "https://github.com/google/blockly/wiki/IfElse";
Blockly.Msg["CONTROLS_IF_IF_TOOLTIP"] = "Add, remove, or reorder sections to reconfigure this if block.";
Blockly.Msg["CONTROLS_IF_MSG_ELSE"] = "else";
Blockly.Msg["CONTROLS_IF_MSG_ELSEIF"] = "else if";
Blockly.Msg["CONTROLS_IF_MSG_IF"] = "if";
Blockly.Msg["CONTROLS_IF_TOOLTIP_1"] = "If a value is true, then do some statements.";
Blockly.Msg["CONTROLS_IF_TOOLTIP_2"] = "If a value is true, then do the first block of statements. Otherwise, do the second block of statements.";
Blockly.Msg["CONTROLS_IF_TOOLTIP_3"] = "If the first value is true, then do the first block of statements. Otherwise, if the second value is true, do the second block of statements.";
Blockly.Msg["CONTROLS_IF_TOOLTIP_4"] = "If the first value is true, then do the first block of statements. Otherwise, if the second value is true, do the second block of statements. If none of the values are true, do the last block of statements.";
Blockly.Msg["CONTROLS_REPEAT_HELPURL"] = "https://en.wikipedia.org/wiki/For_loop";
Blockly.Msg["CONTROLS_REPEAT_INPUT_DO"] = "do";
Blockly.Msg["CONTROLS_REPEAT_TITLE"] = "repeat %1 times";
Blockly.Msg["CONTROLS_REPEAT_TOOLTIP"] = "Do some statements several times.";
Blockly.Msg["CONTROLS_WHILEUNTIL_HELPURL"] = "https://github.com/google/blockly/wiki/Loops#repeat";
Blockly.Msg["CONTROLS_WHILEUNTIL_OPERATOR_UNTIL"] = "repeat until";
Blockly.Msg["CONTROLS_WHILEUNTIL_OPERATOR_WHILE"] = "repeat while";
Blockly.Msg["CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL"] = "While a value is false, then do some statements.";
Blockly.Msg["CONTROLS_WHILEUNTIL_TOOLTIP_WHILE"] = "While a value is true, then do some statements.";
Blockly.Msg["DELETE_ALL_BLOCKS"] = "Delete all %1 blocks?";
Blockly.Msg["DELETE_BLOCK"] = "Delete Block";
Blockly.Msg["DELETE_VARIABLE"] = "Delete the '%1' variable";
Blockly.Msg["DELETE_VARIABLE_CONFIRMATION"] = "Delete %1 uses of the '%2' variable?";
Blockly.Msg["DELETE_X_BLOCKS"] = "Delete %1 Blocks";
Blockly.Msg["DISABLE_BLOCK"] = "Disable Block";
Blockly.Msg["DUPLICATE_BLOCK"] = "Duplicate";
Blockly.Msg["DUPLICATE_COMMENT"] = "Duplicate Comment";
Blockly.Msg["ENABLE_BLOCK"] = "Enable Block";
Blockly.Msg["EXPAND_ALL"] = "Expand Blocks";
Blockly.Msg["EXPAND_BLOCK"] = "Expand Block";
Blockly.Msg["EXTERNAL_INPUTS"] = "External Inputs";
Blockly.Msg["HELP"] = "Help";
Blockly.Msg["INLINE_INPUTS"] = "Inline Inputs";
Blockly.Msg["IOS_CANCEL"] = "Cancel";
Blockly.Msg["IOS_ERROR"] = "Error";
Blockly.Msg["IOS_OK"] = "OK";
Blockly.Msg["IOS_PROCEDURES_ADD_INPUT"] = "+ Add Input";
Blockly.Msg["IOS_PROCEDURES_ALLOW_STATEMENTS"] = "Allow statements";
Blockly.Msg["IOS_PROCEDURES_DUPLICATE_INPUTS_ERROR"] = "This function has duplicate inputs.";
Blockly.Msg["IOS_PROCEDURES_INPUTS"] = "INPUTS";
Blockly.Msg["IOS_VARIABLES_ADD_BUTTON"] = "Add";
Blockly.Msg["IOS_VARIABLES_ADD_VARIABLE"] = "+ Add Variable";
Blockly.Msg["IOS_VARIABLES_DELETE_BUTTON"] = "Delete";
Blockly.Msg["IOS_VARIABLES_EMPTY_NAME_ERROR"] = "You can't use an empty variable name.";
Blockly.Msg["IOS_VARIABLES_RENAME_BUTTON"] = "Rename";
Blockly.Msg["IOS_VARIABLES_VARIABLE_NAME"] = "Variable name";
Blockly.Msg["LISTS_CREATE_EMPTY_HELPURL"] = "https://github.com/google/blockly/wiki/Lists#create-empty-list";
Blockly.Msg["LISTS_CREATE_EMPTY_TITLE"] = "create empty list";
Blockly.Msg["LISTS_CREATE_EMPTY_TOOLTIP"] = "Returns a list, of length 0, containing no data records";
Blockly.Msg["LISTS_CREATE_WITH_CONTAINER_TITLE_ADD"] = "list";
Blockly.Msg["LISTS_CREATE_WITH_CONTAINER_TOOLTIP"] = "Add, remove, or reorder sections to reconfigure this list block.";
Blockly.Msg["LISTS_CREATE_WITH_HELPURL"] = "https://github.com/google/blockly/wiki/Lists#create-list-with";
Blockly.Msg["LISTS_CREATE_WITH_INPUT_WITH"] = "create list with";
Blockly.Msg["LISTS_CREATE_WITH_ITEM_TOOLTIP"] = "Add an item to the list.";
Blockly.Msg["LISTS_CREATE_WITH_TOOLTIP"] = "Create a list with any number of items.";
Blockly.Msg["LISTS_GET_INDEX_FIRST"] = "first";
Blockly.Msg["LISTS_GET_INDEX_FROM_END"] = "# from end";
Blockly.Msg["LISTS_GET_INDEX_FROM_START"] = "#";
Blockly.Msg["LISTS_GET_INDEX_GET"] = "get";
Blockly.Msg["LISTS_GET_INDEX_GET_REMOVE"] = "get and remove";
Blockly.Msg["LISTS_GET_INDEX_LAST"] = "last";
Blockly.Msg["LISTS_GET_INDEX_RANDOM"] = "random";
Blockly.Msg["LISTS_GET_INDEX_REMOVE"] = "remove";
Blockly.Msg["LISTS_GET_INDEX_TAIL"] = "";
Blockly.Msg["LISTS_GET_INDEX_TOOLTIP_GET_FIRST"] = "Returns the first item in a list.";
Blockly.Msg["LISTS_GET_INDEX_TOOLTIP_GET_FROM"] = "Returns the item at the specified position in a list.";
Blockly.Msg["LISTS_GET_INDEX_TOOLTIP_GET_LAST"] = "Returns the last item in a list.";
Blockly.Msg["LISTS_GET_INDEX_TOOLTIP_GET_RANDOM"] = "Returns a random item in a list.";
Blockly.Msg["LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_FIRST"] = "Removes and returns the first item in a list.";
Blockly.Msg["LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_FROM"] = "Removes and returns the item at the specified position in a list.";
Blockly.Msg["LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_LAST"] = "Removes and returns the last item in a list.";
Blockly.Msg["LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_RANDOM"] = "Removes and returns a random item in a list.";
Blockly.Msg["LISTS_GET_INDEX_TOOLTIP_REMOVE_FIRST"] = "Removes the first item in a list.";
Blockly.Msg["LISTS_GET_INDEX_TOOLTIP_REMOVE_FROM"] = "Removes the item at the specified position in a list.";
Blockly.Msg["LISTS_GET_INDEX_TOOLTIP_REMOVE_LAST"] = "Removes the last item in a list.";
Blockly.Msg["LISTS_GET_INDEX_TOOLTIP_REMOVE_RANDOM"] = "Removes a random item in a list.";
Blockly.Msg["LISTS_GET_SUBLIST_END_FROM_END"] = "to # from end";
Blockly.Msg["LISTS_GET_SUBLIST_END_FROM_START"] = "to #";
Blockly.Msg["LISTS_GET_SUBLIST_END_LAST"] = "to last";
Blockly.Msg["LISTS_GET_SUBLIST_HELPURL"] = "https://github.com/google/blockly/wiki/Lists#getting-a-sublist";
Blockly.Msg["LISTS_GET_SUBLIST_START_FIRST"] = "get sub-list from first";
Blockly.Msg["LISTS_GET_SUBLIST_START_FROM_END"] = "get sub-list from # from end";
Blockly.Msg["LISTS_GET_SUBLIST_START_FROM_START"] = "get sub-list from #";
Blockly.Msg["LISTS_GET_SUBLIST_TAIL"] = "";
Blockly.Msg["LISTS_GET_SUBLIST_TOOLTIP"] = "Creates a copy of the specified portion of a list.";
Blockly.Msg["LISTS_INDEX_FROM_END_TOOLTIP"] = "%1 is the last item.";
Blockly.Msg["LISTS_INDEX_FROM_START_TOOLTIP"] = "%1 is the first item.";
Blockly.Msg["LISTS_INDEX_OF_FIRST"] = "find first occurrence of item";
Blockly.Msg["LISTS_INDEX_OF_HELPURL"] = "https://github.com/google/blockly/wiki/Lists#getting-items-from-a-list";
Blockly.Msg["LISTS_INDEX_OF_LAST"] = "find last occurrence of item";
Blockly.Msg["LISTS_INDEX_OF_TOOLTIP"] = "Returns the index of the first/last occurrence of the item in the list. Returns %1 if item is not found.";
Blockly.Msg["LISTS_INLIST"] = "in list";
Blockly.Msg["LISTS_ISEMPTY_HELPURL"] = "https://github.com/google/blockly/wiki/Lists#is-empty";
Blockly.Msg["LISTS_ISEMPTY_TITLE"] = "%1 is empty";
Blockly.Msg["LISTS_ISEMPTY_TOOLTIP"] = "Returns true if the list is empty.";
Blockly.Msg["LISTS_LENGTH_HELPURL"] = "https://github.com/google/blockly/wiki/Lists#length-of";
Blockly.Msg["LISTS_LENGTH_TITLE"] = "length of %1";
Blockly.Msg["LISTS_LENGTH_TOOLTIP"] = "Returns the length of a list.";
Blockly.Msg["LISTS_REPEAT_HELPURL"] = "https://github.com/google/blockly/wiki/Lists#create-list-with";
Blockly.Msg["LISTS_REPEAT_TITLE"] = "create list with item %1 repeated %2 times";
Blockly.Msg["LISTS_REPEAT_TOOLTIP"] = "Creates a list consisting of the given value repeated the specified number of times.";
Blockly.Msg["LISTS_REVERSE_HELPURL"] = "https://github.com/google/blockly/wiki/Lists#reversing-a-list";
Blockly.Msg["LISTS_REVERSE_MESSAGE0"] = "reverse %1";
Blockly.Msg["LISTS_REVERSE_TOOLTIP"] = "Reverse a copy of a list.";
Blockly.Msg["LISTS_SET_INDEX_HELPURL"] = "https://github.com/google/blockly/wiki/Lists#in-list--set";
Blockly.Msg["LISTS_SET_INDEX_INPUT_TO"] = "as";
Blockly.Msg["LISTS_SET_INDEX_INSERT"] = "insert at";
Blockly.Msg["LISTS_SET_INDEX_SET"] = "set";
Blockly.Msg["LISTS_SET_INDEX_TOOLTIP_INSERT_FIRST"] = "Inserts the item at the start of a list.";
Blockly.Msg["LISTS_SET_INDEX_TOOLTIP_INSERT_FROM"] = "Inserts the item at the specified position in a list.";
Blockly.Msg["LISTS_SET_INDEX_TOOLTIP_INSERT_LAST"] = "Append the item to the end of a list.";
Blockly.Msg["LISTS_SET_INDEX_TOOLTIP_INSERT_RANDOM"] = "Inserts the item randomly in a list.";
Blockly.Msg["LISTS_SET_INDEX_TOOLTIP_SET_FIRST"] = "Sets the first item in a list.";
Blockly.Msg["LISTS_SET_INDEX_TOOLTIP_SET_FROM"] = "Sets the item at the specified position in a list.";
Blockly.Msg["LISTS_SET_INDEX_TOOLTIP_SET_LAST"] = "Sets the last item in a list.";
Blockly.Msg["LISTS_SET_INDEX_TOOLTIP_SET_RANDOM"] = "Sets a random item in a list.";
Blockly.Msg["LISTS_SORT_HELPURL"] = "https://github.com/google/blockly/wiki/Lists#sorting-a-list";
Blockly.Msg["LISTS_SORT_ORDER_ASCENDING"] = "ascending";
Blockly.Msg["LISTS_SORT_ORDER_DESCENDING"] = "descending";
Blockly.Msg["LISTS_SORT_TITLE"] = "sort %1 %2 %3";
Blockly.Msg["LISTS_SORT_TOOLTIP"] = "Sort a copy of a list.";
Blockly.Msg["LISTS_SORT_TYPE_IGNORECASE"] = "alphabetic, ignore case";
Blockly.Msg["LISTS_SORT_TYPE_NUMERIC"] = "numeric";
Blockly.Msg["LISTS_SORT_TYPE_TEXT"] = "alphabetic";
Blockly.Msg["LISTS_SPLIT_HELPURL"] = "https://github.com/google/blockly/wiki/Lists#splitting-strings-and-joining-lists";
Blockly.Msg["LISTS_SPLIT_LIST_FROM_TEXT"] = "make list from text";
Blockly.Msg["LISTS_SPLIT_TEXT_FROM_LIST"] = "make text from list";
Blockly.Msg["LISTS_SPLIT_TOOLTIP_JOIN"] = "Join a list of texts into one text, separated by a delimiter.";
Blockly.Msg["LISTS_SPLIT_TOOLTIP_SPLIT"] = "Split text into a list of texts, breaking at each delimiter.";
Blockly.Msg["LISTS_SPLIT_WITH_DELIMITER"] = "with delimiter";
Blockly.Msg["LOGIC_BOOLEAN_FALSE"] = "false";
Blockly.Msg["LOGIC_BOOLEAN_HELPURL"] = "https://github.com/google/blockly/wiki/Logic#values";
Blockly.Msg["LOGIC_BOOLEAN_TOOLTIP"] = "Returns either true or false.";
Blockly.Msg["LOGIC_BOOLEAN_TRUE"] = "true";
Blockly.Msg["LOGIC_COMPARE_HELPURL"] = "https://en.wikipedia.org/wiki/Inequality_(mathematics)";
Blockly.Msg["LOGIC_COMPARE_TOOLTIP_EQ"] = "Return true if both inputs equal each other.";
Blockly.Msg["LOGIC_COMPARE_TOOLTIP_GT"] = "Return true if the first input is greater than the second input.";
Blockly.Msg["LOGIC_COMPARE_TOOLTIP_GTE"] = "Return true if the first input is greater than or equal to the second input.";
Blockly.Msg["LOGIC_COMPARE_TOOLTIP_LT"] = "Return true if the first input is smaller than the second input.";
Blockly.Msg["LOGIC_COMPARE_TOOLTIP_LTE"] = "Return true if the first input is smaller than or equal to the second input.";
Blockly.Msg["LOGIC_COMPARE_TOOLTIP_NEQ"] = "Return true if both inputs are not equal to each other.";
Blockly.Msg["LOGIC_NEGATE_HELPURL"] = "https://github.com/google/blockly/wiki/Logic#not";
Blockly.Msg["LOGIC_NEGATE_TITLE"] = "not %1";
Blockly.Msg["LOGIC_NEGATE_TOOLTIP"] = "Returns true if the input is false. Returns false if the input is true.";
Blockly.Msg["LOGIC_NULL"] = "null";
Blockly.Msg["LOGIC_NULL_HELPURL"] = "https://en.wikipedia.org/wiki/Nullable_type";
Blockly.Msg["LOGIC_NULL_TOOLTIP"] = "Returns null.";
Blockly.Msg["LOGIC_OPERATION_AND"] = "and";
Blockly.Msg["LOGIC_OPERATION_HELPURL"] = "https://github.com/google/blockly/wiki/Logic#logical-operations";
Blockly.Msg["LOGIC_OPERATION_OR"] = "or";
Blockly.Msg["LOGIC_OPERATION_TOOLTIP_AND"] = "Return true if both inputs are true.";
Blockly.Msg["LOGIC_OPERATION_TOOLTIP_OR"] = "Return true if at least one of the inputs is true.";
Blockly.Msg["LOGIC_TERNARY_CONDITION"] = "test";
Blockly.Msg["LOGIC_TERNARY_HELPURL"] = "https://en.wikipedia.org/wiki/%3F:";
Blockly.Msg["LOGIC_TERNARY_IF_FALSE"] = "if false";
Blockly.Msg["LOGIC_TERNARY_IF_TRUE"] = "if true";
Blockly.Msg["LOGIC_TERNARY_TOOLTIP"] = "Check the condition in 'test'. If the condition is true, returns the 'if true' value; otherwise returns the 'if false' value.";
Blockly.Msg["MATH_ADDITION_SYMBOL"] = "+";
Blockly.Msg["MATH_ARITHMETIC_HELPURL"] = "https://en.wikipedia.org/wiki/Arithmetic";
Blockly.Msg["MATH_ARITHMETIC_TOOLTIP_ADD"] = "Return the sum of the two numbers.";
Blockly.Msg["MATH_ARITHMETIC_TOOLTIP_DIVIDE"] = "Return the quotient of the two numbers.";
Blockly.Msg["MATH_ARITHMETIC_TOOLTIP_MINUS"] = "Return the difference of the two numbers.";
Blockly.Msg["MATH_ARITHMETIC_TOOLTIP_MULTIPLY"] = "Return the product of the two numbers.";
Blockly.Msg["MATH_ARITHMETIC_TOOLTIP_POWER"] = "Return the first number raised to the power of the second number.";
Blockly.Msg["MATH_ATAN2_HELPURL"] = "https://en.wikipedia.org/wiki/Atan2";
Blockly.Msg["MATH_ATAN2_TITLE"] = "atan2 of X:%1 Y:%2";
Blockly.Msg["MATH_ATAN2_TOOLTIP"] = "Return the arctangent of point (X, Y) in degrees from -180 to 180.";
Blockly.Msg["MATH_CHANGE_HELPURL"] = "https://en.wikipedia.org/wiki/Programming_idiom#Incrementing_a_counter";
Blockly.Msg["MATH_CHANGE_TITLE"] = "change %1 by %2";
Blockly.Msg["MATH_CHANGE_TOOLTIP"] = "Add a number to variable '%1'.";
Blockly.Msg["MATH_CONSTANT_HELPURL"] = "https://en.wikipedia.org/wiki/Mathematical_constant";
Blockly.Msg["MATH_CONSTANT_TOOLTIP"] = "Return one of the common constants: π (3.141…), e (2.718…), φ (1.618…), sqrt(2) (1.414…), sqrt(½) (0.707…), or ∞ (infinity).";
Blockly.Msg["MATH_CONSTRAIN_HELPURL"] = "https://en.wikipedia.org/wiki/Clamping_(graphics)";
Blockly.Msg["MATH_CONSTRAIN_TITLE"] = "constrain %1 low %2 high %3";
Blockly.Msg["MATH_CONSTRAIN_TOOLTIP"] = "Constrain a number to be between the specified limits (inclusive).";
Blockly.Msg["MATH_DIVISION_SYMBOL"] = "÷";
Blockly.Msg["MATH_IS_DIVISIBLE_BY"] = "is divisible by";
Blockly.Msg["MATH_IS_EVEN"] = "is even";
Blockly.Msg["MATH_IS_NEGATIVE"] = "is negative";
Blockly.Msg["MATH_IS_ODD"] = "is odd";
Blockly.Msg["MATH_IS_POSITIVE"] = "is positive";
Blockly.Msg["MATH_IS_PRIME"] = "is prime";
Blockly.Msg["MATH_IS_TOOLTIP"] = "Check if a number is an even, odd, prime, whole, positive, negative, or if it is divisible by certain number. Returns true or false.";
Blockly.Msg["MATH_IS_WHOLE"] = "is whole";
Blockly.Msg["MATH_MODULO_HELPURL"] = "https://en.wikipedia.org/wiki/Modulo_operation";
Blockly.Msg["MATH_MODULO_TITLE"] = "remainder of %1 ÷ %2";
Blockly.Msg["MATH_MODULO_TOOLTIP"] = "Return the remainder from dividing the two numbers.";
Blockly.Msg["MATH_MULTIPLICATION_SYMBOL"] = "×";
Blockly.Msg["MATH_NUMBER_HELPURL"] = "https://en.wikipedia.org/wiki/Number";
Blockly.Msg["MATH_NUMBER_TOOLTIP"] = "A number.";
Blockly.Msg["MATH_ONLIST_HELPURL"] = "";
Blockly.Msg["MATH_ONLIST_OPERATOR_AVERAGE"] = "average of list";
Blockly.Msg["MATH_ONLIST_OPERATOR_MAX"] = "max of list";
Blockly.Msg["MATH_ONLIST_OPERATOR_MEDIAN"] = "median of list";
Blockly.Msg["MATH_ONLIST_OPERATOR_MIN"] = "min of list";
Blockly.Msg["MATH_ONLIST_OPERATOR_MODE"] = "modes of list";
Blockly.Msg["MATH_ONLIST_OPERATOR_RANDOM"] = "random item of list";
Blockly.Msg["MATH_ONLIST_OPERATOR_STD_DEV"] = "standard deviation of list";
Blockly.Msg["MATH_ONLIST_OPERATOR_SUM"] = "sum of list";
Blockly.Msg["MATH_ONLIST_TOOLTIP_AVERAGE"] = "Return the average (arithmetic mean) of the numeric values in the list.";
Blockly.Msg["MATH_ONLIST_TOOLTIP_MAX"] = "Return the largest number in the list.";
Blockly.Msg["MATH_ONLIST_TOOLTIP_MEDIAN"] = "Return the median number in the list.";
Blockly.Msg["MATH_ONLIST_TOOLTIP_MIN"] = "Return the smallest number in the list.";
Blockly.Msg["MATH_ONLIST_TOOLTIP_MODE"] = "Return a list of the most common item(s) in the list.";
Blockly.Msg["MATH_ONLIST_TOOLTIP_RANDOM"] = "Return a random element from the list.";
Blockly.Msg["MATH_ONLIST_TOOLTIP_STD_DEV"] = "Return the standard deviation of the list.";
Blockly.Msg["MATH_ONLIST_TOOLTIP_SUM"] = "Return the sum of all the numbers in the list.";
Blockly.Msg["MATH_POWER_SYMBOL"] = "^";
Blockly.Msg["MATH_RANDOM_FLOAT_HELPURL"] = "https://en.wikipedia.org/wiki/Random_number_generation";
Blockly.Msg["MATH_RANDOM_FLOAT_TITLE_RANDOM"] = "random fraction";
Blockly.Msg["MATH_RANDOM_FLOAT_TOOLTIP"] = "Return a random fraction between 0.0 (inclusive) and 1.0 (exclusive).";
Blockly.Msg["MATH_RANDOM_INT_HELPURL"] = "https://en.wikipedia.org/wiki/Random_number_generation";
Blockly.Msg["MATH_RANDOM_INT_TITLE"] = "random integer from %1 to %2";
Blockly.Msg["MATH_RANDOM_INT_TOOLTIP"] = "Return a random integer between the two specified limits, inclusive.";
Blockly.Msg["MATH_ROUND_HELPURL"] = "https://en.wikipedia.org/wiki/Rounding";
Blockly.Msg["MATH_ROUND_OPERATOR_ROUND"] = "round";
Blockly.Msg["MATH_ROUND_OPERATOR_ROUNDDOWN"] = "round down";
Blockly.Msg["MATH_ROUND_OPERATOR_ROUNDUP"] = "round up";
Blockly.Msg["MATH_ROUND_TOOLTIP"] = "Round a number up or down.";
Blockly.Msg["MATH_SINGLE_HELPURL"] = "https://en.wikipedia.org/wiki/Square_root";
Blockly.Msg["MATH_SINGLE_OP_ABSOLUTE"] = "absolute";
Blockly.Msg["MATH_SINGLE_OP_ROOT"] = "square root";
Blockly.Msg["MATH_SINGLE_TOOLTIP_ABS"] = "Return the absolute value of a number.";
Blockly.Msg["MATH_SINGLE_TOOLTIP_EXP"] = "Return e to the power of a number.";
Blockly.Msg["MATH_SINGLE_TOOLTIP_LN"] = "Return the natural logarithm of a number.";
Blockly.Msg["MATH_SINGLE_TOOLTIP_LOG10"] = "Return the base 10 logarithm of a number.";
Blockly.Msg["MATH_SINGLE_TOOLTIP_NEG"] = "Return the negation of a number.";
Blockly.Msg["MATH_SINGLE_TOOLTIP_POW10"] = "Return 10 to the power of a number.";
Blockly.Msg["MATH_SINGLE_TOOLTIP_ROOT"] = "Return the square root of a number.";
Blockly.Msg["MATH_SUBTRACTION_SYMBOL"] = "-";
Blockly.Msg["MATH_TRIG_ACOS"] = "acos";
Blockly.Msg["MATH_TRIG_ASIN"] = "asin";
Blockly.Msg["MATH_TRIG_ATAN"] = "atan";
Blockly.Msg["MATH_TRIG_COS"] = "cos";
Blockly.Msg["MATH_TRIG_HELPURL"] = "https://en.wikipedia.org/wiki/Trigonometric_functions";
Blockly.Msg["MATH_TRIG_SIN"] = "sin";
Blockly.Msg["MATH_TRIG_TAN"] = "tan";
Blockly.Msg["MATH_TRIG_TOOLTIP_ACOS"] = "Return the arccosine of a number.";
Blockly.Msg["MATH_TRIG_TOOLTIP_ASIN"] = "Return the arcsine of a number.";
Blockly.Msg["MATH_TRIG_TOOLTIP_ATAN"] = "Return the arctangent of a number.";
Blockly.Msg["MATH_TRIG_TOOLTIP_COS"] = "Return the cosine of a degree (not radian).";
Blockly.Msg["MATH_TRIG_TOOLTIP_SIN"] = "Return the sine of a degree (not radian).";
Blockly.Msg["MATH_TRIG_TOOLTIP_TAN"] = "Return the tangent of a degree (not radian).";
Blockly.Msg["NEW_COLOUR_VARIABLE"] = "Create colour variable...";
Blockly.Msg["NEW_NUMBER_VARIABLE"] = "Create number variable...";
Blockly.Msg["NEW_STRING_VARIABLE"] = "Create string variable...";
Blockly.Msg["NEW_VARIABLE"] = "Create variable...";
Blockly.Msg["NEW_VARIABLE_TITLE"] = "New variable name:";
Blockly.Msg["NEW_VARIABLE_TYPE_TITLE"] = "New variable type:";
Blockly.Msg["ORDINAL_NUMBER_SUFFIX"] = "";
Blockly.Msg["PROCEDURES_ALLOW_STATEMENTS"] = "allow statements";
Blockly.Msg["PROCEDURES_BEFORE_PARAMS"] = "with:";
Blockly.Msg["PROCEDURES_CALLNORETURN_HELPURL"] = "https://en.wikipedia.org/wiki/Subroutine";
Blockly.Msg["PROCEDURES_CALLNORETURN_TOOLTIP"] = "Run the user-defined function '%1'.";
Blockly.Msg["PROCEDURES_CALLRETURN_HELPURL"] = "https://en.wikipedia.org/wiki/Subroutine";
Blockly.Msg["PROCEDURES_CALLRETURN_TOOLTIP"] = "Run the user-defined function '%1' and use its output.";
Blockly.Msg["PROCEDURES_CALL_BEFORE_PARAMS"] = "with:";
Blockly.Msg["PROCEDURES_CREATE_DO"] = "Create '%1'";
Blockly.Msg["PROCEDURES_DEFNORETURN_COMMENT"] = "Describe this function...";
Blockly.Msg["PROCEDURES_DEFNORETURN_DO"] = "";
Blockly.Msg["PROCEDURES_DEFNORETURN_HELPURL"] = "https://en.wikipedia.org/wiki/Subroutine";
Blockly.Msg["PROCEDURES_DEFNORETURN_PROCEDURE"] = "do something";
Blockly.Msg["PROCEDURES_DEFNORETURN_TITLE"] = "to";
Blockly.Msg["PROCEDURES_DEFNORETURN_TOOLTIP"] = "Creates a function with no output.";
Blockly.Msg["PROCEDURES_DEFRETURN_HELPURL"] = "https://en.wikipedia.org/wiki/Subroutine";
Blockly.Msg["PROCEDURES_DEFRETURN_RETURN"] = "return";
Blockly.Msg["PROCEDURES_DEFRETURN_TOOLTIP"] = "Creates a function with an output.";
Blockly.Msg["PROCEDURES_DEF_DUPLICATE_WARNING"] = "Warning: This function has duplicate parameters.";
Blockly.Msg["PROCEDURES_HIGHLIGHT_DEF"] = "Highlight function definition";
Blockly.Msg["PROCEDURES_IFRETURN_HELPURL"] = "http://c2.com/cgi/wiki?GuardClause";
Blockly.Msg["PROCEDURES_IFRETURN_TOOLTIP"] = "If a value is true, then return a second value.";
Blockly.Msg["PROCEDURES_IFRETURN_WARNING"] = "Warning: This block may be used only within a function definition.";
Blockly.Msg["PROCEDURES_MUTATORARG_TITLE"] = "input name:";
Blockly.Msg["PROCEDURES_MUTATORARG_TOOLTIP"] = "Add an input to the function.";
Blockly.Msg["PROCEDURES_MUTATORCONTAINER_TITLE"] = "inputs";
Blockly.Msg["PROCEDURES_MUTATORCONTAINER_TOOLTIP"] = "Add, remove, or reorder inputs to this function.";
Blockly.Msg["REDO"] = "Redo";
Blockly.Msg["REMOVE_COMMENT"] = "Remove Comment";
Blockly.Msg["RENAME_VARIABLE"] = "Rename variable...";
Blockly.Msg["RENAME_VARIABLE_TITLE"] = "Rename all '%1' variables to:";
Blockly.Msg["TEXT_APPEND_HELPURL"] = "https://github.com/google/blockly/wiki/Text#text-modification";
Blockly.Msg["TEXT_APPEND_TITLE"] = "to %1 append text %2";
Blockly.Msg["TEXT_APPEND_TOOLTIP"] = "Append some text to variable '%1'.";
Blockly.Msg["TEXT_CHANGECASE_HELPURL"] = "https://github.com/google/blockly/wiki/Text#adjusting-text-case";
Blockly.Msg["TEXT_CHANGECASE_OPERATOR_LOWERCASE"] = "to lower case";
Blockly.Msg["TEXT_CHANGECASE_OPERATOR_TITLECASE"] = "to Title Case";
Blockly.Msg["TEXT_CHANGECASE_OPERATOR_UPPERCASE"] = "to UPPER CASE";
Blockly.Msg["TEXT_CHANGECASE_TOOLTIP"] = "Return a copy of the text in a different case.";
Blockly.Msg["TEXT_CHARAT_FIRST"] = "get first letter";
Blockly.Msg["TEXT_CHARAT_FROM_END"] = "get letter # from end";
Blockly.Msg["TEXT_CHARAT_FROM_START"] = "get letter #";
Blockly.Msg["TEXT_CHARAT_HELPURL"] = "https://github.com/google/blockly/wiki/Text#extracting-text";
Blockly.Msg["TEXT_CHARAT_LAST"] = "get last letter";
Blockly.Msg["TEXT_CHARAT_RANDOM"] = "get random letter";
Blockly.Msg["TEXT_CHARAT_TAIL"] = "";
Blockly.Msg["TEXT_CHARAT_TITLE"] = "in text %1 %2";
Blockly.Msg["TEXT_CHARAT_TOOLTIP"] = "Returns the letter at the specified position.";
Blockly.Msg["TEXT_COUNT_HELPURL"] = "https://github.com/google/blockly/wiki/Text#counting-substrings";
Blockly.Msg["TEXT_COUNT_MESSAGE0"] = "count %1 in %2";
Blockly.Msg["TEXT_COUNT_TOOLTIP"] = "Count how many times some text occurs within some other text.";
Blockly.Msg["TEXT_CREATE_JOIN_ITEM_TOOLTIP"] = "Add an item to the text.";
Blockly.Msg["TEXT_CREATE_JOIN_TITLE_JOIN"] = "join";
Blockly.Msg["TEXT_CREATE_JOIN_TOOLTIP"] = "Add, remove, or reorder sections to reconfigure this text block.";
Blockly.Msg["TEXT_GET_SUBSTRING_END_FROM_END"] = "to letter # from end";
Blockly.Msg["TEXT_GET_SUBSTRING_END_FROM_START"] = "to letter #";
Blockly.Msg["TEXT_GET_SUBSTRING_END_LAST"] = "to last letter";
Blockly.Msg["TEXT_GET_SUBSTRING_HELPURL"] = "https://github.com/google/blockly/wiki/Text#extracting-a-region-of-text";
Blockly.Msg["TEXT_GET_SUBSTRING_INPUT_IN_TEXT"] = "in text";
Blockly.Msg["TEXT_GET_SUBSTRING_START_FIRST"] = "get substring from first letter";
Blockly.Msg["TEXT_GET_SUBSTRING_START_FROM_END"] = "get substring from letter # from end";
Blockly.Msg["TEXT_GET_SUBSTRING_START_FROM_START"] = "get substring from letter #";
Blockly.Msg["TEXT_GET_SUBSTRING_TAIL"] = "";
Blockly.Msg["TEXT_GET_SUBSTRING_TOOLTIP"] = "Returns a specified portion of the text.";
Blockly.Msg["TEXT_INDEXOF_HELPURL"] = "https://github.com/google/blockly/wiki/Text#finding-text";
Blockly.Msg["TEXT_INDEXOF_OPERATOR_FIRST"] = "find first occurrence of text";
Blockly.Msg["TEXT_INDEXOF_OPERATOR_LAST"] = "find last occurrence of text";
Blockly.Msg["TEXT_INDEXOF_TITLE"] = "in text %1 %2 %3";
Blockly.Msg["TEXT_INDEXOF_TOOLTIP"] = "Returns the index of the first/last occurrence of the first text in the second text. Returns %1 if text is not found.";
Blockly.Msg["TEXT_ISEMPTY_HELPURL"] = "https://github.com/google/blockly/wiki/Text#checking-for-empty-text";
Blockly.Msg["TEXT_ISEMPTY_TITLE"] = "%1 is empty";
Blockly.Msg["TEXT_ISEMPTY_TOOLTIP"] = "Returns true if the provided text is empty.";
Blockly.Msg["TEXT_JOIN_HELPURL"] = "https://github.com/google/blockly/wiki/Text#text-creation";
Blockly.Msg["TEXT_JOIN_TITLE_CREATEWITH"] = "create text with";
Blockly.Msg["TEXT_JOIN_TOOLTIP"] = "Create a piece of text by joining together any number of items.";
Blockly.Msg["TEXT_LENGTH_HELPURL"] = "https://github.com/google/blockly/wiki/Text#text-modification";
Blockly.Msg["TEXT_LENGTH_TITLE"] = "length of %1";
Blockly.Msg["TEXT_LENGTH_TOOLTIP"] = "Returns the number of letters (including spaces) in the provided text.";
Blockly.Msg["TEXT_PRINT_HELPURL"] = "https://github.com/google/blockly/wiki/Text#printing-text";
Blockly.Msg["TEXT_PRINT_TITLE"] = "print %1";
Blockly.Msg["TEXT_PRINT_TOOLTIP"] = "Print the specified text, number or other value.";
Blockly.Msg["TEXT_PROMPT_HELPURL"] = "https://github.com/google/blockly/wiki/Text#getting-input-from-the-user";
Blockly.Msg["TEXT_PROMPT_TOOLTIP_NUMBER"] = "Prompt for user for a number.";
Blockly.Msg["TEXT_PROMPT_TOOLTIP_TEXT"] = "Prompt for user for some text.";
Blockly.Msg["TEXT_PROMPT_TYPE_NUMBER"] = "prompt for number with message";
Blockly.Msg["TEXT_PROMPT_TYPE_TEXT"] = "prompt for text with message";
Blockly.Msg["TEXT_REPLACE_HELPURL"] = "https://github.com/google/blockly/wiki/Text#replacing-substrings";
Blockly.Msg["TEXT_REPLACE_MESSAGE0"] = "replace %1 with %2 in %3";
Blockly.Msg["TEXT_REPLACE_TOOLTIP"] = "Replace all occurances of some text within some other text.";
Blockly.Msg["TEXT_REVERSE_HELPURL"] = "https://github.com/google/blockly/wiki/Text#reversing-text";
Blockly.Msg["TEXT_REVERSE_MESSAGE0"] = "reverse %1";
Blockly.Msg["TEXT_REVERSE_TOOLTIP"] = "Reverses the order of the characters in the text.";
Blockly.Msg["TEXT_TEXT_HELPURL"] = "https://en.wikipedia.org/wiki/String_(computer_science)";
Blockly.Msg["TEXT_TEXT_TOOLTIP"] = "A letter, word, or line of text.";
Blockly.Msg["TEXT_TRIM_HELPURL"] = "https://github.com/google/blockly/wiki/Text#trimming-removing-spaces";
Blockly.Msg["TEXT_TRIM_OPERATOR_BOTH"] = "trim spaces from both sides of";
Blockly.Msg["TEXT_TRIM_OPERATOR_LEFT"] = "trim spaces from left side of";
Blockly.Msg["TEXT_TRIM_OPERATOR_RIGHT"] = "trim spaces from right side of";
Blockly.Msg["TEXT_TRIM_TOOLTIP"] = "Return a copy of the text with spaces removed from one or both ends.";
Blockly.Msg["TODAY"] = "Today";
Blockly.Msg["UNDO"] = "Undo";
Blockly.Msg["UNNAMED_KEY"] = "unnamed";
Blockly.Msg["VARIABLES_DEFAULT_NAME"] = "item";
Blockly.Msg["VARIABLES_GET_CREATE_SET"] = "Create 'set %1'";
Blockly.Msg["VARIABLES_GET_HELPURL"] = "https://github.com/google/blockly/wiki/Variables#get";
Blockly.Msg["VARIABLES_GET_TOOLTIP"] = "Returns the value of this variable.";
Blockly.Msg["VARIABLES_SET"] = "set %1 to %2";
Blockly.Msg["VARIABLES_SET_CREATE_GET"] = "Create 'get %1'";
Blockly.Msg["VARIABLES_SET_HELPURL"] = "https://github.com/google/blockly/wiki/Variables#set";
Blockly.Msg["VARIABLES_SET_TOOLTIP"] = "Sets this variable to be equal to the input.";
Blockly.Msg["VARIABLE_ALREADY_EXISTS"] = "A variable named '%1' already exists.";
Blockly.Msg["VARIABLE_ALREADY_EXISTS_FOR_ANOTHER_TYPE"] = "A variable named '%1' already exists for another type: '%2'.";
Blockly.Msg["WORKSPACE_ARIA_LABEL"] = "Blockly Workspace";
Blockly.Msg["WORKSPACE_COMMENT_DEFAULT_TEXT"] = "Say something...";
Blockly.Msg["CONTROLS_FOREACH_INPUT_DO"] = Blockly.Msg["CONTROLS_REPEAT_INPUT_DO"];
Blockly.Msg["CONTROLS_FOR_INPUT_DO"] = Blockly.Msg["CONTROLS_REPEAT_INPUT_DO"];
Blockly.Msg["CONTROLS_IF_ELSEIF_TITLE_ELSEIF"] = Blockly.Msg["CONTROLS_IF_MSG_ELSEIF"];
Blockly.Msg["CONTROLS_IF_ELSE_TITLE_ELSE"] = Blockly.Msg["CONTROLS_IF_MSG_ELSE"];
Blockly.Msg["CONTROLS_IF_IF_TITLE_IF"] = Blockly.Msg["CONTROLS_IF_MSG_IF"];
Blockly.Msg["CONTROLS_IF_MSG_THEN"] = Blockly.Msg["CONTROLS_REPEAT_INPUT_DO"];
Blockly.Msg["CONTROLS_WHILEUNTIL_INPUT_DO"] = Blockly.Msg["CONTROLS_REPEAT_INPUT_DO"];
Blockly.Msg["LISTS_CREATE_WITH_ITEM_TITLE"] = Blockly.Msg["VARIABLES_DEFAULT_NAME"];
Blockly.Msg["LISTS_GET_INDEX_HELPURL"] = Blockly.Msg["LISTS_INDEX_OF_HELPURL"];
Blockly.Msg["LISTS_GET_INDEX_INPUT_IN_LIST"] = Blockly.Msg["LISTS_INLIST"];
Blockly.Msg["LISTS_GET_SUBLIST_INPUT_IN_LIST"] = Blockly.Msg["LISTS_INLIST"];
Blockly.Msg["LISTS_INDEX_OF_INPUT_IN_LIST"] = Blockly.Msg["LISTS_INLIST"];
Blockly.Msg["LISTS_SET_INDEX_INPUT_IN_LIST"] = Blockly.Msg["LISTS_INLIST"];
Blockly.Msg["MATH_CHANGE_TITLE_ITEM"] = Blockly.Msg["VARIABLES_DEFAULT_NAME"];
Blockly.Msg["PROCEDURES_DEFRETURN_COMMENT"] = Blockly.Msg["PROCEDURES_DEFNORETURN_COMMENT"];
Blockly.Msg["PROCEDURES_DEFRETURN_DO"] = Blockly.Msg["PROCEDURES_DEFNORETURN_DO"];
Blockly.Msg["PROCEDURES_DEFRETURN_PROCEDURE"] = Blockly.Msg["PROCEDURES_DEFNORETURN_PROCEDURE"];
Blockly.Msg["PROCEDURES_DEFRETURN_TITLE"] = Blockly.Msg["PROCEDURES_DEFNORETURN_TITLE"];
Blockly.Msg["TEXT_APPEND_VARIABLE"] = Blockly.Msg["VARIABLES_DEFAULT_NAME"];
Blockly.Msg["TEXT_CREATE_JOIN_ITEM_TITLE_ITEM"] = Blockly.Msg["VARIABLES_DEFAULT_NAME"];
Blockly.Msg["MATH_HUE"] = "230";
Blockly.Msg["LOOPS_HUE"] = "120";
Blockly.Msg["LISTS_HUE"] = "260";
Blockly.Msg["LOGIC_HUE"] = "210";
Blockly.Msg["VARIABLES_HUE"] = "330";
Blockly.Msg["TEXTS_HUE"] = "160";
Blockly.Msg["PROCEDURES_HUE"] = "290";
Blockly.Msg["COLOUR_HUE"] = "20";
Blockly.Msg["VARIABLES_DYNAMIC_HUE"] = "310";

View File

@@ -0,0 +1,98 @@
// 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.Python = factory(root.Blockly);
}
}(this, function(Blockly) {
'use strict';Blockly.Python=new Blockly.Generator("Python");Blockly.Python.addReservedWords("False,None,True,and,as,assert,break,class,continue,def,del,elif,else,except,exec,finally,for,from,global,if,import,in,is,lambda,nonlocal,not,or,pass,print,raise,return,try,while,with,yield,NotImplemented,Ellipsis,__debug__,quit,exit,copyright,license,credits,ArithmeticError,AssertionError,AttributeError,BaseException,BlockingIOError,BrokenPipeError,BufferError,BytesWarning,ChildProcessError,ConnectionAbortedError,ConnectionError,ConnectionRefusedError,ConnectionResetError,DeprecationWarning,EOFError,Ellipsis,EnvironmentError,Exception,FileExistsError,FileNotFoundError,FloatingPointError,FutureWarning,GeneratorExit,IOError,ImportError,ImportWarning,IndentationError,IndexError,InterruptedError,IsADirectoryError,KeyError,KeyboardInterrupt,LookupError,MemoryError,ModuleNotFoundError,NameError,NotADirectoryError,NotImplemented,NotImplementedError,OSError,OverflowError,PendingDeprecationWarning,PermissionError,ProcessLookupError,RecursionError,ReferenceError,ResourceWarning,RuntimeError,RuntimeWarning,StandardError,StopAsyncIteration,StopIteration,SyntaxError,SyntaxWarning,SystemError,SystemExit,TabError,TimeoutError,TypeError,UnboundLocalError,UnicodeDecodeError,UnicodeEncodeError,UnicodeError,UnicodeTranslateError,UnicodeWarning,UserWarning,ValueError,Warning,ZeroDivisionError,_,__build_class__,__debug__,__doc__,__import__,__loader__,__name__,__package__,__spec__,abs,all,any,apply,ascii,basestring,bin,bool,buffer,bytearray,bytes,callable,chr,classmethod,cmp,coerce,compile,complex,copyright,credits,delattr,dict,dir,divmod,enumerate,eval,exec,execfile,exit,file,filter,float,format,frozenset,getattr,globals,hasattr,hash,help,hex,id,input,int,intern,isinstance,issubclass,iter,len,license,list,locals,long,map,max,memoryview,min,next,object,oct,open,ord,pow,print,property,quit,range,raw_input,reduce,reload,repr,reversed,round,set,setattr,slice,sorted,staticmethod,str,sum,super,tuple,type,unichr,unicode,vars,xrange,zip");
Blockly.Python.ORDER_ATOMIC=0;Blockly.Python.ORDER_COLLECTION=1;Blockly.Python.ORDER_STRING_CONVERSION=1;Blockly.Python.ORDER_MEMBER=2.1;Blockly.Python.ORDER_FUNCTION_CALL=2.2;Blockly.Python.ORDER_EXPONENTIATION=3;Blockly.Python.ORDER_UNARY_SIGN=4;Blockly.Python.ORDER_BITWISE_NOT=4;Blockly.Python.ORDER_MULTIPLICATIVE=5;Blockly.Python.ORDER_ADDITIVE=6;Blockly.Python.ORDER_BITWISE_SHIFT=7;Blockly.Python.ORDER_BITWISE_AND=8;Blockly.Python.ORDER_BITWISE_XOR=9;Blockly.Python.ORDER_BITWISE_OR=10;
Blockly.Python.ORDER_RELATIONAL=11;Blockly.Python.ORDER_LOGICAL_NOT=12;Blockly.Python.ORDER_LOGICAL_AND=13;Blockly.Python.ORDER_LOGICAL_OR=14;Blockly.Python.ORDER_CONDITIONAL=15;Blockly.Python.ORDER_LAMBDA=16;Blockly.Python.ORDER_NONE=99;
Blockly.Python.ORDER_OVERRIDES=[[Blockly.Python.ORDER_FUNCTION_CALL,Blockly.Python.ORDER_MEMBER],[Blockly.Python.ORDER_FUNCTION_CALL,Blockly.Python.ORDER_FUNCTION_CALL],[Blockly.Python.ORDER_MEMBER,Blockly.Python.ORDER_MEMBER],[Blockly.Python.ORDER_MEMBER,Blockly.Python.ORDER_FUNCTION_CALL],[Blockly.Python.ORDER_LOGICAL_NOT,Blockly.Python.ORDER_LOGICAL_NOT],[Blockly.Python.ORDER_LOGICAL_AND,Blockly.Python.ORDER_LOGICAL_AND],[Blockly.Python.ORDER_LOGICAL_OR,Blockly.Python.ORDER_LOGICAL_OR]];
Blockly.Python.init=function(a){Blockly.Python.PASS=this.INDENT+"pass\n";Blockly.Python.definitions_=Object.create(null);Blockly.Python.functionNames_=Object.create(null);Blockly.Python.variableDB_?Blockly.Python.variableDB_.reset():Blockly.Python.variableDB_=new Blockly.Names(Blockly.Python.RESERVED_WORDS_);Blockly.Python.variableDB_.setVariableMap(a.getVariableMap());for(var b=[],c=Blockly.Variables.allDeveloperVariables(a),d=0;d<c.length;d++)b.push(Blockly.Python.variableDB_.getName(c[d],Blockly.Names.DEVELOPER_VARIABLE_TYPE)+
" = None");a=Blockly.Variables.allUsedVarModels(a);for(d=0;d<a.length;d++)b.push(Blockly.Python.variableDB_.getName(a[d].getId(),Blockly.VARIABLE_CATEGORY_NAME)+" = None");Blockly.Python.definitions_.variables=b.join("\n")};
Blockly.Python.finish=function(a){var b=[],c=[],d;for(d in Blockly.Python.definitions_){var e=Blockly.Python.definitions_[d];e.match(/^(from\s+\S+\s+)?import\s+\S+/)?b.push(e):c.push(e)}delete Blockly.Python.definitions_;delete Blockly.Python.functionNames_;Blockly.Python.variableDB_.reset();return(b.join("\n")+"\n\n"+c.join("\n\n")).replace(/\n\n+/g,"\n\n").replace(/\n*$/,"\n\n\n")+a};Blockly.Python.scrubNakedValue=function(a){return a+"\n"};
Blockly.Python.quote_=function(a){a=a.replace(/\\/g,"\\\\").replace(/\n/g,"\\\n");var b="'";-1!==a.indexOf("'")&&(-1===a.indexOf('"')?b='"':a=a.replace(/'/g,"\\'"));return b+a+b};Blockly.Python.multiline_quote_=function(a){a=a.replace(/'''/g,"\\'\\'\\'");return"'''"+a+"'''"};
Blockly.Python.scrub_=function(a,b,c){var d="";if(!a.outputConnection||!a.outputConnection.targetConnection){var e=a.getCommentText();e&&(e=Blockly.utils.string.wrap(e,Blockly.Python.COMMENT_WRAP-3),d+=Blockly.Python.prefixLines(e+"\n","# "));for(var f=0;f<a.inputList.length;f++)a.inputList[f].type==Blockly.INPUT_VALUE&&(e=a.inputList[f].connection.targetBlock())&&(e=Blockly.Python.allNestedComments(e))&&(d+=Blockly.Python.prefixLines(e,"# "))}a=a.nextConnection&&a.nextConnection.targetBlock();c=
c?"":Blockly.Python.blockToCode(a);return d+b+c};Blockly.Python.getAdjustedInt=function(a,b,c,d){c=c||0;a.workspace.options.oneBasedIndex&&c--;var e=a.workspace.options.oneBasedIndex?"1":"0";a=Blockly.Python.valueToCode(a,b,c?Blockly.Python.ORDER_ADDITIVE:Blockly.Python.ORDER_NONE)||e;Blockly.isNumber(a)?(a=parseInt(a,10)+c,d&&(a=-a)):(a=0<c?"int("+a+" + "+c+")":0>c?"int("+a+" - "+-c+")":"int("+a+")",d&&(a="-"+a));return a};Blockly.Python.colour={};Blockly.Python.colour_picker=function(a){return[Blockly.Python.quote_(a.getFieldValue("COLOUR")),Blockly.Python.ORDER_ATOMIC]};Blockly.Python.colour_random=function(a){Blockly.Python.definitions_.import_random="import random";return["'#%06x' % random.randint(0, 2**24 - 1)",Blockly.Python.ORDER_FUNCTION_CALL]};
Blockly.Python.colour_rgb=function(a){var b=Blockly.Python.provideFunction_("colour_rgb",["def "+Blockly.Python.FUNCTION_NAME_PLACEHOLDER_+"(r, g, b):"," r = round(min(100, max(0, r)) * 2.55)"," g = round(min(100, max(0, g)) * 2.55)"," b = round(min(100, max(0, b)) * 2.55)"," return '#%02x%02x%02x' % (r, g, b)"]),c=Blockly.Python.valueToCode(a,"RED",Blockly.Python.ORDER_NONE)||0,d=Blockly.Python.valueToCode(a,"GREEN",Blockly.Python.ORDER_NONE)||0;a=Blockly.Python.valueToCode(a,"BLUE",Blockly.Python.ORDER_NONE)||
0;return[b+"("+c+", "+d+", "+a+")",Blockly.Python.ORDER_FUNCTION_CALL]};
Blockly.Python.colour_blend=function(a){var b=Blockly.Python.provideFunction_("colour_blend",["def "+Blockly.Python.FUNCTION_NAME_PLACEHOLDER_+"(colour1, colour2, ratio):"," r1, r2 = int(colour1[1:3], 16), int(colour2[1:3], 16)"," g1, g2 = int(colour1[3:5], 16), int(colour2[3:5], 16)"," b1, b2 = int(colour1[5:7], 16), int(colour2[5:7], 16)"," ratio = min(1, max(0, ratio))"," r = round(r1 * (1 - ratio) + r2 * ratio)"," g = round(g1 * (1 - ratio) + g2 * ratio)"," b = round(b1 * (1 - ratio) + b2 * ratio)",
" return '#%02x%02x%02x' % (r, g, b)"]),c=Blockly.Python.valueToCode(a,"COLOUR1",Blockly.Python.ORDER_NONE)||"'#000000'",d=Blockly.Python.valueToCode(a,"COLOUR2",Blockly.Python.ORDER_NONE)||"'#000000'";a=Blockly.Python.valueToCode(a,"RATIO",Blockly.Python.ORDER_NONE)||0;return[b+"("+c+", "+d+", "+a+")",Blockly.Python.ORDER_FUNCTION_CALL]};Blockly.Python.lists={};Blockly.Python.lists_create_empty=function(a){return["[]",Blockly.Python.ORDER_ATOMIC]};Blockly.Python.lists_create_with=function(a){for(var b=Array(a.itemCount_),c=0;c<a.itemCount_;c++)b[c]=Blockly.Python.valueToCode(a,"ADD"+c,Blockly.Python.ORDER_NONE)||"None";return["["+b.join(", ")+"]",Blockly.Python.ORDER_ATOMIC]};
Blockly.Python.lists_repeat=function(a){var b=Blockly.Python.valueToCode(a,"ITEM",Blockly.Python.ORDER_NONE)||"None";a=Blockly.Python.valueToCode(a,"NUM",Blockly.Python.ORDER_MULTIPLICATIVE)||"0";return["["+b+"] * "+a,Blockly.Python.ORDER_MULTIPLICATIVE]};Blockly.Python.lists_length=function(a){return["len("+(Blockly.Python.valueToCode(a,"VALUE",Blockly.Python.ORDER_NONE)||"[]")+")",Blockly.Python.ORDER_FUNCTION_CALL]};
Blockly.Python.lists_isEmpty=function(a){return["not len("+(Blockly.Python.valueToCode(a,"VALUE",Blockly.Python.ORDER_NONE)||"[]")+")",Blockly.Python.ORDER_LOGICAL_NOT]};
Blockly.Python.lists_indexOf=function(a){var b=Blockly.Python.valueToCode(a,"FIND",Blockly.Python.ORDER_NONE)||"[]",c=Blockly.Python.valueToCode(a,"VALUE",Blockly.Python.ORDER_NONE)||"''";if(a.workspace.options.oneBasedIndex)var d=" 0",e=" + 1",f="";else d=" -1",e="",f=" - 1";if("FIRST"==a.getFieldValue("END"))return a=Blockly.Python.provideFunction_("first_index",["def "+Blockly.Python.FUNCTION_NAME_PLACEHOLDER_+"(my_list, elem):"," try: index = my_list.index(elem)"+e," except: index ="+d," return index"]),
[a+"("+c+", "+b+")",Blockly.Python.ORDER_FUNCTION_CALL];a=Blockly.Python.provideFunction_("last_index",["def "+Blockly.Python.FUNCTION_NAME_PLACEHOLDER_+"(my_list, elem):"," try: index = len(my_list) - my_list[::-1].index(elem)"+f," except: index ="+d," return index"]);return[a+"("+c+", "+b+")",Blockly.Python.ORDER_FUNCTION_CALL]};
Blockly.Python.lists_getIndex=function(a){var b=a.getFieldValue("MODE")||"GET",c=a.getFieldValue("WHERE")||"FROM_START",d=Blockly.Python.valueToCode(a,"VALUE","RANDOM"==c?Blockly.Python.ORDER_NONE:Blockly.Python.ORDER_MEMBER)||"[]";switch(c){case "FIRST":if("GET"==b)return[d+"[0]",Blockly.Python.ORDER_MEMBER];if("GET_REMOVE"==b)return[d+".pop(0)",Blockly.Python.ORDER_FUNCTION_CALL];if("REMOVE"==b)return d+".pop(0)\n";break;case "LAST":if("GET"==b)return[d+"[-1]",Blockly.Python.ORDER_MEMBER];if("GET_REMOVE"==
b)return[d+".pop()",Blockly.Python.ORDER_FUNCTION_CALL];if("REMOVE"==b)return d+".pop()\n";break;case "FROM_START":a=Blockly.Python.getAdjustedInt(a,"AT");if("GET"==b)return[d+"["+a+"]",Blockly.Python.ORDER_MEMBER];if("GET_REMOVE"==b)return[d+".pop("+a+")",Blockly.Python.ORDER_FUNCTION_CALL];if("REMOVE"==b)return d+".pop("+a+")\n";break;case "FROM_END":a=Blockly.Python.getAdjustedInt(a,"AT",1,!0);if("GET"==b)return[d+"["+a+"]",Blockly.Python.ORDER_MEMBER];if("GET_REMOVE"==b)return[d+".pop("+a+")",
Blockly.Python.ORDER_FUNCTION_CALL];if("REMOVE"==b)return d+".pop("+a+")\n";break;case "RANDOM":Blockly.Python.definitions_.import_random="import random";if("GET"==b)return["random.choice("+d+")",Blockly.Python.ORDER_FUNCTION_CALL];d=Blockly.Python.provideFunction_("lists_remove_random_item",["def "+Blockly.Python.FUNCTION_NAME_PLACEHOLDER_+"(myList):"," x = int(random.random() * len(myList))"," return myList.pop(x)"])+"("+d+")";if("GET_REMOVE"==b)return[d,Blockly.Python.ORDER_FUNCTION_CALL];if("REMOVE"==
b)return d+"\n"}throw Error("Unhandled combination (lists_getIndex).");};
Blockly.Python.lists_setIndex=function(a){var b=Blockly.Python.valueToCode(a,"LIST",Blockly.Python.ORDER_MEMBER)||"[]",c=a.getFieldValue("MODE")||"GET",d=a.getFieldValue("WHERE")||"FROM_START",e=Blockly.Python.valueToCode(a,"TO",Blockly.Python.ORDER_NONE)||"None";switch(d){case "FIRST":if("SET"==c)return b+"[0] = "+e+"\n";if("INSERT"==c)return b+".insert(0, "+e+")\n";break;case "LAST":if("SET"==c)return b+"[-1] = "+e+"\n";if("INSERT"==c)return b+".append("+e+")\n";break;case "FROM_START":a=Blockly.Python.getAdjustedInt(a,
"AT");if("SET"==c)return b+"["+a+"] = "+e+"\n";if("INSERT"==c)return b+".insert("+a+", "+e+")\n";break;case "FROM_END":a=Blockly.Python.getAdjustedInt(a,"AT",1,!0);if("SET"==c)return b+"["+a+"] = "+e+"\n";if("INSERT"==c)return b+".insert("+a+", "+e+")\n";break;case "RANDOM":Blockly.Python.definitions_.import_random="import random";b.match(/^\w+$/)?a="":(a=Blockly.Python.variableDB_.getDistinctName("tmp_list",Blockly.VARIABLE_CATEGORY_NAME),d=a+" = "+b+"\n",b=a,a=d);d=Blockly.Python.variableDB_.getDistinctName("tmp_x",
Blockly.VARIABLE_CATEGORY_NAME);a+=d+" = int(random.random() * len("+b+"))\n";if("SET"==c)return a+(b+"["+d+"] = "+e+"\n");if("INSERT"==c)return a+(b+".insert("+d+", "+e+")\n")}throw Error("Unhandled combination (lists_setIndex).");};
Blockly.Python.lists_getSublist=function(a){var b=Blockly.Python.valueToCode(a,"LIST",Blockly.Python.ORDER_MEMBER)||"[]",c=a.getFieldValue("WHERE1"),d=a.getFieldValue("WHERE2");switch(c){case "FROM_START":c=Blockly.Python.getAdjustedInt(a,"AT1");"0"==c&&(c="");break;case "FROM_END":c=Blockly.Python.getAdjustedInt(a,"AT1",1,!0);break;case "FIRST":c="";break;default:throw Error("Unhandled option (lists_getSublist)");}switch(d){case "FROM_START":a=Blockly.Python.getAdjustedInt(a,"AT2",1);break;case "FROM_END":a=
Blockly.Python.getAdjustedInt(a,"AT2",0,!0);Blockly.isNumber(String(a))?"0"==a&&(a=""):(Blockly.Python.definitions_.import_sys="import sys",a+=" or sys.maxsize");break;case "LAST":a="";break;default:throw Error("Unhandled option (lists_getSublist)");}return[b+"["+c+" : "+a+"]",Blockly.Python.ORDER_MEMBER]};
Blockly.Python.lists_sort=function(a){var b=Blockly.Python.valueToCode(a,"LIST",Blockly.Python.ORDER_NONE)||"[]",c=a.getFieldValue("TYPE");a="1"===a.getFieldValue("DIRECTION")?"False":"True";return[Blockly.Python.provideFunction_("lists_sort",["def "+Blockly.Python.FUNCTION_NAME_PLACEHOLDER_+"(my_list, type, reverse):"," def try_float(s):"," try:"," return float(s)"," except:"," return 0"," key_funcs = {",' "NUMERIC": try_float,',' "TEXT": str,',' "IGNORE_CASE": lambda s: str(s).lower()',
" }"," key_func = key_funcs[type]"," list_cpy = list(my_list)"," return sorted(list_cpy, key=key_func, reverse=reverse)"])+"("+b+', "'+c+'", '+a+")",Blockly.Python.ORDER_FUNCTION_CALL]};
Blockly.Python.lists_split=function(a){var b=a.getFieldValue("MODE");if("SPLIT"==b)b=Blockly.Python.valueToCode(a,"INPUT",Blockly.Python.ORDER_MEMBER)||"''",a=Blockly.Python.valueToCode(a,"DELIM",Blockly.Python.ORDER_NONE),a=b+".split("+a+")";else if("JOIN"==b)b=Blockly.Python.valueToCode(a,"INPUT",Blockly.Python.ORDER_NONE)||"[]",a=Blockly.Python.valueToCode(a,"DELIM",Blockly.Python.ORDER_MEMBER)||"''",a=a+".join("+b+")";else throw Error("Unknown mode: "+b);return[a,Blockly.Python.ORDER_FUNCTION_CALL]};
Blockly.Python.lists_reverse=function(a){return["list(reversed("+(Blockly.Python.valueToCode(a,"LIST",Blockly.Python.ORDER_NONE)||"[]")+"))",Blockly.Python.ORDER_FUNCTION_CALL]};Blockly.Python.logic={};
Blockly.Python.controls_if=function(a){var b=0,c="";Blockly.Python.STATEMENT_PREFIX&&(c+=Blockly.Python.injectId(Blockly.Python.STATEMENT_PREFIX,a));do{var d=Blockly.Python.valueToCode(a,"IF"+b,Blockly.Python.ORDER_NONE)||"False";var e=Blockly.Python.statementToCode(a,"DO"+b)||Blockly.Python.PASS;Blockly.Python.STATEMENT_SUFFIX&&(e=Blockly.Python.prefixLines(Blockly.Python.injectId(Blockly.Python.STATEMENT_SUFFIX,a),Blockly.Python.INDENT)+e);c+=(0==b?"if ":"elif ")+d+":\n"+e;++b}while(a.getInput("IF"+
b));if(a.getInput("ELSE")||Blockly.Python.STATEMENT_SUFFIX)e=Blockly.Python.statementToCode(a,"ELSE")||Blockly.Python.PASS,Blockly.Python.STATEMENT_SUFFIX&&(e=Blockly.Python.prefixLines(Blockly.Python.injectId(Blockly.Python.STATEMENT_SUFFIX,a),Blockly.Python.INDENT)+e),c+="else:\n"+e;return c};Blockly.Python.controls_ifelse=Blockly.Python.controls_if;
Blockly.Python.logic_compare=function(a){var b={EQ:"==",NEQ:"!=",LT:"<",LTE:"<=",GT:">",GTE:">="}[a.getFieldValue("OP")],c=Blockly.Python.ORDER_RELATIONAL,d=Blockly.Python.valueToCode(a,"A",c)||"0";a=Blockly.Python.valueToCode(a,"B",c)||"0";return[d+" "+b+" "+a,c]};
Blockly.Python.logic_operation=function(a){var b="AND"==a.getFieldValue("OP")?"and":"or",c="and"==b?Blockly.Python.ORDER_LOGICAL_AND:Blockly.Python.ORDER_LOGICAL_OR,d=Blockly.Python.valueToCode(a,"A",c);a=Blockly.Python.valueToCode(a,"B",c);if(d||a){var e="and"==b?"True":"False";d||(d=e);a||(a=e)}else a=d="False";return[d+" "+b+" "+a,c]};Blockly.Python.logic_negate=function(a){return["not "+(Blockly.Python.valueToCode(a,"BOOL",Blockly.Python.ORDER_LOGICAL_NOT)||"True"),Blockly.Python.ORDER_LOGICAL_NOT]};
Blockly.Python.logic_boolean=function(a){return["TRUE"==a.getFieldValue("BOOL")?"True":"False",Blockly.Python.ORDER_ATOMIC]};Blockly.Python.logic_null=function(a){return["None",Blockly.Python.ORDER_ATOMIC]};
Blockly.Python.logic_ternary=function(a){var b=Blockly.Python.valueToCode(a,"IF",Blockly.Python.ORDER_CONDITIONAL)||"False",c=Blockly.Python.valueToCode(a,"THEN",Blockly.Python.ORDER_CONDITIONAL)||"None";a=Blockly.Python.valueToCode(a,"ELSE",Blockly.Python.ORDER_CONDITIONAL)||"None";return[c+" if "+b+" else "+a,Blockly.Python.ORDER_CONDITIONAL]};Blockly.Python.loops={};Blockly.Python.controls_repeat_ext=function(a){var b=a.getField("TIMES")?String(parseInt(a.getFieldValue("TIMES"),10)):Blockly.Python.valueToCode(a,"TIMES",Blockly.Python.ORDER_NONE)||"0";b=Blockly.isNumber(b)?parseInt(b,10):"int("+b+")";var c=Blockly.Python.statementToCode(a,"DO");c=Blockly.Python.addLoopTrap(c,a)||Blockly.Python.PASS;return"for "+Blockly.Python.variableDB_.getDistinctName("count",Blockly.VARIABLE_CATEGORY_NAME)+" in range("+b+"):\n"+c};
Blockly.Python.controls_repeat=Blockly.Python.controls_repeat_ext;Blockly.Python.controls_whileUntil=function(a){var b="UNTIL"==a.getFieldValue("MODE"),c=Blockly.Python.valueToCode(a,"BOOL",b?Blockly.Python.ORDER_LOGICAL_NOT:Blockly.Python.ORDER_NONE)||"False",d=Blockly.Python.statementToCode(a,"DO");d=Blockly.Python.addLoopTrap(d,a)||Blockly.Python.PASS;b&&(c="not "+c);return"while "+c+":\n"+d};
Blockly.Python.controls_for=function(a){var b=Blockly.Python.variableDB_.getName(a.getFieldValue("VAR"),Blockly.VARIABLE_CATEGORY_NAME),c=Blockly.Python.valueToCode(a,"FROM",Blockly.Python.ORDER_NONE)||"0",d=Blockly.Python.valueToCode(a,"TO",Blockly.Python.ORDER_NONE)||"0",e=Blockly.Python.valueToCode(a,"BY",Blockly.Python.ORDER_NONE)||"1",f=Blockly.Python.statementToCode(a,"DO");f=Blockly.Python.addLoopTrap(f,a)||Blockly.Python.PASS;var l="",h=function(){return Blockly.Python.provideFunction_("upRange",
["def "+Blockly.Python.FUNCTION_NAME_PLACEHOLDER_+"(start, stop, step):"," while start <= stop:"," yield start"," start += abs(step)"])},g=function(){return Blockly.Python.provideFunction_("downRange",["def "+Blockly.Python.FUNCTION_NAME_PLACEHOLDER_+"(start, stop, step):"," while start >= stop:"," yield start"," start -= abs(step)"])};a=function(a,b,c){return"("+a+" <= "+b+") and "+h()+"("+a+", "+b+", "+c+") or "+g()+"("+a+", "+b+", "+c+")"};if(Blockly.isNumber(c)&&Blockly.isNumber(d)&&
Blockly.isNumber(e))c=Number(c),d=Number(d),e=Math.abs(Number(e)),0===c%1&&0===d%1&&0===e%1?(c<=d?(d++,a=0==c&&1==e?d:c+", "+d,1!=e&&(a+=", "+e)):(d--,a=c+", "+d+", -"+e),a="range("+a+")"):(a=c<d?h():g(),a+="("+c+", "+d+", "+e+")");else{var k=function(a,c){Blockly.isNumber(a)?a=Number(a):a.match(/^\w+$/)?a="float("+a+")":(c=Blockly.Python.variableDB_.getDistinctName(b+c,Blockly.VARIABLE_CATEGORY_NAME),l+=c+" = float("+a+")\n",a=c);return a};c=k(c,"_start");d=k(d,"_end");k(e,"_inc");a="number"==typeof c&&
"number"==typeof d?c<d?h(c,d,e):g(c,d,e):a(c,d,e)}return l+="for "+b+" in "+a+":\n"+f};Blockly.Python.controls_forEach=function(a){var b=Blockly.Python.variableDB_.getName(a.getFieldValue("VAR"),Blockly.VARIABLE_CATEGORY_NAME),c=Blockly.Python.valueToCode(a,"LIST",Blockly.Python.ORDER_RELATIONAL)||"[]",d=Blockly.Python.statementToCode(a,"DO");d=Blockly.Python.addLoopTrap(d,a)||Blockly.Python.PASS;return"for "+b+" in "+c+":\n"+d};
Blockly.Python.controls_flow_statements=function(a){var b="";Blockly.Python.STATEMENT_PREFIX&&(b+=Blockly.Python.injectId(Blockly.Python.STATEMENT_PREFIX,a));Blockly.Python.STATEMENT_SUFFIX&&(b+=Blockly.Python.injectId(Blockly.Python.STATEMENT_SUFFIX,a));if(Blockly.Python.STATEMENT_PREFIX){var c=Blockly.Constants.Loops.CONTROL_FLOW_IN_LOOP_CHECK_MIXIN.getSurroundLoop(a);c&&!c.suppressPrefixSuffix&&(b+=Blockly.Python.injectId(Blockly.Python.STATEMENT_PREFIX,c))}switch(a.getFieldValue("FLOW")){case "BREAK":return b+
"break\n";case "CONTINUE":return b+"continue\n"}throw Error("Unknown flow statement.");};Blockly.Python.math={};Blockly.Python.addReservedWords("math,random,Number");Blockly.Python.math_number=function(a){a=Number(a.getFieldValue("NUM"));if(Infinity==a){a='float("inf")';var b=Blockly.Python.ORDER_FUNCTION_CALL}else-Infinity==a?(a='-float("inf")',b=Blockly.Python.ORDER_UNARY_SIGN):b=0>a?Blockly.Python.ORDER_UNARY_SIGN:Blockly.Python.ORDER_ATOMIC;return[a,b]};
Blockly.Python.math_arithmetic=function(a){var b={ADD:[" + ",Blockly.Python.ORDER_ADDITIVE],MINUS:[" - ",Blockly.Python.ORDER_ADDITIVE],MULTIPLY:[" * ",Blockly.Python.ORDER_MULTIPLICATIVE],DIVIDE:[" / ",Blockly.Python.ORDER_MULTIPLICATIVE],POWER:[" ** ",Blockly.Python.ORDER_EXPONENTIATION]}[a.getFieldValue("OP")],c=b[0];b=b[1];var d=Blockly.Python.valueToCode(a,"A",b)||"0";a=Blockly.Python.valueToCode(a,"B",b)||"0";return[d+c+a,b]};
Blockly.Python.math_single=function(a){var b=a.getFieldValue("OP");if("NEG"==b){var c=Blockly.Python.valueToCode(a,"NUM",Blockly.Python.ORDER_UNARY_SIGN)||"0";return["-"+c,Blockly.Python.ORDER_UNARY_SIGN]}Blockly.Python.definitions_.import_math="import math";a="SIN"==b||"COS"==b||"TAN"==b?Blockly.Python.valueToCode(a,"NUM",Blockly.Python.ORDER_MULTIPLICATIVE)||"0":Blockly.Python.valueToCode(a,"NUM",Blockly.Python.ORDER_NONE)||"0";switch(b){case "ABS":c="math.fabs("+a+")";break;case "ROOT":c="math.sqrt("+
a+")";break;case "LN":c="math.log("+a+")";break;case "LOG10":c="math.log10("+a+")";break;case "EXP":c="math.exp("+a+")";break;case "POW10":c="math.pow(10,"+a+")";break;case "ROUND":c="round("+a+")";break;case "ROUNDUP":c="math.ceil("+a+")";break;case "ROUNDDOWN":c="math.floor("+a+")";break;case "SIN":c="math.sin("+a+" / 180.0 * math.pi)";break;case "COS":c="math.cos("+a+" / 180.0 * math.pi)";break;case "TAN":c="math.tan("+a+" / 180.0 * math.pi)"}if(c)return[c,Blockly.Python.ORDER_FUNCTION_CALL];switch(b){case "ASIN":c=
"math.asin("+a+") / math.pi * 180";break;case "ACOS":c="math.acos("+a+") / math.pi * 180";break;case "ATAN":c="math.atan("+a+") / math.pi * 180";break;default:throw Error("Unknown math operator: "+b);}return[c,Blockly.Python.ORDER_MULTIPLICATIVE]};
Blockly.Python.math_constant=function(a){var b={PI:["math.pi",Blockly.Python.ORDER_MEMBER],E:["math.e",Blockly.Python.ORDER_MEMBER],GOLDEN_RATIO:["(1 + math.sqrt(5)) / 2",Blockly.Python.ORDER_MULTIPLICATIVE],SQRT2:["math.sqrt(2)",Blockly.Python.ORDER_MEMBER],SQRT1_2:["math.sqrt(1.0 / 2)",Blockly.Python.ORDER_MEMBER],INFINITY:["float('inf')",Blockly.Python.ORDER_ATOMIC]};a=a.getFieldValue("CONSTANT");"INFINITY"!=a&&(Blockly.Python.definitions_.import_math="import math");return b[a]};
Blockly.Python.math_number_property=function(a){var b=Blockly.Python.valueToCode(a,"NUMBER_TO_CHECK",Blockly.Python.ORDER_MULTIPLICATIVE)||"0",c=a.getFieldValue("PROPERTY");if("PRIME"==c)return Blockly.Python.definitions_.import_math="import math",Blockly.Python.definitions_.from_numbers_import_Number="from numbers import Number",[Blockly.Python.provideFunction_("math_isPrime",["def "+Blockly.Python.FUNCTION_NAME_PLACEHOLDER_+"(n):"," # https://en.wikipedia.org/wiki/Primality_test#Naive_methods",
" # If n is not a number but a string, try parsing it."," if not isinstance(n, Number):"," try:"," n = float(n)"," except:"," return False"," if n == 2 or n == 3:"," return True"," # False if n is negative, is 1, or not whole, or if n is divisible by 2 or 3."," if n <= 1 or n % 1 != 0 or n % 2 == 0 or n % 3 == 0:"," return False"," # Check all the numbers of form 6k +/- 1, up to sqrt(n)."," for x in range(6, int(math.sqrt(n)) + 2, 6):"," if n % (x - 1) == 0 or n % (x + 1) == 0:",
" return False"," return True"])+"("+b+")",Blockly.Python.ORDER_FUNCTION_CALL];switch(c){case "EVEN":var d=b+" % 2 == 0";break;case "ODD":d=b+" % 2 == 1";break;case "WHOLE":d=b+" % 1 == 0";break;case "POSITIVE":d=b+" > 0";break;case "NEGATIVE":d=b+" < 0";break;case "DIVISIBLE_BY":a=Blockly.Python.valueToCode(a,"DIVISOR",Blockly.Python.ORDER_MULTIPLICATIVE);if(!a||"0"==a)return["False",Blockly.Python.ORDER_ATOMIC];d=b+" % "+a+" == 0"}return[d,Blockly.Python.ORDER_RELATIONAL]};
Blockly.Python.math_change=function(a){Blockly.Python.definitions_.from_numbers_import_Number="from numbers import Number";var b=Blockly.Python.valueToCode(a,"DELTA",Blockly.Python.ORDER_ADDITIVE)||"0";a=Blockly.Python.variableDB_.getName(a.getFieldValue("VAR"),Blockly.VARIABLE_CATEGORY_NAME);return a+" = ("+a+" if isinstance("+a+", Number) else 0) + "+b+"\n"};Blockly.Python.math_round=Blockly.Python.math_single;Blockly.Python.math_trig=Blockly.Python.math_single;
Blockly.Python.math_on_list=function(a){var b=a.getFieldValue("OP");a=Blockly.Python.valueToCode(a,"LIST",Blockly.Python.ORDER_NONE)||"[]";switch(b){case "SUM":b="sum("+a+")";break;case "MIN":b="min("+a+")";break;case "MAX":b="max("+a+")";break;case "AVERAGE":Blockly.Python.definitions_.from_numbers_import_Number="from numbers import Number";b=Blockly.Python.provideFunction_("math_mean",["def "+Blockly.Python.FUNCTION_NAME_PLACEHOLDER_+"(myList):"," localList = [e for e in myList if isinstance(e, Number)]",
" if not localList: return"," return float(sum(localList)) / len(localList)"]);b=b+"("+a+")";break;case "MEDIAN":Blockly.Python.definitions_.from_numbers_import_Number="from numbers import Number";b=Blockly.Python.provideFunction_("math_median",["def "+Blockly.Python.FUNCTION_NAME_PLACEHOLDER_+"(myList):"," localList = sorted([e for e in myList if isinstance(e, Number)])"," if not localList: return"," if len(localList) % 2 == 0:"," return (localList[len(localList) // 2 - 1] + localList[len(localList) // 2]) / 2.0",
" else:"," return localList[(len(localList) - 1) // 2]"]);b=b+"("+a+")";break;case "MODE":b=Blockly.Python.provideFunction_("math_modes",["def "+Blockly.Python.FUNCTION_NAME_PLACEHOLDER_+"(some_list):"," modes = []"," # Using a lists of [item, count] to keep count rather than dict",' # to avoid "unhashable" errors when the counted item is itself a list or dict.'," counts = []"," maxCount = 1"," for item in some_list:"," found = False"," for count in counts:"," if count[0] == item:",
" count[1] += 1"," maxCount = max(maxCount, count[1])"," found = True"," if not found:"," counts.append([item, 1])"," for counted_item, item_count in counts:"," if item_count == maxCount:"," modes.append(counted_item)"," return modes"]);b=b+"("+a+")";break;case "STD_DEV":Blockly.Python.definitions_.import_math="import math";b=Blockly.Python.provideFunction_("math_standard_deviation",["def "+Blockly.Python.FUNCTION_NAME_PLACEHOLDER_+"(numbers):"," n = len(numbers)",
" if n == 0: return"," mean = float(sum(numbers)) / n"," variance = sum((x - mean) ** 2 for x in numbers) / n"," return math.sqrt(variance)"]);b=b+"("+a+")";break;case "RANDOM":Blockly.Python.definitions_.import_random="import random";b="random.choice("+a+")";break;default:throw Error("Unknown operator: "+b);}return[b,Blockly.Python.ORDER_FUNCTION_CALL]};
Blockly.Python.math_modulo=function(a){var b=Blockly.Python.valueToCode(a,"DIVIDEND",Blockly.Python.ORDER_MULTIPLICATIVE)||"0";a=Blockly.Python.valueToCode(a,"DIVISOR",Blockly.Python.ORDER_MULTIPLICATIVE)||"0";return[b+" % "+a,Blockly.Python.ORDER_MULTIPLICATIVE]};
Blockly.Python.math_constrain=function(a){var b=Blockly.Python.valueToCode(a,"VALUE",Blockly.Python.ORDER_NONE)||"0",c=Blockly.Python.valueToCode(a,"LOW",Blockly.Python.ORDER_NONE)||"0";a=Blockly.Python.valueToCode(a,"HIGH",Blockly.Python.ORDER_NONE)||"float('inf')";return["min(max("+b+", "+c+"), "+a+")",Blockly.Python.ORDER_FUNCTION_CALL]};
Blockly.Python.math_random_int=function(a){Blockly.Python.definitions_.import_random="import random";var b=Blockly.Python.valueToCode(a,"FROM",Blockly.Python.ORDER_NONE)||"0";a=Blockly.Python.valueToCode(a,"TO",Blockly.Python.ORDER_NONE)||"0";return["random.randint("+b+", "+a+")",Blockly.Python.ORDER_FUNCTION_CALL]};Blockly.Python.math_random_float=function(a){Blockly.Python.definitions_.import_random="import random";return["random.random()",Blockly.Python.ORDER_FUNCTION_CALL]};
Blockly.Python.math_atan2=function(a){Blockly.Python.definitions_.import_math="import math";var b=Blockly.Python.valueToCode(a,"X",Blockly.Python.ORDER_NONE)||"0";return["math.atan2("+(Blockly.Python.valueToCode(a,"Y",Blockly.Python.ORDER_NONE)||"0")+", "+b+") / math.pi * 180",Blockly.Python.ORDER_MULTIPLICATIVE]};Blockly.Python.procedures={};
Blockly.Python.procedures_defreturn=function(a){for(var b=[],c,d=a.workspace,e=Blockly.Variables.allUsedVarModels(d)||[],f=0;c=e[f];f++)c=c.name,-1==a.getVars().indexOf(c)&&b.push(Blockly.Python.variableDB_.getName(c,Blockly.VARIABLE_CATEGORY_NAME));e=Blockly.Variables.allDeveloperVariables(d);for(f=0;f<e.length;f++)b.push(Blockly.Python.variableDB_.getName(e[f],Blockly.Names.DEVELOPER_VARIABLE_TYPE));b=b.length?Blockly.Python.INDENT+"global "+b.join(", ")+"\n":"";d=Blockly.Python.variableDB_.getName(a.getFieldValue("NAME"),Blockly.PROCEDURE_CATEGORY_NAME);
c="";Blockly.Python.STATEMENT_PREFIX&&(c+=Blockly.Python.injectId(Blockly.Python.STATEMENT_PREFIX,a));Blockly.Python.STATEMENT_SUFFIX&&(c+=Blockly.Python.injectId(Blockly.Python.STATEMENT_SUFFIX,a));c&&(c=Blockly.Python.prefixLines(c,Blockly.Python.INDENT));var l="";Blockly.Python.INFINITE_LOOP_TRAP&&(l=Blockly.Python.prefixLines(Blockly.Python.injectId(Blockly.Python.INFINITE_LOOP_TRAP,a),Blockly.Python.INDENT));var h=Blockly.Python.statementToCode(a,"STACK"),g=Blockly.Python.valueToCode(a,"RETURN",
Blockly.Python.ORDER_NONE)||"",k="";h&&g&&(k=c);g?g=Blockly.Python.INDENT+"return "+g+"\n":h||(h=Blockly.Python.PASS);var m=[];e=a.getVars();for(f=0;f<e.length;f++)m[f]=Blockly.Python.variableDB_.getName(e[f],Blockly.VARIABLE_CATEGORY_NAME);b="def "+d+"("+m.join(", ")+"):\n"+b+c+l+h+k+g;b=Blockly.Python.scrub_(a,b);Blockly.Python.definitions_["%"+d]=b;return null};Blockly.Python.procedures_defnoreturn=Blockly.Python.procedures_defreturn;
Blockly.Python.procedures_callreturn=function(a){for(var b=Blockly.Python.variableDB_.getName(a.getFieldValue("NAME"),Blockly.PROCEDURE_CATEGORY_NAME),c=[],d=a.getVars(),e=0;e<d.length;e++)c[e]=Blockly.Python.valueToCode(a,"ARG"+e,Blockly.Python.ORDER_NONE)||"None";return[b+"("+c.join(", ")+")",Blockly.Python.ORDER_FUNCTION_CALL]};Blockly.Python.procedures_callnoreturn=function(a){return Blockly.Python.procedures_callreturn(a)[0]+"\n"};
Blockly.Python.procedures_ifreturn=function(a){var b="if "+(Blockly.Python.valueToCode(a,"CONDITION",Blockly.Python.ORDER_NONE)||"False")+":\n";Blockly.Python.STATEMENT_SUFFIX&&(b+=Blockly.Python.prefixLines(Blockly.Python.injectId(Blockly.Python.STATEMENT_SUFFIX,a),Blockly.Python.INDENT));a.hasReturnValue_?(a=Blockly.Python.valueToCode(a,"VALUE",Blockly.Python.ORDER_NONE)||"None",b+=Blockly.Python.INDENT+"return "+a+"\n"):b+=Blockly.Python.INDENT+"return\n";return b};Blockly.Python.texts={};Blockly.Python.text=function(a){return[Blockly.Python.quote_(a.getFieldValue("TEXT")),Blockly.Python.ORDER_ATOMIC]};Blockly.Python.text_multiline=function(a){return[Blockly.Python.multiline_quote_(a.getFieldValue("TEXT")),Blockly.Python.ORDER_ATOMIC]};Blockly.Python.text.forceString_=function(a){return Blockly.Python.text.forceString_.strRegExp.test(a)?a:"str("+a+")"};Blockly.Python.text.forceString_.strRegExp=/^\s*'([^']|\\')*'\s*$/;
Blockly.Python.text_join=function(a){switch(a.itemCount_){case 0:return["''",Blockly.Python.ORDER_ATOMIC];case 1:return a=Blockly.Python.valueToCode(a,"ADD0",Blockly.Python.ORDER_NONE)||"''",a=Blockly.Python.text.forceString_(a),[a,Blockly.Python.ORDER_FUNCTION_CALL];case 2:var b=Blockly.Python.valueToCode(a,"ADD0",Blockly.Python.ORDER_NONE)||"''";a=Blockly.Python.valueToCode(a,"ADD1",Blockly.Python.ORDER_NONE)||"''";a=Blockly.Python.text.forceString_(b)+" + "+Blockly.Python.text.forceString_(a);
return[a,Blockly.Python.ORDER_ADDITIVE];default:b=[];for(var c=0;c<a.itemCount_;c++)b[c]=Blockly.Python.valueToCode(a,"ADD"+c,Blockly.Python.ORDER_NONE)||"''";a=Blockly.Python.variableDB_.getDistinctName("x",Blockly.VARIABLE_CATEGORY_NAME);a="''.join([str("+a+") for "+a+" in ["+b.join(", ")+"]])";return[a,Blockly.Python.ORDER_FUNCTION_CALL]}};
Blockly.Python.text_append=function(a){var b=Blockly.Python.variableDB_.getName(a.getFieldValue("VAR"),Blockly.VARIABLE_CATEGORY_NAME);a=Blockly.Python.valueToCode(a,"TEXT",Blockly.Python.ORDER_NONE)||"''";return b+" = str("+b+") + "+Blockly.Python.text.forceString_(a)+"\n"};Blockly.Python.text_length=function(a){return["len("+(Blockly.Python.valueToCode(a,"VALUE",Blockly.Python.ORDER_NONE)||"''")+")",Blockly.Python.ORDER_FUNCTION_CALL]};
Blockly.Python.text_isEmpty=function(a){return["not len("+(Blockly.Python.valueToCode(a,"VALUE",Blockly.Python.ORDER_NONE)||"''")+")",Blockly.Python.ORDER_LOGICAL_NOT]};
Blockly.Python.text_indexOf=function(a){var b="FIRST"==a.getFieldValue("END")?"find":"rfind",c=Blockly.Python.valueToCode(a,"FIND",Blockly.Python.ORDER_NONE)||"''";b=(Blockly.Python.valueToCode(a,"VALUE",Blockly.Python.ORDER_MEMBER)||"''")+"."+b+"("+c+")";return a.workspace.options.oneBasedIndex?[b+" + 1",Blockly.Python.ORDER_ADDITIVE]:[b,Blockly.Python.ORDER_FUNCTION_CALL]};
Blockly.Python.text_charAt=function(a){var b=a.getFieldValue("WHERE")||"FROM_START",c=Blockly.Python.valueToCode(a,"VALUE",Blockly.Python.ORDER_MEMBER)||"''";switch(b){case "FIRST":return[c+"[0]",Blockly.Python.ORDER_MEMBER];case "LAST":return[c+"[-1]",Blockly.Python.ORDER_MEMBER];case "FROM_START":return a=Blockly.Python.getAdjustedInt(a,"AT"),[c+"["+a+"]",Blockly.Python.ORDER_MEMBER];case "FROM_END":return a=Blockly.Python.getAdjustedInt(a,"AT",1,!0),[c+"["+a+"]",Blockly.Python.ORDER_MEMBER];case "RANDOM":return Blockly.Python.definitions_.import_random=
"import random",[Blockly.Python.provideFunction_("text_random_letter",["def "+Blockly.Python.FUNCTION_NAME_PLACEHOLDER_+"(text):"," x = int(random.random() * len(text))"," return text[x];"])+"("+c+")",Blockly.Python.ORDER_FUNCTION_CALL]}throw Error("Unhandled option (text_charAt).");};
Blockly.Python.text_getSubstring=function(a){var b=a.getFieldValue("WHERE1"),c=a.getFieldValue("WHERE2"),d=Blockly.Python.valueToCode(a,"STRING",Blockly.Python.ORDER_MEMBER)||"''";switch(b){case "FROM_START":b=Blockly.Python.getAdjustedInt(a,"AT1");"0"==b&&(b="");break;case "FROM_END":b=Blockly.Python.getAdjustedInt(a,"AT1",1,!0);break;case "FIRST":b="";break;default:throw Error("Unhandled option (text_getSubstring)");}switch(c){case "FROM_START":a=Blockly.Python.getAdjustedInt(a,"AT2",1);break;case "FROM_END":a=
Blockly.Python.getAdjustedInt(a,"AT2",0,!0);Blockly.isNumber(String(a))?"0"==a&&(a=""):(Blockly.Python.definitions_.import_sys="import sys",a+=" or sys.maxsize");break;case "LAST":a="";break;default:throw Error("Unhandled option (text_getSubstring)");}return[d+"["+b+" : "+a+"]",Blockly.Python.ORDER_MEMBER]};
Blockly.Python.text_changeCase=function(a){var b={UPPERCASE:".upper()",LOWERCASE:".lower()",TITLECASE:".title()"}[a.getFieldValue("CASE")];return[(Blockly.Python.valueToCode(a,"TEXT",Blockly.Python.ORDER_MEMBER)||"''")+b,Blockly.Python.ORDER_FUNCTION_CALL]};Blockly.Python.text_trim=function(a){var b={LEFT:".lstrip()",RIGHT:".rstrip()",BOTH:".strip()"}[a.getFieldValue("MODE")];return[(Blockly.Python.valueToCode(a,"TEXT",Blockly.Python.ORDER_MEMBER)||"''")+b,Blockly.Python.ORDER_FUNCTION_CALL]};
Blockly.Python.text_print=function(a){return"print("+(Blockly.Python.valueToCode(a,"TEXT",Blockly.Python.ORDER_NONE)||"''")+")\n"};
Blockly.Python.text_prompt_ext=function(a){var b=Blockly.Python.provideFunction_("text_prompt",["def "+Blockly.Python.FUNCTION_NAME_PLACEHOLDER_+"(msg):"," try:"," return raw_input(msg)"," except NameError:"," return input(msg)"]),c=a.getField("TEXT")?Blockly.Python.quote_(a.getFieldValue("TEXT")):Blockly.Python.valueToCode(a,"TEXT",Blockly.Python.ORDER_NONE)||"''";b=b+"("+c+")";"NUMBER"==a.getFieldValue("TYPE")&&(b="float("+b+")");return[b,Blockly.Python.ORDER_FUNCTION_CALL]};
Blockly.Python.text_prompt=Blockly.Python.text_prompt_ext;Blockly.Python.text_count=function(a){var b=Blockly.Python.valueToCode(a,"TEXT",Blockly.Python.ORDER_MEMBER)||"''";a=Blockly.Python.valueToCode(a,"SUB",Blockly.Python.ORDER_NONE)||"''";return[b+".count("+a+")",Blockly.Python.ORDER_MEMBER]};
Blockly.Python.text_replace=function(a){var b=Blockly.Python.valueToCode(a,"TEXT",Blockly.Python.ORDER_MEMBER)||"''",c=Blockly.Python.valueToCode(a,"FROM",Blockly.Python.ORDER_NONE)||"''";a=Blockly.Python.valueToCode(a,"TO",Blockly.Python.ORDER_NONE)||"''";return[b+".replace("+c+", "+a+")",Blockly.Python.ORDER_MEMBER]};Blockly.Python.text_reverse=function(a){return[(Blockly.Python.valueToCode(a,"TEXT",Blockly.Python.ORDER_MEMBER)||"''")+"[::-1]",Blockly.Python.ORDER_MEMBER]};Blockly.Python.variables={};Blockly.Python.variables_get=function(a){return[Blockly.Python.variableDB_.getName(a.getFieldValue("VAR"),Blockly.VARIABLE_CATEGORY_NAME),Blockly.Python.ORDER_ATOMIC]};Blockly.Python.variables_set=function(a){var b=Blockly.Python.valueToCode(a,"VALUE",Blockly.Python.ORDER_NONE)||"0";return Blockly.Python.variableDB_.getName(a.getFieldValue("VAR"),Blockly.VARIABLE_CATEGORY_NAME)+" = "+b+"\n"};Blockly.Python.variablesDynamic={};Blockly.Python.variables_get_dynamic=Blockly.Python.variables_get;Blockly.Python.variables_set_dynamic=Blockly.Python.variables_set;
return Blockly.Python;
}));
//# sourceMappingURL=python_compressed.js.map

File diff suppressed because one or more lines are too long

602
clover_blocks/www/blocks.js Normal file
View File

@@ -0,0 +1,602 @@
/*
* 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.
*/
const COLOR_FLIGHT = 293;
const COLOR_STATE = 36;
const COLOR_LED = 143;
const COLOR_GPIO = 200;
const DOCS_URL = 'https://clover.coex.tech/en/blocks.html';
var frameIds = [["body", "BODY"], ["markers map", "ARUCO_MAP"], ["marker", "ARUCO"], ["last navigate target", "NAVIGATE_TARGET"], ["map", "MAP"]];
function considerFrameId(e) {
if (!(e instanceof Blockly.Events.Change || e instanceof Blockly.Events.Create)) return;
var frameId = this.getFieldValue('FRAME_ID');
// set appropriate coordinates labels
if (this.getInput('X')) { // block has x-y-z fields
if (frameId == 'BODY' || frameId == 'NAVIGATE_TARGET' || frameId == 'BASE_LINK') {
this.getInput('X').fieldRow[0].setValue('forward');
this.getInput('Y').fieldRow[0].setValue('left');
this.getInput('Z').fieldRow[0].setValue('up');
} else {
this.getInput('X').fieldRow[0].setValue('x');
this.getInput('Y').fieldRow[0].setValue('y');
this.getInput('Z').fieldRow[0].setValue('z');
}
}
if (this.getInput('ID')) { // block has marker id field
this.getInput('ID').setVisible(frameId == 'ARUCO'); // toggle id field
}
this.render();
}
function updateSetpointBlock(e) {
if (!(e instanceof Blockly.Events.Change || e instanceof Blockly.Events.Create)) return;
considerFrameId.apply(this, arguments);
var type = this.getFieldValue('TYPE');
var velocity = type == 'VELOCITY';
var attitude = type == 'ATTITUDE' || type == 'RATES';
this.getInput('VX').setVisible(velocity);
this.getInput('VY').setVisible(velocity);
this.getInput('VZ').setVisible(velocity);
this.getInput('YAW').setVisible(attitude);
this.getInput('PITCH').setVisible(attitude);
this.getInput('ROLL').setVisible(attitude);
this.getInput('THRUST').setVisible(attitude);
this.getInput('RELATIVE_TO').setVisible(type != 'RATES');
if (type == 'RATES') {
this.getInput('ID').setVisible(false);
}
this.render();
}
Blockly.Blocks['navigate'] = {
init: function () {
this.appendDummyInput()
.appendField("navigate to point");
this.appendValueInput("X")
.setCheck("Number")
.appendField("forward");
this.appendValueInput("Y")
.setCheck("Number")
.appendField("left");
this.appendValueInput("Z")
.setCheck("Number")
.appendField("up");
this.appendDummyInput()
.appendField("relative to")
.appendField(new Blockly.FieldDropdown(frameIds), "FRAME_ID");
this.appendValueInput("ID")
.setCheck("Number")
.appendField("with ID")
.setVisible(false)
this.appendValueInput("SPEED")
.setCheck("Number")
.appendField("with speed");
this.appendDummyInput()
.appendField("wait")
.appendField(new Blockly.FieldCheckbox("TRUE"), "WAIT");
this.setInputsInline(false);
this.setPreviousStatement(true, null);
this.setNextStatement(true, null);
this.setColour(COLOR_FLIGHT);
this.setTooltip("Navigate to the specified point, coordinates are in meters.");
this.setHelpUrl(DOCS_URL + '#' + this.type);
this.setOnChange(considerFrameId);
}
};
Blockly.Blocks['set_velocity'] = {
init: function () {
this.appendDummyInput()
.appendField("set velocity");
this.appendValueInput("X")
.setCheck("Number")
.appendField("forward");
this.appendValueInput("Y")
.setCheck("Number")
.appendField("left");
this.appendValueInput("Z")
.setCheck("Number")
.appendField("up");
this.appendDummyInput()
.appendField("relative to")
.appendField(new Blockly.FieldDropdown(frameIds), "FRAME_ID");
this.appendValueInput("ID")
.setCheck("Number")
.appendField("with ID")
.setVisible(false)
this.setInputsInline(false);
this.setPreviousStatement(true, null);
this.setNextStatement(true, null);
this.setColour(COLOR_FLIGHT);
this.setTooltip("Set the drone velocity in meters per second (cancels navigation requests).");
this.setHelpUrl(DOCS_URL + '#' + this.type);
this.setOnChange(considerFrameId);
}
};
Blockly.Blocks['setpoint'] = {
init: function () {
this.appendDummyInput()
.appendField("set");
this.appendDummyInput()
.appendField(new Blockly.FieldDropdown([["velocity", "VELOCITY"], ["attitude", "ATTITUDE"], ["angular rates", "RATES"]]), "TYPE");
this.appendValueInput("VX")
.setCheck("Number")
.appendField("vx");
this.appendValueInput("VY")
.setCheck("Number")
.appendField("vy");
this.appendValueInput("VZ")
.setCheck("Number")
.appendField("vz");
this.appendValueInput("PITCH")
.setCheck("Number")
.appendField("pitch")
.setVisible(false);
this.appendValueInput("ROLL")
.setCheck("Number")
.appendField("roll")
.setVisible(false);
this.appendValueInput("YAW")
.setCheck("Number")
.appendField("yaw")
.setVisible(false);
this.appendValueInput("THRUST")
.setCheck("Number")
.appendField("thrust")
.setVisible(false);
this.appendDummyInput('RELATIVE_TO')
.appendField("relative to")
.appendField(new Blockly.FieldDropdown(frameIds), "FRAME_ID");
this.appendValueInput("ID")
.setCheck("Number")
.appendField("with ID")
.setVisible(false);
this.setInputsInline(false);
this.setPreviousStatement(true, null);
this.setNextStatement(true, null);
this.setColour(COLOR_FLIGHT);
this.setTooltip("Set the drone's setpoints of different types (cancels navigation requests).");
this.setHelpUrl(DOCS_URL + '#' + this.type);
this.setOnChange(updateSetpointBlock);
}
};
Blockly.Blocks['rangefinder_distance'] = {
init: function () {
this.appendDummyInput()
.appendField("current rangefinder distance");
this.setOutput(true, "Number");
this.setColour(COLOR_STATE);
this.setTooltip("");
this.setHelpUrl(DOCS_URL + '#' + this.type);
}
};
Blockly.Blocks['get_position'] = {
init: function () {
this.appendDummyInput()
.appendField("current")
.appendField(new Blockly.FieldDropdown([["x", "X"], ["y", "Y"], ["z", "Z"], ["vx", "VX"], ["vy", "VY"], ["vz", "VZ"]]), "FIELD")
.appendField("relative to")
.appendField(new Blockly.FieldDropdown(frameIds), "FRAME_ID");
this.appendValueInput("ID")
.setCheck("Number")
.appendField("with ID")
.setVisible(false)
this.setOutput(true, "Number");
this.setColour(COLOR_STATE);
this.setTooltip("Returns current position or velocity in meters or meters per second.");
this.setHelpUrl(DOCS_URL + '#' + this.type);
this.setOnChange(considerFrameId);
}
};
Blockly.Blocks['get_yaw'] = {
init: function () {
this.appendDummyInput()
.appendField("current yaw relative to")
.appendField(new Blockly.FieldDropdown(frameIds), "FRAME_ID");
this.appendValueInput("ID")
.setCheck("Number")
.appendField("with ID")
.setVisible(false)
this.setOutput(true, "Number");
this.setColour(COLOR_STATE);
this.setTooltip("Returns current yaw in degree (not radian).");
this.setHelpUrl(DOCS_URL + '#' + this.type);
this.setOnChange(considerFrameId);
}
};
Blockly.Blocks['get_attitude'] = {
init: function () {
this.appendDummyInput()
.appendField("current")
.appendField(new Blockly.FieldDropdown([["pitch", "PITCH"], ["roll", "ROLL"], ["pitch rate", "PITCH_RATE"], ["roll rate", "ROLL_RATE"], ["yaw rate", "YAW_RATE"]]), "FIELD");
this.setOutput(true, "Number");
this.setColour(COLOR_STATE);
this.setTooltip("Returns current orientation or angle rates in degree or degree per second (not radian).");
this.setHelpUrl(DOCS_URL + '#' + this.type);
}
};
Blockly.Blocks['voltage'] = {
init: function () {
this.appendDummyInput()
.appendField("current")
.appendField(new Blockly.FieldDropdown([["total", "VOLTAGE"], ["cell", "CELL_VOLTAGE"]]), "TYPE")
.appendField("voltage");
this.setOutput(true, "Number");
this.setColour(COLOR_STATE);
this.setTooltip("Returns current battery voltage in volts.");
this.setHelpUrl(DOCS_URL + '#' + this.type);
}
};
Blockly.Blocks['armed'] = {
init: function () {
this.appendDummyInput()
.appendField("armed?");
this.setOutput(true, "Boolean");
this.setColour(COLOR_STATE);
this.setTooltip("Returns if the drone armed.");
this.setHelpUrl(DOCS_URL + '#' + this.type);
}
};
Blockly.Blocks['mode'] = {
init: function () {
this.appendDummyInput()
.appendField("current flight mode");
this.setOutput(true, "String");
this.setColour(COLOR_STATE);
this.setTooltip("");
this.setHelpUrl(DOCS_URL + '#' + this.type);
}
};
Blockly.Blocks['wait_arrival'] = {
init: function () {
this.appendDummyInput()
.appendField("wait arrival");
this.setPreviousStatement(true, null);
this.setNextStatement(true, null);
this.setColour(COLOR_FLIGHT);
this.setTooltip("Wait until the drone arrives to the navigation target.");
this.setHelpUrl(DOCS_URL + '#' + this.type);
}
};
Blockly.Blocks['get_time'] = {
init: function () {
this.appendDummyInput()
.appendField("time");
this.setOutput(true, "Number");
this.setColour(COLOR_STATE);
this.setTooltip("Returns current timestamp in seconds.");
this.setHelpUrl(DOCS_URL + '#' + this.type);
}
};
Blockly.Blocks['arrived'] = {
init: function () {
this.appendDummyInput()
.appendField("arrived?");
this.setOutput(true, "Boolean");
this.setColour(COLOR_STATE);
this.setTooltip("Returns if the drone arrived to the navigation target.");
this.setHelpUrl(DOCS_URL + '#' + this.type);
}
};
Blockly.Blocks['set_led'] = {
init: function () {
this.appendDummyInput()
.appendField("set LED");
this.appendValueInput("INDEX")
.setCheck("Number");
this.appendValueInput("COLOR")
.setCheck("Colour")
.appendField("to color");
this.setInputsInline(true);
this.setPreviousStatement(true, null);
this.setNextStatement(true, null);
this.setColour(COLOR_LED);
this.setTooltip("Set an individual LED to specified color.");
this.setHelpUrl(DOCS_URL + '#' + this.type);
}
};
Blockly.Blocks['set_effect'] = {
init: function () {
this.appendDummyInput()
.appendField("set LED effect")
.appendField(new Blockly.FieldDropdown([["fill", "FILL"], ["blink", "BLINK"], ["fast blink", "BLINK_FAST"], ["fade", "FADE"], ["wipe", "WIPE"], ["flash", "FLASH"], ["rainbow", "RAINBOW"], ["rainbow fill", "RAINBOW_FILL"]]), "EFFECT");
this.appendValueInput("COLOR")
.setCheck("Colour")
.appendField("with color");
this.setInputsInline(true);
this.setPreviousStatement(true, null);
this.setNextStatement(true, null);
this.setColour(COLOR_LED);
this.setTooltip("Set desired LED strip effect.");
this.setHelpUrl(DOCS_URL + '#' + this.type);
this.setOnChange(function(e) {
if (!(e instanceof Blockly.Events.Change || e instanceof Blockly.Events.Create)) return;
// Hide color field on some effects
var effect = this.getFieldValue('EFFECT');
var hideColor = effect == 'RAINBOW' || effect == 'RAINBOW_FILL';
this.inputList[1].setVisible(!hideColor);
this.render();
});
}
};
Blockly.Blocks['led_count'] = {
init: function () {
this.appendDummyInput()
.appendField("LED count");
this.setOutput(true, "Number");
this.setColour(COLOR_LED);
this.setTooltip("Returns the number of LEDs (configured in led.launch).");
this.setHelpUrl(DOCS_URL + '#' + this.type);
}
};
Blockly.Blocks['take_off'] = {
init: function () {
this.appendValueInput("ALT")
.setCheck("Number")
.appendField("take off to");
this.appendDummyInput()
.appendField("wait")
.appendField(new Blockly.FieldCheckbox("TRUE"), "WAIT");
this.setPreviousStatement(true, null);
this.setNextStatement(true, null);
this.setColour(COLOR_FLIGHT);
this.setTooltip("Take off on desired altitude in meters");
this.setHelpUrl(DOCS_URL + '#' + this.type);
}
};
Blockly.Blocks['land'] = {
init: function () {
this.appendDummyInput()
.appendField("land");
this.appendDummyInput()
.appendField("wait")
.appendField(new Blockly.FieldCheckbox("TRUE"), "WAIT");
this.setInputsInline(true);
this.setPreviousStatement(true, null);
this.setNextStatement(true, null);
this.setColour(COLOR_FLIGHT);
this.setTooltip("");
this.setHelpUrl(DOCS_URL + '#' + this.type);
}
};
Blockly.Blocks['global_position'] = {
init: function () {
this.appendDummyInput()
.appendField("current")
.appendField(new Blockly.FieldDropdown([["latitude", "LATITUDE"], ["longitude", "LONGITUDE"], ["altitude", "ALTITUDE"]]), "NAME");
this.setOutput(true, "Number");
this.setColour(230);
this.setTooltip("");
this.setHelpUrl(DOCS_URL + '#' + this.type);
}
};
Blockly.Blocks['on_take_off'] = {
init: function () {
this.appendStatementInput("TAKE_OFF")
.setCheck(null)
.appendField("When took off");
this.setColour(230);
this.setTooltip("");
this.setHelpUrl(DOCS_URL + '#' + this.type);
}
};
Blockly.Blocks['on_landing'] = {
init: function () {
this.appendStatementInput("LAND")
.setCheck(null)
.appendField("When landed");
this.setColour(230);
this.setTooltip("");
this.setHelpUrl(DOCS_URL + '#' + this.type);
}
};
Blockly.Blocks['on_armed'] = {
init: function () {
this.appendStatementInput("ARMED")
.setCheck(null)
.appendField("when armed");
this.setColour(230);
this.setTooltip("");
this.setHelpUrl(DOCS_URL + '#' + this.type);
}
};
Blockly.FieldAngle.WRAP = 180;
Blockly.FieldAngle.ROUND = 10;
Blockly.Blocks['angle'] = {
init: function () {
this.appendDummyInput()
.appendField(new Blockly.FieldAngle(90), "ANGLE");
this.setOutput(true, "Number");
this.setColour(230);
this.setTooltip("");
this.setHelpUrl(DOCS_URL + '#' + this.type);
}
};
Blockly.Blocks['set_yaw'] = {
init: function () {
this.appendValueInput("YAW")
.setCheck("Number")
.appendField("rotate by");
this.appendDummyInput()
.appendField("relative to")
.appendField(new Blockly.FieldDropdown([["body", "body"], ["markers map", "aruco_map"], ["last navigate target", "navigate_target"]]), "FRAME_ID");
this.appendDummyInput()
.appendField("wait")
.appendField(new Blockly.FieldCheckbox("TRUE"), "WAIT");
this.setInputsInline(true);
this.setPreviousStatement(true, null);
this.setNextStatement(true, null);
this.setColour(COLOR_FLIGHT);
this.setTooltip("Rotate the drone to the specified angle in degree (not radian).");
this.setHelpUrl(DOCS_URL + '#' + this.type);
}
};
Blockly.Blocks['distance'] = {
init: function () {
this.appendDummyInput()
.appendField("distance to point");
this.appendValueInput("X")
.setCheck("Number")
.appendField("x");
this.appendValueInput("Y")
.setCheck("Number")
.appendField("y");
this.appendValueInput("Z")
.setCheck("Number")
.appendField("z");
this.appendDummyInput()
.appendField("relative to")
.appendField(new Blockly.FieldDropdown([["markers map", "ARUCO_MAP"], ["marker", "ARUCO"], ["last navigate target", "NAVIGATE_TARGET"]]), "FRAME_ID");
this.appendValueInput("ID")
.setCheck("Number")
.appendField("with ID")
.setVisible(false);
this.setInputsInline(false);
this.setOutput(true, "Number");
this.setColour(COLOR_STATE);
this.setTooltip("Returns the distance to the given point in meters.");
this.setHelpUrl(DOCS_URL + '#' + this.type);
this.setOnChange(considerFrameId);
}
};
Blockly.Blocks['wait'] = {
init: function () {
this.appendDummyInput()
.appendField("wait");
this.appendValueInput("TIME")
.setCheck("Number");
this.appendDummyInput()
.appendField("seconds");
this.setPreviousStatement(true, null);
this.setNextStatement(true, null);
this.setColour(COLOR_FLIGHT);
this.setTooltip("");
this.setHelpUrl(DOCS_URL + '#' + this.type);
}
};
var keys = [['up', 'UP'], ['down', 'DOWN'], ['left', 'LEFT'], ['right', 'RIGHT'], ['space', 'SPACE']];
Blockly.Blocks['key_pressed'] = {
init: function () {
this.appendDummyInput()
.appendField("key")
.appendField(new Blockly.FieldDropdown(keys, "NAME"))
.appendField("pressed");
this.appendStatementInput("PRESSED")
.setCheck(null);
this.setPreviousStatement(true, null);
this.setNextStatement(true, null);
this.setColour(230);
this.setTooltip("");
this.setHelpUrl(DOCS_URL + '#' + this.type);
}
};
Blockly.Blocks['gpio_read'] = {
init: function () {
this.appendValueInput("PIN")
.setCheck("Number")
.appendField("read GPIO pin");
this.setOutput(true, "Boolean");
this.setColour(COLOR_GPIO);
this.setTooltip("Returns if there is voltage on a GPIO pin.");
this.setHelpUrl(DOCS_URL + '#GPIO');
}
};
Blockly.Blocks['gpio_write'] = {
init: function () {
this.appendValueInput("PIN")
.setCheck("Number")
.appendField("set GPIO pin");
this.appendValueInput("LEVEL")
.setCheck("Boolean")
.appendField("to");
this.setInputsInline(true);
this.setColour(COLOR_GPIO);
this.setPreviousStatement(true, null);
this.setNextStatement(true, null);
this.setTooltip("Set GPIO pin level.");
this.setHelpUrl(DOCS_URL + '#GPIO');
}
};
Blockly.Blocks['set_servo'] = {
init: function () {
this.appendValueInput("PIN")
.setCheck("Number")
.appendField("set GPIO pin");
this.appendValueInput("PWM")
.setCheck("Number")
.appendField("to PWM");
this.setInputsInline(true);
this.setColour(COLOR_GPIO);
this.setPreviousStatement(true, null);
this.setNextStatement(true, null);
this.setTooltip("Set PWM on a GPIO pin to control servo. PWM is specified in range of 5002500 μs.");
this.setHelpUrl(DOCS_URL + '#GPIO');
}
};
Blockly.Blocks['set_duty_cycle'] = {
init: function () {
this.appendValueInput("PIN")
.setCheck("Number")
.appendField("set GPIO pin");
this.appendValueInput("DUTY_CYCLE")
.setCheck("Number")
.appendField("to duty cycle");
this.setInputsInline(true);
this.setColour(COLOR_GPIO);
this.setPreviousStatement(true, null);
this.setNextStatement(true, null);
this.setTooltip("Set PWM duty cycle on a GPIO pin (better to control LEDs, etc). Duty cycle is set in range of 01.");
this.setHelpUrl(DOCS_URL + '#GPIO');
}
};

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,83 @@
/*
Monokai Sublime style. Derived from Monokai by noformnocontent http://nn.mit-license.org/
*/
.hljs {
display: block;
overflow-x: auto;
padding: 0.5em;
background: #23241f;
}
.hljs,
.hljs-tag,
.hljs-subst {
color: #f8f8f2;
}
.hljs-strong,
.hljs-emphasis {
color: #a8a8a2;
}
.hljs-bullet,
.hljs-quote,
.hljs-number,
.hljs-regexp,
.hljs-literal,
.hljs-link {
color: #ae81ff;
}
.hljs-code,
.hljs-title,
.hljs-section,
.hljs-selector-class {
color: #a6e22e;
}
.hljs-strong {
font-weight: bold;
}
.hljs-emphasis {
font-style: italic;
}
.hljs-keyword,
.hljs-selector-tag,
.hljs-name,
.hljs-attr {
color: #f92672;
}
.hljs-symbol,
.hljs-attribute {
color: #66d9ef;
}
.hljs-params,
.hljs-class .hljs-title {
color: #f8f8f2;
}
.hljs-string,
.hljs-type,
.hljs-built_in,
.hljs-builtin-name,
.hljs-selector-id,
.hljs-selector-attr,
.hljs-selector-pseudo,
.hljs-addition,
.hljs-variable,
.hljs-template-variable {
color: #e6db74;
}
.hljs-comment,
.hljs-deletion,
.hljs-meta {
color: #75715e;
}

View File

@@ -0,0 +1,488 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Clover Blocks</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" href="main.css">
<link rel="icon" href="data:,">
<link rel="stylesheet" href="highlight/styles/monokai-sublime.css">
<script src="highlight/highlight.pack.js"></script>
<script src="roslib.js"></script>
<script src="blockly/blockly_compressed.js"></script>
<script src="blockly/python_compressed.js"></script>
<script src="blockly/blocks_compressed.js"></script>
<script src="blockly/msg/js/en.js"></script>
<script type="module" src="main.js"></script>
</head>
<body data-tab="blocks">
<ul id="tabs">
<li class="selected" data-tab="blocks">Blocks</li>
<li data-tab="python">Python</li>
</ul>
<div id="tools">
<div class="disconnected">Disconnected</div>
<div class="backend-fail">Failed to contact with <code>clover_blocks</code> node, see <a href="../clover/docs/en/blocks.html#configuration" target="_blank">configuration</a> documentation.</div>
<div id="running">Running...</div>
<select id="program-name" title="Files are saved in <package_path>/programs/.">
<option value="" disabled selected>&lt;Unsaved&gt;</option>
<option value="@clear">Clear...</option>
<option style="font-style: italic;" value="@save">Save... (Ctrl+S)</option>
<optgroup label="User" data-type="user"></optgroup>
<optgroup label="Examples" data-type="example"></optgroup>
</select>
<button onclick="runProgram()" disabled id="run" disabled title="Run the program">Run</button>
<button onclick="stopProgram()" title="Stop the program">Stop</button>
<button onclick="land()" title="Stop the program and land">Land</button>
</div>
<div id="notifications"></div>
<div id="blockly" class="content"></div>
<pre id="python" class="content" contenteditable oncut="return false" onpaste="return false" onkeydown="if(!event.metaKey) return false" spellcheck="false"></pre>
<xml xmlns="https://developers.google.com/blockly/xml" id="toolbox" style="display: none">
<category name="Flight" colour="#870b99">
<block type="take_off">
<value name="ALT"><shadow type="math_number"><field name="NUM">1</field></shadow></value>
</block>
<block type="navigate">
<value name="X"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
<value name="Y"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
<value name="Z"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
<value name="SPEED"><shadow type="math_number"><field name="NUM">0.5</field></shadow></value>
<value name="ID"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
</block>
<block type="land"></block>
<block type="set_yaw">
<value name="YAW"><shadow type="angle"></shadow></value>
</block>
<block type="wait">
<value name="TIME"><shadow type="math_number"><field name="NUM">3</field></shadow></value>
</block>
<block type="wait_arrival"></block>
<block type="setpoint">
<value name="VX"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
<value name="VY"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
<value name="VZ"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
<value name="PITCH"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
<value name="ROLL"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
<value name="YAW"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
<value name="THRUST"><shadow type="math_number"><field name="NUM">0.5</field></shadow></value>
<value name="ID"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
</block>
<!-- <block type="key_pressed"></block> -->
</category>
<category name="State" colour="#ff9a00">
<block type="get_position">
<field name="FRAME_ID">ARUCO_MAP</field>
<value name="ID"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
</block>
<block type="get_yaw">
<field name="FRAME_ID">ARUCO_MAP</field>
<value name="ID"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
</block>
<block type="get_attitude"></block>
<block type="distance">
<value name="X"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
<value name="Y"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
<value name="Z"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
<value name="ID"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
</block>
<block type="get_time"></block>
<block type="arrived"></block>
<block type="rangefinder_distance"></block>
<block type="mode"></block>
<block type="armed"></block>
<block type="voltage"></block>
</category>
<category name="LED" colour="#02d754">
<block type="set_effect">
<value name="COLOR"><shadow type="colour_picker"></shadow></value>
</block>
<block type="set_led">
<value name="INDEX"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
<value name="COLOR"><shadow type="colour_picker"></shadow></value>
</block>
<block type="led_count"></block>
</category>
<category name="GPIO" colour="#5b97cc">
<block type="gpio_read">
<value name="PIN"><shadow type="math_number"><field name="NUM">1</field></shadow></value>
</block>
<block type="gpio_write">
<value name="PIN"><shadow type="math_number"><field name="NUM">1</field></shadow></value>
<value name="LEVEL"><shadow type="logic_boolean"></shadow></value>
</block>
<block type="set_servo">
<value name="PIN"><shadow type="math_number"><field name="NUM">1</field></shadow></value>
<value name="PWM"><shadow type="math_number"><field name="NUM">1500</field></shadow></value>
</block>
<block type="set_duty_cycle">
<value name="PIN"><shadow type="math_number"><field name="NUM">1</field></shadow></value>
<value name="DUTY_CYCLE"><shadow type="math_number"><field name="NUM">0.5</field></shadow></value>
</block>
</category>
<sep></sep>
<category name="Logic" colour="#5b80a5">
<block type="controls_if"></block>
<block type="logic_compare">
<field name="OP">EQ</field>
</block>
<block type="logic_operation">
<field name="OP">AND</field>
</block>
<block type="logic_negate"></block>
<block type="logic_boolean">
<field name="BOOL">TRUE</field>
</block>
<block type="logic_null"></block>
<block type="logic_ternary"></block>
</category>
<category name="Loops" colour="#5ba55b">
<block type="controls_repeat_ext">
<value name="TIMES">
<shadow type="math_number">
<field name="NUM">10</field>
</shadow>
</value>
</block>
<block type="controls_whileUntil">
<field name="MODE">WHILE</field>
</block>
<block type="controls_for">
<field name="VAR" id="SMJ.7:a)p9(!mF0x)WK~">i</field>
<value name="FROM">
<shadow type="math_number">
<field name="NUM">1</field>
</shadow>
</value>
<value name="TO">
<shadow type="math_number">
<field name="NUM">10</field>
</shadow>
</value>
<value name="BY">
<shadow type="math_number">
<field name="NUM">1</field>
</shadow>
</value>
</block>
<block type="controls_forEach">
<field name="VAR" id="#hg!+5qncbZDpf;c6Qiw">j</field>
</block>
<block type="controls_flow_statements">
<field name="FLOW">BREAK</field>
</block>
</category>
<category name="Math" colour="#5b67a5">
<block type="math_number">
<field name="NUM">0</field>
</block>
<block type="math_arithmetic">
<field name="OP">ADD</field>
<value name="A">
<shadow type="math_number">
<field name="NUM">1</field>
</shadow>
</value>
<value name="B">
<shadow type="math_number">
<field name="NUM">1</field>
</shadow>
</value>
</block>
<block type="math_single">
<field name="OP">ROOT</field>
<value name="NUM">
<shadow type="math_number">
<field name="NUM">9</field>
</shadow>
</value>
</block>
<block type="math_trig">
<field name="OP">SIN</field>
<value name="NUM">
<shadow type="math_number">
<field name="NUM">45</field>
</shadow>
</value>
</block>
<block type="math_constant">
<field name="CONSTANT">PI</field>
</block>
<block type="math_number_property">
<mutation divisor_input="false"></mutation>
<field name="PROPERTY">EVEN</field>
<value name="NUMBER_TO_CHECK">
<shadow type="math_number">
<field name="NUM">0</field>
</shadow>
</value>
</block>
<block type="math_round">
<field name="OP">ROUND</field>
<value name="NUM">
<shadow type="math_number">
<field name="NUM">3.1</field>
</shadow>
</value>
</block>
<block type="math_on_list">
<mutation op="SUM"></mutation>
<field name="OP">SUM</field>
</block>
<block type="math_modulo">
<value name="DIVIDEND">
<shadow type="math_number">
<field name="NUM">64</field>
</shadow>
</value>
<value name="DIVISOR">
<shadow type="math_number">
<field name="NUM">10</field>
</shadow>
</value>
</block>
<block type="math_constrain">
<value name="VALUE">
<shadow type="math_number">
<field name="NUM">50</field>
</shadow>
</value>
<value name="LOW">
<shadow type="math_number">
<field name="NUM">1</field>
</shadow>
</value>
<value name="HIGH">
<shadow type="math_number">
<field name="NUM">100</field>
</shadow>
</value>
</block>
<block type="math_random_int">
<value name="FROM">
<shadow type="math_number">
<field name="NUM">1</field>
</shadow>
</value>
<value name="TO">
<shadow type="math_number">
<field name="NUM">100</field>
</shadow>
</value>
</block>
<block type="math_random_float"></block>
</category>
<category name="Text" colour="#5ba58c">
<block type="text">
<field name="TEXT"></field>
</block>
<block type="text_join">
<mutation items="2"></mutation>
</block>
<block type="text_append">
<field name="VAR" id="SmwB.@#2p5}McHySTA:V">item</field>
<value name="TEXT">
<shadow type="text">
<field name="TEXT"></field>
</shadow>
</value>
</block>
<block type="text_length">
<value name="VALUE">
<shadow type="text">
<field name="TEXT">abc</field>
</shadow>
</value>
</block>
<block type="text_isEmpty">
<value name="VALUE">
<shadow type="text">
<field name="TEXT"></field>
</shadow>
</value>
</block>
<block type="text_indexOf">
<field name="END">FIRST</field>
<value name="VALUE">
<block type="variables_get">
<field name="VAR" id="C^Ay41*aH0(G-jwM$x4w">text</field>
</block>
</value>
<value name="FIND">
<shadow type="text">
<field name="TEXT">abc</field>
</shadow>
</value>
</block>
<block type="text_charAt">
<mutation at="true"></mutation>
<field name="WHERE">FROM_START</field>
<value name="VALUE">
<block type="variables_get">
<field name="VAR" id="C^Ay41*aH0(G-jwM$x4w">text</field>
</block>
</value>
</block>
<block type="text_getSubstring">
<mutation at1="true" at2="true"></mutation>
<field name="WHERE1">FROM_START</field>
<field name="WHERE2">FROM_START</field>
<value name="STRING">
<block type="variables_get">
<field name="VAR" id="C^Ay41*aH0(G-jwM$x4w">text</field>
</block>
</value>
</block>
<block type="text_changeCase">
<field name="CASE">UPPERCASE</field>
<value name="TEXT">
<shadow type="text">
<field name="TEXT">abc</field>
</shadow>
</value>
</block>
<block type="text_trim">
<field name="MODE">BOTH</field>
<value name="TEXT">
<shadow type="text">
<field name="TEXT">abc</field>
</shadow>
</value>
</block>
<block type="text_print">
<value name="TEXT">
<shadow type="text">
<field name="TEXT">abc</field>
</shadow>
</value>
</block>
<block type="text_prompt_ext">
<mutation type="TEXT"></mutation>
<field name="TYPE">TEXT</field>
<value name="TEXT">
<shadow type="text">
<field name="TEXT">abc</field>
</shadow>
</value>
</block>
</category>
<category name="Lists" colour="#745ba5">
<block type="lists_create_with">
<mutation items="0"></mutation>
</block>
<block type="lists_create_with">
<mutation items="3"></mutation>
</block>
<block type="lists_repeat">
<value name="NUM">
<shadow type="math_number">
<field name="NUM">5</field>
</shadow>
</value>
</block>
<block type="lists_length"></block>
<block type="lists_isEmpty"></block>
<block type="lists_indexOf">
<field name="END">FIRST</field>
<value name="VALUE">
<block type="variables_get">
<field name="VAR" id="+/w.~s(u:tRiXNBG5eR#">list</field>
</block>
</value>
</block>
<block type="lists_getIndex">
<mutation statement="false" at="true"></mutation>
<field name="MODE">GET</field>
<field name="WHERE">FROM_START</field>
<value name="VALUE">
<block type="variables_get">
<field name="VAR" id="+/w.~s(u:tRiXNBG5eR#">list</field>
</block>
</value>
</block>
<block type="lists_setIndex">
<mutation at="true"></mutation>
<field name="MODE">SET</field>
<field name="WHERE">FROM_START</field>
<value name="LIST">
<block type="variables_get">
<field name="VAR" id="+/w.~s(u:tRiXNBG5eR#">list</field>
</block>
</value>
</block>
<block type="lists_getSublist">
<mutation at1="true" at2="true"></mutation>
<field name="WHERE1">FROM_START</field>
<field name="WHERE2">FROM_START</field>
<value name="LIST">
<block type="variables_get">
<field name="VAR" id="+/w.~s(u:tRiXNBG5eR#">list</field>
</block>
</value>
</block>
<block type="lists_split">
<mutation mode="SPLIT"></mutation>
<field name="MODE">SPLIT</field>
<value name="DELIM">
<shadow type="text">
<field name="TEXT">,</field>
</shadow>
</value>
</block>
<block type="lists_sort">
<field name="TYPE">NUMERIC</field>
<field name="DIRECTION">1</field>
</block>
</category>
<category name="Colour" colour="#a5745b">
<block type="colour_picker">
<field name="COLOUR">#ff0000</field>
</block>
<block type="colour_random"></block>
<block type="colour_rgb">
<value name="RED">
<shadow type="math_number">
<field name="NUM">100</field>
</shadow>
</value>
<value name="GREEN">
<shadow type="math_number">
<field name="NUM">50</field>
</shadow>
</value>
<value name="BLUE">
<shadow type="math_number">
<field name="NUM">0</field>
</shadow>
</value>
</block>
<block type="colour_blend">
<value name="COLOUR1">
<shadow type="colour_picker">
<field name="COLOUR">#ff0000</field>
</shadow>
</value>
<value name="COLOUR2">
<shadow type="colour_picker">
<field name="COLOUR">#3333ff</field>
</shadow>
</value>
<value name="RATIO">
<shadow type="math_number">
<field name="NUM">0.5</field>
</shadow>
</value>
</block>
</category>
<sep></sep>
<category name="Variables" colour="#a55b80" custom="VARIABLE"></category>
<category name="Functions" colour="#995ba5" custom="PROCEDURE"></category>
</xml>
</body>
</html>

136
clover_blocks/www/main.css Normal file
View File

@@ -0,0 +1,136 @@
body, html {
width: 100%;
height: 100%;
}
body {
padding: 0;
margin: 0;
display: grid;
grid-template-areas:
"tabs tools"
"content content";
grid-template-rows: min-content 1fr;
grid-template-columns: 1fr min-content ;
font-family: sans-serif;
font-size: 16px;
}
#tools, #tabs {
background: #dddddd;
border-bottom: 2px rgb(192, 192, 192) solid;
white-space: nowrap;
}
#tools {
grid-area: tools;
white-space: nowrap;
padding: 10px;
display: flex;
align-items: center;
}
#program-name {
width: 150px;
border: none;
font-size: 14px;
padding: 5px;
outline: none;
background: transparent;
text-overflow: ellipsis;
}
#program-name:focus {
background: white;
}
.changed #program-name {
color: red;
}
.disconnected {
color: red;
margin-right: 20px;
}
.connected .disconnected { display: none; }
.backend-fail {
margin-right: 20px;
display: none;
}
@keyframes blink {
0% { opacity: 1; }
50% { opacity: 0.5; }
100% { opacity: 1; }
}
#running {
color: green;
margin-right: 20px;
animation: 1s blink linear infinite;
display: none;
}
.running #running { display: block; }
#tools button {
font-size: 20px;
margin-left: 10px;
}
#tabs {
grid-area: tabs;
list-style: none;
margin: 0;
padding: 10px;
}
#tabs li {
font-size: 18px;
display: inline-block;
padding: 5px 10px;
cursor: pointer;
}
#tabs li.selected {
background: white;
}
#run {
width: 70px;
}
#notifications {
position: absolute;
right: 10px;
background: rgba(0, 0, 0, 0.7);
color: white;
max-width: 350px;
top: 53px;
/* border: white solid 3px; */
z-index: 1;
padding: 10px;
font-family: monospace;
font-size: 16px;
white-space: pre;
max-height: 400px;
overflow: scroll;
}
#notifications:empty { display: none; }
.content {
display: none !important;
grid-area: content;
}
body[data-tab="blocks"] #blockly { display: block !important; }
body[data-tab="python"] #python { display: block !important; }
#python {
margin: 0px;
padding: 10px;
z-index: 999999; /* overlap some Blockly's popups */
}

325
clover_blocks/www/main.js Normal file
View File

@@ -0,0 +1,325 @@
/*
* 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.
*/
import * as ros from './ros.js';
import './blocks.js';
import {generateCode, generateUserCode} from './python.js';
// Tabs
document.getElementById('tabs').addEventListener('click', function(e) {
var tab = e.target.getAttribute('data-tab');
if (tab) {
for (let elem of e.target.parentElement.querySelectorAll('[data-tab]')) {
elem.classList.remove('selected');
}
e.target.classList.add('selected');
document.body.setAttribute('data-tab', tab);
}
});
var workspace = Blockly.inject('blockly', {
toolbox: document.getElementById('toolbox'),
grid: {
spacing: 25,
length: 3,
colour: '#ccc',
snap: true
},
zoom: { controls: true, wheel: true },
media: 'blockly/media/',
});
function readParams() {
return Promise.all([
ros.readParam('navigate_tolerance', true, 0.2),
ros.readParam('yaw_tolerance', true, 20),
ros.readParam('sleep_time', true, 0.2),
ros.readParam('confirm_run', true, true),
]);
}
var ready = readParams(); // initialization complete promise
var pythonArea = document.getElementById('python');
// update Python code
workspace.addChangeListener(function(e) {
ready.then(function() {
pythonArea.innerHTML = generateUserCode(workspace);
hljs.highlightBlock(pythonArea);
});
});
var running = false;
var runRequest = false;
new ROSLIB.Topic({ ros: ros.ros, name: ros.priv + 'block', messageType: 'std_msgs/String' }).subscribe(function(msg) {
workspace.highlightBlock(msg.data);
runRequest = false;
update();
});
new ROSLIB.Topic({ ros: ros.ros, name: ros.priv + 'running' }).subscribe(function(msg) {
running = msg.data;
runRequest = false;
if (!running) {
workspace.highlightBlock('');
}
update();
});
var notifElem = document.getElementById('notifications');
function z(n) { return (n < 10 ? '0' : '') + n; } // add leading zero
new ROSLIB.Topic({ ros: ros.ros, name: ros.priv + 'print', messageType: 'std_msgs/String'}).subscribe(function(msg) {
var d = new Date(); // TODO: use rosgraph_msgs/Log?
var timestamp = `${z(d.getHours())}:${z(d.getMinutes())}:${z(d.getSeconds())}`;
notifElem.innerHTML += `${timestamp}: ${msg.data}\n`;
notifElem.scrollTop = notifElem.scrollHeight;
});
new ROSLIB.Topic({ ros: ros.ros, name: ros.priv + 'error', messageType: 'std_msgs/String'}).subscribe(function(msg) {
alert('Error: ' + msg.data);
});
var runButton = document.getElementById('run');
function update() {
document.body.classList.toggle('running', running);
runButton.disabled = !ros.ros.isConnected || runRequest || running;
}
var shownPrompts = new Set();
new ROSLIB.Topic({ ros: ros.ros, name: ros.priv + 'prompt', messageType: 'clover_blocks/Prompt'}).subscribe(function(msg) {
if (shownPrompts.has(msg.id)) return;
shownPrompts.add(msg.id);
var response = prompt(msg.message);
new ROSLIB.Topic({
ros: ros.ros,
name: ros.priv + 'input/' + msg.id,
messageType: 'std_msgs/String',
latch: true
}).publish(new ROSLIB.Message({ data: response || '' }));
});
window.stopProgram = function() {
ros.stopService.callService(new ROSLIB.ServiceRequest(), function(res) {
if (!res.success) alert(res.message);
}, err => alert(err))
}
ros.ros.on('connection', update);
ros.ros.on('close', update);
ready.then(() => runButton.disabled = false);
window.runProgram = function() {
if (ros.params.confirm_run && !confirm('Run program?')) return;
runRequest = true;
update();
var code = generateCode(workspace);
console.log(code);
ros.runService.callService(new ROSLIB.ServiceRequest({ code: code } ), function(res) {
if (!res.success) {
runRequest = false;
update();
alert(res.message);
}
}, function(err) {
runRequest = false;
update();
alert(err);
})
}
window.land = function() {
window.stopProgram();
ros.landService.callService(new ROSLIB.ServiceRequest(), function(result) {
}, function(err) {
alert('Unable to land: ' + err);
});
}
function getProgramXml() {
var xmlDom = Blockly.Xml.workspaceToDom(workspace);
return Blockly.Xml.domToPrettyText(xmlDom);
}
function setProgramXml(xml) {
workspace.clear();
if (xml) {
let xmlDom = Blockly.Xml.textToDom(xml);
Blockly.Xml.domToWorkspace(xmlDom, workspace);
}
}
workspace.addChangeListener(function(e) {
localStorage.setItem('xml', getProgramXml());
});
var programSelect = document.querySelector('#program-name');
var userPrograms = programSelect.querySelector('optgroup[data-type=user]');
var examplePrograms = programSelect.querySelector('optgroup[data-type=example]');
var programs = {};
var program = '';
function loadWorkspace() {
var xml = localStorage.getItem('xml');
if (xml) {
setProgramXml(xml);
}
program = localStorage.getItem('program') || '';
}
loadWorkspace();
function loadPrograms() {
ros.loadService.callService(new ROSLIB.ServiceRequest(), function(res) {
if (!res.success) alert(res.message);
for (let i = 0; i < res.names.length; i++) {
let name = res.names[i];
let program = res.programs[i];
let option = document.createElement('option');
option.innerHTML = res.names[i];
if (name.startsWith('examples/')) {
examplePrograms.appendChild(option);
} else {
userPrograms.appendChild(option);
}
programs[name] = program;
}
if (program) {
programSelect.value = program;
}
updateChanged();
}, function(err) {
document.querySelector('.backend-fail').style.display = 'inline';
alert(`Error loading programs list.\n\nHave you enabled clover_blocks in clover.launch?`);
runButton.disabled = true;
})
}
loadPrograms();
function getProgramName() {
if (programSelect.value.startsWith('@')) {
return ''
}
return programSelect.value;
}
function updateChanged() {
var name = program;
document.body.classList.toggle('changed', name in programs && (programs[name].trim() != getProgramXml().trim()));
}
workspace.addChangeListener(function(e) {
if (e instanceof Blockly.Events.Change || e instanceof Blockly.Events.Create || e instanceof Blockly.Events.Delete) {
updateChanged();
}
});
function saveProgram() {
var name = getProgramName();
if (!name) {
name = prompt('Enter new program name:');
if (!name) {
programSelect.value = program;
return;
}
if (!name.endsWith('.xml')) {
name += '.xml';
}
let option = document.createElement('option');
option.innerHTML = name;
userPrograms.appendChild(option);
}
let xml = getProgramXml();
ros.storeService.callService(new ROSLIB.ServiceRequest({
name: name,
program: xml
}), function(result) {
if (!result.success) {
alert(result.message);
return;
}
console.log(result.message);
programSelect.blur();
program = name;
localStorage.setItem('program', name);
programs[name] = xml;
programSelect.value = program;
updateChanged();
}, function(err) {
// TODO: restore previous state correctly
alert('Unable to store: ' + err);
programSelect.blur();
programSelect.value = program;
});
}
window.addEventListener('keydown', function(e) {
if ((e.metaKey || e.ctrlKey) && e.key == 's') { // ctrl+s/cmd+s
e.preventDefault();
if (!document.body.classList.contains('changed')) { // if not changed, ignore
return;
}
saveProgram();
}
});
programSelect.addEventListener('change', function(e) {
if (programSelect.value == '@clear') {
if (!confirm('Clear workspace?')) {
programSelect.value = program;
return;
}
localStorage.removeItem('program');
program = '';
setProgramXml('');
programSelect.value = program;
programSelect.blur();
} else if (programSelect.value == '@save') {
saveProgram();
} else {
// load program
if (program == '' || document.body.classList.contains('changed')) {
if (!confirm('Discard changes?')) {
programSelect.value = program;
return;
}
}
let name = programSelect.value;
let lastProgram = getProgramXml();
programSelect.blur();
try {
setProgramXml(programs[name]);
program = name;
localStorage.setItem('program', name);
} catch(e) {
alert(e);
setProgramXml(lastProgram);
program = ''
programSelect.value = program;
}
updateChanged();
}
});

466
clover_blocks/www/python.js Normal file
View File

@@ -0,0 +1,466 @@
/*
* 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.
*/
import {params} from './ros.js';
// If any new block imports any library, add that library name here.
Blockly.Python.addReservedWords('_b,_print');
Blockly.Python.addReservedWords('rospy,srv,Trigger,get_telemetry,navigate,set_velocity,land');
Blockly.Python.addReservedWords('navigate_wait,land_wait,wait_arrival,wait_yaw,get_distance');
Blockly.Python.addReservedWords('pigpio,pi,Range');
Blockly.Python.addReservedWords('SetLEDEffect,set_effect,led_count,get_led_count');
Blockly.Python.addReservedWords('SetLEDs,LEDState,set_leds');
const IMPORT_SRV = `from clover import srv
from std_srvs.srv import Trigger`;
const NAVIGATE_WAIT = () => `\ndef navigate_wait(x=0, y=0, z=0, speed=0.5, frame_id='body', auto_arm=False):
res = navigate(x=x, y=y, z=z, yaw=float('nan'), speed=speed, frame_id=frame_id, auto_arm=auto_arm)
if not res.success:
raise Exception(res.message)
while not rospy.is_shutdown():
telem = get_telemetry(frame_id='navigate_target')
if math.sqrt(telem.x ** 2 + telem.y ** 2 + telem.z ** 2) < ${params.navigate_tolerance}:
return
rospy.sleep(${params.sleep_time})\n`;
const LAND_WAIT = () => `\ndef land_wait():
land()
while get_telemetry().armed:
rospy.sleep(${params.sleep_time})\n`;
const WAIT_YAW = () => `\ndef wait_yaw():
while not rospy.is_shutdown():
telem = get_telemetry(frame_id='navigate_target')
if abs(telem.yaw) < math.radians(${params.yaw_tolerance}):
return
rospy.sleep(${params.sleep_time})\n`;
const WAIT_ARRIVAL = () => `\ndef wait_arrival():
while not rospy.is_shutdown():
telem = get_telemetry(frame_id='navigate_target')
if math.sqrt(telem.x ** 2 + telem.y ** 2 + telem.z ** 2) < ${params.navigate_tolerance}:
return
rospy.sleep(${params.sleep_time})\n`;
const ARRIVED = () => `\ndef arrived():
telem = get_telemetry(frame_id='navigate_target')
return math.sqrt(telem.x ** 2 + telem.y ** 2 + telem.z ** 2) < ${params.navigate_tolerance}\n`
const GET_DISTANCE = `\ndef get_distance(x, y, z, frame_id):
telem = get_telemetry(frame_id)
return math.sqrt((x - telem.x) ** 2 + (y - telem.y) ** 2 + (z - telem.z) ** 2)\n`;
var rosDefinitions = {};
function generateROSDefinitions() {
// order for ROS definitions is significant, so generate all ROS definitions as one
var code = `rospy.init_node('flight')\n\n`;
if (rosDefinitions.offboard) {
code += `get_telemetry = rospy.ServiceProxy('get_telemetry', srv.GetTelemetry)\n`;
code += `navigate = rospy.ServiceProxy('navigate', srv.Navigate)\n`;
if (rosDefinitions.setVelocity) {
code += `set_velocity = rospy.ServiceProxy('set_velocity', srv.SetVelocity)\n`;
}
if (rosDefinitions.setAttitude) {
code += `set_attitude = rospy.ServiceProxy('set_attitude', srv.SetAttitude)\n`;
}
if (rosDefinitions.setRates) {
code += `set_rates = rospy.ServiceProxy('set_rates', srv.SetRates)\n`;
}
code += `land = rospy.ServiceProxy('land', Trigger)\n`;
}
if (rosDefinitions.setEffect) {
Blockly.Python.definitions_['import_led_effect'] = 'from clover.srv import SetLEDEffect';
code += `set_effect = rospy.ServiceProxy('led/set_effect', SetLEDEffect, persistent=True)\n`;
}
if (rosDefinitions.setLeds) {
Blockly.Python.definitions_['import_set_led'] = 'from led_msgs.srv import SetLEDs\nfrom led_msgs.msg import LEDState';
code += `set_leds = rospy.ServiceProxy('led/set_leds', SetLEDs, persistent=True)\n`;
}
if (rosDefinitions.ledStateArray) {
Blockly.Python.definitions_['import_led_state_array'] = 'from led_msgs.msg import LEDStateArray';
}
if (rosDefinitions.navigateWait) {
Blockly.Python.definitions_['import_math'] = 'import math';
code += NAVIGATE_WAIT();
}
if (rosDefinitions.landWait) {
code += LAND_WAIT();
}
if (rosDefinitions.waitArrival) {
Blockly.Python.definitions_['import_math'] = 'import math';
code += WAIT_ARRIVAL();
}
if (rosDefinitions.arrived) {
Blockly.Python.definitions_['import_math'] = 'import math';
code += ARRIVED();
}
if (rosDefinitions.waitYaw) {
Blockly.Python.definitions_['import_math'] = 'import math';
code += WAIT_YAW();
}
if (rosDefinitions.distance) {
Blockly.Python.definitions_['import_math'] = 'import math';
code += GET_DISTANCE;
}
Blockly.Python.definitions_['ros'] = code;
}
function initNode() {
Blockly.Python.definitions_['import_rospy'] = 'import rospy';
generateROSDefinitions();
}
function simpleOffboard() {
rosDefinitions.offboard = true;
Blockly.Python.definitions_['import_srv'] = IMPORT_SRV;
initNode();
}
// Adjust indentation
Blockly.Python.INDENT = ' ';
export function generateUserCode(workspace) {
rosDefinitions = {};
Blockly.Python.STATEMENT_PREFIX = null;
return Blockly.Python.workspaceToCode(workspace);
}
export function generateCode(workspace) {
rosDefinitions = {};
Blockly.Python.STATEMENT_PREFIX = '_b(%1)\n';
var code = Blockly.Python.workspaceToCode(workspace);
return code;
}
function buildFrameId(block) {
let frame = block.getFieldValue('FRAME_ID').toLowerCase();
let id = Blockly.Python.valueToCode(block, 'ID', Blockly.Python.ORDER_NONE);
if (frame == 'aruco') { // aruco marker frame
if (id.match(/^[0-9]+$/)) { // id is positive integer
return `'${frame}_${id}'`;
} else { // something else...
return `'${frame}_' + str(int(${id}))`;
}
} else {
return `'${frame}'`;
}
}
Blockly.Python.navigate = function(block) {
let x = Blockly.Python.valueToCode(block, 'X', Blockly.Python.ORDER_NONE);
let y = Blockly.Python.valueToCode(block, 'Y', Blockly.Python.ORDER_NONE);
let z = Blockly.Python.valueToCode(block, 'Z', Blockly.Python.ORDER_NONE);
let frameId = buildFrameId(block);
let speed = Blockly.Python.valueToCode(block, 'SPEED', Blockly.Python.ORDER_NONE);
let params = [`x=${x}`, `y=${y}`, `z=${z}`, `frame_id=${frameId}`, `speed=${speed}`];
simpleOffboard();
if (block.getFieldValue('WAIT') == 'TRUE') {
rosDefinitions.navigateWait = true;
simpleOffboard();
return `navigate_wait(${params.join(', ')})\n`;
} else {
if (frameId != 'body') {
params.push(`yaw=float('nan')`);
}
return `navigate(${params.join(', ')})\n`;
}
}
Blockly.Python.set_velocity = function(block) {
let x = Blockly.Python.valueToCode(block, 'X', Blockly.Python.ORDER_NONE);
let y = Blockly.Python.valueToCode(block, 'Y', Blockly.Python.ORDER_NONE);
let z = Blockly.Python.valueToCode(block, 'Z', Blockly.Python.ORDER_NONE);
let frameId = buildFrameId(block);
simpleOffboard();
if (frameId == `'body'`) {
return `set_velocity(vx=${x}, vy=${y}, vz=${z}, frame_id=${frameId})\n`;
} else {
return `set_velocity(vx=${x}, vy=${y}, vz=${z}, yaw=float('nan'), frame_id=${frameId})\n`;
}
}
Blockly.Python.take_off = function(block) {
simpleOffboard();
let z = Blockly.Python.valueToCode(block, 'ALT', Blockly.Python.ORDER_NONE);
if (block.getFieldValue('WAIT') == 'TRUE') {
rosDefinitions.navigateWait = true;
simpleOffboard();
return `navigate_wait(z=${z}, frame_id='body', auto_arm=True)\n`;
} else {
return `navigate(z=${z}, frame_id='body', auto_arm=True)\n`;
}
}
Blockly.Python.land = function(block) {
simpleOffboard();
if (block.getFieldValue('WAIT') == 'TRUE') {
rosDefinitions.landWait = true;
simpleOffboard();
return `land_wait()\n`;
} else {
return 'land()\n';
}
}
Blockly.Python.angle = function(block) {
// return [block.getFieldValue('ANGLE'), Blockly.Python.ORDER_UNARY_SIGN];
Blockly.Python.definitions_['import_math'] = 'import math';
return [`math.radians(${block.getFieldValue('ANGLE')})`, Blockly.Python.ORDER_FUNCTION_CALL];
}
Blockly.Python.set_yaw = function(block) {
simpleOffboard();
let yaw = Blockly.Python.valueToCode(block, 'YAW', Blockly.Python.ORDER_NONE);
let frameId = buildFrameId(block);
let code = `navigate(x=float('nan'), y=float('nan'), z=float('nan'), yaw=${yaw}, frame_id=${frameId})\n`;
if (block.getFieldValue('WAIT') == 'TRUE') {
rosDefinitions.waitYaw = true;
simpleOffboard();
code += 'wait_yaw()\n';
}
return code;
}
Blockly.Python.wait_arrival = function(block) {
rosDefinitions.waitArrival = true;
simpleOffboard();
return 'wait_arrival()\n';
}
Blockly.Python.get_time = function(block) {
initNode();
return ['rospy.get_time()', Blockly.Python.ORDER_FUNCTION_CALL];
}
Blockly.Python.arrived = function(block) {
rosDefinitions.arrived = true;
simpleOffboard();
return ['arrived()', Blockly.Python.ORDER_FUNCTION_CALL];
}
Blockly.Python.wait = function(block) {
initNode();
return `rospy.sleep(${Blockly.Python.valueToCode(block, 'TIME', Blockly.Python.ORDER_NONE)})\n`;
}
Blockly.Python.setpoint = function(block) {
var type = block.getFieldValue('TYPE');
let frameId = buildFrameId(block);
let vx = Blockly.Python.valueToCode(block, 'VX', Blockly.Python.ORDER_NONE);
let vy = Blockly.Python.valueToCode(block, 'VY', Blockly.Python.ORDER_NONE);
let vz = Blockly.Python.valueToCode(block, 'VZ', Blockly.Python.ORDER_NONE);
let yaw = Blockly.Python.valueToCode(block, 'YAW', Blockly.Python.ORDER_NONE);
let pitch = Blockly.Python.valueToCode(block, 'PITCH', Blockly.Python.ORDER_NONE);
let roll = Blockly.Python.valueToCode(block, 'ROLL', Blockly.Python.ORDER_NONE);
let thrust = Blockly.Python.valueToCode(block, 'THRUST', Blockly.Python.ORDER_NONE);
if (type == 'VELOCITY') {
rosDefinitions.setVelocity = true;
simpleOffboard();
return `set_velocity(vx=${vx}, vy=${vy}, vz=${vz}, frame_id=${frameId}, yaw=float('nan'))\n`;
} else if (type == 'ATTITUDE') {
rosDefinitions.setAttitude = true;
simpleOffboard();
return `set_attitude(pitch=${pitch}, roll=${roll}, yaw=${yaw}, thrust=${thrust}, frame_id=${frameId})\n`;
} else if (type == 'RATES') {
rosDefinitions.setRates = true;
simpleOffboard();
return `set_rates(pitch_rate=${pitch}, roll_rate=${roll}, yaw_rate=${yaw}, thrust=${thrust})\n`;
}
}
Blockly.Python.get_position = function(block) {
simpleOffboard();
let frameId = buildFrameId(block);
var code = `get_telemetry(${frameId}).${block.getFieldValue('FIELD').toLowerCase()}`;
return [code, Blockly.Python.ORDER_FUNCTION_CALL];
}
Blockly.Python.get_yaw = function(block) {
simpleOffboard();
Blockly.Python.definitions_['import_math'] = 'import math';
let frameId = buildFrameId(block);
var code = `math.degrees(get_telemetry(${frameId}).yaw)`;
return [code, Blockly.Python.ORDER_FUNCTION_CALL];
}
Blockly.Python.get_attitude = function(block) {
simpleOffboard();
Blockly.Python.definitions_['import_math'] = 'import math';
var code = `math.degrees(get_telemetry().${block.getFieldValue('FIELD').toLowerCase()})`;
return [code, Blockly.Python.ORDER_FUNCTION_CALL];
}
Blockly.Python.distance = function(block) {
rosDefinitions.distance = true;
simpleOffboard();
let x = Blockly.Python.valueToCode(block, 'X', Blockly.Python.ORDER_NONE);
let y = Blockly.Python.valueToCode(block, 'Y', Blockly.Python.ORDER_NONE);
let z = Blockly.Python.valueToCode(block, 'Z', Blockly.Python.ORDER_NONE);
let frameId = buildFrameId(block);
return [`get_distance(${x}, ${y}, ${z}, ${frameId})`, Blockly.Python.ORDER_FUNCTION_CALL]
}
Blockly.Python.rangefinder_distance = function(block) {
initNode();
Blockly.Python.definitions_['import_range'] = 'from sensor_msgs.msg import Range';
return [`rospy.wait_for_message('rangefinder/range', Range).range`, Blockly.Python.ORDER_FUNCTION_CALL]
}
Blockly.Python.mode = function(block) {
simpleOffboard();
return [`get_telemetry().mode`, Blockly.Python.ORDER_FUNCTION_CALL]
}
Blockly.Python.armed = function(block) {
simpleOffboard();
return [`get_telemetry().armed`, Blockly.Python.ORDER_FUNCTION_CALL]
}
Blockly.Python.voltage = function(block) {
simpleOffboard();
var code = `get_telemetry().${block.getFieldValue('TYPE').toLowerCase()}`;
return [code, Blockly.Python.ORDER_FUNCTION_CALL];
}
function parseColor(color) {
return {
r: parseInt(color.substr(2, 2), 16),
g: parseInt(color.substr(4, 2), 16),
b: parseInt(color.substr(6, 2), 16)
}
}
const PARSE_COLOR = `def ${Blockly.Python.FUNCTION_NAME_PLACEHOLDER_}(color):
return {'r': int(color[1:3], 16), 'g': int(color[3:5], 16), 'b': int(color[5:7], 16)}`;
// TODO: weird code with colour_rgb block
Blockly.Python.set_effect = function(block) {
rosDefinitions.setEffect = true;
initNode();
var effect = block.getFieldValue('EFFECT').toLowerCase();
if (effect == 'rainbow' || effect == 'rainbow_fill') {
return `set_effect(effect='${effect}')\n`;
} else {
let colorCode = Blockly.Python.valueToCode(block, 'COLOR', Blockly.Python.ORDER_NONE);
if (/^'(.*)'$/.test(colorCode)) { // is simple string
let color = parseColor(colorCode);
return `set_effect(effect='${effect}', r=${color.r}, g=${color.g}, b=${color.b})\n`;
} else {
let parseColor = Blockly.Python.provideFunction_('parse_color', [PARSE_COLOR]);
return `set_effect(effect='${effect}', **${parseColor}(${colorCode}))\n`;
}
}
}
Blockly.Python.set_led = function(block) {
rosDefinitions.setLeds = true;
initNode();
var index = Blockly.Python.valueToCode(block, 'INDEX', Blockly.Python.ORDER_NONE);
var colorCode = Blockly.Python.valueToCode(block, 'COLOR', Blockly.Python.ORDER_NONE);
if (/^'(.*)'$/.test(colorCode)) { // is simple string
let color = parseColor(colorCode);
return `set_leds([LEDState(index=${index}, r=${color.r}, g=${color.g}, b=${color.b})])\n`;
} else {
let parseColor = Blockly.Python.provideFunction_('parse_color', [PARSE_COLOR]);
return `set_leds([LEDState(index=${index}, **${parseColor}(${colorCode}))])\n`;
}
}
const GET_LED_COUNT = `led_count = None
def get_led_count():
global led_count
if led_count is None:
led_count = len(rospy.wait_for_message('led/state', LEDStateArray, timeout=10).leds)
return led_count\n`;
Blockly.Python.led_count = function(block) {
rosDefinitions.ledStateArray = true;
initNode();
Blockly.Python.definitions_['get_led_count'] = GET_LED_COUNT;
return [`get_led_count()`, Blockly.Python.ORDER_FUNCTION_CALL]
}
function pigpio() {
Blockly.Python.definitions_['import_pigpio'] = 'import pigpio';
Blockly.Python.definitions_['init_pigpio'] = 'pi = pigpio.pi()';
}
const GPIO_READ = `\ndef gpio_read(pin):
pi.set_mode(pin, pigpio.INPUT)
return pi.read(pin)\n`;
const GPIO_WRITE = `\ndef gpio_write(pin, level):
pi.set_mode(pin, pigpio.OUTPUT)
pi.write(pin, level)\n`;
const SET_SERVO = `\ndef set_servo(pin, pwm):
pi.set_mode(pin, pigpio.OUTPUT)
pi.set_servo_pulsewidth(pin, pwm)\n`;
const SET_DUTY_CYCLE = `\ndef set_duty_cycle(pin, duty_cycle):
pi.set_mode(pin, pigpio.OUTPUT)
pi.set_PWM_dutycycle(pin, duty_cycle * 255)\n`;
Blockly.Python.gpio_read = function(block) {
pigpio();
Blockly.Python.definitions_['gpio_read'] = GPIO_READ;
var pin = Blockly.Python.valueToCode(block, 'PIN', Blockly.Python.ORDER_NONE);
return [`gpio_read(${pin})`, Blockly.Python.ORDER_FUNCTION_CALL];
}
Blockly.Python.gpio_write = function(block) {
pigpio();
Blockly.Python.definitions_['gpio_write'] = GPIO_WRITE;
var pin = Blockly.Python.valueToCode(block, 'PIN', Blockly.Python.ORDER_NONE);
var level = Blockly.Python.valueToCode(block, 'LEVEL', Blockly.Python.ORDER_NONE);
return `gpio_write(${pin}, ${level})\n`;
}
Blockly.Python.set_servo = function(block) {
pigpio();
Blockly.Python.definitions_['set_servo'] = SET_SERVO;
var pin = Blockly.Python.valueToCode(block, 'PIN', Blockly.Python.ORDER_NONE);
var pwm = Blockly.Python.valueToCode(block, 'PWM', Blockly.Python.ORDER_NONE);
return `set_servo(${pin}, ${pwm})\n`;
}
Blockly.Python.set_duty_cycle = function(block) {
pigpio();
Blockly.Python.definitions_['set_duty_cycle'] = SET_DUTY_CYCLE;
var pin = Blockly.Python.valueToCode(block, 'PIN', Blockly.Python.ORDER_NONE);
var dutyCycle = Blockly.Python.valueToCode(block, 'DUTY_CYCLE', Blockly.Python.ORDER_NONE);
return `set_duty_cycle(${pin}, ${dutyCycle})\n`;
}

69
clover_blocks/www/ros.js Normal file
View File

@@ -0,0 +1,69 @@
/*
* 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.
*/
var url = 'ws://' + location.hostname + ':9090';
export var ros = new ROSLIB.Ros({ url });
ros.on('connection', function () {
console.log('connection')
document.body.classList.add('connected');
});
ros.on('close', function () {
console.log('close')
document.body.classList.remove('connected');
setTimeout(function() {
ros.connect(url);
}, 2000);
});
ros.on('error', function(err) {
});
export const namespace = '/';
export const priv = namespace + 'clover_blocks/';
export var params = {}; // parameters storage
export function readParam(name, fromUrl, _default) {
return new Promise(function(resolve, reject) {
if (params[name] !== undefined) {
resolve();
return;
}
// read from url
if (fromUrl && ((params[name] = new URL(window.location.href).searchParams.get(name)) !== null)) {
resolve();
return;
}
// read from ROS params
new ROSLIB.Param({ ros: ros, name: priv + name }).get(function(val) {
if (val === null) {
if (_default === undefined) {
alert('Cannot read required parameter ' + name);
reject();
} else {
params[name] = _default;
resolve();
}
return;
}
params[name] = val;
resolve();
})
});
}
export var runService = new ROSLIB.Service({ ros: ros, name: priv + 'run', serviceType: 'clover_blocks/Run' });
export var stopService = new ROSLIB.Service({ ros: ros, name: priv + 'stop', serviceType: 'std_srvs/Trigger' });
export var loadService = new ROSLIB.Service({ ros: ros, name : priv + 'load', serviceType : 'clover_blocks/Load' });
export var storeService = new ROSLIB.Service({ ros: ros, name : priv + 'store', serviceType : 'clover_blocks/Store' });
export var landService = new ROSLIB.Service({ ros: ros, name : namespace + 'land', serviceType : 'std_srvs/Trigger' });

View File

@@ -97,7 +97,7 @@ function encode(value) {
writeUint64(length);
}
}
function encodeItem(value) {
var i;
@@ -109,7 +109,7 @@ function encode(value) {
return writeUint8(0xf6);
if (value === undefined)
return writeUint8(0xf7);
switch (typeof value) {
case "number":
if (Math.floor(value) === value) {
@@ -171,12 +171,12 @@ function encode(value) {
}
}
}
encodeItem(value);
if ("slice" in data)
return data.slice(0, offset);
var ret = new ArrayBuffer(offset);
var retView = new DataView(ret);
for (var i = 0; i < offset; ++i)
@@ -187,7 +187,7 @@ function encode(value) {
function decode(data, tagger, simpleValue) {
var dataView = new DataView(data);
var offset = 0;
if (typeof tagger !== "function")
tagger = function(value) { return value; };
if (typeof simpleValue !== "function")
@@ -208,14 +208,14 @@ function decode(data, tagger, simpleValue) {
var sign = value & 0x8000;
var exponent = value & 0x7c00;
var fraction = value & 0x03ff;
if (exponent === 0x7c00)
exponent = 0xff << 10;
else if (exponent !== 0)
exponent += (127 - 15) << 10;
else if (fraction !== 0)
return fraction * POW_2_24;
tempDataView.setUint32(0, sign << 16 | exponent << 13 | fraction << 13);
return tempDataView.getFloat32(0);
}
@@ -1902,7 +1902,7 @@ function SimpleActionServer(options) {
this.nextGoal = null; // the one that'll be preempting
goalListener.subscribe(function(goalMessage) {
if(that.currentGoal) {
that.nextGoal = goalMessage;
// needs to happen AFTER rest is set up
@@ -1953,7 +1953,7 @@ function SimpleActionServer(options) {
}
if(that.currentGoal && isEarlier(that.currentGoal.goal_id.stamp,
cancelMessage.stamp)) {
that.emit('cancel');
}
}
@@ -1978,7 +1978,7 @@ SimpleActionServer.prototype.__proto__ = EventEmitter2.prototype;
*/
SimpleActionServer.prototype.setSucceeded = function(result2) {
var resultMessage = new Message({
status : {goal_id : this.currentGoal.goal_id, status : 3},
@@ -3812,7 +3812,7 @@ var tf = module.exports = {
mixin(Ros, ['TFClient'], tf);
},{"../core/Ros":14,"../mixin":26,"./TFClient":27}],29:[function(require,module,exports){
/**
* @fileOverview
* @fileOverview
* @author Benjamin Pitzer - ben.pitzer@gmail.com
* @author Russell Toris - rctoris@wpi.edu
*/
@@ -3843,7 +3843,7 @@ function UrdfBox(options) {
module.exports = UrdfBox;
},{"../math/Vector3":24,"./UrdfTypes":38}],30:[function(require,module,exports){
/**
* @fileOverview
* @fileOverview
* @author Benjamin Pitzer - ben.pitzer@gmail.com
* @author Russell Toris - rctoris@wpi.edu
*/
@@ -3867,7 +3867,7 @@ function UrdfColor(options) {
module.exports = UrdfColor;
},{}],31:[function(require,module,exports){
/**
* @fileOverview
* @fileOverview
* @author Benjamin Pitzer - ben.pitzer@gmail.com
* @author Russell Toris - rctoris@wpi.edu
*/
@@ -3983,7 +3983,7 @@ module.exports = UrdfJoint;
},{"../math/Pose":21,"../math/Quaternion":22,"../math/Vector3":24}],33:[function(require,module,exports){
/**
* @fileOverview
* @fileOverview
* @author Benjamin Pitzer - ben.pitzer@gmail.com
* @author Russell Toris - rctoris@wpi.edu
*/
@@ -4012,7 +4012,7 @@ function UrdfLink(options) {
module.exports = UrdfLink;
},{"./UrdfVisual":39}],34:[function(require,module,exports){
/**
* @fileOverview
* @fileOverview
* @author Benjamin Pitzer - ben.pitzer@gmail.com
* @author Russell Toris - rctoris@wpi.edu
*/
@@ -4062,7 +4062,7 @@ module.exports = UrdfMaterial;
},{"./UrdfColor":30,"object-assign":3}],35:[function(require,module,exports){
/**
* @fileOverview
* @fileOverview
* @author Benjamin Pitzer - ben.pitzer@gmail.com
* @author Russell Toris - rctoris@wpi.edu
*/
@@ -4099,7 +4099,7 @@ function UrdfMesh(options) {
module.exports = UrdfMesh;
},{"../math/Vector3":24,"./UrdfTypes":38}],36:[function(require,module,exports){
/**
* @fileOverview
* @fileOverview
* @author Benjamin Pitzer - ben.pitzer@gmail.com
* @author Russell Toris - rctoris@wpi.edu
*/
@@ -4170,7 +4170,7 @@ function UrdfModel(options) {
// Check for a material
for( var j=0; j<link.visuals.length; j++ )
{
var mat = link.visuals[j].material;
var mat = link.visuals[j].material;
if ( mat !== null ) {
if (this.materials[mat.name] !== void 0) {
link.visuals[j].material = this.materials[mat.name];
@@ -4196,7 +4196,7 @@ module.exports = UrdfModel;
},{"./UrdfJoint":32,"./UrdfLink":33,"./UrdfMaterial":34,"xmldom":45}],37:[function(require,module,exports){
/**
* @fileOverview
* @fileOverview
* @author Benjamin Pitzer - ben.pitzer@gmail.com
* @author Russell Toris - rctoris@wpi.edu
*/
@@ -4226,7 +4226,7 @@ module.exports = {
},{}],39:[function(require,module,exports){
/**
* @fileOverview
* @fileOverview
* @author Benjamin Pitzer - ben.pitzer@gmail.com
* @author Russell Toris - rctoris@wpi.edu
*/

View File

@@ -0,0 +1,10 @@
cmake_minimum_required(VERSION 3.0)
project(clover_description)
find_package(catkin REQUIRED)
catkin_package()
install(DIRECTORY launch DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION})
install(DIRECTORY meshes DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION})
install(DIRECTORY urdf DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION})

View File

@@ -0,0 +1,29 @@
# `clover_description` ROS package
This package contains models and URDF descriptions for the Clover 4 drone. These descriptions can be used for Gazebo simulation environment.
Note that in order to use these descriptions in Gazebo, you need to use the plugins from [PX4 `sitl_gazebo` package](https://github.com/PX4/sitl_gazebo) and `clover_simulation` package.
## Usage
The descriptions are provided as [`xacro`-enabled](https://wiki.ros.org/xacro) URDF files. A [`spawn_drone.launch`](launch/spawn_drone.launch) file that spawns the model in a running Gazebo instance is also provided for convenience.
You may provide additional parameters for `spawn_drone.launch` as well:
* `main_camera` (*boolean*, default: *true*) - controls whether the drone will have a downward-facing camera attached;
* `rangefinder` (*boolean*, default: *true*) - controls whether the drone will have a downward-facing laser rangefinder attached;
* `led` (*boolean*, default: *true*) - controls whether the drone will have a programmable LED strip (requires plugins from `clover_simulation`);
* `gps` (*boolean*, default: *true*) - controls whether the drone will have a simulated GPS attached (requires plugins from `sitl_gazebo`);
* `maintain_camera_rate` (*boolean*, default: *false*) - slow down the simultion to maintain camera publishing rate (internally changes the camera plugin from `libgazebo_ros_camera.so` to `libthrottling_camera.so` from [`clover_simulation`](../clover_simulation/README.md#throttling-camera-plugin-throttling_camera)).
For example, in order to spawn a drone without a GPS module, you may use the following command:
```bash
roslaunch clover_description spawn_drone.launch gps:=false
```
## Tweaking
By default, the `spawn_drone.launch` command will use the [`clover4.xacro` description file](urdf/clover/clover4.xacro). This is a "high-level" description of the drone, mainly used to attach additional sensors.
The drone "physics" may be tweaked by changing the [`clover4_physics.xacro` file](urdf/clover/clover4_physics.xacro).

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<launch>
<arg name="cmd" default="$(find xacro)/xacro $(find clover_description)/urdf/sensors/rpi_cam.urdf.xacro"/>
<param command="$(arg cmd)" name="camera_description"/>
<node name="$(anon spawn)" output="screen" pkg="gazebo_ros" type="spawn_model" args="-urdf -param camera_description -model rpi_camera -package_to_model"/>
</launch>

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<launch>
<!-- Toggleable model parameters -->
<!-- Main camera -->
<arg name="main_camera" default="true"/>
<!-- Slow simulation down to maintain camera rate -->
<arg name="maintain_camera_rate" default="false"/>
<arg name="rangefinder" default="true"/>
<arg name="led" default="true"/>
<arg name="gps" default="true"/>
<!-- Use physics parameters from CAD programs -->
<arg name="use_clover_physics" default="false"/>
<arg name="cmd" default="$(find xacro)/xacro $(find clover_description)/urdf/clover/clover4.xacro main_camera:=$(arg main_camera) rangefinder:=$(arg rangefinder) led:=$(arg led) gps:=$(arg gps) maintain_camera_rate:=$(arg maintain_camera_rate) use_clover_physics:=$(arg use_clover_physics)"/>
<param command="$(arg cmd)" name="drone_description"/>
<!-- Note: -package_to_model replaces all mentions of "package://" with "model://" in urdf URIs -->
<node name="$(anon spawn)" output="screen" pkg="gazebo_ros" type="spawn_model" args="-urdf -param drone_description -model clover -z 0.3"/>
</launch>

View File

@@ -0,0 +1,7 @@
<launch>
<arg name="model" default="$(find clover_description)/urdf/drones/clover4.xacro"/>
<param name="robot_description" command="$(find xacro)/xacro.py $(arg model)"/>
<node name="rviz" pkg="rviz" type="rviz" required="true"/>
</launch>

View File

@@ -0,0 +1,21 @@
material Polycarbonate
{
technique
{
pass
{
scene_blend alpha_blend
depth_write off
ambient 0.57 0.531 0.444 1
diffuse 0.57 0.531 0.444 1
specular 0.5 0.5 0.5 1
texture_unit
{
colour_op_ex source1 src_current src_current 0 1 0
alpha_op_ex source1 src_manual src_current 0.72
}
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 149 KiB

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,27 @@
<package format="2">
<name>clover_description</name>
<version>0.0.1</version>
<description>The clover_description package provides URDF models of the Clover series of quadcopters.</description>
<maintainer email="sfalexrog@gmail.com">Alexey Rogachevskiy</maintainer>
<author>Alexey Rogachevskiy</author>
<author>Andrey Ryabov</author>
<author>Arthur Golubtsov</author>
<author>Oleg Kalachev</author>
<author>Svyatoslav Zhuravlev</author>
<license>MIT</license>
<url type="website">https://github.com/CopterExpress/clover</url>
<url type="bugtracker">https://github.com/CopterExpress/clover/issues</url>
<buildtool_depend>catkin</buildtool_depend>
<depend>xacro</depend>
<exec_depend>gazebo_ros</exec_depend>
<exec_depend>gazebo_plugins</exec_depend>
<export>
<gazebo_ros gazebo_media_path="${prefix}"/>
</export>
</package>

View File

@@ -0,0 +1,83 @@
<?xml version="1.0" encoding="UTF-8"?>
<robot name="camera_sensor" xmlns:xacro="http://ros.org/wiki/xacro">
<xacro:macro name="camera_sensor_base"
params="name width height rate horizontal_fov frame_name k1 k2 k3 p1 p2 cx cy cx_prime ros_plugin_name min_rate window_size max_st_dev">
<gazebo reference="${name}_link">
<sensor type="camera" name="${name}">
<camera>
<horizontal_fov>${horizontal_fov}</horizontal_fov>
<image>
<format>B8G8R8</format>
<width>${width}</width>
<height>${height}</height>
</image>
<clip>
<near>0.02</near>
<far>1000</far>
</clip>
<distortion>
<k1>${k1}</k1>
<k2>${k2}</k2>
<k3>${k3}</k3>
<p1>${p1}</p1>
<p2>${p2}</p2>
<center>${(cx - 0.5)/ width} ${(cy - 0.5) / height}</center>
</distortion>
</camera>
<always_on>1</always_on>
<update_rate>${rate}</update_rate>
<visualize>1</visualize>
<plugin name="camera_plugin" filename="${ros_plugin_name}">
<alwaysOn>true</alwaysOn>
<imageTopicName>image_raw</imageTopicName>
<cameraTopicName>camera_info</cameraTopicName>
<updateRate>${rate}</updateRate>
<cameraName>${name}</cameraName>
<frameName>${frame_name}</frameName>
<CxPrime>${cx_prime}</CxPrime>
<Cx>${cx}</Cx>
<Cy>${cy}</Cy>
<distortionK1>${k1}</distortionK1>
<distortionK2>${k2}</distortionK2>
<distortionK3>${k3}</distortionK3>
<distortionT1>${p1}</distortionT1>
<distortionT2>${p2}</distortionT2>
<!-- throttling_camera specific options start here -->
<minUpdateRate>${min_rate}</minUpdateRate>
<windowSize>${window_size}</windowSize>
<maxStDev>${max_st_dev}</maxStDev>
</plugin>
</sensor>
</gazebo>
</xacro:macro>
<xacro:macro name="camera_sensor" params="name width height rate horizontal_fov k1:=0 k2:=0 k3:=0 p1:=0 p2:=0 do_throttling:=false">
<xacro:if value="${do_throttling}">
<xacro:property name="ros_plugin_name" value="libthrottling_camera.so"/>
</xacro:if>
<xacro:unless value="${do_throttling}">
<xacro:property name="ros_plugin_name" value="libgazebo_ros_camera.so"/>
</xacro:unless>
<xacro:camera_sensor_base
name="${name}"
width="${width}"
height="${height}"
rate="${rate}"
horizontal_fov="${horizontal_fov}"
frame_name="/${name}_optical"
k1="${k1}"
k2="${k2}"
k3="${k3}"
p1="${p1}"
p2="${p2}"
cx="${(width + 1.0)/2.0}"
cy="${(height + 1.0)/2.0}"
cx_prime="${(width + 1.0)/2.0}"
ros_plugin_name="${ros_plugin_name}"
min_rate="${rate * 0.95}"
window_size="${rate}"
max_st_dev="${3.0 / rate}"
/>
</xacro:macro>
</robot>

View File

@@ -0,0 +1,46 @@
<?xml version="1.0"?>
<robot name="clover_camera" xmlns:xacro="http://ros.org/wiki/xacro">
<xacro:arg name="main_camera" default="true"/>
<xacro:arg name="rangefinder" default="true"/>
<xacro:arg name="led" default="true"/>
<xacro:arg name="gps" default="true"/>
<xacro:arg name="maintain_camera_rate" default="false"/>
<xacro:arg name="use_clover_physics" default="false"/>
<xacro:include filename="$(find clover_description)/urdf/clover/clover4_base.xacro" />
<xacro:include filename="$(find clover_description)/urdf/sensors/rpi_cam.urdf.xacro"/>
<xacro:include filename="$(find clover_description)/urdf/sensors/distance_sensor.urdf.xacro"/>
<xacro:include filename="$(find clover_description)/urdf/leds/led_strip.xacro"/>
<!-- Create camera plugin -->
<xacro:if value="$(arg main_camera)">
<xacro:rpi_cam name="main_camera" parent="base_link" x="0.055" y="0.0" z="-0.03" roll="0" pitch="${pi / 2}" yaw="0" width="320" height="240" rate="40" do_throttling="$(arg maintain_camera_rate)"/>
</xacro:if>
<!-- Create rangefinder plugin -->
<xacro:if value="$(arg rangefinder)">
<xacro:distance_sensor parent="base_link" x="0.0" y="0.0" z="-0.04" roll="0" pitch="${pi / 2}" yaw="0"/>
</xacro:if>
<!-- Instantiate LED strip -->
<xacro:if value="$(arg led)">
<xacro:led_strip
name="led"
parent="base_link"
radius="0.08"
bulb_radius="0.006"
led_count="58"
use_plugin="true"
z="-0.002"/>
</xacro:if>
<xacro:if value="$(arg gps)">
<!-- Instantiate gps plugin. -->
<xacro:gps_plugin_macro
namespace="${namespace}"
gps_noise="true"
/>
</xacro:if>
</robot>

View File

@@ -0,0 +1,183 @@
<?xml version="1.0"?>
<robot name="clover" xmlns:xacro="http://ros.org/wiki/xacro">
<!-- Properties that can be assigned at build time as arguments.
Is there a reason not to make all properties arguments?
-->
<xacro:arg name='name' default='iris' />
<xacro:arg name='mavlink_addr' default='INADDR_ANY' />
<xacro:arg name='mavlink_udp_port' default='14560' />
<xacro:arg name='mavlink_tcp_port' default='4560' />
<xacro:arg name='serial_enabled' default='false' />
<xacro:arg name='serial_device' default='/dev/ttyACM0' />
<xacro:arg name='baudrate' default='921600' />
<xacro:arg name='qgc_addr' default='INADDR_ANY' />
<xacro:arg name='qgc_udp_port' default='14550' />
<xacro:arg name='sdk_addr' default='INADDR_ANY' />
<xacro:arg name='sdk_udp_port' default='14540' />
<xacro:arg name='hil_mode' default='false' />
<xacro:arg name='hil_state_level' default='false' />
<xacro:arg name='send_vision_estimation' default='false' />
<xacro:arg name='send_odometry' default='false' />
<xacro:arg name='enable_lockstep' default='true' />
<xacro:arg name='use_tcp' default='true' />
<xacro:arg name='vehicle_is_tailsitter' default='false' />
<xacro:arg name='visual_material' default='DarkGrey' />
<xacro:arg name='enable_mavlink_interface' default='true' />
<xacro:arg name='enable_wind' default='false' />
<!-- The following causes segfault with multiple vehicles if defaults to true!!! -->
<xacro:arg name='enable_ground_truth' default='false' />
<xacro:arg name='enable_logging' default='false' />
<xacro:arg name='log_file' default='iris' />
<!-- macros for gazebo plugins, sensors -->
<xacro:include filename="$(find clover_description)/urdf/component_snippets.urdf.xacro" />
<!-- Instantiate iris "mechanics" -->
<xacro:include filename="$(find clover_description)/urdf/clover/clover4_physics.xacro" />
<xacro:if value="$(arg enable_wind)">
<xacro:wind_plugin_macro
namespace="${namespace}"
wind_direction="0 0 1"
wind_force_mean="0.7"
xyz_offset="1 0 0"
wind_gust_direction="0 0 0"
wind_gust_duration="0"
wind_gust_start="0"
wind_gust_force_mean="0"
/>
</xacro:if>
<!-- Instantiate magnetometer plugin. -->
<xacro:magnetometer_plugin_macro
namespace="${namespace}"
pub_rate="20"
noise_density="0.0004"
random_walk="0.0000064"
bias_correlation_time="600"
mag_topic="/mag"
>
</xacro:magnetometer_plugin_macro>
<!-- Instantiate barometer plugin. -->
<xacro:barometer_plugin_macro
namespace="${namespace}"
pub_rate="10"
baro_topic="/baro"
>
</xacro:barometer_plugin_macro>
<xacro:if value="$(arg enable_mavlink_interface)">
<!-- Instantiate mavlink telemetry interface. -->
<xacro:mavlink_interface_macro
namespace="${namespace}"
imu_sub_topic="/imu"
gps_sub_topic="/gps"
mag_sub_topic="/mag"
baro_sub_topic="/baro"
mavlink_addr="$(arg mavlink_addr)"
mavlink_udp_port="$(arg mavlink_udp_port)"
mavlink_tcp_port="$(arg mavlink_tcp_port)"
serial_enabled="$(arg serial_enabled)"
serial_device="$(arg serial_device)"
baudrate="$(arg baudrate)"
qgc_addr="$(arg qgc_addr)"
qgc_udp_port="$(arg qgc_udp_port)"
sdk_addr="$(arg sdk_addr)"
sdk_udp_port="$(arg sdk_udp_port)"
hil_mode="$(arg hil_mode)"
hil_state_level="$(arg hil_state_level)"
vehicle_is_tailsitter="$(arg vehicle_is_tailsitter)"
send_vision_estimation="$(arg send_vision_estimation)"
send_odometry="$(arg send_odometry)"
enable_lockstep="$(arg enable_lockstep)"
use_tcp="$(arg use_tcp)"
>
</xacro:mavlink_interface_macro>
</xacro:if>
<!-- Mount an ADIS16448 IMU. -->
<xacro:imu_plugin_macro
namespace="${namespace}"
imu_suffix=""
parent_link="base_link"
imu_topic="/imu"
mass_imu_sensor="0.015"
gyroscope_noise_density="0.0003394"
gyroscopoe_random_walk="0.000038785"
gyroscope_bias_correlation_time="1000.0"
gyroscope_turn_on_bias_sigma="0.0087"
accelerometer_noise_density="0.004"
accelerometer_random_walk="0.006"
accelerometer_bias_correlation_time="300.0"
accelerometer_turn_on_bias_sigma="0.1960"
>
<inertia ixx="0.00001" ixy="0.0" ixz="0.0" iyy="0.00001" iyz="0.0" izz="0.00001" />
<origin xyz="0 0 0" rpy="0 0 0" />
</xacro:imu_plugin_macro>
<xacro:if value="$(arg enable_ground_truth)">
<!-- Mount an IMU providing ground truth. -->
<xacro:imu_plugin_macro
namespace="${namespace}"
imu_suffix="gt"
parent_link="base_link"
imu_topic="ground_truth/imu"
mass_imu_sensor="0.00001"
gyroscope_noise_density="0.0"
gyroscopoe_random_walk="0.0"
gyroscope_bias_correlation_time="1000.0"
gyroscope_turn_on_bias_sigma="0.0"
accelerometer_noise_density="0.0"
accelerometer_random_walk="0.0"
accelerometer_bias_correlation_time="300.0"
accelerometer_turn_on_bias_sigma="0.0"
>
<inertia ixx="0.00001" ixy="0.0" ixz="0.0" iyy="0.00001" iyz="0.0" izz="0.00001" />
<origin xyz="0 0 0" rpy="0 0 0" />
</xacro:imu_plugin_macro>
<!-- Mount a generic odometry sensor providing ground truth. -->
<xacro:odometry_plugin_macro
namespace="${namespace}/ground_truth"
odometry_sensor_suffix="gt"
parent_link="base_link"
pose_topic="pose"
pose_with_covariance_topic="pose_with_covariance"
position_topic="position"
transform_topic="transform"
odometry_topic="odometry"
parent_frame_id="world"
mass_odometry_sensor="0.00001"
measurement_divisor="1"
measurement_delay="0"
unknown_delay="0.0"
noise_normal_position="0 0 0"
noise_normal_quaternion="0 0 0"
noise_normal_linear_velocity="0 0 0"
noise_normal_angular_velocity="0 0 0"
noise_uniform_position="0 0 0"
noise_uniform_quaternion="0 0 0"
noise_uniform_linear_velocity="0 0 0"
noise_uniform_angular_velocity="0 0 0"
enable_odometry_map="false"
odometry_map=""
image_scale=""
>
<inertia ixx="0.00001" ixy="0.0" ixz="0.0" iyy="0.00001" iyz="0.0" izz="0.00001" /> <!-- [kg.m^2] [kg.m^2] [kg.m^2] [kg.m^2] [kg.m^2] [kg.m^2] -->
<origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0" />
</xacro:odometry_plugin_macro>
</xacro:if>
<xacro:if value="$(arg enable_logging)">
<!-- Instantiate a logger -->
<xacro:bag_plugin_macro
namespace="${namespace}"
bag_file="$(arg log_file)"
rotor_velocity_slowdown_sim="${rotor_velocity_slowdown_sim}"
>
</xacro:bag_plugin_macro>
</xacro:if>
</robot>

View File

@@ -0,0 +1,235 @@
<?xml version="1.0"?>
<!--
Copyright 2015 Fadri Furrer, ASL, ETH Zurich, Switzerland
Copyright 2015 Michael Burri, ASL, ETH Zurich, Switzerland
Copyright 2015 Mina Kamel, ASL, ETH Zurich, Switzerland
Copyright 2015 Janosch Nikolic, ASL, ETH Zurich, Switzerland
Copyright 2015 Markus Achtelik, ASL, ETH Zurich, Switzerland
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.
-->
<!--
Clover 4 base model, collision boxes, and other fun stuff. Derived heavily from multirotor_base.xacro
from PX4 simulation config files.
-->
<robot xmlns:xacro="http://ros.org/wiki/xacro">
<!-- Main multirotor link -->
<!-- We still allow specifying a different mass and inertia matrix.
Body width and height are allowed as parameters, but are ignored for the most part.
Color is not applicable, since the model has textures and is a compound thingy anyway.
-->
<xacro:macro name="clover4_base_macro"
params="robot_namespace mass body_width body_height *inertia">
<link name="base_link"></link>
<!-- Note: For some reason this file relies on base_link_inertia becoming base_link.
I don't even want to know.
-sfalexrog
-->
<joint name="base_joint" type="fixed">
<origin xyz="0 0 0" rpy="0 0 0" />
<parent link="base_link" />
<child link="base_link_inertia" />
</joint>
<link name="base_link_inertia">
<inertial>
<mass value="${mass}" /> <!-- [kg] -->
<origin xyz="0 0 0" />
<xacro:insert_block name="inertia" />
</inertial>
<visual name="body">
<origin xyz="0 0 0.025" rpy="0 0 0" />
<geometry>
<!-- Note: Texture files are expected to be in the same directory as the model -->
<mesh filename="package://clover_description/meshes/clover4/clover_body_solid.dae"
scale="1 1 1"/>
</geometry>
</visual>
<!-- Now, a person with some experience in building urdf/xacro files would say something along the lines of:
"Hey, a link may have multiple visual tags, each with its own material!"
Hear my answer, o experienced one. First, you are absolutely correct, you may add multiple visual
elements to a single link. Second, once you try to put some transparency on your visual elements,
you'll have it vanish into thin fucking air - Gazebo (as of version 9!) gives fuck all about your
"color" attributes and whatnot. Third, oh, you want to reference the visual element from your
"gazebo" tag down the line? Well fuck you, buddy, because you can only reference links and joints!
So yeah, good luck improving this.
- sfalexrog
-->
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<box size="${body_width} ${body_width} ${body_height}" /> <!-- [m] [m] [m] -->
</geometry>
</collision>
</link>
<!-- This link is here for a single reason: to draw transparent parts (oh, and also to be the bane of my existence).
I'm going to leave helpful comments for the poor soul who's going to attempt to improve this file.
-sfalexrog
-->
<link name="base_guard_link">
<!-- So we've established we actually need a link. Now, if you look at the SDF spec for some reason
(much like I did), you'll notice you don't really need much for a link to exist. And that is a
big fucking lie. Sure, go ahead, create a link without inertial or collision properties,
see how far that gets you (spoiler: it will get you fucking nowhere, because Gazebo will
devour your puny link and all that it stands for).
- sfalexrog
-->
<inertial>
<!-- We give an oh-so-tiny mass to the link to avoid it being destroyed by Gazebo -->
<mass value="0.015"/> <!-- [kg] -->
<origin xyz="0 0 0" rpy="0 0 0"/>
<!-- We also give it some inertia, so that the physics engine does not go crazy
(and to avoid it being devoured by Gazebo)
-->
<inertia
ixx="${1/12 * 0.015 * 0.01 * 0.01}"
iyy="${1/12 * 0.015 * 0.01 * 0.01}"
izz="${1/12 * 0.015 * 0.01 * 0.01}"
ixy="0.0" ixz="0.0" iyz="0.0"
/>
</inertial>
<!-- We may want to have proper collision tucked away here at some point, though -->
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<box size="0.01 0.01 0.01" /> <!-- [m] [m] [m] -->
</geometry>
</collision>
<visual>
<!-- This origin should be the same as in "base_link_inertia" visuals -->
<origin xyz="0 0 0.025" rpy="0 0 0"/>
<geometry>
<mesh filename="package://clover_description/meshes/clover4/clover_guards_transparent.dae"
scale="1 1 1"/>
</geometry>
<!-- Let me reiterate that you can't put the "material" tag to any effect -->
</visual>
</link>
<!-- Now, the last thing to do is to attach the base_guard_link to base_link. Since
base_guard_link has no meaning by itself, it must be fixed rigidly to base_link.
Except Gazebo absolutely loves "optimizing away" your fixed joints and all
child links with them! How do we deal with that nonsense? Read on to find out!
- sfalexrog
-->
<joint name="base_guard_joint" type="fixed">
<origin xyz="0 0 0" rpy="0 0 0"/>
<parent link="base_link" />
<child link="base_guard_link" />
</joint>
<!-- attach multirotor_base_plugin to the base_link -->
<gazebo>
<plugin filename="libgazebo_multirotor_base_plugin.so" name="rosbag">
<robotNamespace>${robot_namespace}</robotNamespace>
<linkName>base_link</linkName>
<rotorVelocitySlowdownSim>${rotor_velocity_slowdown_sim}</rotorVelocitySlowdownSim>
</plugin>
</gazebo>
<!-- And here's what you've been waiting for: the way to save a fixed joint from
Gazebo's wrath! You plead for it to save your joint, and it might answer
(depending on the version, apparently).
Note that it is done in other places as well.
- sfalexrog
-->
<gazebo reference="base_guard_joint">
<disableFixedJointLumping>true</disableFixedJointLumping>
<preserveFixedJoint>true</preserveFixedJoint>
</gazebo>
<!-- ...and here's another absolutely terrible idea: you can only set a material for
the whole link, not for its part. And that's incompatible with URDF's material
definition. Yeah.
- sfalexrog
-->
<gazebo reference="base_guard_link">
<material>Polycarbonate</material>
</gazebo>
<gazebo reference="base_link">
<kp>10000.0</kp>
<kd>10.0</kd>
<maxVel>10.0</maxVel>
<minDepth>0.001</minDepth>
</gazebo>
</xacro:macro>
<!-- Rotor joint and link, modified for Clover purposes -->
<xacro:macro name="vertical_clover_rotor"
params="robot_namespace suffix direction motor_constant moment_constant parent mass_rotor radius_rotor time_constant_up time_constant_down max_rot_velocity motor_number rotor_drag_coefficient rolling_moment_coefficient mesh *origin *inertia">
<joint name="rotor_${motor_number}_joint" type="continuous">
<xacro:insert_block name="origin" />
<axis xyz="0 0 1" />
<!-- TODO(ff): not currently set because it's not yet supported -->
<!-- <limit effort="2000" velocity="${max_rot_velocity}" /> -->
<parent link="${parent}" />
<child link="rotor_${motor_number}" />
</joint>
<!-- TODO(ff): not currently set because it's not yet supported -->
<!-- <gazebo reference="rotor_${motor_number}_joint"> <axis> <xyz>0 0 1</xyz>
<limit> <velocity> ${max_rot_velocity} </velocity> </limit> </axis> </gazebo> -->
<link name="rotor_${motor_number}">
<inertial>
<mass value="${mass_rotor}" /> <!-- [kg] -->
<xacro:insert_block name="inertia" />
</inertial>
<visual>
<geometry>
<mesh filename="package://clover_description/meshes/${mesh}"
scale="1 1 1" />
</geometry>
</visual>
<collision>
<geometry>
<cylinder length="0.005" radius="${radius_rotor}" /> <!-- [m] -->
</geometry>
</collision>
</link>
<gazebo>
<plugin name="${suffix}_motor_model" filename="libgazebo_motor_model.so">
<robotNamespace>${robot_namespace}</robotNamespace>
<jointName>rotor_${motor_number}_joint</jointName>
<linkName>rotor_${motor_number}</linkName>
<turningDirection>${direction}</turningDirection>
<timeConstantUp>${time_constant_up}</timeConstantUp>
<timeConstantDown>${time_constant_down}</timeConstantDown>
<maxRotVelocity>${max_rot_velocity}</maxRotVelocity>
<motorConstant>${motor_constant}</motorConstant>
<momentConstant>${moment_constant}</momentConstant>
<commandSubTopic>/gazebo/command/motor_speed</commandSubTopic>
<motorNumber>${motor_number}</motorNumber>
<rotorDragCoefficient>${rotor_drag_coefficient}</rotorDragCoefficient>
<rollingMomentCoefficient>${rolling_moment_coefficient}</rollingMomentCoefficient>
<motorSpeedPubTopic>/motor_speed/${motor_number}</motorSpeedPubTopic>
<rotorVelocitySlowdownSim>${rotor_velocity_slowdown_sim}</rotorVelocitySlowdownSim>
<!--
<gazebo_joint_control_pid>
<p>0.1</p>
<i>0</i>
<d>0</d>
<iMax>0</iMax>
<iMin>0</iMin>
<cmdMax>3</cmdMax>
<cmdMin>-3</cmdMin>
</gazebo_joint_control_pid>
-->
</plugin>
</gazebo>
<!-- <gazebo reference="rotor_${motor_number}">
<material>Gazebo/${color}</material>
</gazebo> -->
</xacro:macro>
</robot>

View File

@@ -0,0 +1,178 @@
<?xml version="1.0"?>
<!-- Typical physics values for the Clover drone.
NB: Right now the values are wrong, and taken from the 3DR Iris drone description.
This is subject to change as correct values are calculated.
-->
<robot name="iris" xmlns:xacro="http://ros.org/wiki/xacro">
<!-- Properties -->
<xacro:property name="namespace" value="" />
<xacro:property name="rotor_velocity_slowdown_sim" value="10" />
<xacro:if value="$(arg use_clover_physics)">
<xacro:property name="mass" value="0.7" /> <!-- [kg] -->
</xacro:if>
<xacro:unless value="$(arg use_clover_physics)">
<xacro:property name="mass" value="1.5" /> <!-- [kg] -->
</xacro:unless>
<xacro:property name="body_width" value="0.35" /> <!-- [m] -->
<xacro:property name="body_height" value="0.124" /> <!-- [m] -->
<xacro:property name="mass_rotor" value="0.01" /> <!-- [kg] -->
<xacro:property name="arm_length_front_x" value="0.083" /> <!-- [m] -->
<xacro:property name="arm_length_back_x" value="0.083" /> <!-- [m] -->
<xacro:property name="arm_length_front_y" value="0.083" /> <!-- [m] -->
<xacro:property name="arm_length_back_y" value="0.083" /> <!-- [m] -->
<xacro:property name="rotor_offset_top" value="0.026" /> <!-- [m] -->
<xacro:property name="radius_rotor" value="0.06" /> <!-- [m] -->
<xacro:property name="motor_constant" value="8.54858e-06" /> <!-- [kg.m/s^2] -->
<xacro:property name="moment_constant" value="0.06" /> <!-- [m] -->
<xacro:property name="time_constant_up" value="0.0125" /> <!-- [s] -->
<xacro:property name="time_constant_down" value="0.025" /> <!-- [s] -->
<xacro:property name="max_rot_velocity" value="1100" /> <!-- [rad/s] -->
<xacro:property name="sin30" value="0.5" />
<xacro:property name="cos30" value="0.866025403784" />
<xacro:property name="sqrt2" value="1.4142135623730951" />
<xacro:property name="rotor_drag_coefficient" value="1.75e-04" />
<xacro:property name="rolling_moment_coefficient" value="0.000001" />
<xacro:property name="color" value="$(arg visual_material)" />
<!-- Property Blocks -->
<!-- Clover body inertia -->
<!-- Values from CAD program -->
<xacro:if value="$(arg use_clover_physics)">
<xacro:property name="body_inertia">
<inertia
ixx="0.0347563"
iyy="0.0458929"
izz="0.0977"
ixy="0.0" ixz="0.0" iyz="0.0" /> <!-- [kg.m^2] [kg.m^2] [kg.m^2] [kg.m^2] [kg.m^2] [kg.m^2] -->
</xacro:property>
</xacro:if>
<!-- Box-like inertia -->
<xacro:unless value="$(arg use_clover_physics)">
<xacro:property name="body_inertia">
<inertia
ixx="${1/12 * mass * (body_height * body_height + body_width * body_width)}"
iyy="${1/12 * mass * (body_height * body_height + body_width * body_width)}"
izz="${1/12 * mass * (body_width * body_width + body_width * body_width)}"
ixy="0.0" ixz="0.0" iyz="0.0" /> <!-- [kg.m^2] [kg.m^2] [kg.m^2] [kg.m^2] [kg.m^2] [kg.m^2] -->
</xacro:property>
</xacro:unless>
<!-- inertia of a single rotor -->
<!-- Values from CAD program -->
<xacro:if value="$(arg use_clover_physics)">
<xacro:property name="rotor_inertia">
<inertia
ixx="${1.375 * 0.001 * 0.001 * rotor_velocity_slowdown_sim}"
iyy="${2.153 * 0.001 * 0.001 * rotor_velocity_slowdown_sim}"
izz="${1.375 * 0.001 * 0.001 * rotor_velocity_slowdown_sim}"
ixy="0.0" ixz="0.0" iyz="0.0" /> <!-- [kg.m^2] [kg.m^2] [kg.m^2] [kg.m^2] [kg.m^2] [kg.m^2] -->
</xacro:property>
</xacro:if>
<!-- Values for a cuboid (cuboid. Height=3mm, width=15mm) -->
<xacro:unless value="$(arg use_clover_physics)">
<xacro:property name="rotor_inertia">
<inertia
ixx="${1/12 * mass_rotor * (0.015 * 0.015 + 0.003 * 0.003) * rotor_velocity_slowdown_sim}"
iyy="${1/12 * mass_rotor * (4 * radius_rotor * radius_rotor + 0.003 * 0.003) * rotor_velocity_slowdown_sim}"
izz="${1/12 * mass_rotor * (4 * radius_rotor * radius_rotor + 0.015 * 0.015) * rotor_velocity_slowdown_sim}"
ixy="0.0" ixz="0.0" iyz="0.0" /> <!-- [kg.m^2] [kg.m^2] [kg.m^2] [kg.m^2] [kg.m^2] [kg.m^2] -->
</xacro:property>
</xacro:unless>
<!-- Included URDF Files -->
<xacro:include filename="$(find clover_description)/urdf/clover/clover4_macros.xacro" />
<!-- Instantiate multirotor_base_macro once -->
<xacro:clover4_base_macro
robot_namespace="${namespace}"
mass="${mass}"
body_width="${body_width}"
body_height="${body_height}"
>
<xacro:insert_block name="body_inertia" />
</xacro:clover4_base_macro>
<!-- Instantiate rotors -->
<xacro:vertical_clover_rotor
robot_namespace="${namespace}"
suffix="front_right"
direction="ccw"
motor_constant="${motor_constant}"
moment_constant="${moment_constant}"
parent="base_link"
mass_rotor="${mass_rotor}"
radius_rotor="${radius_rotor}"
time_constant_up="${time_constant_up}"
time_constant_down="${time_constant_down}"
max_rot_velocity="${max_rot_velocity}"
motor_number="0"
rotor_drag_coefficient="${rotor_drag_coefficient}"
rolling_moment_coefficient="${rolling_moment_coefficient}"
mesh="clover4/prop_ccw.dae">
<origin xyz="${arm_length_front_x} -${arm_length_front_y} ${rotor_offset_top}" rpy="0 0 0" />
<xacro:insert_block name="rotor_inertia" />
</xacro:vertical_clover_rotor>
<xacro:vertical_clover_rotor
robot_namespace="${namespace}"
suffix="back_left"
direction="ccw"
motor_constant="${motor_constant}"
moment_constant="${moment_constant}"
parent="base_link"
mass_rotor="${mass_rotor}"
radius_rotor="${radius_rotor}"
time_constant_up="${time_constant_up}"
time_constant_down="${time_constant_down}"
max_rot_velocity="${max_rot_velocity}"
motor_number="1"
rotor_drag_coefficient="${rotor_drag_coefficient}"
rolling_moment_coefficient="${rolling_moment_coefficient}"
mesh="clover4/prop_ccw.dae">
<origin xyz="-${arm_length_back_x} ${arm_length_back_y} ${rotor_offset_top}" rpy="0 0 0" />
<xacro:insert_block name="rotor_inertia" />
</xacro:vertical_clover_rotor>
<xacro:vertical_clover_rotor robot_namespace="${namespace}"
suffix="front_left"
direction="cw"
motor_constant="${motor_constant}"
moment_constant="${moment_constant}"
parent="base_link"
mass_rotor="${mass_rotor}"
radius_rotor="${radius_rotor}"
time_constant_up="${time_constant_up}"
time_constant_down="${time_constant_down}"
max_rot_velocity="${max_rot_velocity}"
motor_number="2"
rotor_drag_coefficient="${rotor_drag_coefficient}"
rolling_moment_coefficient="${rolling_moment_coefficient}"
mesh="clover4/prop_cw.dae">
<origin xyz="${arm_length_front_x} ${arm_length_front_y} ${rotor_offset_top}" rpy="0 0 0" />
<xacro:insert_block name="rotor_inertia" />
</xacro:vertical_clover_rotor>
<xacro:vertical_clover_rotor robot_namespace="${namespace}"
suffix="back_right"
direction="cw"
motor_constant="${motor_constant}"
moment_constant="${moment_constant}"
parent="base_link"
mass_rotor="${mass_rotor}"
radius_rotor="${radius_rotor}"
time_constant_up="${time_constant_up}"
time_constant_down="${time_constant_down}"
max_rot_velocity="${max_rot_velocity}"
motor_number="3"
rotor_drag_coefficient="${rotor_drag_coefficient}"
rolling_moment_coefficient="${rolling_moment_coefficient}"
mesh="clover4/prop_cw.dae">
<origin xyz="-${arm_length_back_x} -${arm_length_back_y} ${rotor_offset_top}" rpy="0 0 0" />
<xacro:insert_block name="rotor_inertia" />
</xacro:vertical_clover_rotor>
</robot>

View File

@@ -0,0 +1,613 @@
<?xml version="1.0"?>
<!--
Copyright 2015 Fadri Furrer, ASL, ETH Zurich, Switzerland
Copyright 2015 Michael Burri, ASL, ETH Zurich, Switzerland
Copyright 2015 Mina Kamel, ASL, ETH Zurich, Switzerland
Copyright 2015 Janosch Nikolic, ASL, ETH Zurich, Switzerland
Copyright 2015 Markus Achtelik, ASL, ETH Zurich, Switzerland
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.
-->
<robot xmlns:xacro="http://ros.org/wiki/xacro">
<!-- Macro to add logging to a bag file. -->
<xacro:macro name="bag_plugin_macro"
params="namespace bag_file rotor_velocity_slowdown_sim">
<gazebo>
<plugin filename="librotors_gazebo_bag_plugin.so" name="rosbag">
<robotNamespace>${namespace}</robotNamespace>
<bagFileName>${bag_file}</bagFileName>
<linkName>base_link</linkName>
<frameId>base_link</frameId>
<commandAttitudeThrustSubTopic>command/attitude</commandAttitudeThrustSubTopic>
<commandAttitudeThrustPubTopic>command/attitude</commandAttitudeThrustPubTopic>
<rotorVelocitySlowdownSim>${rotor_velocity_slowdown_sim}</rotorVelocitySlowdownSim>
</plugin>
</gazebo>
</xacro:macro>
<!-- Macro to add a camera. -->
<xacro:macro name="camera_macro"
params="namespace parent_link camera_suffix frame_rate
horizontal_fov image_width image_height image_format min_distance
max_distance noise_mean noise_stddev enable_visual *cylinder *origin">
<link name="${namespace}/camera_${camera_suffix}_link">
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<xacro:insert_block name="cylinder" />
</geometry>
</collision>
<xacro:if value="${enable_visual}">
<visual>
<origin xyz="0 0 0" rpy="0 1.57079632679 0" />
<geometry>
<xacro:insert_block name="cylinder" />
</geometry>
<material name="red" />
</visual>
</xacro:if>
<inertial>
<mass value="1e-5" />
<origin xyz="0 0 0" rpy="0 0 0" />
<inertia ixx="1e-6" ixy="0" ixz="0" iyy="1e-6" iyz="0" izz="1e-6" />
</inertial>
</link>
<joint name="${namespace}/camera_${camera_suffix}_joint" type="fixed">
<xacro:insert_block name="origin" />
<parent link="${parent_link}" />
<child link="${namespace}/camera_${camera_suffix}_link" />
</joint>
<gazebo reference="${namespace}/camera_${camera_suffix}_link">
<sensor type="camera" name="${namespace}_camera_${camera_suffix}">
<update_rate>${frame_rate}</update_rate>
<camera name="head">
<horizontal_fov>${horizontal_fov}</horizontal_fov>
<image>
<width>${image_width}</width>
<height>${image_height}</height>
<format>${image_format}</format>
</image>
<clip>
<near>${min_distance}</near>
<far>${max_distance}</far>
</clip>
<noise>
<type>gaussian</type>
<!-- Noise is sampled independently per pixel on each frame.
That pixel's noise value is added to each of its color
channels, which at that point lie in the range [0,1]. -->
<mean>${noise_mean}</mean>
<stddev>${noise_stddev}</stddev>
</noise>
</camera>
<plugin name="${namespace}_camera_${camera_suffix}_controller" filename="libgazebo_ros_camera.so">
<robotNamespace>${namespace}</robotNamespace>
<alwaysOn>true</alwaysOn>
<updateRate>${frame_rate}</updateRate>
<cameraName>camera_${camera_suffix}</cameraName>
<imageTopicName>image_raw</imageTopicName>
<cameraInfoTopicName>camera_info</cameraInfoTopicName>
<frameName>camera_${camera_suffix}_link</frameName>
<hackBaseline>0.0</hackBaseline>
<distortionK1>0.0</distortionK1>
<distortionK2>0.0</distortionK2>
<distortionK3>0.0</distortionK3>
<distortionT1>0.0</distortionT1>
<distortionT2>0.0</distortionT2>
</plugin>
</sensor>
</gazebo>
</xacro:macro>
<!-- Macro to add the controller interface. -->
<xacro:macro name="controller_plugin_macro" params="namespace imu_sub_topic">
<gazebo>
<plugin name="controller_interface" filename="libgazebo_controller_interface.so">
<robotNamespace>${namespace}</robotNamespace>
<commandAttitudeThrustSubTopic>/command/attitude</commandAttitudeThrustSubTopic>
<commandRateThrustSubTopic>/command/rate</commandRateThrustSubTopic>
<commandMotorSpeedSubTopic>/command/motor_speed</commandMotorSpeedSubTopic>
<imuSubTopic>/${imu_sub_topic}</imuSubTopic>
<motorSpeedCommandPubTopic>/gazebo/command/motor_speed</motorSpeedCommandPubTopic>
</plugin>
</gazebo>
</xacro:macro>
<!-- Macro to add the gps_plugin. -->
<xacro:macro name="gps_plugin_macro" params="namespace gps_noise">
<gazebo>
<plugin name="gps_plugin" filename="libgazebo_gps_plugin.so">
<robotNamespace>${namespace}</robotNamespace>
<gpsNoise>${gps_noise}</gpsNoise>
</plugin>
</gazebo>
</xacro:macro>
<!-- Macro to add the magnetometer_plugin. -->
<xacro:macro name="magnetometer_plugin_macro" params="namespace pub_rate noise_density random_walk bias_correlation_time mag_topic">
<gazebo>
<plugin name="magnetometer_plugin" filename="libgazebo_magnetometer_plugin.so">
<robotNamespace>${namespace}</robotNamespace>
<pubRate>${pub_rate}</pubRate>
<noiseDensity>${noise_density}</noiseDensity>
<randomWalk>${random_walk}</randomWalk>
<biasCorrelationTime>${bias_correlation_time}</biasCorrelationTime>
<magTopic>${mag_topic}</magTopic>
</plugin>
</gazebo>
</xacro:macro>
<!-- Macro to add the barometer_plugin. -->
<xacro:macro name="barometer_plugin_macro" params="namespace pub_rate baro_topic">
<gazebo>
<plugin name="barometer_plugin" filename="libgazebo_barometer_plugin.so">
<robotNamespace>${namespace}</robotNamespace>
<pubRate>${pub_rate}</pubRate>
<baroTopic>${baro_topic}</baroTopic>
</plugin>
</gazebo>
</xacro:macro>
<!-- Macro to add the mavlink interface. -->
<xacro:macro name="mavlink_interface_macro" params="namespace imu_sub_topic gps_sub_topic mag_sub_topic baro_sub_topic mavlink_addr mavlink_udp_port mavlink_tcp_port serial_enabled serial_device baudrate qgc_addr qgc_udp_port sdk_addr sdk_udp_port hil_mode hil_state_level vehicle_is_tailsitter send_vision_estimation send_odometry enable_lockstep use_tcp">
<gazebo>
<plugin name="mavlink_interface" filename="libgazebo_mavlink_interface.so">
<robotNamespace>${namespace}</robotNamespace>
<imuSubTopic>${imu_sub_topic}</imuSubTopic>
<gpsSubTopic>${gps_sub_topic}</gpsSubTopic>
<magSubTopic>${mag_sub_topic}</magSubTopic>
<baroSubTopic>${baro_sub_topic}</baroSubTopic>
<mavlink_addr>$(arg mavlink_addr)</mavlink_addr>
<mavlink_udp_port>$(arg mavlink_udp_port)</mavlink_udp_port>
<mavlink_tcp_port>$(arg mavlink_tcp_port)</mavlink_tcp_port>
<serialEnabled>$(arg serial_enabled)</serialEnabled>
<serialDevice>$(arg serial_device)</serialDevice>
<baudRate>$(arg baudrate)</baudRate>
<qgc_addr>$(arg qgc_addr)</qgc_addr>
<qgc_udp_port>$(arg qgc_udp_port)</qgc_udp_port>
<sdk_addr>$(arg sdk_addr)</sdk_addr>
<sdk_udp_port>$(arg sdk_udp_port)</sdk_udp_port>
<hil_mode>$(arg hil_mode)</hil_mode>
<hil_state_level>$(arg hil_state_level)</hil_state_level>
<vehicle_is_tailsitter>$(arg vehicle_is_tailsitter)</vehicle_is_tailsitter>
<send_vision_estimation>$(arg send_vision_estimation)</send_vision_estimation>
<send_odometry>$(arg send_odometry)</send_odometry>
<enable_lockstep>$(arg enable_lockstep)</enable_lockstep>
<use_tcp>$(arg use_tcp)</use_tcp>
<motorSpeedCommandPubTopic>/gazebo/command/motor_speed</motorSpeedCommandPubTopic>
<control_channels>
<channel name="rotor1">
<input_index>0</input_index>
<input_offset>0</input_offset>
<input_scaling>1000</input_scaling>
<zero_position_disarmed>0</zero_position_disarmed>
<zero_position_armed>100</zero_position_armed>
<joint_control_type>velocity</joint_control_type>
</channel>
<channel name="rotor2">
<input_index>1</input_index>
<input_offset>0</input_offset>
<input_scaling>1000</input_scaling>
<zero_position_disarmed>0</zero_position_disarmed>
<zero_position_armed>100</zero_position_armed>
<joint_control_type>velocity</joint_control_type>
</channel>
<channel name="rotor3">
<input_index>2</input_index>
<input_offset>0</input_offset>
<input_scaling>1000</input_scaling>
<zero_position_disarmed>0</zero_position_disarmed>
<zero_position_armed>100</zero_position_armed>
<joint_control_type>velocity</joint_control_type>
</channel>
<channel name="rotor4">
<input_index>3</input_index>
<input_offset>0</input_offset>
<input_scaling>1000</input_scaling>
<zero_position_disarmed>0</zero_position_disarmed>
<zero_position_armed>100</zero_position_armed>
<joint_control_type>velocity</joint_control_type>
</channel>
<channel name="rotor5">
<input_index>4</input_index>
<input_offset>1</input_offset>
<input_scaling>324.6</input_scaling>
<zero_position_disarmed>0</zero_position_disarmed>
<zero_position_armed>0</zero_position_armed>
<joint_control_type>velocity</joint_control_type>
<joint_control_pid>
<p>0.1</p>
<i>0</i>
<d>0</d>
<iMax>0.0</iMax>
<iMin>0.0</iMin>
<cmdMax>2</cmdMax>
<cmdMin>-2</cmdMin>
</joint_control_pid>
<joint_name>zephyr_delta_wing::propeller_joint</joint_name>
</channel>
<channel name="rotor6">
<input_index>5</input_index>
<input_offset>0</input_offset>
<!-- joint limits [-0.524, 0.524] -->
<input_scaling>0.524</input_scaling>
<zero_position_disarmed>0</zero_position_disarmed>
<zero_position_armed>0</zero_position_armed>
<joint_control_type>position</joint_control_type>
<joint_name>zephyr_delta_wing::flap_left_joint</joint_name>
<joint_control_pid>
<p>10.0</p>
<i>0</i>
<d>0</d>
<iMax>0</iMax>
<iMin>0</iMin>
<cmdMax>20</cmdMax>
<cmdMin>-20</cmdMin>
</joint_control_pid>
</channel>
<channel name="rotor7">
<input_index>6</input_index>
<input_offset>0</input_offset>
<!-- joint limits [-0.524, 0.524] -->
<input_scaling>0.524</input_scaling>
<zero_position_disarmed>0</zero_position_disarmed>
<zero_position_armed>0</zero_position_armed>
<joint_control_type>position</joint_control_type>
<joint_name>zephyr_delta_wing::flap_right_joint</joint_name>
<joint_control_pid>
<p>10.0</p>
<i>0</i>
<d>0</d>
<iMax>0</iMax>
<iMin>0</iMin>
<cmdMax>20</cmdMax>
<cmdMin>-20</cmdMin>
</joint_control_pid>
</channel>
<channel name="rotor8">
<input_index>7</input_index>
<input_offset>0</input_offset>
<input_scaling>0.524</input_scaling>
<zero_position_disarmed>0</zero_position_disarmed>
<zero_position_armed>0</zero_position_armed>
<joint_control_type>position</joint_control_type>
</channel>
</control_channels>
</plugin>
</gazebo>
</xacro:macro>
<!-- Macro to add an IMU. -->
<xacro:macro name="imu_plugin_macro"
params="namespace imu_suffix parent_link imu_topic
mass_imu_sensor gyroscope_noise_density gyroscopoe_random_walk
gyroscope_bias_correlation_time gyroscope_turn_on_bias_sigma
accelerometer_noise_density accelerometer_random_walk
accelerometer_bias_correlation_time accelerometer_turn_on_bias_sigma
*inertia *origin">
<!-- IMU link -->
<link name="${namespace}/imu${imu_suffix}_link">
<inertial>
<xacro:insert_block name="inertia" />
<mass value="${mass_imu_sensor}" /> <!-- [kg] -->
<origin xyz="0 0 0" rpy="0 0 0" />
</inertial>
</link>
<!-- IMU joint -->
<!-- For some reason, PX4 (and RotorS before them) used a "revolute" joint. Since IMU should be fixed relative to the drone frame, I'm changing this to "fixed", we'll see how that goes. -sfalexrog -->
<joint name="${namespace}/imu${imu_suffix}_joint" type="fixed">
<xacro:insert_block name="origin" />
<parent link="${parent_link}" />
<child link="${namespace}/imu${imu_suffix}_link" />
<!-- <limit upper="0" lower="0" effort="0" velocity="0" /> -->
</joint>
<!-- Well, now I know why: because Gazebo helpfully tries to "lump together" all fixed joints. Apparently there's a way to disable this behavior that is implemented here in its entirety: -->
<gazebo reference="${namespace}/imu${imu_suffix}_joint">
<disableFixedJointLumping>true</disableFixedJointLumping>
<preserveFixedJoint>true</preserveFixedJoint>
</gazebo>
<gazebo>
<plugin filename="libgazebo_imu_plugin.so" name="rotors_gazebo_imu${imu_suffix}_plugin">
<!-- A good description of the IMU parameters can be found in the kalibr documentation:
https://github.com/ethz-asl/kalibr/wiki/IMU-Noise-Model-and-Intrinsics -->
<robotNamespace>${namespace}</robotNamespace> <!-- (string, required): ros namespace in which the messages are published -->
<linkName>${namespace}/imu${imu_suffix}_link</linkName> <!-- (string, required): name of the body which holds the IMU sensor -->
<imuTopic>${imu_topic}</imuTopic> <!-- (string): name of the sensor output topic and prefix of service names (defaults to imu) -->
<gyroscopeNoiseDensity>${gyroscope_noise_density}</gyroscopeNoiseDensity> <!-- Gyroscope noise density (two-sided spectrum) [rad/s/sqrt(Hz)] -->
<gyroscopeRandomWalk>${gyroscopoe_random_walk}</gyroscopeRandomWalk> <!-- Gyroscope bias random walk [rad/s/s/sqrt(Hz)] -->
<gyroscopeBiasCorrelationTime>${gyroscope_bias_correlation_time}</gyroscopeBiasCorrelationTime> <!-- Gyroscope bias correlation time constant [s] -->
<gyroscopeTurnOnBiasSigma>${gyroscope_turn_on_bias_sigma}</gyroscopeTurnOnBiasSigma> <!-- Gyroscope turn on bias standard deviation [rad/s] -->
<accelerometerNoiseDensity>${accelerometer_noise_density}</accelerometerNoiseDensity> <!-- Accelerometer noise density (two-sided spectrum) [m/s^2/sqrt(Hz)] -->
<accelerometerRandomWalk>${accelerometer_random_walk}</accelerometerRandomWalk> <!-- Accelerometer bias random walk. [m/s^2/s/sqrt(Hz)] -->
<accelerometerBiasCorrelationTime>${accelerometer_bias_correlation_time}</accelerometerBiasCorrelationTime> <!-- Accelerometer bias correlation time constant [s] -->
<accelerometerTurnOnBiasSigma>${accelerometer_turn_on_bias_sigma}</accelerometerTurnOnBiasSigma> <!-- Accelerometer turn on bias standard deviation [m/s^2] -->
</plugin>
</gazebo>
</xacro:macro>
<!-- Macro to add a generic odometry sensor. -->
<xacro:macro name="odometry_plugin_macro"
params="
namespace odometry_sensor_suffix parent_link pose_topic pose_with_covariance_topic
position_topic transform_topic odometry_topic parent_frame_id
mass_odometry_sensor measurement_divisor measurement_delay unknown_delay
noise_normal_position noise_normal_quaternion noise_normal_linear_velocity
noise_normal_angular_velocity noise_uniform_position
noise_uniform_quaternion noise_uniform_linear_velocity
noise_uniform_angular_velocity enable_odometry_map odometry_map
image_scale *inertia *origin">
<!-- odometry link -->
<link name="${namespace}/odometry_sensor${odometry_sensor_suffix}_link">
<inertial>
<xacro:insert_block name="inertia" />
<mass value="${mass_odometry_sensor}" /> <!-- [kg] -->
</inertial>
</link>
<!-- odometry joint -->
<joint name="${namespace}/odometry_sensor${odometry_sensor_suffix}_joint" type="revolute">
<parent link="${parent_link}" />
<xacro:insert_block name="origin" />
<child link="${namespace}/odometry_sensor${odometry_sensor_suffix}_link" />
<limit upper="0" lower="0" effort="0" velocity="0" />
</joint>
<gazebo>
<plugin filename="librotors_gazebo_odometry_plugin.so" name="odometry_sensor${odometry_sensor_suffix}">
<linkName>${namespace}/odometry_sensor${odometry_sensor_suffix}_link</linkName>
<robotNamespace>${namespace}</robotNamespace>
<poseTopic>${pose_topic}</poseTopic>
<poseWithCovarianceTopic>${pose_with_covariance_topic}</poseWithCovarianceTopic>
<positionTopic>${position_topic}</positionTopic>
<transformTopic>${transform_topic}</transformTopic>
<odometryTopic>${odometry_topic}</odometryTopic>
<parentFrameId>${parent_frame_id}</parentFrameId> <!-- Use the scoped link name here. e.g. Model::link. -->
<measurementDivisor>${measurement_divisor}</measurementDivisor> <!-- only every (seq % measurementDivisor) == 0 measurement is published [int] -->
<measurementDelay>${measurement_delay}</measurementDelay> <!-- time that measurement gets held back before it's published in [simulation cycles (int)] -->
<unknownDelay>${unknown_delay}</unknownDelay> <!-- additional delay, that just gets added to the timestamp [s] -->
<noiseNormalPosition>${noise_normal_position}</noiseNormalPosition> <!-- standard deviation of additive white gaussian noise [m] -->
<noiseNormalQuaternion>${noise_normal_quaternion}</noiseNormalQuaternion> <!-- standard deviation white gaussian noise [rad]: q_m = q*quaternionFromSmallAngleApproximation(noiseNormalQ) -->
<noiseNormalLinearVelocity>${noise_normal_linear_velocity}</noiseNormalLinearVelocity> <!-- standard deviation of additive white gaussian noise [m/s] -->
<noiseNormalAngularVelocity>${noise_normal_angular_velocity}</noiseNormalAngularVelocity> <!-- standard deviation of additive white gaussian noise [rad/s] -->
<noiseUniformPosition>${noise_uniform_position}</noiseUniformPosition> <!-- symmetric bounds of uniform noise [m] -->
<noiseUniformQuaternion>${noise_uniform_quaternion}</noiseUniformQuaternion> <!-- symmetric bounds of uniform noise [rad], computation see above -->
<noiseUniformLinearVelocity>${noise_uniform_linear_velocity}</noiseUniformLinearVelocity> <!-- symmetric bounds of uniform noise [m/s] -->
<noiseUniformAngularVelocity>${noise_uniform_angular_velocity}</noiseUniformAngularVelocity> <!-- symmetric bounds of uniform noise [rad/s] -->
<xacro:if value="${enable_odometry_map}">
<covarianceImage>package://rotors_gazebo/resource/${odometry_map}</covarianceImage>
<covarianceImageScale>${image_scale}</covarianceImageScale>
</xacro:if>
</plugin>
</gazebo>
</xacro:macro>
<!-- Macro to add the wind plugin. -->
<xacro:macro name="wind_plugin_macro"
params="namespace xyz_offset wind_direction wind_force_mean
wind_gust_direction wind_gust_duration wind_gust_start
wind_gust_force_mean">
<gazebo>
<plugin filename="libgazebo_wind_plugin.so" name="wind_plugin">
<frameId>base_link</frameId>
<linkName>base_link</linkName>
<robotNamespace>${namespace}</robotNamespace>
<xyzOffset>${xyz_offset}</xyzOffset> <!-- [m] [m] [m] -->
<windDirection>${wind_direction}</windDirection>
<windForceMean>${wind_force_mean}</windForceMean> <!-- [N] -->
<windGustDirection>${wind_gust_direction}</windGustDirection>
<windGustDuration>${wind_gust_duration}</windGustDuration> <!-- [s] -->
<windGustStart>${wind_gust_start}</windGustStart> <!-- [s] -->
<windGustForceMean>${wind_gust_force_mean}</windGustForceMean> <!-- [N] -->
</plugin>
</gazebo>
</xacro:macro>
<!-- VI sensor macros -->
<!-- Macro to add a VI-sensor camera. -->
<xacro:macro name="vi_sensor_camera_macro"
params="namespace parent_link camera_suffix frame_rate *origin">
<xacro:camera_macro
namespace="${namespace}"
parent_link="${parent_link}"
camera_suffix="${camera_suffix}"
frame_rate="${frame_rate}"
horizontal_fov="1.3962634"
image_width="752"
image_height="480"
image_format="L8"
min_distance="0.02"
max_distance="30"
noise_mean="0.0"
noise_stddev="0.007"
enable_visual="false">
<cylinder length="0.01" radius="0.007" />
<xacro:insert_block name="origin" />
</xacro:camera_macro>
</xacro:macro>
<!-- Macro to add a depth camera on the VI-sensor. -->
<xacro:macro name="vi_sensor_depth_macro"
params="namespace parent_link camera_suffix frame_rate *origin">
<link name="${namespace}/camera_${camera_suffix}_link">
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<cylinder length="0.01" radius="0.007" />
</geometry>
</collision>
<inertial>
<mass value="1e-5" />
<origin xyz="0 0 0" rpy="0 0 0" />
<inertia ixx="1e-6" ixy="0" ixz="0" iyy="1e-6" iyz="0" izz="1e-6" />
</inertial>
</link>
<joint name="${namespace}/camera_${camera_suffix}_joint" type="fixed">
<xacro:insert_block name="origin" />
<parent link="${parent_link}" />
<child link="${namespace}/camera_${camera_suffix}_link" />
<limit upper="0" lower="0" effort="0" velocity="0" />
</joint>
<!-- Optical center of camera -->
<link name="${namespace}/camera_${camera_suffix}_optical_center_link" />
<joint name="${namespace}/camera_${camera_suffix}_optical_center_joint" type="fixed">
<origin xyz="0 0 0" rpy="${-PI/2} 0 ${-PI/2}" />
<parent link="${namespace}/camera_${camera_suffix}_link" />
<child link="${namespace}/camera_${camera_suffix}_optical_center_link" />
<limit upper="0" lower="0" effort="0" velocity="0" />
</joint>
<gazebo reference="${namespace}/camera_${camera_suffix}_link">
<sensor type="depth" name="${namespace}_camera_{camera_suffix}">
<always_on>true</always_on>
<update_rate>${frame_rate}</update_rate>
<camera>
<horizontal_fov>2</horizontal_fov>
<image>
<format>L8</format>
<width>640</width>
<height>480</height>
</image>
<clip>
<near>0.01</near>
<far>100</far>
</clip>
</camera>
<plugin name="${namespace}_camera_{camera_suffix}" filename="libgazebo_ros_openni_kinect.so">
<robotNamespace>${namespace}</robotNamespace>
<alwaysOn>true</alwaysOn>
<baseline>0.11</baseline>
<updateRate>${frame_rate}</updateRate>
<cameraName>camera_${camera_suffix}</cameraName>
<imageTopicName>camera/image_raw</imageTopicName>
<cameraInfoTopicName>camera/camera_info</cameraInfoTopicName>
<depthImageTopicName>depth/disparity</depthImageTopicName>
<depthImageCameraInfoTopicName>depth/camera_info</depthImageCameraInfoTopicName>
<pointCloudTopicName>depth/points</pointCloudTopicName>
<frameName>camera_${camera_suffix}_optical_center_link</frameName>
<pointCloudCutoff>0.5</pointCloudCutoff>
<distortionK1>0.0</distortionK1>
<distortionK2>0.0</distortionK2>
<distortionK3>0.0</distortionK3>
<distortionT1>0.0</distortionT1>
<distortionT2>0.0</distortionT2>
</plugin>
</sensor>
</gazebo>
</xacro:macro>
<!-- VI-Sensor Macro -->
<xacro:macro name="vi_sensor_macro" params="namespace parent_link *origin">
<!-- Vi Sensor Link -->
<link name="${namespace}/vi_sensor_link">
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<box size="0.03 0.133 0.057" />
</geometry>
</collision>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="package://rotors_description/meshes/vi_sensor.dae" scale="1 1 1" />
</geometry>
</visual>
<inertial>
<mass value="0.13" />
<origin xyz="0 0 0" rpy="0 0 0" />
<inertia ixx="1e-6" ixy="0" ixz="0" iyy="1e-6" iyz="0" izz="1e-6" />
</inertial>
</link>
<joint name="{namespace}_vi_sensor_joint" type="fixed">
<xacro:insert_block name="origin" />
<parent link="${parent_link}" />
<child link="${namespace}/vi_sensor_link" />
</joint>
<!-- Cameras -->
<xacro:if value="$(arg enable_cameras)">
<!-- Left Camera -->
<xacro:vi_sensor_camera_macro
namespace="${namespace}" parent_link="${namespace}/vi_sensor_link"
camera_suffix="left" frame_rate="30.0">
<origin xyz="0.015 0.055 0.0065" rpy="0 0 0" />
</xacro:vi_sensor_camera_macro>
<!-- Right Camera -->
<xacro:vi_sensor_camera_macro namespace="${namespace}"
parent_link="${namespace}/vi_sensor_link"
camera_suffix="right" frame_rate="30.0">
<origin xyz="0.015 -0.055 0.0065" rpy="0 0 0" />
</xacro:vi_sensor_camera_macro>
</xacro:if>
<!-- Depth Sensor -->
<xacro:if value="$(arg enable_depth)">
<xacro:vi_sensor_depth_macro
namespace="${namespace}" parent_link="${namespace}/vi_sensor_link"
camera_suffix="depth" frame_rate="30.0">
<origin xyz="0.015 0.055 0.0065" rpy="0 0 0" />
</xacro:vi_sensor_depth_macro>
</xacro:if>
<!-- Groundtruth -->
<xacro:if value="$(arg enable_ground_truth)">
<!-- Odometry Sensor -->
<xacro:odometry_plugin_macro
namespace="${namespace}/ground_truth"
odometry_sensor_suffix=""
parent_link="${namespace}/vi_sensor_link"
pose_topic="pose"
pose_with_covariance_topic="pose_with_covariance"
position_topic="position"
transform_topic="transform"
odometry_topic="odometry"
parent_frame_id="world"
mass_odometry_sensor="0.00001"
measurement_divisor="1"
measurement_delay="0"
unknown_delay="0.0"
noise_normal_position="0 0 0"
noise_normal_quaternion="0 0 0"
noise_normal_linear_velocity="0 0 0"
noise_normal_angular_velocity="0 0 0"
noise_uniform_position="0 0 0"
noise_uniform_quaternion="0 0 0"
noise_uniform_linear_velocity="0 0 0"
noise_uniform_angular_velocity="0 0 0"
enable_odometry_map="false"
odometry_map=""
image_scale="">
<inertia ixx="0.00001" ixy="0.0" ixz="0.0" iyy="0.00001" iyz="0.0" izz="0.00001" /> <!-- [kg.m^2] [kg.m^2] [kg.m^2] [kg.m^2] [kg.m^2] [kg.m^2] -->
<origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0" />
</xacro:odometry_plugin_macro>
</xacro:if>
<!-- ADIS16448 IMU. -->
<xacro:imu_plugin_macro
namespace="${namespace}"
imu_suffix=""
parent_link="${namespace}/vi_sensor_link"
imu_topic="/imu"
mass_imu_sensor="0.015"
gyroscope_noise_density="0.0003394"
gyroscopoe_random_walk="0.000038785"
gyroscope_bias_correlation_time="1000.0"
gyroscope_turn_on_bias_sigma="0.0087"
accelerometer_noise_density="0.004"
accelerometer_random_walk="0.006"
accelerometer_bias_correlation_time="300.0"
accelerometer_turn_on_bias_sigma="0.1960">
<inertia ixx="0.00001" ixy="0.0" ixz="0.0" iyy="0.00001" iyz="0.0" izz="0.00001" />
<origin xyz="0.015 0 0.0113" rpy="0 0 0" />
</xacro:imu_plugin_macro>
</xacro:macro>
</robot>

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