Compare commits
140 Commits
new-readme
...
dec-depth
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fbc9c22cb3 | ||
|
|
32da94aeae | ||
|
|
207dc88579 | ||
|
|
58f6ac4b39 | ||
|
|
688e4f0ca9 | ||
|
|
7cbe823700 | ||
|
|
df681e0a79 | ||
|
|
8aad2fc363 | ||
|
|
3c8dd14c9d | ||
|
|
3a20bc3212 | ||
|
|
1105cd8750 | ||
|
|
43d7e7c70b | ||
|
|
7a1e885df1 | ||
|
|
9a9c2d5c9f | ||
|
|
eaeb146878 | ||
|
|
2452be05ff | ||
|
|
a4336a39c9 | ||
|
|
9cdf7dea41 | ||
|
|
b90dc3c020 | ||
|
|
91252d8d50 | ||
|
|
c4b94390e9 | ||
|
|
1b4167365e | ||
|
|
01ec592abb | ||
|
|
e2e2e04381 | ||
|
|
27e0189cf5 | ||
|
|
e3d89cbc4c | ||
|
|
a0ac85e0d3 | ||
|
|
83e5911110 | ||
|
|
05d634d2d3 | ||
|
|
4967d651bd | ||
|
|
91f948d3f4 | ||
|
|
ebf55244f4 | ||
|
|
5b6d08e25d | ||
|
|
8036214406 | ||
|
|
5d3c8c89cb | ||
|
|
2075fa52ef | ||
|
|
b0e1e1ffae | ||
|
|
4482f973db | ||
|
|
b1c7ee6b66 | ||
|
|
ff9e669352 | ||
|
|
6c8291749f | ||
|
|
039d2438cd | ||
|
|
048a605f2f | ||
|
|
0e0d4fe5cc | ||
|
|
37bbd7522c | ||
|
|
f206b2ea18 | ||
|
|
ae7f3ccfbc | ||
|
|
da28c61f1e | ||
|
|
b7ce588b07 | ||
|
|
f663a62c1e | ||
|
|
5b370ee96b | ||
|
|
e0512c209e | ||
|
|
7385f673de | ||
|
|
5629b0a9b3 | ||
|
|
9de0034fb9 | ||
|
|
ae29fe00d3 | ||
|
|
7295c6c040 | ||
|
|
11bdf2d3da | ||
|
|
89ccca3c30 | ||
|
|
50218a8822 | ||
|
|
11d5da5db2 | ||
|
|
757129782e | ||
|
|
f7c1f4330d | ||
|
|
b36292cdb8 | ||
|
|
a9f6fe329b | ||
|
|
f89cc92736 | ||
|
|
60755fa1b5 | ||
|
|
792352d072 | ||
|
|
2f6d9639c1 | ||
|
|
d52175bb30 | ||
|
|
be6e894b80 | ||
|
|
2f6125ce54 | ||
|
|
c6a238c671 | ||
|
|
28851f39ad | ||
|
|
4a2fbf3e22 | ||
|
|
c3b549df0d | ||
|
|
21d922beb2 | ||
|
|
88ef00e043 | ||
|
|
1b85a9cdf2 | ||
|
|
d5a79babad | ||
|
|
4d80e6f6cf | ||
|
|
8ce4de191b | ||
|
|
ecaab1650f | ||
|
|
a62107132d | ||
|
|
423490304b | ||
|
|
ee9c956fc7 | ||
|
|
14e49a4f7b | ||
|
|
d4d25c61a2 | ||
|
|
106209d79b | ||
|
|
333cf9655f | ||
|
|
2ddf831842 | ||
|
|
68f23babcc | ||
|
|
4f0a099152 | ||
|
|
acfb858598 | ||
|
|
c0f15fc1e6 | ||
|
|
e94e552da3 | ||
|
|
6b5e3464f1 | ||
|
|
768c0be5a5 | ||
|
|
892c6f853b | ||
|
|
8b690900b9 | ||
|
|
17c210919d | ||
|
|
ebeb3f58d6 | ||
|
|
3b19346a44 | ||
|
|
32d27f3f66 | ||
|
|
585af026d4 | ||
|
|
b52f702723 | ||
|
|
3e4f1ab8a9 | ||
|
|
513f4f419a | ||
|
|
6db5602a5a | ||
|
|
fb17a3f926 | ||
|
|
70e1675f9a | ||
|
|
e03eaa51c4 | ||
|
|
b346af92ae | ||
|
|
cb2cf48f39 | ||
|
|
ebec850010 | ||
|
|
5a8db188f6 | ||
|
|
4376bbb723 | ||
|
|
1d694e1a15 | ||
|
|
54de7fca3a | ||
|
|
32f5584082 | ||
|
|
d7c0fb33ff | ||
|
|
d16e7bf155 | ||
|
|
26bd1e2d8f | ||
|
|
e12577cf0e | ||
|
|
12544a69af | ||
|
|
f471280bef | ||
|
|
2ff9887ac4 | ||
|
|
2ecfb28a5d | ||
|
|
091483226f | ||
|
|
af16414c77 | ||
|
|
696214e717 | ||
|
|
15fd156ce0 | ||
|
|
82bfb961a4 | ||
|
|
c50637c60f | ||
|
|
a26f0f41e7 | ||
|
|
28690491c2 | ||
|
|
ca1323faf3 | ||
|
|
b713b49f3f | ||
|
|
62a77519e6 | ||
|
|
e1bf8aade5 |
2
.gitattributes
vendored
@@ -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
@@ -4,3 +4,5 @@
|
||||
node_modules/
|
||||
_book/
|
||||
package-lock.json
|
||||
clover_blocks/programs/*.*
|
||||
!clover_blocks/programs/examples/*
|
||||
|
||||
@@ -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
|
||||
},
|
||||
|
||||
24
.travis.yml
@@ -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}
|
||||
@@ -73,8 +74,11 @@ jobs:
|
||||
node_js:
|
||||
- "10"
|
||||
before_script:
|
||||
- sudo sh -c "echo ttf-mscorefonts-installer msttcorefonts/accepted-mscorefonts-eula select true | debconf-set-selections"
|
||||
- sudo apt update && sudo apt install -y calibre msttcorefonts
|
||||
- npm install gitbook-cli -g
|
||||
- npm install markdownlint-cli -g
|
||||
- npm install svgexport -g
|
||||
- gitbook -V
|
||||
- markdownlint -V
|
||||
script:
|
||||
@@ -83,6 +87,7 @@ jobs:
|
||||
- ./check_unused_assets.py
|
||||
- gitbook install
|
||||
- gitbook build
|
||||
- gitbook pdf ./ _book/clover.pdf
|
||||
deploy:
|
||||
provider: pages
|
||||
local_dir: _book
|
||||
@@ -95,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
|
||||
@@ -110,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/
|
||||
|
||||
12
README.md
@@ -1,12 +1,14 @@
|
||||
# COEX Clover Drone Kit
|
||||
# clover🍀: create autonomous drones easily
|
||||
|
||||
<img src="docs/assets/clever4-front-white.png" align="right" width="400px" alt="Clover Drone">
|
||||
<img src="docs/assets/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
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
Generate map file for aruco_map nodelet.
|
||||
|
||||
Usage:
|
||||
genmap.py <length> <x> <y> <dist_x> <dist_y> [<first>] [--top-left | --bottom-left]
|
||||
genmap.py <length> <x> <y> <dist_x> <dist_y> [<first>] [<x0>] [<y0>] [--top-left | --bottom-left]
|
||||
genmap.py (-h | --help)
|
||||
|
||||
Options:
|
||||
@@ -23,6 +23,8 @@ Options:
|
||||
<dist_x> Distance between markers along X axis
|
||||
<dist_y> Distance between markers along Y axis
|
||||
<first> First marker ID [default: 0]
|
||||
<x0> X coordinate for the first marker [default: 0]
|
||||
<y0> Y coordinate for the first marker [default: 0]
|
||||
--top-left First marker is on top-left (default)
|
||||
--bottom-left First marker is on bottom-left
|
||||
|
||||
@@ -39,20 +41,22 @@ arguments = docopt(__doc__)
|
||||
|
||||
length = float(arguments['<length>'])
|
||||
first = int(arguments['<first>'] if arguments['<first>'] is not None else 0)
|
||||
x0 = float(arguments['<x0>'] if arguments['<x0>'] is not None else 0)
|
||||
y0 = float(arguments['<y0>'] if arguments['<y0>'] is not None else 0)
|
||||
markers_x = int(arguments['<x>'])
|
||||
markers_y = int(arguments['<y>'])
|
||||
dist_x = float(arguments['<dist_x>'])
|
||||
dist_y = float(arguments['<dist_y>'])
|
||||
bottom_left = arguments['--bottom-left']
|
||||
|
||||
max_y = (markers_y - 1) * dist_y
|
||||
max_y = y0 + (markers_y - 1) * dist_y
|
||||
|
||||
print('# id\tlength\tx\ty\tz\trot_z\trot_y\trot_x')
|
||||
for y in range(markers_y):
|
||||
for x in range(markers_x):
|
||||
pos_x = x * dist_x
|
||||
pos_y = y * dist_y
|
||||
pos_x = x0 + x * dist_x
|
||||
pos_y = y0 + y * dist_y
|
||||
if not bottom_left:
|
||||
pos_y = max_y - pos_y
|
||||
print('{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\t'.format(first, length, pos_x, pos_y, 0, 0, 0, 0))
|
||||
print('{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}'.format(first, length, pos_x, pos_y, 0, 0, 0, 0))
|
||||
first += 1
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -32,7 +32,7 @@ monkey --version
|
||||
pigpiod -v
|
||||
i2cdetect -V
|
||||
butterfly -h
|
||||
espeak --version
|
||||
# espeak --version
|
||||
mjpg_streamer --version
|
||||
|
||||
# ros stuff
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
```
|
||||
|
||||
@@ -10,9 +10,12 @@
|
||||
<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}"/>
|
||||
|
||||
@@ -53,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)"/>
|
||||
@@ -65,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">
|
||||
|
||||
@@ -2,13 +2,17 @@
|
||||
<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"/>
|
||||
@@ -31,7 +35,7 @@
|
||||
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>
|
||||
</node>
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
87
clover_blocks/CMakeLists.txt
Normal file
@@ -0,0 +1,87 @@
|
||||
cmake_minimum_required(VERSION 2.8.3)
|
||||
project(clover_blocks)
|
||||
|
||||
find_package(catkin REQUIRED COMPONENTS message_generation)
|
||||
|
||||
add_message_files(
|
||||
FILES
|
||||
Prompt.msg
|
||||
)
|
||||
|
||||
add_service_files(
|
||||
FILES
|
||||
Run.srv
|
||||
Load.srv
|
||||
Store.srv
|
||||
)
|
||||
|
||||
generate_messages(
|
||||
DEPENDENCIES
|
||||
# std_msgs # Or other packages containing msgs
|
||||
)
|
||||
|
||||
catkin_package(
|
||||
# INCLUDE_DIRS include
|
||||
# LIBRARIES roslaunch_editor
|
||||
CATKIN_DEPENDS message_runtime
|
||||
# DEPENDS system_lib
|
||||
)
|
||||
|
||||
#############
|
||||
## Install ##
|
||||
#############
|
||||
|
||||
# all install targets should use catkin DESTINATION variables
|
||||
# See http://ros.org/doc/api/catkin/html/adv_user_guide/variables.html
|
||||
|
||||
## Mark executable scripts (Python etc.) for installation
|
||||
## in contrast to setup.py, you can choose the destination
|
||||
# install(PROGRAMS
|
||||
# scripts/my_python_script
|
||||
# DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
|
||||
# )
|
||||
|
||||
## Mark executables for installation
|
||||
## See http://docs.ros.org/melodic/api/catkin/html/howto/format1/building_executables.html
|
||||
# install(TARGETS ${PROJECT_NAME}_node
|
||||
# RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
|
||||
# )
|
||||
|
||||
## Mark libraries for installation
|
||||
## See http://docs.ros.org/melodic/api/catkin/html/howto/format1/building_libraries.html
|
||||
# install(TARGETS ${PROJECT_NAME}
|
||||
# ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
|
||||
# LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
|
||||
# RUNTIME DESTINATION ${CATKIN_GLOBAL_BIN_DESTINATION}
|
||||
# )
|
||||
|
||||
## Mark cpp header files for installation
|
||||
# install(DIRECTORY include/${PROJECT_NAME}/
|
||||
# DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION}
|
||||
# FILES_MATCHING PATTERN "*.h"
|
||||
# PATTERN ".svn" EXCLUDE
|
||||
# )
|
||||
|
||||
## Mark other files for installation (e.g. launch and bag files, etc.)
|
||||
# install(FILES
|
||||
# # myfile1
|
||||
# # myfile2
|
||||
# DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}
|
||||
# )
|
||||
|
||||
catkin_install_python(PROGRAMS src/clover_blocks
|
||||
DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
|
||||
)
|
||||
|
||||
#############
|
||||
## Testing ##
|
||||
#############
|
||||
|
||||
## Add gtest based cpp test target and link libraries
|
||||
# catkin_add_gtest(${PROJECT_NAME}-test test/test_roslaunch_editor.cpp)
|
||||
# if(TARGET ${PROJECT_NAME}-test)
|
||||
# target_link_libraries(${PROJECT_NAME}-test ${PROJECT_NAME})
|
||||
# endif()
|
||||
|
||||
## Add folders to be run by python nosetests
|
||||
# catkin_add_nosetests(test)
|
||||
54
clover_blocks/README.md
Normal file
@@ -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.
|
||||
2
clover_blocks/msg/Prompt.msg
Normal file
@@ -0,0 +1,2 @@
|
||||
string message # message for prompt
|
||||
string id # user response should be published to ~input/<id> topic
|
||||
48
clover_blocks/package.xml
Normal file
@@ -0,0 +1,48 @@
|
||||
<?xml version="1.0"?>
|
||||
<package format="2">
|
||||
<name>clover_blocks</name>
|
||||
<version>0.0.0</version>
|
||||
<description>Blockly programming support for Clover</description>
|
||||
<maintainer email="okalachev@gmail.com">Oleg Kalachev</maintainer>
|
||||
<license>MIT</license>
|
||||
|
||||
<!-- Url tags are optional, but multiple are allowed, one per tag -->
|
||||
<!-- Optional attribute type can be: website, bugtracker, or repository -->
|
||||
<url type="repository">https://github.com/CopterExpress/clover</url>
|
||||
|
||||
<!-- Author tags are optional, multiple are allowed, one per tag -->
|
||||
<!-- Authors do not have to be maintainers, but could be -->
|
||||
<author email="okalachev@gmail.com">Oleg Kalachev</author>
|
||||
|
||||
<!-- The *depend tags are used to specify dependencies -->
|
||||
<!-- Dependencies can be catkin packages or system dependencies -->
|
||||
<!-- Examples: -->
|
||||
<!-- Use depend as a shortcut for packages that are both build and exec dependencies -->
|
||||
<!-- <depend>roscpp</depend> -->
|
||||
<!-- Note that this is equivalent to the following: -->
|
||||
<!-- <build_depend>roscpp</build_depend> -->
|
||||
<!-- <exec_depend>roscpp</exec_depend> -->
|
||||
<!-- Use build_depend for packages you need at compile time: -->
|
||||
<!-- <build_depend>message_generation</build_depend> -->
|
||||
<!-- Use build_export_depend for packages you need in order to build against this package: -->
|
||||
<!-- <build_export_depend>message_generation</build_export_depend> -->
|
||||
<!-- Use buildtool_depend for build tool packages: -->
|
||||
<!-- <buildtool_depend>catkin</buildtool_depend> -->
|
||||
<!-- Use exec_depend for packages you need at runtime: -->
|
||||
<!-- <exec_depend>message_runtime</exec_depend> -->
|
||||
<!-- Use test_depend for packages you need only for testing: -->
|
||||
<!-- <test_depend>gtest</test_depend> -->
|
||||
<!-- Use doc_depend for packages you need only for building documentation: -->
|
||||
<!-- <doc_depend>doxygen</doc_depend> -->
|
||||
<buildtool_depend>catkin</buildtool_depend>
|
||||
|
||||
<depend>message_generation</depend>
|
||||
<depend>message_runtime</depend>
|
||||
<depend>rospy</depend>
|
||||
|
||||
<!-- The export tag contains other, unspecified, tags -->
|
||||
<export>
|
||||
<!-- Other tools can request additional information be placed here -->
|
||||
|
||||
</export>
|
||||
</package>
|
||||
73
clover_blocks/programs/examples/aruco-map.xml
Normal file
@@ -0,0 +1,73 @@
|
||||
<xml xmlns="https://developers.google.com/blockly/xml">
|
||||
<block type="take_off" id="w4,Hi[}0b.mX68K|hOJv" x="38" y="63">
|
||||
<field name="WAIT">TRUE</field>
|
||||
<value name="ALT">
|
||||
<shadow type="math_number" id="q!Ln!d`jutn*3Gy56B0A">
|
||||
<field name="NUM">1.5</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<next>
|
||||
<block type="controls_repeat_ext" id="fD/HWb]kb!^pYK{UkM;0">
|
||||
<value name="TIMES">
|
||||
<shadow type="math_number" id="%FP0k|kaf~A`0N;M{Oiy">
|
||||
<field name="NUM">10</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<statement name="DO">
|
||||
<block type="navigate" id="qM5ROiq4r:$lq}8U_wol">
|
||||
<field name="FRAME_ID">ARUCO_MAP</field>
|
||||
<field name="WAIT">TRUE</field>
|
||||
<value name="X">
|
||||
<shadow type="math_number" id="=MBV;9zGH8S`Xh57/E;C">
|
||||
<field name="NUM">0</field>
|
||||
</shadow>
|
||||
<block type="text_prompt_ext" id="rUDMP2~yFR!7E`Q]pR/i">
|
||||
<mutation type="NUMBER"></mutation>
|
||||
<field name="TYPE">NUMBER</field>
|
||||
<value name="TEXT">
|
||||
<shadow type="text" id="z+Q3vS?)kxHl?HTcuI?(">
|
||||
<field name="TEXT">Enter X</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
</value>
|
||||
<value name="Y">
|
||||
<shadow type="math_number" id="%$`av8A.w}vNqG-]1GOn">
|
||||
<field name="NUM">0</field>
|
||||
</shadow>
|
||||
<block type="text_prompt_ext" id=".AjhH^Rh*i}+14s)K-:g">
|
||||
<mutation type="NUMBER"></mutation>
|
||||
<field name="TYPE">NUMBER</field>
|
||||
<value name="TEXT">
|
||||
<shadow type="text" id="x#)(kg5PB;0uZA5;{(GT">
|
||||
<field name="TEXT">Enter Y</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
</value>
|
||||
<value name="Z">
|
||||
<shadow type="math_number" id="T,]q+-@V;tCYLy$-(o3}">
|
||||
<field name="NUM">1.5</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="ID">
|
||||
<shadow type="math_number" id="VRo?s#0d)Gl@9fsK@gR=">
|
||||
<field name="NUM">0</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="SPEED">
|
||||
<shadow type="math_number" id="mmZc+d=W})w.AN*B_dSe">
|
||||
<field name="NUM">0.5</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
</statement>
|
||||
<next>
|
||||
<block type="land" id="JB%yj/X~K=8A2=Q*7cRs">
|
||||
<field name="WAIT">TRUE</field>
|
||||
</block>
|
||||
</next>
|
||||
</block>
|
||||
</next>
|
||||
</block>
|
||||
</xml>
|
||||
64
clover_blocks/programs/examples/aruco-marker.xml
Normal file
@@ -0,0 +1,64 @@
|
||||
<xml xmlns="https://developers.google.com/blockly/xml">
|
||||
<block type="take_off" id="R@=3~Fq;AxFaRO{ZDFUM" x="37" y="63">
|
||||
<field name="WAIT">TRUE</field>
|
||||
<value name="ALT">
|
||||
<shadow type="math_number" id="cf8/,N48N;Fvn@EWB%,B">
|
||||
<field name="NUM">1.5</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<next>
|
||||
<block type="controls_repeat_ext" id="qc$F96[A^%lj,-l[fhX+">
|
||||
<value name="TIMES">
|
||||
<shadow type="math_number" id="v0QgNIpGzMIb`P@8D@}.">
|
||||
<field name="NUM">3</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<statement name="DO">
|
||||
<block type="navigate" id="]kCOMt@NSf$9:N1Fx~dE">
|
||||
<field name="FRAME_ID">ARUCO</field>
|
||||
<field name="WAIT">TRUE</field>
|
||||
<value name="X">
|
||||
<shadow type="math_number" id="}(7P5,{6%^::Gzb;kRaf">
|
||||
<field name="NUM">0</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="Y">
|
||||
<shadow type="math_number" id="hz#sFvY/;IL5IRLQVa.K">
|
||||
<field name="NUM">0</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="Z">
|
||||
<shadow type="math_number" id="n0ULZn64%k.:,l(,D?TZ">
|
||||
<field name="NUM">1</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="ID">
|
||||
<shadow type="math_number" id="P4^twyJo6as#G1}^Dlq8">
|
||||
<field name="NUM">0</field>
|
||||
</shadow>
|
||||
<block type="text_prompt_ext" id="ot;{twL$8`H*Wiafz3D,">
|
||||
<mutation type="NUMBER"></mutation>
|
||||
<field name="TYPE">NUMBER</field>
|
||||
<value name="TEXT">
|
||||
<shadow type="text" id="@P[TyPj[n1*;EcUC?lwF">
|
||||
<field name="TEXT">Enter marker ID</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
</value>
|
||||
<value name="SPEED">
|
||||
<shadow type="math_number" id="WrsUXacZ2w)D]d?:@ec_">
|
||||
<field name="NUM">0.5</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
</statement>
|
||||
<next>
|
||||
<block type="land" id="G5+Fs.6Y4(K9Dw`wjgua">
|
||||
<field name="WAIT">TRUE</field>
|
||||
</block>
|
||||
</next>
|
||||
</block>
|
||||
</next>
|
||||
</block>
|
||||
</xml>
|
||||
97
clover_blocks/programs/examples/flight-led.xml
Normal file
@@ -0,0 +1,97 @@
|
||||
<xml xmlns="https://developers.google.com/blockly/xml">
|
||||
<block type="take_off" id="R@=3~Fq;AxFaRO{ZDFUM" x="162" y="63">
|
||||
<field name="WAIT">TRUE</field>
|
||||
<value name="ALT">
|
||||
<shadow type="math_number" id="cf8/,N48N;Fvn@EWB%,B">
|
||||
<field name="NUM">1</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<next>
|
||||
<block type="set_effect" id="5K8qDD0/@Ab!k+ad6IPj">
|
||||
<field name="EFFECT">FILL</field>
|
||||
<value name="COLOR">
|
||||
<shadow type="colour_picker" id="=*Y*L:1DI7y$.RR$6jNQ">
|
||||
<field name="COLOUR">#ff0000</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<next>
|
||||
<block type="navigate" id="kE7TL3+{PI.GiXby~SyO">
|
||||
<field name="FRAME_ID">BODY</field>
|
||||
<field name="WAIT">TRUE</field>
|
||||
<value name="X">
|
||||
<shadow type="math_number" id="W$eQ*Xv/e(*`e%8--W:P">
|
||||
<field name="NUM">1</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="Y">
|
||||
<shadow type="math_number" id="i~r{%5mpU|}FL|9p!5yG">
|
||||
<field name="NUM">0</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="Z">
|
||||
<shadow type="math_number" id="fZ{Cz~^LoWvM#,#;!co_">
|
||||
<field name="NUM">0</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="ID">
|
||||
<shadow type="math_number" id="z$Hj|-?4e3zy@Jx$/THh">
|
||||
<field name="NUM">0</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="SPEED">
|
||||
<shadow type="math_number" id="FjRM:aex=:/l_``^zcO]">
|
||||
<field name="NUM">0.5</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<next>
|
||||
<block type="set_effect" id="Q^DQoW/6G$7#YPlXc}%]">
|
||||
<field name="EFFECT">FILL</field>
|
||||
<value name="COLOR">
|
||||
<shadow type="colour_picker" id="mjOY6]=uMY4$r8^n:n|}">
|
||||
<field name="COLOUR">#3333ff</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<next>
|
||||
<block type="navigate" id=";|j@sXR{9)9@ran7g[;~">
|
||||
<field name="FRAME_ID">BODY</field>
|
||||
<field name="WAIT">TRUE</field>
|
||||
<value name="X">
|
||||
<shadow type="math_number" id="TH(htYn}*/a{[::sFQ[x">
|
||||
<field name="NUM">-1</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="Y">
|
||||
<shadow type="math_number" id="h@`=v?y+Tuh,I)wv?+`f">
|
||||
<field name="NUM">0</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="Z">
|
||||
<shadow type="math_number" id="pzz:U%?m/~~B$-FF!fD?">
|
||||
<field name="NUM">0</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="ID">
|
||||
<shadow type="math_number" id="Ze2Nun;[K{X.1kE0anMF">
|
||||
<field name="NUM">0</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="SPEED">
|
||||
<shadow type="math_number" id="+[A5TSXF:hw4T1M]e~Wu">
|
||||
<field name="NUM">0.5</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<next>
|
||||
<block type="land" id="G5+Fs.6Y4(K9Dw`wjgua">
|
||||
<field name="WAIT">TRUE</field>
|
||||
</block>
|
||||
</next>
|
||||
</block>
|
||||
</next>
|
||||
</block>
|
||||
</next>
|
||||
</block>
|
||||
</next>
|
||||
</block>
|
||||
</next>
|
||||
</block>
|
||||
</xml>
|
||||
308
clover_blocks/programs/examples/flip.xml
Normal file
@@ -0,0 +1,308 @@
|
||||
<xml xmlns="https://developers.google.com/blockly/xml">
|
||||
<variables>
|
||||
<variable id="MEu_bi$Xk5e(#Q`Yu=k0">start_x</variable>
|
||||
<variable id="GtS.)8^|(~RL*T4.~#VV">start_y</variable>
|
||||
<variable id="jksdQ,]0F47Xan8{_(?+">start_z</variable>
|
||||
</variables>
|
||||
<block type="procedures_defnoreturn" id="Uo{/lI4(1i[O|U^jyh]y" x="188" y="62">
|
||||
<field name="NAME">flip</field>
|
||||
<comment pinned="false" h="80" w="160">Describe this function...</comment>
|
||||
<statement name="STACK">
|
||||
<block type="procedures_callnoreturn" id="G7`l2OBCg~ISh(XsrkRv">
|
||||
<mutation name="memorize position"></mutation>
|
||||
<next>
|
||||
<block type="setpoint" id="8%(,J3=!CEUALDr5gFT+">
|
||||
<field name="TYPE">RATES</field>
|
||||
<field name="FRAME_ID">BODY</field>
|
||||
<comment pinned="false" h="80" w="160">bump up</comment>
|
||||
<value name="VX">
|
||||
<shadow type="math_number" id="!@rHW+cG*U6.Py:.;qc)">
|
||||
<field name="NUM">0</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="VY">
|
||||
<shadow type="math_number" id="n#I~l7$%1m^@Oz+w;k[H">
|
||||
<field name="NUM">0</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="VZ">
|
||||
<shadow type="math_number" id="l0EphD#!^4jX#%UA-rxX">
|
||||
<field name="NUM">0</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="PITCH">
|
||||
<shadow type="math_number" id="Ad.[@07QtZcb;(:^,Kjg">
|
||||
<field name="NUM">0</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="ROLL">
|
||||
<shadow type="math_number" id="50,,4i.p6KR:e3/l|k2i">
|
||||
<field name="NUM">0</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="YAW">
|
||||
<shadow type="math_number" id="(:O}={3*LZ#KDiC@;b[F">
|
||||
<field name="NUM">0</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="THRUST">
|
||||
<shadow type="math_number" id="EC6yU;oHb#y-8?W`B/JC">
|
||||
<field name="NUM">1</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="ID">
|
||||
<shadow type="math_number" id="k.X^bynEZH2dyG1=Y6lr">
|
||||
<field name="NUM">0</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<next>
|
||||
<block type="wait" id="#ga)cR4TsF[9t?;zc1H~">
|
||||
<value name="TIME">
|
||||
<shadow type="math_number" id="zg5fX7[;O,lQt*Txg|tn">
|
||||
<field name="NUM">0.2</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<next>
|
||||
<block type="setpoint" id="j,yf}/lrx*2J}X%^5LG{">
|
||||
<field name="TYPE">RATES</field>
|
||||
<field name="FRAME_ID">BODY</field>
|
||||
<comment pinned="false" h="80" w="160">maximum pitch rate</comment>
|
||||
<value name="VX">
|
||||
<shadow type="math_number" id="^WF12EBPyIXhZdpsj;=D">
|
||||
<field name="NUM">0</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="VY">
|
||||
<shadow type="math_number" id="C/uBsz]ObzwyHxGqzUP_">
|
||||
<field name="NUM">0</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="VZ">
|
||||
<shadow type="math_number" id="4?)O3G/j`UuVTMImLq{T">
|
||||
<field name="NUM">0</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="PITCH">
|
||||
<shadow type="math_number" id="sdCVFTmVkc0v2|*3Fy~h">
|
||||
<field name="NUM">30</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="ROLL">
|
||||
<shadow type="math_number" id="V1o!AV[`!OnhrUP4~$=!">
|
||||
<field name="NUM">0</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="YAW">
|
||||
<shadow type="math_number" id="0e;`*B;ghV*%=ETSvzSM">
|
||||
<field name="NUM">0</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="THRUST">
|
||||
<shadow type="math_number" id="K@VAeOH2bEwc#@7_M!@l">
|
||||
<field name="NUM">0.2</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="ID">
|
||||
<shadow type="math_number" id="c6ycgS!D}bIK8pl8oAv+">
|
||||
<field name="NUM">0</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<next>
|
||||
<block type="controls_whileUntil" id="H_6do.wf6H.E$?5;$uw8">
|
||||
<field name="MODE">WHILE</field>
|
||||
<value name="BOOL">
|
||||
<block type="logic_boolean" id="pu%GH9}wZvUV{RnGk6Se">
|
||||
<field name="BOOL">TRUE</field>
|
||||
</block>
|
||||
</value>
|
||||
<statement name="DO">
|
||||
<block type="controls_if" id="l0w?CSzU]S(K9eL-1(1r">
|
||||
<value name="IF0">
|
||||
<block type="logic_operation" id="gtXXJ7OFs=1BynL@|R{.">
|
||||
<field name="OP">OR</field>
|
||||
<value name="A">
|
||||
<block type="logic_compare" id="2%9ns$Jk(8M[8$~9o(=*">
|
||||
<field name="OP">GT</field>
|
||||
<value name="A">
|
||||
<block type="math_single" id="#gr7h89vpJyc8bnbWU9O">
|
||||
<field name="OP">ABS</field>
|
||||
<value name="NUM">
|
||||
<shadow type="math_number" id="5paq?]87z}}+@~NALY}.">
|
||||
<field name="NUM">9</field>
|
||||
</shadow>
|
||||
<block type="get_attitude" id="awk0-~@[jWFVQ+ZPi=Dc">
|
||||
<field name="FIELD">PITCH</field>
|
||||
</block>
|
||||
</value>
|
||||
</block>
|
||||
</value>
|
||||
<value name="B">
|
||||
<block type="math_number" id=",4p]fd#kwxy`DtCMtraW">
|
||||
<field name="NUM">90</field>
|
||||
</block>
|
||||
</value>
|
||||
</block>
|
||||
</value>
|
||||
<value name="B">
|
||||
<block type="logic_compare" id="?TPG3^?eg4l~CKM,3DfO">
|
||||
<field name="OP">GT</field>
|
||||
<value name="A">
|
||||
<block type="math_single" id="yS*/-+E3TMpZ]%`1j*Up">
|
||||
<field name="OP">ABS</field>
|
||||
<value name="NUM">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">9</field>
|
||||
</shadow>
|
||||
<block type="get_attitude" id="~E3RS!on]G(?IZ?SVA#J">
|
||||
<field name="FIELD">ROLL</field>
|
||||
</block>
|
||||
</value>
|
||||
</block>
|
||||
</value>
|
||||
<value name="B">
|
||||
<block type="math_number" id="#y??I}u#OmOTK:`!pkNS">
|
||||
<field name="NUM">90</field>
|
||||
</block>
|
||||
</value>
|
||||
</block>
|
||||
</value>
|
||||
</block>
|
||||
</value>
|
||||
<statement name="DO0">
|
||||
<block type="controls_flow_statements" id="X|V=r3BDKw9fv)u5EY3.">
|
||||
<field name="FLOW">BREAK</field>
|
||||
</block>
|
||||
</statement>
|
||||
</block>
|
||||
</statement>
|
||||
<next>
|
||||
<block type="procedures_callnoreturn" id="?;sq{#xJ+]bD`yJ#(]o1">
|
||||
<mutation name="navigate to memorized position"></mutation>
|
||||
</block>
|
||||
</next>
|
||||
</block>
|
||||
</next>
|
||||
</block>
|
||||
</next>
|
||||
</block>
|
||||
</next>
|
||||
</block>
|
||||
</next>
|
||||
</block>
|
||||
</statement>
|
||||
</block>
|
||||
<block type="procedures_defnoreturn" id="!Tv!F*.A}=]dW=C6X@-`" x="538" y="62">
|
||||
<field name="NAME">memorize position</field>
|
||||
<comment pinned="false" h="80" w="160">Describe this function...</comment>
|
||||
<statement name="STACK">
|
||||
<block type="variables_set" id="s,o|q7_A/fSV+c0E)b^k">
|
||||
<field name="VAR" id="MEu_bi$Xk5e(#Q`Yu=k0">start_x</field>
|
||||
<value name="VALUE">
|
||||
<block type="get_position" id="f#,.s%hV^19JJ;G/DO):">
|
||||
<field name="FIELD">X</field>
|
||||
<field name="FRAME_ID">MAP</field>
|
||||
<value name="ID">
|
||||
<shadow type="math_number" id="rJRRE#;#JFg}gY+34LVi">
|
||||
<field name="NUM">0</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
</value>
|
||||
<next>
|
||||
<block type="variables_set" id="E+2Pfn3JOB{r=DQ3f1b`">
|
||||
<field name="VAR" id="GtS.)8^|(~RL*T4.~#VV">start_y</field>
|
||||
<value name="VALUE">
|
||||
<block type="get_position" id="J#y+_prX(DI;j8gFO81H">
|
||||
<field name="FIELD">Y</field>
|
||||
<field name="FRAME_ID">MAP</field>
|
||||
<value name="ID">
|
||||
<shadow type="math_number" id="_bnV`SCa:|Vl#sRlYA@}">
|
||||
<field name="NUM">0</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
</value>
|
||||
<next>
|
||||
<block type="variables_set" id="t*TI?p-h#qC_kC)jM8q0">
|
||||
<field name="VAR" id="jksdQ,]0F47Xan8{_(?+">start_z</field>
|
||||
<value name="VALUE">
|
||||
<block type="get_position" id="Xh#8yLEMxT|bQ}jYvk6/">
|
||||
<field name="FIELD">Z</field>
|
||||
<field name="FRAME_ID">MAP</field>
|
||||
<value name="ID">
|
||||
<shadow type="math_number" id="z3FTv/jt^H7dVYx1`$$C">
|
||||
<field name="NUM">0</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
</value>
|
||||
</block>
|
||||
</next>
|
||||
</block>
|
||||
</next>
|
||||
</block>
|
||||
</statement>
|
||||
</block>
|
||||
<block type="procedures_defnoreturn" id="$p3=p^fBz}k5Pg1]svL," x="563" y="213">
|
||||
<field name="NAME">navigate to memorized position</field>
|
||||
<comment pinned="false" h="80" w="160">Describe this function...</comment>
|
||||
<statement name="STACK">
|
||||
<block type="navigate" id="#7b*(J_t9t)V,Arb)3;R">
|
||||
<field name="FRAME_ID">MAP</field>
|
||||
<field name="WAIT">TRUE</field>
|
||||
<value name="X">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">0</field>
|
||||
</shadow>
|
||||
<block type="variables_get" id="X#O!NcMac+a9WKL;l7?W">
|
||||
<field name="VAR" id="MEu_bi$Xk5e(#Q`Yu=k0">start_x</field>
|
||||
</block>
|
||||
</value>
|
||||
<value name="Y">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">0</field>
|
||||
</shadow>
|
||||
<block type="variables_get" id="C(=1.r0+01|e3+3qs%SD">
|
||||
<field name="VAR" id="GtS.)8^|(~RL*T4.~#VV">start_y</field>
|
||||
</block>
|
||||
</value>
|
||||
<value name="Z">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">0</field>
|
||||
</shadow>
|
||||
<block type="variables_get" id="?v;YVETEO%vUqgmiro^/">
|
||||
<field name="VAR" id="jksdQ,]0F47Xan8{_(?+">start_z</field>
|
||||
</block>
|
||||
</value>
|
||||
<value name="ID">
|
||||
<shadow type="math_number" id="ALt.9P$^_]upK}Q9_M,5">
|
||||
<field name="NUM">0</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="SPEED">
|
||||
<shadow type="math_number" id="ObO|OuF^d,1|Bx{Y`O-R">
|
||||
<field name="NUM">5</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
</statement>
|
||||
</block>
|
||||
<block type="take_off" id="I1oP(b3wChmK?WgPtULz" x="563" y="612">
|
||||
<field name="WAIT">TRUE</field>
|
||||
<value name="ALT">
|
||||
<shadow type="math_number" id="N1EX*JgURuG783yO+B^X">
|
||||
<field name="NUM">2</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<next>
|
||||
<block type="procedures_callnoreturn" id="(Dc3I{36Kt@1KHN7Ef.m">
|
||||
<mutation name="flip"></mutation>
|
||||
<next>
|
||||
<block type="land" id=";c+lM(v6`13$VK`~jZ|5">
|
||||
<field name="WAIT">TRUE</field>
|
||||
</block>
|
||||
</next>
|
||||
</block>
|
||||
</next>
|
||||
</block>
|
||||
</xml>
|
||||
184
clover_blocks/programs/examples/functions.xml
Normal file
@@ -0,0 +1,184 @@
|
||||
<xml xmlns="https://developers.google.com/blockly/xml">
|
||||
<variables>
|
||||
<variable id="MEu_bi$Xk5e(#Q`Yu=k0">start_x</variable>
|
||||
<variable id="GtS.)8^|(~RL*T4.~#VV">start_y</variable>
|
||||
<variable id="jksdQ,]0F47Xan8{_(?+">start_z</variable>
|
||||
</variables>
|
||||
<block type="procedures_defnoreturn" id="X0i)DAnNpakX]gr;CC0}" x="588" y="88">
|
||||
<field name="NAME">memorize position</field>
|
||||
<comment pinned="false" h="80" w="160">Describe this function...</comment>
|
||||
<statement name="STACK">
|
||||
<block type="variables_set" id="Ma4s0pE-i`P:(-|?c88v">
|
||||
<field name="VAR" id="MEu_bi$Xk5e(#Q`Yu=k0">start_x</field>
|
||||
<value name="VALUE">
|
||||
<block type="get_position" id="~uz[sPf[(s,15#x]3tAw">
|
||||
<field name="FIELD">X</field>
|
||||
<field name="FRAME_ID">MAP</field>
|
||||
<value name="ID">
|
||||
<shadow type="math_number" id="V#rB*5s3W^*q75}gScM(">
|
||||
<field name="NUM">0</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
</value>
|
||||
<next>
|
||||
<block type="variables_set" id="I07,0;W#gn1CNY697O9n">
|
||||
<field name="VAR" id="GtS.)8^|(~RL*T4.~#VV">start_y</field>
|
||||
<value name="VALUE">
|
||||
<block type="get_position" id="z(5A+nNX@tWO[J/X6Hb(">
|
||||
<field name="FIELD">Y</field>
|
||||
<field name="FRAME_ID">MAP</field>
|
||||
<value name="ID">
|
||||
<shadow type="math_number" id="6dM^zy88YqX`3ch{IH{1">
|
||||
<field name="NUM">0</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
</value>
|
||||
<next>
|
||||
<block type="variables_set" id="AGgAbxz?+u_2^h;Xo4oc">
|
||||
<field name="VAR" id="jksdQ,]0F47Xan8{_(?+">start_z</field>
|
||||
<value name="VALUE">
|
||||
<block type="get_position" id="k~cMq+jvE^,Ip*kKUmw]">
|
||||
<field name="FIELD">Z</field>
|
||||
<field name="FRAME_ID">MAP</field>
|
||||
<value name="ID">
|
||||
<shadow type="math_number" id="Q8RU-5,2UG*IfCeZg0cy">
|
||||
<field name="NUM">0</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
</value>
|
||||
</block>
|
||||
</next>
|
||||
</block>
|
||||
</next>
|
||||
</block>
|
||||
</statement>
|
||||
</block>
|
||||
<block type="take_off" id=".0#}q|m$C-GD,.fM/9:/" x="137" y="162">
|
||||
<field name="WAIT">TRUE</field>
|
||||
<value name="ALT">
|
||||
<shadow type="math_number" id="H6G+npH8DS90OnBKP;qy">
|
||||
<field name="NUM">2</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<next>
|
||||
<block type="procedures_callnoreturn" id="_r_hGwEy6uksz}DtrwxW">
|
||||
<mutation name="memorize position"></mutation>
|
||||
<next>
|
||||
<block type="navigate" id="e{_?bCxQ}za#e~~A2GmJ">
|
||||
<field name="FRAME_ID">ARUCO_MAP</field>
|
||||
<field name="WAIT">TRUE</field>
|
||||
<value name="X">
|
||||
<shadow type="math_number" id="}P]lFX9(He:*{Rvp8[.q">
|
||||
<field name="NUM">0</field>
|
||||
</shadow>
|
||||
<block type="text_prompt_ext" id="L:~.WP`isNJ/TBBP75.{">
|
||||
<mutation type="NUMBER"></mutation>
|
||||
<field name="TYPE">NUMBER</field>
|
||||
<value name="TEXT">
|
||||
<shadow type="text" id="v87(;,:4$T8wgbByRv`z">
|
||||
<field name="TEXT">Input X</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
</value>
|
||||
<value name="Y">
|
||||
<shadow type="math_number" id="eMbKtnF{B^P*p7zRD8SX">
|
||||
<field name="NUM">0</field>
|
||||
</shadow>
|
||||
<block type="text_prompt_ext" id="fp[sLcO^N:q}9k6zYwnN">
|
||||
<mutation type="NUMBER"></mutation>
|
||||
<field name="TYPE">NUMBER</field>
|
||||
<value name="TEXT">
|
||||
<shadow type="text" id="2Bqk0Rm@h*Vef,,:0c!+">
|
||||
<field name="TEXT">Input Y</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
</value>
|
||||
<value name="Z">
|
||||
<shadow type="math_number" id="1sLNKuRa}07Ek(eHK;/S">
|
||||
<field name="NUM">2</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="ID">
|
||||
<shadow type="math_number" id="guM,}nHUz^a?_H3(b{FW">
|
||||
<field name="NUM">0</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="SPEED">
|
||||
<shadow type="math_number" id=".EckPx#fD.[:qSk}5/-J">
|
||||
<field name="NUM">0.5</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<next>
|
||||
<block type="text_print" id="d,aouAR0nE80mk6qTAti">
|
||||
<value name="TEXT">
|
||||
<shadow type="text" id="Yr%NvOOvCnrBP8}m]bn`">
|
||||
<field name="TEXT">Point reached. Going back.</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<next>
|
||||
<block type="procedures_callnoreturn" id="B0o:6T%+wpg{O%s27:bH">
|
||||
<mutation name="navigate to memorized position"></mutation>
|
||||
<next>
|
||||
<block type="land" id="~%g;%HDPtF!lqHY}KO@/">
|
||||
<field name="WAIT">TRUE</field>
|
||||
</block>
|
||||
</next>
|
||||
</block>
|
||||
</next>
|
||||
</block>
|
||||
</next>
|
||||
</block>
|
||||
</next>
|
||||
</block>
|
||||
</next>
|
||||
</block>
|
||||
<block type="procedures_defnoreturn" id="POzAJb%[yRgBrEn9K*x{" x="587" y="362">
|
||||
<field name="NAME">navigate to memorized position</field>
|
||||
<comment pinned="false" h="80" w="160">Describe this function...</comment>
|
||||
<statement name="STACK">
|
||||
<block type="navigate" id="VX|`@L*bU=aY`4!l=Pbw">
|
||||
<field name="FRAME_ID">BODY</field>
|
||||
<field name="WAIT">TRUE</field>
|
||||
<value name="X">
|
||||
<shadow type="math_number" id="c$==spjA=Vin//l1(1V@">
|
||||
<field name="NUM">0</field>
|
||||
</shadow>
|
||||
<block type="variables_get" id="LxP$oVs*8[{CH4o^RY._">
|
||||
<field name="VAR" id="MEu_bi$Xk5e(#Q`Yu=k0">start_x</field>
|
||||
</block>
|
||||
</value>
|
||||
<value name="Y">
|
||||
<shadow type="math_number" id="a)/FEG4j=.584f/:2$?H">
|
||||
<field name="NUM">0</field>
|
||||
</shadow>
|
||||
<block type="variables_get" id="_-Ib6#usbp0NS}DZ7OGS">
|
||||
<field name="VAR" id="GtS.)8^|(~RL*T4.~#VV">start_y</field>
|
||||
</block>
|
||||
</value>
|
||||
<value name="Z">
|
||||
<shadow type="math_number" id="q*MDfPe,V]rfhC1pnltq">
|
||||
<field name="NUM">0</field>
|
||||
</shadow>
|
||||
<block type="variables_get" id="VY4xW2Y|=*AH3`s2C@)5">
|
||||
<field name="VAR" id="jksdQ,]0F47Xan8{_(?+">start_z</field>
|
||||
</block>
|
||||
</value>
|
||||
<value name="ID">
|
||||
<shadow type="math_number" id="OiuYE,a3PqUW.vst+Qc$">
|
||||
<field name="NUM">0</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="SPEED">
|
||||
<shadow type="math_number" id="X2Ou?:=kt:`aOq,#Q-)+">
|
||||
<field name="NUM">0.5</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
</statement>
|
||||
</block>
|
||||
</xml>
|
||||
91
clover_blocks/programs/examples/led-compass.xml
Normal file
@@ -0,0 +1,91 @@
|
||||
<xml xmlns="https://developers.google.com/blockly/xml">
|
||||
<block type="controls_whileUntil" id="U1it{GcGuSS:=[xiwZr1" x="113" y="113">
|
||||
<field name="MODE">WHILE</field>
|
||||
<value name="BOOL">
|
||||
<block type="logic_boolean" id="]7ZDRwde}/RqjQCX}aVW">
|
||||
<field name="BOOL">TRUE</field>
|
||||
</block>
|
||||
</value>
|
||||
<statement name="DO">
|
||||
<block type="set_effect" id="WI0zqOz/z3].cR/6UWHn">
|
||||
<field name="EFFECT">FILL</field>
|
||||
<value name="COLOR">
|
||||
<shadow type="colour_picker" id="B`6;Xv{s2TFp8Yd=ZpSD">
|
||||
<field name="COLOUR">#000000</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<next>
|
||||
<block type="set_led" id="^Vcs}ki?#ctf7rAchix$">
|
||||
<value name="INDEX">
|
||||
<shadow type="math_number" id="U;VWW$[*LOF7Gf,~?YR7">
|
||||
<field name="NUM">0</field>
|
||||
</shadow>
|
||||
<block type="math_arithmetic" id="AI6PZBd`]_Z%_~4c-%dB">
|
||||
<field name="OP">MULTIPLY</field>
|
||||
<value name="A">
|
||||
<shadow type="math_number" id="|p}X]`SedK3).F/;}NlB">
|
||||
<field name="NUM">1</field>
|
||||
</shadow>
|
||||
<block type="math_arithmetic" id="-haE#:,cg{-J=NZERA;F">
|
||||
<field name="OP">DIVIDE</field>
|
||||
<value name="A">
|
||||
<shadow type="math_number" id="::st;ot}[r]csqceURu*">
|
||||
<field name="NUM">1</field>
|
||||
</shadow>
|
||||
<block type="math_arithmetic" id="a%+LN)F~=Igg+,p02[qo">
|
||||
<field name="OP">ADD</field>
|
||||
<value name="A">
|
||||
<shadow type="math_number" id="*yIGN((y)/^z0:f5:VDw">
|
||||
<field name="NUM">1</field>
|
||||
</shadow>
|
||||
<block type="get_yaw" id="mf%77q30bEqNfc/3`Mtb">
|
||||
<field name="FRAME_ID">MAP</field>
|
||||
<value name="ID">
|
||||
<shadow type="math_number" id="xb32G.N#ip`|^Xv*MOmY">
|
||||
<field name="NUM">0</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
</value>
|
||||
<value name="B">
|
||||
<shadow type="math_number" id="T/fTrm;j2Azgav;gI{ZW">
|
||||
<field name="NUM">180</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
</value>
|
||||
<value name="B">
|
||||
<shadow type="math_number" id="Wo1/ZCeir,u6/.e1H+BB">
|
||||
<field name="NUM">360</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
</value>
|
||||
<value name="B">
|
||||
<shadow type="math_number" id="jENTcXz0C5/=)Xpd!}LL">
|
||||
<field name="NUM">1</field>
|
||||
</shadow>
|
||||
<block type="led_count" id="vM@X8s!xa]v}AaK6PWF5"></block>
|
||||
</value>
|
||||
</block>
|
||||
</value>
|
||||
<value name="COLOR">
|
||||
<shadow type="colour_picker" id="+vw3bff.5c[=_w,Xm^C(">
|
||||
<field name="COLOUR">#3366ff</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<next>
|
||||
<block type="wait" id="DT%f$bn1*1El5zsgUW8Y">
|
||||
<value name="TIME">
|
||||
<shadow type="math_number" id="~Y0hNY[_^#v@aZkE-TH[">
|
||||
<field name="NUM">0.1</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
</next>
|
||||
</block>
|
||||
</next>
|
||||
</block>
|
||||
</statement>
|
||||
</block>
|
||||
</xml>
|
||||
30
clover_blocks/programs/examples/led.xml
Normal file
@@ -0,0 +1,30 @@
|
||||
<xml xmlns="https://developers.google.com/blockly/xml">
|
||||
<block type="controls_whileUntil" id="A:_Z_27?K?HlScug%u|5" x="113" y="113">
|
||||
<field name="MODE">WHILE</field>
|
||||
<value name="BOOL">
|
||||
<block type="logic_boolean" id="/rlzePJ6XJcv22J(Q4bs">
|
||||
<field name="BOOL">TRUE</field>
|
||||
</block>
|
||||
</value>
|
||||
<statement name="DO">
|
||||
<block type="set_effect" id="GT~AX(j]r)u^,f_n0agS">
|
||||
<field name="EFFECT">WIPE</field>
|
||||
<value name="COLOR">
|
||||
<shadow type="colour_picker" id="G`I)ZAuIGcnWNyO@N(sH">
|
||||
<field name="COLOUR">#ff0000</field>
|
||||
</shadow>
|
||||
<block type="colour_random" id="}(${|~%[}eJ.QUY?FWi_"></block>
|
||||
</value>
|
||||
<next>
|
||||
<block type="wait" id="Ux(J;l+?5Gq2n^*!jXj^">
|
||||
<value name="TIME">
|
||||
<shadow type="math_number" id=")As9bASkedr9x@M)*)Pf">
|
||||
<field name="NUM">1</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
</next>
|
||||
</block>
|
||||
</statement>
|
||||
</block>
|
||||
</xml>
|
||||
29
clover_blocks/programs/examples/print-range.xml
Normal file
@@ -0,0 +1,29 @@
|
||||
<xml xmlns="https://developers.google.com/blockly/xml">
|
||||
<block type="controls_whileUntil" id="^{D$=!y=ahJx0.^n?Ycr" x="88" y="137">
|
||||
<field name="MODE">WHILE</field>
|
||||
<value name="BOOL">
|
||||
<block type="logic_boolean" id="a;~UtFPf9O3B)#QBJxL6">
|
||||
<field name="BOOL">TRUE</field>
|
||||
</block>
|
||||
</value>
|
||||
<statement name="DO">
|
||||
<block type="text_print" id="doqF#i]/ulE{Z0l/{9`K">
|
||||
<value name="TEXT">
|
||||
<shadow type="text" id="Y$c,rIaw^bNs2wIw%[^(">
|
||||
<field name="TEXT">abc</field>
|
||||
</shadow>
|
||||
<block type="rangefinder_distance" id="Pi+WekHZvZ0p}JY-^CLQ"></block>
|
||||
</value>
|
||||
<next>
|
||||
<block type="wait" id="=$,8TaKwL75Pb,_tsCVU">
|
||||
<value name="TIME">
|
||||
<shadow type="math_number" id="/r+hzH#G0Ru#ES+}MxUl">
|
||||
<field name="NUM">0.5</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
</next>
|
||||
</block>
|
||||
</statement>
|
||||
</block>
|
||||
</xml>
|
||||
88
clover_blocks/programs/examples/rotation.xml
Normal file
@@ -0,0 +1,88 @@
|
||||
<xml xmlns="https://developers.google.com/blockly/xml">
|
||||
<block type="take_off" id="R@=3~Fq;AxFaRO{ZDFUM" x="162" y="112">
|
||||
<field name="WAIT">TRUE</field>
|
||||
<value name="ALT">
|
||||
<shadow type="math_number" id="cf8/,N48N;Fvn@EWB%,B">
|
||||
<field name="NUM">1</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<next>
|
||||
<block type="navigate" id="[r%uJ2rw#3j.sLSBFf`N">
|
||||
<field name="FRAME_ID">BODY</field>
|
||||
<field name="WAIT">TRUE</field>
|
||||
<value name="X">
|
||||
<shadow type="math_number" id="5;@1vq|;OnlHl#FFDm/}">
|
||||
<field name="NUM">1</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="Y">
|
||||
<shadow type="math_number" id="Jhe8@=gRxwj0oO*Q1F7V">
|
||||
<field name="NUM">0</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="Z">
|
||||
<shadow type="math_number" id="wms+rWijPhnI/K++At;P">
|
||||
<field name="NUM">0</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="ID">
|
||||
<shadow type="math_number" id=".^-h~@m]zg3il)O.9eFy">
|
||||
<field name="NUM">0</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="SPEED">
|
||||
<shadow type="math_number" id="3L3FgAO(:aZp/:V%u=P7">
|
||||
<field name="NUM">0.5</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<next>
|
||||
<block type="set_yaw" id="nE,tYuHO--PmXN:*g@D%">
|
||||
<field name="FRAME_ID">body</field>
|
||||
<field name="WAIT">TRUE</field>
|
||||
<value name="YAW">
|
||||
<shadow type="angle" id="DoJ$Yj+fVT~cj=E%N:4+">
|
||||
<field name="ANGLE">180</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<next>
|
||||
<block type="navigate" id="H6W4@QtAT{_xo0.fO^z4">
|
||||
<field name="FRAME_ID">BODY</field>
|
||||
<field name="WAIT">TRUE</field>
|
||||
<value name="X">
|
||||
<shadow type="math_number" id="|.KN1m1O$/3=C,WG3sx}">
|
||||
<field name="NUM">1</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="Y">
|
||||
<shadow type="math_number" id="oyc7mxrbA/@[_1wtPER^">
|
||||
<field name="NUM">0</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="Z">
|
||||
<shadow type="math_number" id="dh}ac~kErkIIFeIZB,)d">
|
||||
<field name="NUM">0</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="ID">
|
||||
<shadow type="math_number" id="q+{la-yiw61v9BSV-y};">
|
||||
<field name="NUM">0</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="SPEED">
|
||||
<shadow type="math_number" id=";dk@RJGfZ)5w*:m4kp==">
|
||||
<field name="NUM">0.5</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<next>
|
||||
<block type="land" id="53Ssv6^lEHoD?tBO{5Sh">
|
||||
<field name="WAIT">TRUE</field>
|
||||
</block>
|
||||
</next>
|
||||
</block>
|
||||
</next>
|
||||
</block>
|
||||
</next>
|
||||
</block>
|
||||
</next>
|
||||
</block>
|
||||
</xml>
|
||||
86
clover_blocks/programs/examples/simple-flight.xml
Normal file
@@ -0,0 +1,86 @@
|
||||
<xml xmlns="https://developers.google.com/blockly/xml">
|
||||
<block type="take_off" id="R@=3~Fq;AxFaRO{ZDFUM" x="62" y="88">
|
||||
<field name="WAIT">TRUE</field>
|
||||
<value name="ALT">
|
||||
<shadow type="math_number" id="cf8/,N48N;Fvn@EWB%,B">
|
||||
<field name="NUM">1</field>
|
||||
</shadow>
|
||||
<block type="text_prompt_ext" id="#BO!OX5u-GHwsu!KzBT5">
|
||||
<mutation type="NUMBER"></mutation>
|
||||
<field name="TYPE">NUMBER</field>
|
||||
<value name="TEXT">
|
||||
<shadow type="text" id="unay$W?6.WiDB!|[3GN3">
|
||||
<field name="TEXT">Enter flight altitude</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
</value>
|
||||
<next>
|
||||
<block type="navigate" id="BOHP@mRbJamp}]6/yc,n">
|
||||
<field name="FRAME_ID">BODY</field>
|
||||
<field name="WAIT">TRUE</field>
|
||||
<value name="X">
|
||||
<shadow type="math_number" id="hzjq`v(`k6vZN],.8%Hf">
|
||||
<field name="NUM">1</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="Y">
|
||||
<shadow type="math_number" id="FIVk1Hm_+CU8XB~t@?S;">
|
||||
<field name="NUM">0</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="Z">
|
||||
<shadow type="math_number" id="B{LoNO6}MgJ.JeN+8]tR">
|
||||
<field name="NUM">0</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="ID">
|
||||
<shadow type="math_number" id="w!F}PhAG[Gn;5XsIg$XL">
|
||||
<field name="NUM">0</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="SPEED">
|
||||
<shadow type="math_number" id="AjhS9Wf?M`%}A(H_bW9K">
|
||||
<field name="NUM">0.5</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<next>
|
||||
<block type="navigate" id="|Aa[hh]OAUS+b7I?;3VJ">
|
||||
<field name="FRAME_ID">BODY</field>
|
||||
<field name="WAIT">TRUE</field>
|
||||
<value name="X">
|
||||
<shadow type="math_number" id="=n#)78Bd*!iJyzF=dSA*">
|
||||
<field name="NUM">-1</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="Y">
|
||||
<shadow type="math_number" id="YqC+GQF/[G=li}/s_o)q">
|
||||
<field name="NUM">0</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="Z">
|
||||
<shadow type="math_number" id="~xsT!Ug+uMeU5F/Y4;k5">
|
||||
<field name="NUM">0</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="ID">
|
||||
<shadow type="math_number" id="%WRn|02iTTwCG:zB}dSf">
|
||||
<field name="NUM">0</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="SPEED">
|
||||
<shadow type="math_number" id="A#q.M,b1D*,13Ldp.F2w">
|
||||
<field name="NUM">0.5</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<next>
|
||||
<block type="land" id="_U*W_q1)l+l@#^TC%w)w">
|
||||
<field name="WAIT">TRUE</field>
|
||||
</block>
|
||||
</next>
|
||||
</block>
|
||||
</next>
|
||||
</block>
|
||||
</next>
|
||||
</block>
|
||||
</xml>
|
||||
60
clover_blocks/programs/examples/takeoff-land.xml
Normal file
@@ -0,0 +1,60 @@
|
||||
<xml xmlns="https://developers.google.com/blockly/xml">
|
||||
<block type="text_print" id="2p:*N].+8c=|ZJXCWVn[" x="162" y="88">
|
||||
<value name="TEXT">
|
||||
<shadow type="text" id="9H+7z4A.pZ:Zz)7}H3Y7">
|
||||
<field name="TEXT">Start mission</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<next>
|
||||
<block type="take_off" id="R@=3~Fq;AxFaRO{ZDFUM">
|
||||
<field name="WAIT">TRUE</field>
|
||||
<value name="ALT">
|
||||
<shadow type="math_number" id="cf8/,N48N;Fvn@EWB%,B">
|
||||
<field name="NUM">1</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<next>
|
||||
<block type="text_print" id="|Tdajnf]Rz@ajSW`0Skv">
|
||||
<value name="TEXT">
|
||||
<shadow type="text" id="Wqr:Hf~r2d.A%S7%%4.)">
|
||||
<field name="TEXT">Take off complete, wait</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<next>
|
||||
<block type="wait" id="-]MH9NMT#N-w!5yI1$F)">
|
||||
<value name="TIME">
|
||||
<shadow type="math_number" id="?k~ZUNcrx-iC;LFmarbm">
|
||||
<field name="NUM">3</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<next>
|
||||
<block type="text_print" id="SXJsfN{P13FL~n9;|%P:">
|
||||
<value name="TEXT">
|
||||
<shadow type="text" id="[Z5,f|El:VX5zcRaMtXa">
|
||||
<field name="TEXT">Land</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<next>
|
||||
<block type="land" id="G5+Fs.6Y4(K9Dw`wjgua">
|
||||
<field name="WAIT">TRUE</field>
|
||||
<next>
|
||||
<block type="text_print" id="o?bvXARz^4_U0oXC-#.V">
|
||||
<value name="TEXT">
|
||||
<shadow type="text" id="`tadiS,[OZ`#f:=u.:9~">
|
||||
<field name="TEXT">Mission finished</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
</next>
|
||||
</block>
|
||||
</next>
|
||||
</block>
|
||||
</next>
|
||||
</block>
|
||||
</next>
|
||||
</block>
|
||||
</next>
|
||||
</block>
|
||||
</next>
|
||||
</block>
|
||||
</xml>
|
||||
137
clover_blocks/programs/examples/variables.xml
Normal file
@@ -0,0 +1,137 @@
|
||||
<xml xmlns="https://developers.google.com/blockly/xml">
|
||||
<variables>
|
||||
<variable id="]gc9ItU#$!=G*S._3$n~">start_x</variable>
|
||||
<variable id="D9Oy(29I!!k9+(#zI,4}">start_y</variable>
|
||||
</variables>
|
||||
<block type="take_off" id="X]W2Bs=WFxFsco{Yr`bH" x="87" y="88">
|
||||
<field name="WAIT">TRUE</field>
|
||||
<value name="ALT">
|
||||
<shadow type="math_number" id="{7ezr;C@IT-IJ3ylt?];">
|
||||
<field name="NUM">1.5</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<next>
|
||||
<block type="variables_set" id="IWR_j|7pUxrIGrxGLP#Z">
|
||||
<field name="VAR" id="]gc9ItU#$!=G*S._3$n~">start_x</field>
|
||||
<value name="VALUE">
|
||||
<block type="get_position" id="ze{FI]Vv*E3kZ1Zw6Bmn">
|
||||
<field name="FIELD">X</field>
|
||||
<field name="FRAME_ID">ARUCO_MAP</field>
|
||||
<value name="ID">
|
||||
<shadow type="math_number" id="s~^V?k;dKa(($OG9mAK)">
|
||||
<field name="NUM">0</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
</value>
|
||||
<next>
|
||||
<block type="variables_set" id="s3rE=-6+;f(Lua_uKGY[">
|
||||
<field name="VAR" id="D9Oy(29I!!k9+(#zI,4}">start_y</field>
|
||||
<value name="VALUE">
|
||||
<block type="get_position" id="?t+jp;7Bk#wzoz=DY{sO">
|
||||
<field name="FIELD">Y</field>
|
||||
<field name="FRAME_ID">ARUCO_MAP</field>
|
||||
<value name="ID">
|
||||
<shadow type="math_number" id="KybjD~@_RFIx=8Dzbz4v">
|
||||
<field name="NUM">0</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
</value>
|
||||
<next>
|
||||
<block type="text_print" id="|xj}5M(:.~`Vpq-GKi/0">
|
||||
<value name="TEXT">
|
||||
<shadow type="text" id="{o^2v+y6L^1#[Y8r_sdW">
|
||||
<field name="TEXT">Fly to point 0, 0</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<next>
|
||||
<block type="navigate" id="-8]peWQRtTK$1X0J0w!2">
|
||||
<field name="FRAME_ID">ARUCO_MAP</field>
|
||||
<field name="WAIT">TRUE</field>
|
||||
<value name="X">
|
||||
<shadow type="math_number" id="PT`K-=?U^uL#BXouoi~G">
|
||||
<field name="NUM">0</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="Y">
|
||||
<shadow type="math_number" id="d_1oBZdx#`_kbWf_Qu$g">
|
||||
<field name="NUM">0</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="Z">
|
||||
<shadow type="math_number" id="}Y,E=*pJaG+HOSB*f=aX">
|
||||
<field name="NUM">1.5</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="ID">
|
||||
<shadow type="math_number" id=")/%?zNt|6thYwjowvbk/">
|
||||
<field name="NUM">0</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="SPEED">
|
||||
<shadow type="math_number" id="MH}%@OvXa*FK-Vx63RTd">
|
||||
<field name="NUM">0.5</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<next>
|
||||
<block type="text_print" id="IVsd5hFEwp@V?3f02p},">
|
||||
<value name="TEXT">
|
||||
<shadow type="text" id="0gcgp`mZ,9oDTS[4vWpT">
|
||||
<field name="TEXT">Fly to initial point</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<next>
|
||||
<block type="navigate" id=":;b$YH+6#3h/_$Fa}0w_">
|
||||
<field name="FRAME_ID">ARUCO_MAP</field>
|
||||
<field name="WAIT">TRUE</field>
|
||||
<value name="X">
|
||||
<shadow type="math_number" id=";GL$bb^Yu;vQrhxy(o:S">
|
||||
<field name="NUM">0</field>
|
||||
</shadow>
|
||||
<block type="variables_get" id="tsqWR~saO6UU^*iLmDZ(">
|
||||
<field name="VAR" id="]gc9ItU#$!=G*S._3$n~">start_x</field>
|
||||
</block>
|
||||
</value>
|
||||
<value name="Y">
|
||||
<shadow type="math_number" id="y5|y58G[s-,qnfB/@Xr%">
|
||||
<field name="NUM">0</field>
|
||||
</shadow>
|
||||
<block type="variables_get" id="Hfzk#prb}M2BbEWIAX3k">
|
||||
<field name="VAR" id="D9Oy(29I!!k9+(#zI,4}">start_y</field>
|
||||
</block>
|
||||
</value>
|
||||
<value name="Z">
|
||||
<shadow type="math_number" id="B%NGa`:z=[Kpij2$0p}T">
|
||||
<field name="NUM">1.5</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="ID">
|
||||
<shadow type="math_number" id="KaLkf?/NkD_7q@=J1G|I">
|
||||
<field name="NUM">0</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="SPEED">
|
||||
<shadow type="math_number" id="Agwx#kYhdE}!dY!St:L1">
|
||||
<field name="NUM">0.5</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<next>
|
||||
<block type="land" id="8y]p?)Zm2}*+;0HP]p1$">
|
||||
<field name="WAIT">TRUE</field>
|
||||
</block>
|
||||
</next>
|
||||
</block>
|
||||
</next>
|
||||
</block>
|
||||
</next>
|
||||
</block>
|
||||
</next>
|
||||
</block>
|
||||
</next>
|
||||
</block>
|
||||
</next>
|
||||
</block>
|
||||
</next>
|
||||
</block>
|
||||
</xml>
|
||||
BIN
clover_blocks/screenshot.png
Normal file
|
After Width: | Height: | Size: 182 KiB |
184
clover_blocks/src/clover_blocks
Executable 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()
|
||||
5
clover_blocks/srv/Load.srv
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
bool success
|
||||
string message
|
||||
string[] names
|
||||
string[] programs
|
||||
4
clover_blocks/srv/Run.srv
Normal file
@@ -0,0 +1,4 @@
|
||||
string code # code in Python
|
||||
---
|
||||
bool success
|
||||
string message
|
||||
5
clover_blocks/srv/Store.srv
Normal file
@@ -0,0 +1,5 @@
|
||||
string name
|
||||
string program
|
||||
---
|
||||
bool success
|
||||
string message
|
||||
202
clover_blocks/www/blockly/LICENSE
Executable file
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
60
clover_blocks/www/blockly/README.md
Executable file
@@ -0,0 +1,60 @@
|
||||
# Blockly [](https://travis-ci.org/google/blockly)
|
||||
|
||||
|
||||
Google's Blockly is a web-based, visual programming editor. Users can drag
|
||||
blocks together to build programs. All code is free and open source.
|
||||
|
||||
**The project page is https://developers.google.com/blockly/**
|
||||
|
||||

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