Introduction¶
Radler framework takes its inspiration from the Robot Operating System (ROS). In Radler framework, the sensors, controllers, and actuators are constructed from functional units called nodes. Each node executes independently with a period determined by a local clock and scheduling constraints. Radler supports a publish/subscribe architecture where nodes communicate by publishing on certain topics and subscribing to other topics.
Getting Started¶
Find the documentation at https://sri-csl.github.io/radler/
To install¶
To checkout repository:
git clone https://github.com/SRI-CSL/radler.git
cd radler
git submodule update --init --recursive
To get Radler working on a clean version of Ubuntu 14.04:
sudo apt-get install cmake python3-pip
sudo pip3 install tarjan pyyaml
To install ROS:
sudo sh -c 'echo "deb http://packages.ros.org/ros/ubuntu trusty main" > /etc/apt/sources.list.d/ros-latest.list'
wget https://raw.githubusercontent.com/ros/rosdistro/master/ros.key -O - | sudo apt-key add -
sudo apt-get update
sudo apt-get install ros-indigo-ros-base
(echo ; echo "# Setup for ROS" ; echo "source /opt/ros/indigo/setup.bash" ) >> ~/.bashrc
source ~/.bashrc
To compile and run¶
Compile¶
Radler generates files from the RADL file into a usual ROS catkin structure, then a call to catkin_make will generate the executables as usual. (e.g., see examples/pubsub/pubsub.radl):
mkdir -p /tmp/catkin_ws/src
./radler.sh --ws_dir /tmp/catkin_ws/src compile examples/pubsub/single_machine/pubsub.radl --plant plant --ROS
cd /tmp/catkin_ws
catkin_make
Run¶
Since pubsub defines a plant, Radler has generated a launch file to run the requested nodes. The simplest way of running it is to source the catkin workspace we just compiled and use roslaunch:
source devel/setup.bash
roslaunch pubsub pubsub.plant.host_computer.launch
You should see the output of the two nodes explaining that they are communicating. For more details see pubsub example documentation.
You can stop everything by typing Ctrl-C.
Radler usage¶
Commands¶
radler.sh¶
radler.sh is a simple bash script used to run the actual python Radler script radler/main.py
Radler script¶
The script may be used to handle object files with the subcommands
obj_version
and obj_compatible
or to compile .radl description
with the compile
subcommand. The script has two levels of arguments.
The arguments before the subcommand and the one after, specific to the
subcommand.
Generic arguments¶
The full list of arguments is provided by the --help
argument.
--ws_dir
This is used to specify where is the workspace directory. The
workspace directory is where the files are generated. This is by
default equal to src
.
Compile subcommand¶
The full list of arguments for the compile
subcommand is available
as the --help
argument to the subcommand
(radler.sh compile --help
).
The radl description file is a positional compulsory argument. It has to be a file with the ‘.radl’ extension.
--object_files
To compile .radl description which depends on other modules, one has to give the object files of those modules with this argument.
--object_dest
By default, the generated object file has the same name as its module, with the ‘.radlo’ extension. Moreover it is by default written at the root of the workspace directory. This command can be used to change the name and directory of the generated object file.
Examples¶
The following examples demonstrate the features of Radler.
- Pubsub: How to write pub/sub on single/multi machine
- House_thermo: Toy example of a distributed house heating system modeling
- Controller_gateway: How to write a gateway to forward back and forth messages between Radler world and a local sandboxed ROS
- Drone: AR.Drone demos using ardronelib sdk and ardrone_autonomy ROS driver
- Raspberrypi: Raspberry Pi 2/B+ demo using GPIO
- Android: Android demos via ROS-enabled Android application and Android NDK
Pubsub¶
This simple examples demonstrate how to write pubsub on single/multi machine.
To generate the files from the .radl description, do:
mkdir -p /tmp/catkin_ws/src
cd /path/to/radler
./radler.sh --ws_dir=/tmp/catkin_ws/src compile examples/pubsub/single_machine/pubsub.radl --plant plant --ROS
cd /tmp/catkin_ws
catkin_make
You can then run all of them (in different terminals for more clarity):
roscore
./devel/lib/pubsub/talker
./devel/lib/pubsub/listener
For multi_machine testing, use a different .radl file in multi_machine directory as below:
./radler.sh --ws_dir=/tmp/catkin_ws/src compile examples/pubsub/multi_machine/pubsub.radl --plant plant --ROS
For running on multi_machine setup, copy executables to the corresponding machines. Refer to plant section of .radl file for the IPs as below.
scp /tmp/catkin_ws/devel/lib/pubsub/talker 192.168.10.202:~
scp /tmp/catkin_ws/devel/lib/pubsub/listener 192.168.10.203:~
On machine 192.168.10.201:
roscore
On machine 192.168.10.202:
./talker
On machine 192.168.10.203:
./listener
House_thermo¶
Toy example modeling a distributed house heating system. It uses the same Radler features as the pubsub example.
Controller_Gateway¶
This example demonstrates how to write a gateway to forward back-and-forth messages between Radler world and a local sandboxed ROS.
Radler world is defined in the radl_files folder. It consists of .radl description containing three nodes. A device stub publishing an integer, an actuator stub subscribing to a float and a gateway node for an external controller node, forwarding device msgs to it and forwarding actuators msgs from it.
To complete the example, a stub of a sandboxed ROS controller node is also provided in the sandboxed_ros_controller folder.
There are basically two interesting files, the gateway.radl description of Radler system which defines the gateway node and the associated gateway.h file implementing the actual gateway.
To generate the files from .radl description, do:
mkdir -p /tmp/catkin_ws/src
cd /path/to/radler
./radler.sh --ws_dir /tmp/catkin_ws/src compile examples/controller_gateway/radl_files/gateway.radl --ROS
Then to compile everything and get ROS executables, do:
ln -s /path/to/radler/example/controller_gateway/sandboxed_ros_controller /tmp/catkin_ws/src
cd /tmp/catkin_ws
catkin_make
You can then run all of them (in different terminals for more clarity):
roscore
./devel/lib/gateway/device_node
./devel/lib/gateway/actuator_node
./devel/lib/gateway/controller_gateway
./devel/lib/sandboxed_ros_controller/sandboxed_ros_controller_node
Demos using AR.Drone on ROS¶
This application uses code snippets from the following open source projects.
- ROS driver for Parrot AR-Drone 1.0 and 2.0 quadrocopters https://github.com/AutonomyLab/ardrone_autonomy
- Modified ARDroneLib based on official ARDroneSDK 2.0.1 https://github.com/AutonomyLab/ardronelib
- AR.FreeFlight/AR.Drone 2.0 SDK https://github.com/yolanother/AR.FreeFlight
- OpenCV https://github.com/opencv/opencv
- TensorFlow object detection https://github.com/tensorflow/models/tree/master/research/object_detection
- PS-Drone http://www.playsheep.de/drone
Some additional links:
- AR.Drone Developer Guide http://www.msh-tools.com/ardrone/ARDrone_Developer_Guide.pdf
- Parrot developer site for SDK http://developer.parrot.com/docs/SDK3/#how-to-build-the-sdk
- Control the AR.Drone LEDs http://gauth.fr/2011/09/control-the-ar-drone-leds/
For Demos #1 and #2, OpenCV is required to find line with the Hough transform. In .radl file, edit opencv_houghline to enable line detection.
git clone https://github.com/opencv/opencv.git
cd /path/to/opencv
mkdir build
cd build
cmake -D CMAKE_BUILD_TYPE=RELEASE -D CMAKE_INSTALL_PREFIX=/usr/local ..
make
sudo make install
Demo #1 with sridrone_ardronelib¶
Compile up-to-date ardronelib sdk.
git submodule update --init --recursive
cd examples/drone/sridrone_ardronelib/ardronelib
make
sudo make install --makefile=../Makefile INSTALL_PREFIX=/usr/local
Edit drone_ip in examples/drone/sridrone_ardronelib/sridrone_ardronelib.radl with your drone’s IP if needed.
DEFS
drone_ip: string "192.168.1.xxx"
Run ROS master.
roscore
Run the sridrone_ardronelib example.
mkdir -p /tmp/catkin_ws/src
cd /path/to/radler
./radler.sh --ws_dir /tmp/catkin_ws/src compile examples/drone/sridrone_ardronelib/sridrone_ardronelib.radl --plant plant --ROS
cd /tmp/catkin_ws
catkin_make
cd /tmp/catkin_ws/devel/lib/sridrone_ardronelib
./sridrone
./led
./camera
./key
./timer
./landing
Demo #2 with sridrone_ardrone_autonomy¶
Download and install ROS ardrone_driver from https://github.com/AutonomyLab/ardrone_autonomy.git.
sudo apt-get install ros-indigo-ardrone-autonomy
sudo apt-get install ros-indigo-image-view
Run ROS master and ardrone_driver.
roscore
rosrun ardrone_autonomy ardrone_driver
Note. Use -ip ${Your Drone’s IP address} if your drone does not have the default IP address.
rosrun ardrone_autonomy ardrone_driver -ip 192.168.1.xxx
Run the sridrone_ardrone_autonomy example.
cd /path/to/radler
./radler.sh --ws_dir /tmp/catkin_ws/src compile examples/drone/sridrone_ardrone_autonomy/sridrone_ardrone_autonomy.radl --plant plant --ROS
cd /tmp/catkin_ws
catkin_make
cd /tmp/catkin_ws/devel/lib/sridrone_ardrone_autonomy
./sridrone
./led
./camera
You can also run ros image_view node to check the camera feed or record it.
rosrun image_view image_view image:=/ardrone/front/image_raw
rosrun image_view image_view image:=/ardrone/bottom/image_raw
rosrun image_view video_recorder image:="/ardrone/front/image_raw" _filename:="/tmp/video_front_camera.avi"
Demo #3 object detection with PS-Drone and TensorFlow¶
Install Python OpenCV2.
sudo apt-get install python-opencv
Download ps_drone.py and firstVideo.py from http://www.playsheep.de/drone to the same directory (e.g., ps_drone). Test video streaming from AR. Drone using firstVideo.py. A window named PS-Drone with front camera feed should show up. We tested on Ubuntu 14.04.
cd /path/to/ps_drone
python firstVideo.py
Install TensorFlow and related packages. For details, refer https://www.tensorflow.org/install/install_linux.
sudo apt-get install python-pip
sudo pip install -U pip
sudo pip install --upgrade setuptools
sudo pip install --upgrade --target=/usr/lib/python2.7/dist-packages tensorflow
sudo apt-get install --fix-missing python-matplotlib
Download models built with TensorFlow.
git clone https://github.com/tensorflow/tensorflow.git
cd tensorflow
git clone https://github.com/tensorflow/models.git
Download a Single Shot Multibox Detector (SSD) with MobileNet model from here, and untar to /path/to/tensorflow/models/object_detection directory.
tar zxvf ssd_mobilenet_v1_coco_11_06_2017.tar.gz -C /path/to/tensorflow/models/object_detection
Download and install Protobuf.
git clone https://github.com/google/protobuf.git
sudo apt-get install autoconf libtool
cd protobuf
./autogen.sh
./configure
make
sudo make install
Compile Protobuf libraries.
export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH
cd /path/to/tensorflow/models
protoc object_detection/protos/*.proto --python_out=.
Add to PYTHONPATH.
export PYTHONPATH=/path/to/ps_drone:/path/to/radler/examples/drone/sridrone_pydrone_tf_obj_detection/src:/path/to/tensorflow/models:/usr/lib/python2.7/dist-packages:$PYTHONPATH
Compile up-to-date ardronelib sdk. Skip this step if you already installed ardronelib sdk for Demo #1.
git submodule update --init --recursive
cd examples/drone/sridrone_ardronelib/ardronelib
make
sudo make install --makefile=../Makefile INSTALL_PREFIX=/usr/local
Edit drone_ip in examples/drone/sridrone_pydrone_tf_obj_detection/sridrone_pydrone_tf_obj_detection.radl with your drone’s IP if needed.
DEFS
drone_ip: string "192.168.1.xxx"
Run ROS master.
roscore
Compile the sridrone_pydrone_tf_obj_detection example.
mkdir -p /tmp/catkin_ws/src
cd /path/to/radler
./radler.sh --ws_dir /tmp/catkin_ws/src compile examples/drone/sridrone_pydrone_tf_obj_detection/sridrone_pydrone_tf_obj_detection.radl --plant plant --ROS
cd /tmp/catkin_ws
catkin_make
Create symbolic links to find model and label.
cd /tmp/catkin_ws/devel/lib/sridrone_sridrone_pydrone_tf_obj_detection
ln -s /path/to/tensorflow/models/object_detection/data .
ln -s /path/to/tensorflow/models/object_detection/ssd_mobilenet_v1_coco_11_06_2017 .
Run the example.
./sridrone
./led
Even though the models can detect multiple objects simultaneously, led_anim topic only considers the object with the highest score. Led lights are blinking green (or red) when person (or teddy bear) is detected with the highest score. When dog (or cat) is detected with the highest score, the right (or left) led lights turn red.
Demos using Raspberry Pi 2/B+¶
Some additional links:
- Raspberry Pi WiFi connection https://learn.adafruit.com/adafruits-raspberry-pi-lesson-3-network-setup/setting-up-wifi-with-raspbian
- RPi easy SD card setup http://elinux.org/RPi_Easy_SD_Card_Setup
- Raspberry Pi GPIO https://www.raspberrypi.org/documentation/usage/gpio-plus-and-raspi2/
- Python on Debian Wheezy http://www.extellisys.com/articles/python-on-debian-wheezy
Preparing your Raspberry Pi¶
On your host Linux machine, insert the microSD card that comes with Raspberry Pi to extract its disk image.
lsblk
sudo dd bs=4M if=/dev/sdb | gzip > raspbian.img.gz
Now eject the microSD card that comes with Raspberry Pi, and insert a larger microSD card to copy the disk image.
unzip --stdout raspbian.img.gz | sudo dd bs=4M of=/dev/sdb
sudo sync
Boot with a larger microSD to provide enough disk space for this demo (we recommend 16GBytes). Run the following command to expand the disk space.
sudo raspi-config
Preparing LED and Button¶
Understand GPIO on Raspberry Pi. We use GPIO 4 and 17 for the LED output and for the Button input, respectively.
We use GPIOClass from http://www.hertaville.com/introduction-to-accessing-the-raspberry-pis-gpio-in-c.html.
Installing Radler on Raspberry Pi¶
Install Python 3.4 on the Raspberry Pi.
sudo apt-get install libncurses5-dev libncursesw5-dev libreadline6-dev
tar -zxf /path/to/your/Python-3.4.3.tgz
cd Python-3.4.3
./configure --prefix=/usr/local/opt/python-3.4.3
make
sudo make install
export PATH=$PATH:/usr/local/opt/python-3.4.3/bin
Install tarjan and pyyaml on the Raspberry Pi.
sudo pip3.4 install tarjan
sudo pip3.4 install pyyaml
Install ROS Indigo on the Raspberry Pi.
Follow the instructions in Step 2 at http://wiki.ros.org/ROSberryPi.
We recommend increasing the swapfile size of Raspberry Pi to 800MBytes (default is 100MBytes) by editing CONF_SWAPSIZE in /etc/dphys-swapfile, and restart dphys-swapfile.
/etc/init.d/dphys-swapfile stop
/etc/init.d/dphys-swapfile start
CMake 2.8.12 or higher is required on the Raspberry Pi.
wget http://www.cmake.org/files/v3.2/cmake-3.2.2.tar.gz
tar xf cmake-3.2.2.tar.gz
cd cmake-3.2.2
./configure
make
sudo apt-get install checkinstall
sudo checkinstall
dpkg -i ~/cmake-3.2.2/cmake_3.2.2-1_armhf.deb
Run the demo on Raspberry Pi¶
Run ROS master on the Raspberry Pi.
export ROS_MASTER_URI=http://localhost:11311
roscore
Edit target_link_libraries
in packagen.py (under ralder/radlr/cgen).
target_link_libraries({lib_target} {lib_static_libs} rt)
Run the raspberrypi example.
mkdir -p /tmp/catkin_ws/src
cd /path/to/radler
./radler.sh --ws_dir /tmp/catkin_ws/src compile examples/raspberrypi/raspberrypi.radl --plant plant --ROS
cd /tmp/catkin_ws
catkin_make
cd /tmp/catkin_ws/devel/lib/raspberrypi
sudo chown -v root.root led
sudo chmod 4755 led
./led
./button
Demos using Android¶
Demo #1 implements sensing (of GPS, compass, touch screen) and controlling (of speaker volume) of Android device via ROS-enabled Android application using Gradle-Android studio environment. This demo is sensing GPS, compass, and touch screen coordinate from Android device, and publishing those under android_gps, android_compass, and android_touch topics, respectively. Radler nodes (named as gps, compass, and touch on the workstation side) are subscribing to corresponding topics and publishing them in Radler world. The controller Radler node subscribes to topics from gps, compass, and touch Radler nodes, and controls volume of the Android ringtone by publishing ROS topic.
Demo #2 implements touchscreen event detection via getevent
system command and displays on the window of terminal emulator application.
Some additional links:
- Rosjava/android_core https://github.com/rosjava/android_core
- Using native ROS code on Android http://wiki.ros.org/android_ndk
- Android Studio https://developer.android.com/studio
- Android NDK https://developer.android.com/ndk
Demo #1 Sensing and controlling of Android device via ROS-enabled Android application using Gradle-Android studio environment¶
Our example included in android_core directory is based on the tutorial example (android_tutorial_pubsub) from https://github.com/rosjava/android_core.
Install Android studio, and import android_core project to compile/generate/install signed apk file via Android studio (recommended).
Alternatively, you can use Gradle command as below.
cd /path/to/radler/examples/android/android_core
Edit sdk.dir
in local.properties.
sdk.dir=/path/to/android/sdk
Execute a build with the Wrapper.
sudo ./gradlew
If you are not using Android studio, you need to sign your unsigned apk android_tutorial_pubsub-release-unsigned.apk under android_tutorial_pubsub/build/output/apk, and install signed apk.
cd /path/to/radler/examples/android/android_core/android_tutorial_pubsub/build/output/apk
keytool -genkey -v -keystore debug.keystore -alias radler -keyalg RSA -keysize 2048 -validity 20000
jarsigner -verbose -keystore debug.keystore android_tutorial_pubsub-release-unsigned.apk radler
adb install android_tutorial_pubsub-release-unsigned.apk
For ease of testing, USB tethering can be used. Note that USB tethering is not required for deployment.
Enable USB tethering mode on your Android device. ifconfig
on your workstation will show you the usb0
interface.
usb0 Link encap:Ethernet HWaddr xx:xx:xx:xx:xx:xx
inet addr:192.168.42.11 Bcast:192.168.42.255 Mask:255.255.255.0
Set environment variables on your workstation.
export ROS_MASTER_URI=http://192.168.42.11:11311
export ROS_HOSTNAME=192.168.42.11
Run ROS master on your workstation.
roscore
Edit master_ip in examples/android/sensor_pubsub/sensor_pubsub.radl if needed.
DEFS
master_ip: string "192.168.42.11"
Run the sensor_pubsub example. Note that when you open a new terminal to run Radler nodes, set environment variables (i.e., ROS_MASTER_URI and ROS_HOSTNAME).
mkdir -p /tmp/catkin_ws/src
cd /path/to/radler
./radler.sh --ws_dir /tmp/catkin_ws/src compile examples/android/sensor_pubsub/sensor_pubsub.radl --plant plant --ROS
cd /tmp/catkin_ws
catkin_make
cd /tmp/catkin_ws/devel/lib/sensor_pubsub
./gps
./compass
./touch
./controller
When you start PubSubTutorial application on your Android device, enter Master_URI as your workstation IP (192.168.42.11 in this demo). You can now observe your compass and GPS coordinates on the top of the screen. When you touch the screen, the ringtone sound will be played with volume that is proportional to the ratio of current x-coordinate and screen width.
Demo #2 Touchscreen event detector using Android NDK¶
Prepare build environment to build native ROS nodes using the Android NDK as described in http://wiki.ros.org/android_ndk/Tutorials/BuildingNativeROSPackages. do_docker.sh takes some time to complete.
git clone https://github.com/ekumenlabs/roscpp_android.git
cd roscpp_android
./do_docker.sh
Copy do_radler.sh script to the Docker workspace for cross compilation of Radler nodes.
cp /path/to/radler/examples/android/do_radler.sh /path/to/roscpp_android/
Edit android_ip and master_ip in examples/android/touch_detector/touch_detector.radl if needed. The android_ip is your Android device’s IP (192.168.42.129 in this demo). The master_ip is your workstation’s IP (i.e., Ubuntu machine where you run ROS master; 192.168.42.11 in this demo). Refer to http://wiki.ros.org/ROS/EnvironmentVariables for further explanation.
DEFS
android_ip: string "192.168.42.129"
master_ip: string "192.168.42.11"
Set environment variables on your workstation.
export ROS_MASTER_URI=http://192.168.42.11:11311
export ROS_HOSTNAME=192.168.42.11
Run ROS master on your workstation.
roscore
Compile the touch_detector example.
cd /path/to/radler
./radler.sh --ws_dir=/path/to/roscpp_android/output/catkin_ws/src compile examples/android/touch_detector/touch_detector.radl --plant plant --ROS
sudo docker run --rm=true -t -v /path/to/roscpp_android:/opt/roscpp_android -v /path/to/roscpp_android/output:/opt/roscpp_output -i ekumenlabs/rosndk /opt/roscpp_android/do_radler.sh /opt/roscpp_output
Copy Radler nodes for the touch_detector example.
cd /path/to/roscpp_android/output/catkin_ws/devel/lib/touch_detector
adb push touch /data/data
adb push detector /data/data
Run touch Radler node on your Android device. On your workstation, connect to you Android device via ADB.
adb shell
su
mount -o rw,remount /
export ROS_MASTER_URI=http://192.168.42.11:11311
export ROS_HOSTNAME=192.168.42.129
cd /data/data
./touch
Run detector Radler node.
adb shell
su
export ROS_MASTER_URI=http://192.168.42.11:11311
export ROS_HOSTNAME=192.168.42.129
cd /data/data
./detector
Alternatively, run Radler node on your Android device. Download an Android application (.apk) for Terminal Emulator for Android (e.g., https://github.com/jackpal/Android-Terminal-Emulator), and run it on your Android device. On the terminal emulator, run the following commands.
su
export ROS_MASTER_URI=http://192.168.42.11:11311
export ROS_HOSTNAME=192.168.42.129
cd /data/data
./detector
Now you will see O on both windows (i.e., Android Terminal Emulator and ADB shell) when you touch your Android’s screen. Otherwise X will be displayed.
Radler language specification¶
File constraints¶
A radl description file is a text file, preferably encoded in utf8, with extension ”.radl”. Such a file is called in short a radl file.
Meta syntax rules¶
A radl file define a module whose name is the file name. A module is a set of value declarations. A declaration may be an alias, used to declare a value equal to another one but with a new name. The name of a value may be omitted, note that it’ll prevent the user to reference this value. The type of a value may also be omitted, note that if the type is ambiguous, the inference will choose one. When defining a class field, one may declare a new value or refer to another value by its identifier.
Grammar¶
module := decl*
decl := alias | NAME? (':' TYPE)? TYPEVALUE | NAME? (':' CLASS)? class_value
alias := NAME = identifier
class_value := '{' field * '}'
field := FIELDNAME value +
value := identifier | decl
identifier := root_name | identifier '.' NAME
root_name := NAME
Grammar terminals¶
The NAME
terminal is a spaceless word beginning with a letter which
may contain numbers and underscore (the regex
[a-zA-Z][a-zA-Z0-9_]*
). NAME
is also restricted not to be a
keyword, or begin with ‘radl’. The other terminals are defined by the
language.py file, with
declarations of the form
'type' TYPE 'REGEX' TYPEVALUE
and
'class' CLASS (FIELDNAME (TYPE|CLASS) ('*'|'+'|'?')?)*
Keywords¶
Keywords are all TYPE
, CLASS
and FIELDNAME
plus the
extra_keywords
entry of
language.py.
White spaces¶
The syntax requires all terminals to be whitespace separated. A whitespace is any number greater than 1 of space, tabulation and end of line.
Comments¶
After #
, any character is discarded up to the end of line.
Identifier scoping rules¶
Scoping regions¶
A file MODULE
.radl defines an implicit scoping region for its
content whose name is MODULE
. Sub-regions are defined by class
values. A class value declaration defines one and only one nested
scoping region, syntactically delimited by matching braces, whose name
is the declaration NAME
.
Local unicity of names¶
Inside each region (not comprising its sub-regions), NAME
of each
declarations are unique. Moreover, each module name MODULE
is
ensured to be unique.
Global unicity of qualified names¶
A qualified name (qname) is an identifier whose root_name
is a
module name. By unicity of module names and declaration names, a qname
defines a value uniquely.
Resolution of identifiers to qualified names¶
When resolving an identifier, the first step is the resolution of its
root_name
, which is matched with the closest enclosing scoping region
or in the last resort to another module name. At that point, the
root_name
is replaced by the qualified name of its matching region
and the identifier becomes a qualified name.
Example¶
---- file: module1.radl ----
x : int32 0
s : struct { FIELDS x : int32 42 }
----------------------------
This file declares three values whose qnames are module1.x
,
module1.s
and module1.s.x
.
---- file: module2.radl ----
x : int32 0
s : struct { FIELDS
x : int32 0
s2 : struct { FIELDS
y = x
y2 = s.x
y3 = module2.s.x
z = module2.x
a = module1.s.x
}
}
----------------------------
This file declares nine values whose qnames are module2.x
,
module2.s
, module2.s.x
, midule2.s.s2
, module2.s.s2.y
,
module2.s.s2.y2
, module2.s.s2.y3
, module2.s.s2.z
, and
module2.s.s2.a
. When defining the alias y = x
, the identifier
x
is resolved by looking for a name x
defined in s2
(which
doesn’t exist) then in the enclosing region s
where it is found,
thus resolving to module2.s.x
. s.x
in the declaration of y2
is resolved to the same qname by first resolving s
to module2.s
and replacing it to get module2.s.x
. When resolving the identifiers
module2.s.x
, module2.x
, and module1.s.x
, the root_name
is
found to be a module name meaning that these identifiers are already
qualified names.
Shadowing¶
While using qualified names for identifiers permit to overcome most possible shadowings, a region will shadow any module with identical name.
Example¶
--- file: module3.radl ---
module1 : struct { FIELDS
y = module1.x
}
--------------------------
In this file, the resolution of module1.x
will result in
module3.module1.x
since the closest enclosing region named
module1
is the one defined in module3
. Thus the definition of
the struct named module1
shadows the actual module module1
.
Note that in this example, since module3.module1.x
isn’t
declared, the compiler will complain that it is missing.
Radler language¶
Logical level¶
The two main classes used to describe the logical level are node and topic. Their full definitions may be found in language.py. The most important elements are:
class node
PUBLISHES publication *
SUBSCRIBES subscription *
CXX cxx_class
PERIOD duration
WCET duration
class topic
FIELDS int8/uint8/int16/uint16/int32/uint32/int64/uint64/
float32/float64/
bool/struct/array/duration/time +
Nodes¶
When creating a node, Radler will construct one instance of the
provided C++ class (the CXX
field). The step method of this
instance will be called at a fixed frequency defined by the node’s
period (the PERIOD
field). This step function needs to be proven
to have a worst case execution time no greater than the one described
here in the WCET
field. At each call, the step function is
provided with the messages received from its subscriptions and is
required to write the messages it has to publish.
Topics¶
A topic (defined uniquely by its name) is a purely logical way to define point-to-point communications between one producer and multiple consumers. There can be exactly one node publishing to a topic while many nodes can subscribe to it.
External files and PATH
¶
File reference¶
An external file is described by a string representing its path. This path may be absolute (for example “/etc/example.cfg”) or relative (“src/defs.h”). Relative paths are resolved relatively to the working directory of the scoping region. Absolute path files are not recommended, but may be useful in very specific cases.
Working directory of a region, PATH
and MODULE_BASE_PATH
¶
If a PATH
field exists in a region, it is used to set the working
directory of this region relatively to the parent region’s working
directory. The module working directory is the directory of the
module file except when it is redefined by the MODULE_BASE_PATH
of the module_settings
value.
Example¶
---- file: path1.radl ----
n: node {
PATH "nodes/example"
CXX { PATH "src" FILENAME "n.cpp" }
}
--------------------------
Considering that path1.radl is in “/tmp/pathexample”, the working
directory inside the region n
is “/tmp/pathexample/nodes/example”
and the file referred by “n.cpp” is
“/tmp/pathexample/nodes/example/src/n.cpp”
---- file: path2.radl ----
n: node {
PATH "nodes/example"
CXX { PATH "src" FILENAME "n.cpp" }
}
settings : module_settings {
MODULE_BASE_PATH "/usr/pathexample"
}
--------------------------
By adding this module_settings
value, “n.cpp” refers to
“/usr/pathexample/nodes/example/src/n.cpp”.
Libraries¶
Any user code needing libraries has to be declared as using a library
with the LIB
field. This field allows two forms of libraries,
cmake_library
and static_library
.
Static library¶
Static libraries are of the simplest form, gathering a set of source
files in their CXX
field while the library header files are found
in the HEADER_PATHS
paths.
Cmake library¶
The cmake library enables the use of arbitrarily complex libraries
since it is a user defined cmake script. The CMAKE_MODULE
field
provide the cmake module name used to find it in the working
directory. COMPONENTS
are the required components from this
module, used when calling the cmake find_module
command. By
default, it is expected that the module defines two variables named
${CMAKE_MODULE}_LIBRARIES
and ${CMAKE_MODULE}_INCLUDE_DIRS
defining respectively the libraries to be linked against and the
include directories. Both variable names can be specified if their
naming doesn’t follow this convention, respectively with the fields
CMAKE_VAR_LIBRARIES
and CMAKE_VAR_INCLUDE_DIRS
.
Typing¶
Inference¶
When a value is type annotated, it is checked to be of this type. If no type annotation is provided, the kind of the value is extracted from the ones possible in the value declaration context. If there is an ambiguity, it chooses the first relevant one in the order of declaration in language.py.
Checking¶
User values are checked to be correct with their type with the
function check_type
found in
language.py.
Subtyping¶
Subtyping allows for example to use a 16bits integer (int16
)
where a 32bits integer is required (int32
). For now, subtyping is
only done on sized_types
by allowing a type to be used in a
bigger version. sized_types
and possible sizes are explicit in
language.py. User values
are checked to fit the type’s size with the function
check_type_size
.
User code¶
Compilation¶
Include directories¶
The generated code is setup so that user code is compiled with the necessary include folders, i.e., the specified library header folders and the working directory of the file references.
Compiler definitions¶
The user code is compiled with the following definitions:
IN_RADL_GENERATED_CONTEXT
: The basic definition to detect if the code is compiled within Radler compilation chain.RADL_NODE_NAME
: The name of the current radl node.RADL_NODE_QNAME
: Which is set to the node qname (with dot separators, for example “modulename.nodename
”).RADL_MODULE_NAME
: The name of the current radl module.RADL_MODULE
: A pointer to the current module ast root.RADL_HEADER
: The radl generated header to be included by the user code (defining input and output data types).RADL_STATE
: The radl default name to be used for the C state type (if not specified in the radl file).RADL_STEP_FUN
: The default name to be used for the C step function (if not specified in the radl file).RADL_INIT_FUN
: The default name to be used for the C init function (if not specified in the radl file).RADL_FINISH_FUN
: The default name to be used for the C finish function (if not specified in the radl file).
C++ node definition¶
Each node is a Mealy machine. The user provide a class which will be
instantiated with the default constructor to generate an instance
representing the state of the machine. Then, the step
method of this
instance will be called to execute one step of the machine. The
signature of the step
method is required to be:
void step(const radl_in_t*, const radl_inflags_t*, radl_out_t*, radl_outflags_t*)
The four argument types are structures defined in the generated header file.
The input structure radl_in_t
¶
This structure has one field per subscription of the node. The field
name is the radl name of the subscription. Each field is in turn a
structure reflecting the topic of the subscription, whose fields are
the name of the radl topic FIELDS
.
The output structure radl_out_t
¶
It is similar to the input structure except that is is used by the step function to publish its publications. To this effect, the step function has to fill the output structure.
The flag structures radl_in_flags_t
, radl_out_flags_t
¶
Similarly to the input and output structures, these have a field for
each subscription (resp. publication). The type of each field is a
radl_flags_t
as described in
radl_flags.h.
C node definition¶
This is the same as the C++ case, except that instead of a class, the user needs to provide 4 things. To describe them, we use the default names provided by radl, but those may be decided in the radl file.
- A state type, that we call
RADL_STATE
here - An init function of signature :
void RADL_INIT_FUN(RADL_STATE *)
- A step function of signature :
void RADL_STEP_FUN(RADL_STATE *, const radl_in_t*, const radl_inflags_t*, radl_out_t*, radl_outflags_t*)
- A finish function of signature :
void RADL_FINISH_FUN(RADL_STATE *)
The flags¶
Computation of the output flags¶
The main idea of flags is to have some Boolean metadata attached to messages, which by default propagate through nodes. To this effect, the default value of the output flags of each publication of a node are set to the logical OR of all the flags of its subscriptions, equivalent to the following pseudocode:
v = 0;
for each s in subscriptions:
v = v OR in_flags->s
for each p in publications:
out_flags->p = v
Then, the input flags are given to the step function as read-only while the preset output flags are provided to the step function to give it a chance to turn on or off the desired flags. So, if the step function doesn’t change the output flags, they will propagate the input flags.
Computation of the input flag radl_STALE
¶
The so-called stale flag has the broad meaning that its associated
value isn’t “fresh”. More precisely, it either means that the
publisher of the value flagged it as stale (by automatic propagation
or by choice) or that no new message arrived since the last call to
the step function. In the latter case, the step function gets the
same input value (the mailbox hasn’t changed) but it is flagged as
stale. To check if a subscription s
is stale, one simply calls
radl_is_stale(in_flags->s)
which returns a Boolean.
Computation of the input flag radl_TIMEOUT
¶
The so-called timeout flag has the broad meaning that its associated
value has violated the timing constraints. More precisely, it either
means that the publisher of the value flagged it as timeout (by
automatic propagation or by choice) or that we haven’t received a
message since period of the publisher plus the maxlatency. In the
latter case, timing constraints are exceeded and something unexpected
is happening. To check if a subscription s
is timeout, one simply
calls radl_is_timeout(in_flags->s)
which returns a Boolean.
Turning off/on output flags¶
The function radl_turn_on
is used to turn on a flag. For example
to turn on the stale flag of the publication p
, we would do:
radl_turn_on(rald_STALE, &out_flags->p);
To turn off flags, the similar function rald_turn_off
should be
used.
Introspection¶
To allow more robust and generic user code, it is possible to access values defined in the radl description.
Modules root entries¶
The generic way of doing so is to use the generated module root ast
access functions. For example if we want to read the radl value of
qname module1.x
, one will first call the radlast_module1
function to get to the module root entry.
Shortcuts¶
Instead of needing to know the current module name and call the
corresponding root ast access function, one can use the provided
macro RADL_MODULE
. When writing a step function, one can directly
access the node it is used in by using the RADL_THIS
macro.
Field access¶
The generic rule is that fields of values are the names of the declared value inside this value. This name is in turn
- a pointer to the value (the generic case)
- the value itself (for a
topic
, astruct
or inside them).
The DEFS
field¶
To allow the user to define values which are not radl relevant, but
that should be statically defined and shared, we added the DEFS
field in the node
and topic
classes.
Example¶
Consider the following radl description:
---- file: introspec.radl ----
x : int32 1
t : topic { FIELDS
a : int32 2
}
n : node {
DEFS
y : int32 3
s : struct { FIELDS
z : int32 4
}
PATH working_dir "nodes/example"
CXX { PATH "src" FILENAME "n.cpp" }
}
----------------------------
In the user code, if one wants to get the value of x
(1):
*(radlast_introspec()->x)
or
*(RADL_MODULE->x)
Note the indirection, since we are in the generic case, the field
x
is a pointer to its value. To access a
, since it is inside
a topic
, we don’t need the indirection:
RADL_MODULE->t.a
Note that t
is a topic so it is not a pointer, its field are
accessed directly. This also applies to any other value, for example
to read “nodes/example”, one will do:
*(RADL_MODULE->n->working_dir)
or, when writing the step function for n
:
*(RADL_THIS->working_dir)
Note that in order to access this value, we gave it a name in the
radl description. Finally, to sum it up, to access z
when
writing the step function of n
:
RADL_THIS->s.z