├── deps
└── COLCON_IGNORE
├── .gitignore
├── .gitmodules
├── res
├── values
│ ├── colors.xml
│ └── strings.xml
└── drawable
│ ├── ic_launcher.xml
│ └── ic_launcher_foreground.xml
├── docs
├── privacy_policy.md
└── problems_encountered.md
├── src
├── log.h
├── sensors
│ ├── illuminance_sensor.cc
│ ├── gyroscope_sensor.h
│ ├── barometer_sensor.h
│ ├── illuminance_sensor.h
│ ├── accelerometer_sensor.h
│ ├── magnetometer_sensor.h
│ ├── barometer_sensor.cc
│ ├── gyroscope_sensor.cc
│ ├── accelerometer_sensor.cc
│ └── magnetometer_sensor.cc
├── display_topic.h
├── camera_descriptor.h
├── sensor_descriptor.cc
├── jvm.h
├── camera_descriptor.cc
├── controller.h
├── camera_manager.h
├── sensors.h
├── sensor.h
├── controllers
│ ├── ros_domain_id_controller.h
│ ├── barometer_sensor_controller.h
│ ├── gyroscope_sensor_controller.h
│ ├── magnetometer_sensor_controller.h
│ ├── accelerometer_sensor_controller.h
│ ├── illuminance_sensor_controller.h
│ ├── list_controller.h
│ ├── camera_controller.h
│ ├── illuminance_sensor_controller.cc
│ ├── barometer_sensor_controller.cc
│ ├── gyroscope_sensor_controller.cc
│ ├── accelerometer_sensor_controller.cc
│ ├── list_controller.cc
│ ├── magnetometer_sensor_controller.cc
│ ├── camera_controller.cc
│ └── ros_domain_id_controller.cc
├── sensor_descriptor.h
├── events.h
├── gui.h
├── ros_interface.cc
├── camera_device.h
├── CMakeLists.txt
├── sensor.cc
├── sensors.cc
├── camera_manager.cc
├── ros_interface.h
├── gui.cc
├── jvm.cc
├── camera_device.cc
└── android_entry_point.cc
├── AndroidManifest.xml.in
├── dep_build.cmake
├── scripts
└── generate_ros_superbuild.py
├── ros.repos
├── README.md
├── CMakeLists.txt
└── dependencies.cmake
/deps/COLCON_IGNORE:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | build
2 | deps
3 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "hello_android/DearImGui"]
2 | path = src/DearImGui
3 | url = git@github.com:ocornut/imgui.git
4 |
--------------------------------------------------------------------------------
/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #ffffff
4 |
5 |
--------------------------------------------------------------------------------
/docs/privacy_policy.md:
--------------------------------------------------------------------------------
1 | # Sensors for ROS Privacy Policy
2 |
3 | This App does not collect any data, nor does it transmit user information to any third party.
4 |
--------------------------------------------------------------------------------
/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Sensors for ROS
4 | Sensors for ROS
5 |
6 |
--------------------------------------------------------------------------------
/src/log.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | #define LOGI(...) \
6 | ((void)__android_log_print(ANDROID_LOG_INFO, "sensors_for_ros", __VA_ARGS__))
7 | #define LOGW(...) \
8 | ((void)__android_log_print(ANDROID_LOG_WARN, "sensors_for_ros", __VA_ARGS__))
9 |
--------------------------------------------------------------------------------
/res/drawable/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/src/sensors/illuminance_sensor.cc:
--------------------------------------------------------------------------------
1 | #include "sensors/illuminance_sensor.h"
2 |
3 | #include "log.h"
4 |
5 | using sensors_for_ros::IlluminanceSensor;
6 |
7 | void IlluminanceSensor::OnEvent(const ASensorEvent& event) {
8 | if (ASENSOR_TYPE_LIGHT != event.type) {
9 | LOGW("Event type was unexpected: %d", event.type);
10 | return;
11 | }
12 | sensor_msgs::msg::Illuminance msg;
13 | msg.illuminance = event.light;
14 | Emit(msg);
15 | }
16 |
--------------------------------------------------------------------------------
/src/display_topic.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "imgui.h"
4 | #include "ros_interface.h"
5 |
6 | namespace sensors_for_ros {
7 | template
8 | void DisplayTopic(const char* title, const Publisher& pub) {
9 | std::string title_str{title};
10 | title_str += " Topic";
11 | if (ImGui::CollapsingHeader(title_str.c_str(), ImGuiTreeNodeFlags_None)) {
12 | ImGui::TextWrapped("%s", pub.Topic());
13 | ImGui::TextWrapped("%s", pub.Type());
14 | }
15 | }
16 | } // namespace sensors_for_ros
17 |
--------------------------------------------------------------------------------
/src/sensors/gyroscope_sensor.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | #include "events.h"
6 | #include "sensor.h"
7 |
8 | namespace sensors_for_ros {
9 | class GyroscopeSensor
10 | : public Sensor,
11 | public event::Emitter {
12 | public:
13 | using Sensor::Sensor;
14 | virtual ~GyroscopeSensor() = default;
15 |
16 | protected:
17 | void OnEvent(const ASensorEvent& event) override;
18 | };
19 | } // namespace sensors_for_ros
20 |
--------------------------------------------------------------------------------
/src/sensors/barometer_sensor.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | #include "events.h"
6 | #include "sensor.h"
7 |
8 | namespace sensors_for_ros {
9 | class BarometerSensor : public Sensor,
10 | public event::Emitter {
11 | public:
12 | using Sensor::Sensor;
13 | virtual ~BarometerSensor() = default;
14 |
15 | protected:
16 | void OnEvent(const ASensorEvent& event) override;
17 | };
18 | } // namespace sensors_for_ros
19 |
--------------------------------------------------------------------------------
/src/sensors/illuminance_sensor.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | #include "events.h"
6 | #include "sensor.h"
7 |
8 | namespace sensors_for_ros {
9 | class IlluminanceSensor : public Sensor,
10 | public event::Emitter {
11 | public:
12 | using Sensor::Sensor;
13 | virtual ~IlluminanceSensor() = default;
14 |
15 | protected:
16 | void OnEvent(const ASensorEvent& event) override;
17 | };
18 | } // namespace sensors_for_ros
19 |
--------------------------------------------------------------------------------
/src/sensors/accelerometer_sensor.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | #include "events.h"
6 | #include "sensor.h"
7 |
8 | namespace sensors_for_ros {
9 | class AccelerometerSensor
10 | : public Sensor,
11 | public event::Emitter {
12 | public:
13 | using Sensor::Sensor;
14 | virtual ~AccelerometerSensor() = default;
15 |
16 | protected:
17 | void OnEvent(const ASensorEvent& event) override;
18 | };
19 | } // namespace sensors_for_ros
20 |
--------------------------------------------------------------------------------
/src/camera_descriptor.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | #include
6 |
7 | namespace sensors_for_ros {
8 | struct CameraDescriptor {
9 | std::string GetName() const;
10 |
11 | // An id identifying the camera
12 | std::string id;
13 |
14 | // Which way the lens if facing (back, external or front).
15 | acamera_metadata_enum_acamera_lens_facing lens_facing;
16 | // TODO intrinsics, supported resolutions, supported frame rates,
17 | // distortion parameters, etc.
18 | };
19 | } // namespace sensors_for_ros
20 |
--------------------------------------------------------------------------------
/src/sensors/magnetometer_sensor.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | #include "events.h"
6 | #include "sensor.h"
7 |
8 | namespace sensors_for_ros {
9 | const float kMicroTeslaPerTesla = 1000000;
10 |
11 | class MagnetometerSensor
12 | : public Sensor,
13 | public event::Emitter {
14 | public:
15 | using Sensor::Sensor;
16 | virtual ~MagnetometerSensor() = default;
17 |
18 | protected:
19 | void OnEvent(const ASensorEvent& event) override;
20 | };
21 | } // namespace sensors_for_ros
22 |
--------------------------------------------------------------------------------
/src/sensor_descriptor.cc:
--------------------------------------------------------------------------------
1 | #include "sensor_descriptor.h"
2 |
3 | using sensors_for_ros::SensorDescriptor;
4 |
5 | SensorDescriptor::SensorDescriptor(ASensorRef _sensor_ref)
6 | : sensor_ref(_sensor_ref) {
7 | name = ASensor_getName(sensor_ref);
8 | type = ASensor_getType(sensor_ref);
9 | type_str = ASensor_getStringType(sensor_ref);
10 | vendor = ASensor_getVendor(sensor_ref);
11 | handle = ASensor_getHandle(sensor_ref);
12 | min_delay = ASensor_getMinDelay(sensor_ref);
13 | resolution = ASensor_getResolution(sensor_ref);
14 | // TODO(sloretz) position ???
15 | }
16 |
--------------------------------------------------------------------------------
/src/sensors/barometer_sensor.cc:
--------------------------------------------------------------------------------
1 | #include "sensors/barometer_sensor.h"
2 |
3 | #include "log.h"
4 |
5 | using sensors_for_ros::BarometerSensor;
6 |
7 | void BarometerSensor::OnEvent(const ASensorEvent& event) {
8 | if (ASENSOR_TYPE_PRESSURE != event.type) {
9 | LOGW("Event type was unexpected: %d", event.type);
10 | return;
11 | }
12 | sensor_msgs::msg::FluidPressure msg;
13 | // TODO(sloretz) header and frame id
14 | // Convert millibar to pascals
15 | const int kPascalsPerMillibar = 100;
16 | msg.fluid_pressure = event.pressure * kPascalsPerMillibar;
17 | Emit(msg);
18 | }
19 |
--------------------------------------------------------------------------------
/src/jvm.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | #include
6 | #include
7 |
8 | // Functions that interact with the jvm
9 |
10 | namespace sensors_for_ros {
11 | std::string GetPackageName(ANativeActivity* activity);
12 |
13 | void RequestPermission(ANativeActivity* activity, const char* permission);
14 |
15 | bool HasPermission(ANativeActivity* activity, const char* permission);
16 |
17 | std::string GetCacheDir(ANativeActivity* activity);
18 |
19 | std::vector GetNetworkInterfaces(ANativeActivity* activity);
20 | } // namespace sensors_for_ros
21 |
--------------------------------------------------------------------------------
/src/camera_descriptor.cc:
--------------------------------------------------------------------------------
1 | #include "camera_descriptor.h"
2 |
3 | #include
4 |
5 | using sensors_for_ros::CameraDescriptor;
6 |
7 | std::string CameraDescriptor::GetName() const {
8 | std::stringstream name;
9 | switch (lens_facing) {
10 | case ACAMERA_LENS_FACING_BACK:
11 | name << "Back ";
12 | break;
13 | case ACAMERA_LENS_FACING_EXTERNAL:
14 | name << "External ";
15 | break;
16 | case ACAMERA_LENS_FACING_FRONT:
17 | name << "Front ";
18 | break;
19 | default:
20 | break;
21 | }
22 |
23 | name << "camera (" << id << ")";
24 | return name.str();
25 | }
26 |
--------------------------------------------------------------------------------
/src/sensors/gyroscope_sensor.cc:
--------------------------------------------------------------------------------
1 | #include "sensors/gyroscope_sensor.h"
2 |
3 | #include "log.h"
4 |
5 | using sensors_for_ros::GyroscopeSensor;
6 |
7 | void GyroscopeSensor::OnEvent(const ASensorEvent& event) {
8 | if (ASENSOR_TYPE_GYROSCOPE != event.type) {
9 | LOGW("Event type was unexpected: %d", event.type);
10 | return;
11 | }
12 | // LOGI("vector %f, %f, %f", event.vector.x, event.vector.y, event.vector.z);
13 | geometry_msgs::msg::TwistStamped msg;
14 | // TODO(sloretz) header and frame id
15 | msg.twist.angular.x = event.vector.x;
16 | msg.twist.angular.y = event.vector.y;
17 | msg.twist.angular.z = event.vector.z;
18 | Emit(msg);
19 | }
20 |
--------------------------------------------------------------------------------
/src/controller.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | namespace sensors_for_ros {
6 | class Controller {
7 | public:
8 | Controller(const std::string& unique_id) : unique_id_(unique_id) {}
9 | virtual ~Controller() = default;
10 |
11 | // Called by the GUI to draw a frame
12 | virtual void DrawFrame() = 0;
13 |
14 | // Called by list controller to display this in a list
15 | virtual std::string PrettyName() const = 0;
16 |
17 | // Called by list controller to uniquely identify this in a system
18 | const char* UniqueId() const { return unique_id_.c_str(); }
19 |
20 | private:
21 | const std::string unique_id_;
22 | };
23 | } // namespace sensors_for_ros
24 |
--------------------------------------------------------------------------------
/src/sensors/accelerometer_sensor.cc:
--------------------------------------------------------------------------------
1 | #include "sensors/accelerometer_sensor.h"
2 |
3 | #include "log.h"
4 |
5 | using sensors_for_ros::AccelerometerSensor;
6 |
7 | void AccelerometerSensor::OnEvent(const ASensorEvent& event) {
8 | if (ASENSOR_TYPE_ACCELEROMETER != event.type) {
9 | LOGW("Event type was unexpected: %d", event.type);
10 | return;
11 | }
12 | // LOGI("vector %f, %f, %f", event.vector.x, event.vector.y, event.vector.z);
13 | geometry_msgs::msg::AccelStamped msg;
14 | // TODO(sloretz) header and frame id
15 | msg.accel.linear.x = event.vector.x;
16 | msg.accel.linear.y = event.vector.y;
17 | msg.accel.linear.z = event.vector.z;
18 | Emit(msg);
19 | }
20 |
--------------------------------------------------------------------------------
/src/camera_manager.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | #include
6 |
7 | #include "camera_descriptor.h"
8 | #include "camera_device.h"
9 |
10 | namespace sensors_for_ros {
11 | class CameraManager {
12 | public:
13 | CameraManager();
14 | ~CameraManager();
15 |
16 | bool HasCameras() const { return !cameras_.empty(); }
17 |
18 | const std::vector& GetCameras() { return cameras_; }
19 |
20 | std::unique_ptr OpenCamera(const CameraDescriptor& desc) const;
21 |
22 | private:
23 | void DiscoverCameras();
24 |
25 | ACameraManager* native_manager_ = nullptr;
26 | std::vector cameras_;
27 | };
28 | } // namespace sensors_for_ros
29 |
--------------------------------------------------------------------------------
/src/sensors/magnetometer_sensor.cc:
--------------------------------------------------------------------------------
1 | #include "sensors/magnetometer_sensor.h"
2 |
3 | #include "log.h"
4 |
5 | using sensors_for_ros::MagnetometerSensor;
6 |
7 | void MagnetometerSensor::OnEvent(const ASensorEvent& event) {
8 | if (ASENSOR_TYPE_MAGNETIC_FIELD != event.type) {
9 | LOGW("Event type was unexpected: %d", event.type);
10 | return;
11 | }
12 | // LOGI("vector %f, %f, %f", event.vector.x, event.vector.y, event.vector.z);
13 | sensor_msgs::msg::MagneticField msg;
14 | // TODO(sloretz) header and frame id
15 | msg.magnetic_field.x = event.magnetic.x / kMicroTeslaPerTesla;
16 | msg.magnetic_field.y = event.magnetic.y / kMicroTeslaPerTesla;
17 | msg.magnetic_field.z = event.magnetic.z / kMicroTeslaPerTesla;
18 | Emit(msg);
19 | }
20 |
--------------------------------------------------------------------------------
/src/sensors.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 |
6 | #include
7 |
8 | #include "events.h"
9 | #include "ros_interface.h"
10 | #include "sensor.h"
11 | #include "sensor_descriptor.h"
12 |
13 | namespace sensors_for_ros {
14 | class Sensors {
15 | public:
16 | Sensors(ANativeActivity* activity);
17 | ~Sensors() = default;
18 |
19 | void Initialize();
20 | void Shutdown();
21 |
22 | const std::vector>& GetSensors() { return sensors_; };
23 |
24 | private:
25 | std::vector QuerySensors();
26 |
27 | void EventLoop();
28 |
29 | ASensorManager* sensor_manager_ = nullptr;
30 |
31 | std::vector> sensors_;
32 | };
33 | } // namespace sensors_for_ros
34 |
--------------------------------------------------------------------------------
/src/sensor.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | #include
6 | #include
7 |
8 | #include "sensor_descriptor.h"
9 |
10 | namespace sensors_for_ros {
11 | class Sensor {
12 | public:
13 | Sensor(ASensorManager* manager, SensorDescriptor desc)
14 | : manager_(manager), descriptor_(desc) {}
15 | virtual ~Sensor() = default;
16 |
17 | void Initialize();
18 | void Shutdown();
19 |
20 | const SensorDescriptor& Descriptor() { return descriptor_; }
21 |
22 | protected:
23 | void EventLoop();
24 |
25 | virtual void OnEvent(const ASensorEvent& event) = 0;
26 |
27 | private:
28 | const SensorDescriptor descriptor_;
29 |
30 | ASensorManager* manager_ = nullptr;
31 | std::atomic shutdown_;
32 | std::thread queue_thread_;
33 | ALooper* looper_ = nullptr;
34 | };
35 | } // namespace sensors_for_ros
36 |
--------------------------------------------------------------------------------
/src/controllers/ros_domain_id_controller.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "controller.h"
4 | #include "events.h"
5 |
6 | #include
7 |
8 | #include
9 | #include
10 |
11 | namespace sensors_for_ros {
12 |
13 | // There should only be one of these in existance.
14 | constexpr const char* kRosDomainIdControllerId = "ros_domain_id_controller";
15 |
16 | class RosDomainIdController : public Controller,
17 | public event::Emitter {
18 | public:
19 | RosDomainIdController(ANativeActivity* activity);
20 | virtual ~RosDomainIdController(){};
21 |
22 | // Called by the GUI to draw a frame
23 | void DrawFrame() override;
24 |
25 | std::string PrettyName() const override { return "ROS Domain ID"; }
26 |
27 | private:
28 | std::string preferred_interface_;
29 | std::vector network_interfaces_;
30 | };
31 | } // namespace sensors_for_ros
32 |
--------------------------------------------------------------------------------
/src/controllers/barometer_sensor_controller.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | #include "controller.h"
6 | #include "events.h"
7 | #include "log.h"
8 | #include "ros_interface.h"
9 | #include "sensors/barometer_sensor.h"
10 |
11 | namespace sensors_for_ros {
12 | // Handles interface between sensor, ROS, and GUI
13 | class BarometerSensorController
14 | : public Controller,
15 | public event::Emitter {
16 | public:
17 | BarometerSensorController(BarometerSensor* sensor, RosInterface& ros);
18 |
19 | virtual ~BarometerSensorController() = default;
20 |
21 | void DrawFrame() override;
22 |
23 | std::string PrettyName() const override;
24 |
25 | protected:
26 | void OnSensorReading(const sensor_msgs::msg::FluidPressure& event);
27 |
28 | private:
29 | sensor_msgs::msg::FluidPressure last_msg_;
30 | BarometerSensor* sensor_;
31 | Publisher publisher_;
32 | };
33 | } // namespace sensors_for_ros
34 |
--------------------------------------------------------------------------------
/src/controllers/gyroscope_sensor_controller.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | #include "controller.h"
6 | #include "events.h"
7 | #include "log.h"
8 | #include "ros_interface.h"
9 | #include "sensors/gyroscope_sensor.h"
10 |
11 | namespace sensors_for_ros {
12 | // Handles interface between sensor, ROS, and GUI
13 | class GyroscopeSensorController
14 | : public Controller,
15 | public event::Emitter {
16 | public:
17 | GyroscopeSensorController(GyroscopeSensor* sensor, RosInterface& ros);
18 |
19 | virtual ~GyroscopeSensorController() = default;
20 |
21 | void DrawFrame() override;
22 |
23 | std::string PrettyName() const override;
24 |
25 | protected:
26 | void OnGyroReading(const geometry_msgs::msg::TwistStamped& event);
27 |
28 | private:
29 | geometry_msgs::msg::TwistStamped last_msg_;
30 | GyroscopeSensor* sensor_;
31 | Publisher publisher_;
32 | };
33 | } // namespace sensors_for_ros
34 |
--------------------------------------------------------------------------------
/src/controllers/magnetometer_sensor_controller.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | #include "controller.h"
6 | #include "events.h"
7 | #include "log.h"
8 | #include "ros_interface.h"
9 | #include "sensors/magnetometer_sensor.h"
10 |
11 | namespace sensors_for_ros {
12 | // Handles interface between sensor, ROS, and GUI
13 | class MagnetometerSensorController
14 | : public Controller,
15 | public event::Emitter {
16 | public:
17 | MagnetometerSensorController(MagnetometerSensor* sensor, RosInterface& ros);
18 |
19 | virtual ~MagnetometerSensorController() = default;
20 |
21 | void DrawFrame() override;
22 |
23 | std::string PrettyName() const override;
24 |
25 | protected:
26 | void OnSensorReading(const sensor_msgs::msg::MagneticField& event);
27 |
28 | private:
29 | sensor_msgs::msg::MagneticField last_msg_;
30 | MagnetometerSensor* sensor_;
31 | Publisher publisher_;
32 | };
33 | } // namespace sensors_for_ros
34 |
--------------------------------------------------------------------------------
/src/controllers/accelerometer_sensor_controller.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | #include "controller.h"
6 | #include "events.h"
7 | #include "log.h"
8 | #include "ros_interface.h"
9 | #include "sensors/accelerometer_sensor.h"
10 |
11 | namespace sensors_for_ros {
12 | // Handles interface between sensor, ROS, and GUI
13 | class AccelerometerSensorController
14 | : public Controller,
15 | public event::Emitter {
16 | public:
17 | AccelerometerSensorController(AccelerometerSensor* sensor, RosInterface& ros);
18 |
19 | virtual ~AccelerometerSensorController() = default;
20 |
21 | void DrawFrame() override;
22 |
23 | std::string PrettyName() const override;
24 |
25 | protected:
26 | void OnSensorReading(const geometry_msgs::msg::AccelStamped& event);
27 |
28 | private:
29 | geometry_msgs::msg::AccelStamped last_msg_;
30 | AccelerometerSensor* sensor_;
31 | Publisher publisher_;
32 | };
33 | } // namespace sensors_for_ros
34 |
--------------------------------------------------------------------------------
/src/controllers/illuminance_sensor_controller.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | #include "controller.h"
6 | #include "events.h"
7 | #include "log.h"
8 | #include "ros_interface.h"
9 | #include "sensors/illuminance_sensor.h"
10 |
11 | namespace sensors_for_ros {
12 | // Handles interface between sensor, ROS, and GUI
13 | class IlluminanceSensorController
14 | : public Controller,
15 | public event::Emitter {
16 | public:
17 | // TODO reference to GUI? Or maybe GUI has reference to this!
18 | IlluminanceSensorController(IlluminanceSensor* sensor, RosInterface& ros);
19 |
20 | virtual ~IlluminanceSensorController() = default;
21 |
22 | void DrawFrame() override;
23 |
24 | std::string PrettyName() const override;
25 |
26 | protected:
27 | void OnIlluminanceChanged(const sensor_msgs::msg::Illuminance& event);
28 |
29 | private:
30 | sensor_msgs::msg::Illuminance last_msg_;
31 | IlluminanceSensor* sensor_;
32 | Publisher publisher_;
33 | };
34 | } // namespace sensors_for_ros
35 |
--------------------------------------------------------------------------------
/src/controllers/list_controller.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | #include "controller.h"
6 | #include "events.h"
7 | #include "log.h"
8 |
9 | namespace sensors_for_ros {
10 |
11 | // There's only supposed to be one of these in the process
12 | constexpr const char *kListControllerId = "list_controller";
13 |
14 | class ListController : public Controller,
15 | public event::Emitter,
16 | public event::Emitter {
17 | public:
18 | using event::Emitter::SetListener;
19 | using event::Emitter::SetListener;
20 | using event::Emitter::Emit;
21 | using event::Emitter::Emit;
22 |
23 | ListController();
24 |
25 | void AddController(const Controller *controller);
26 |
27 | virtual ~ListController() = default;
28 |
29 | void DrawFrame() override;
30 |
31 | std::string PrettyName() const override { return "List Controller"; }
32 |
33 | private:
34 | std::vector controllers_;
35 | };
36 | } // namespace sensors_for_ros
37 |
--------------------------------------------------------------------------------
/src/sensor_descriptor.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include
3 | #include
4 |
5 | namespace sensors_for_ros {
6 | // TODO document who uses this
7 | struct SensorDescriptor {
8 | explicit SensorDescriptor(ASensorRef _sensor_ref);
9 | SensorDescriptor(const SensorDescriptor& other) = default;
10 | ~SensorDescriptor() = default;
11 |
12 | const char* PrettyType() const {
13 | switch (type) {
14 | case ASENSOR_TYPE_LIGHT:
15 | return "Light";
16 | break;
17 | case ASENSOR_TYPE_GYROSCOPE:
18 | return "Gyroscope";
19 | break;
20 | case ASENSOR_TYPE_ACCELEROMETER:
21 | return "Accelerometer";
22 | break;
23 | case ASENSOR_TYPE_PRESSURE:
24 | return "Barometer";
25 | break;
26 | case ASENSOR_TYPE_MAGNETIC_FIELD:
27 | return "Magnetometer";
28 | break;
29 | default:
30 | return type_str;
31 | }
32 | }
33 |
34 | ASensorRef sensor_ref;
35 | const char* name;
36 | const char* type_str;
37 | const char* vendor;
38 | int type;
39 | int handle;
40 | int min_delay;
41 | float resolution;
42 | };
43 | } // namespace sensors_for_ros
44 |
--------------------------------------------------------------------------------
/src/controllers/camera_controller.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | #include "camera_device.h"
6 | #include "camera_manager.h"
7 | #include "controller.h"
8 | #include "events.h"
9 | #include "ros_interface.h"
10 |
11 | namespace sensors_for_ros {
12 | class CameraController : public Controller,
13 | public event::Emitter {
14 | public:
15 | CameraController(CameraManager* camera_manager,
16 | const CameraDescriptor& camera_descriptor,
17 | RosInterface& ros);
18 | virtual ~CameraController();
19 |
20 | // Called by the GUI to draw a frame
21 | void DrawFrame() override;
22 |
23 | std::string PrettyName() const override;
24 |
25 | protected:
26 | void OnImage(
27 | const std::pair& info_image);
28 |
29 | private:
30 | void EnableCamera();
31 | void DisableCamera();
32 |
33 | CameraManager* camera_manager_;
34 | const CameraDescriptor camera_descriptor_;
35 | std::unique_ptr device_;
36 | Publisher info_pub_;
37 | Publisher image_pub_;
38 | };
39 | } // namespace sensors_for_ros
40 |
--------------------------------------------------------------------------------
/src/events.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 |
6 | namespace sensors_for_ros {
7 | namespace event {
8 | // Event fired to indicate the ROS_DOMAIN_ID was set
9 | struct RosDomainIdChanged {
10 | int32_t id;
11 | std::string interface;
12 | };
13 |
14 | struct SensorEvent {
15 | // Sensor handle (ASensorEvent::sensor) uniquely identifying the sensor
16 | int handle;
17 | };
18 |
19 | // Indicates an illuminance sensor got a new reading
20 | struct IlluminanceChanged : SensorEvent {
21 | // SI unit: lux (lx)
22 | float light;
23 | };
24 |
25 | // Indicates a controller wants to exit back to the previous page
26 | struct GuiNavigateBack {};
27 |
28 | // Indicates a controller wants to display a specific sensor
29 | struct GuiNavigateTo {
30 | // The unique ID of a controller to navigate to
31 | std::string unique_id;
32 | };
33 |
34 | template
35 | using Listener = std::function;
36 |
37 | template
38 | class Emitter {
39 | public:
40 | void Emit(const EventType& event) {
41 | if (event_listener_) {
42 | event_listener_(event);
43 | }
44 | }
45 |
46 | void SetListener(Listener listener) { event_listener_ = listener; }
47 |
48 | private:
49 | Listener event_listener_;
50 | };
51 | } // namespace event
52 | } // namespace sensors_for_ros
53 |
--------------------------------------------------------------------------------
/src/gui.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 |
8 | #include
9 | #include
10 | #include
11 | #include
12 |
13 | #include "controller.h"
14 | #include "events.h"
15 |
16 | namespace sensors_for_ros {
17 | class GUI {
18 | public:
19 | GUI();
20 | ~GUI();
21 |
22 | void Start(ANativeActivity* activity, ANativeWindow* window);
23 | void Stop();
24 |
25 | void SetInputQueue(AInputQueue* queue);
26 | void RemoveInputQueue();
27 |
28 | void SetController(Controller* controller);
29 |
30 | private:
31 | void InitializeDearImGui(ANativeWindow* window);
32 | void TerminateDearImGui();
33 | bool InitializeDisplay(ANativeWindow* window);
34 | void TerminateDisplay();
35 | void DrawFrame();
36 | void ShowROSDomainIdPicker();
37 | void DrawingLoop(ANativeWindow* window,
38 | std::promise promise_first_frame);
39 | void CheckInput();
40 |
41 | EGLDisplay display_;
42 | EGLSurface surface_;
43 | EGLContext context_;
44 | int32_t width_;
45 | int32_t height_;
46 |
47 | std::thread draw_thread_;
48 | std::atomic exit_loop_;
49 |
50 | std::mutex iqueue_mtx_;
51 | AInputQueue* iqueue_ = nullptr;
52 |
53 | ANativeActivity* activity_;
54 |
55 | Controller* active_controller = nullptr;
56 | };
57 | } // namespace sensors_for_ros
58 |
--------------------------------------------------------------------------------
/AndroidManifest.xml.in:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
11 |
12 |
20 |
24 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/src/ros_interface.cc:
--------------------------------------------------------------------------------
1 | #include "ros_interface.h"
2 |
3 | #include "log.h"
4 |
5 | using sensors_for_ros::RosInterface;
6 |
7 | RosInterface::RosInterface() {}
8 |
9 | void RosInterface::Initialize(size_t ros_domain_id) {
10 | rclcpp::InitOptions init_options;
11 | init_options.set_domain_id(ros_domain_id);
12 | init_options.shutdown_on_signal = false;
13 | context_ = std::make_shared();
14 | context_->init(0, nullptr, init_options);
15 |
16 | rclcpp::NodeOptions node_options;
17 | node_options.context(context_);
18 | node_ = std::make_shared("sensors_for_ros", node_options);
19 |
20 | rclcpp::ExecutorOptions executor_options;
21 | executor_options.context = context_;
22 | executor_ = std::make_shared(
23 | executor_options);
24 | executor_->add_node(node_);
25 |
26 | executor_thread_ = std::thread(&rclcpp::Executor::spin, executor_.get());
27 |
28 | NotifyInitChanged();
29 | }
30 |
31 | void RosInterface::Shutdown() {
32 | context_->shutdown("RosInterface asked to Shutdown");
33 | NotifyInitChanged();
34 | executor_thread_.join();
35 | node_.reset();
36 | executor_.reset();
37 | context_.reset();
38 | }
39 |
40 | bool RosInterface::Initialized() const {
41 | return context_ && context_->is_valid();
42 | }
43 |
44 | rclcpp::Context::SharedPtr RosInterface::get_context() const {
45 | return context_;
46 | }
47 |
48 | rclcpp::Node::SharedPtr RosInterface::get_node() const { return node_; }
49 |
50 | void RosInterface::AddObserver(std::function init_or_shutdown) {
51 | observers_.push_back(init_or_shutdown);
52 | }
53 |
54 | void RosInterface::NotifyInitChanged() {
55 | for (auto& observer : observers_) {
56 | LOGI("Notifying observer %p", &observer);
57 | observer();
58 | }
59 | // Still want observations? ask for them again
60 | observers_.clear();
61 | }
62 |
--------------------------------------------------------------------------------
/src/controllers/illuminance_sensor_controller.cc:
--------------------------------------------------------------------------------
1 | #include "controllers/illuminance_sensor_controller.h"
2 |
3 | #include "display_topic.h"
4 | #include "imgui.h"
5 |
6 | namespace sensors_for_ros {
7 | IlluminanceSensorController::IlluminanceSensorController(
8 | IlluminanceSensor* sensor, RosInterface& ros)
9 | : sensor_(sensor),
10 | publisher_(ros),
11 | Controller(std::string(sensor->Descriptor().name) +
12 | sensor->Descriptor().vendor) {
13 | sensor->SetListener(
14 | std::bind(&IlluminanceSensorController::OnIlluminanceChanged, this,
15 | std::placeholders::_1));
16 |
17 | // TODO allow publisher topic to be set from GUI
18 | publisher_.SetTopic("illuminance");
19 | // TODO allow publisher to be enabled/disabled from GUI
20 | publisher_.Enable();
21 |
22 | // TODO allow GUI to change topic and QoS
23 | }
24 |
25 | void IlluminanceSensorController::OnIlluminanceChanged(
26 | const sensor_msgs::msg::Illuminance& msg) {
27 | last_msg_ = msg;
28 | publisher_.Publish(msg);
29 | }
30 |
31 | void IlluminanceSensorController::DrawFrame() {
32 | bool show_dialog = true;
33 | ImGui::Begin("Illuminace Senosr", &show_dialog,
34 | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize |
35 | ImGuiWindowFlags_NoTitleBar);
36 | if (ImGui::Button("< Back")) {
37 | LOGI("Asked to go back");
38 | Emit(event::GuiNavigateBack{});
39 | }
40 | ImGui::Text("Illuminance Sensor");
41 | ImGui::Separator();
42 | ImGui::Text("Name: %s", sensor_->Descriptor().name);
43 | ImGui::Text("Vendor: %s", sensor_->Descriptor().vendor);
44 | ImGui::Separator();
45 | DisplayTopic("", publisher_);
46 | ImGui::Separator();
47 | ImGui::Text("Last measurement: %.2f lx", last_msg_.illuminance);
48 | ImGui::End();
49 | }
50 |
51 | std::string IlluminanceSensorController::PrettyName() const {
52 | return "Light Sensor";
53 | }
54 | } // namespace sensors_for_ros
55 |
--------------------------------------------------------------------------------
/src/controllers/barometer_sensor_controller.cc:
--------------------------------------------------------------------------------
1 | #include "controllers/barometer_sensor_controller.h"
2 |
3 | #include "display_topic.h"
4 | #include "imgui.h"
5 |
6 | namespace sensors_for_ros {
7 | BarometerSensorController::BarometerSensorController(BarometerSensor* sensor,
8 | RosInterface& ros)
9 | : sensor_(sensor),
10 | publisher_(ros),
11 | Controller(std::string(sensor->Descriptor().name) +
12 | sensor->Descriptor().vendor) {
13 | sensor->SetListener(std::bind(&BarometerSensorController::OnSensorReading,
14 | this, std::placeholders::_1));
15 |
16 | // TODO allow publisher topic to be set from GUI
17 | publisher_.SetTopic("barometer");
18 | // TODO allow publisher to be enabled/disabled from GUI
19 | publisher_.Enable();
20 |
21 | // TODO allow GUI to change topic and QoS
22 | }
23 |
24 | void BarometerSensorController::OnSensorReading(
25 | const sensor_msgs::msg::FluidPressure& msg) {
26 | last_msg_ = msg;
27 | publisher_.Publish(msg);
28 | }
29 |
30 | void BarometerSensorController::DrawFrame() {
31 | bool show_dialog = true;
32 | ImGui::Begin("Barometer", &show_dialog,
33 | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize |
34 | ImGuiWindowFlags_NoTitleBar);
35 | if (ImGui::Button("< Back")) {
36 | LOGI("Asked to go back");
37 | Emit(event::GuiNavigateBack{});
38 | }
39 | ImGui::Text("Barometer");
40 | ImGui::Separator();
41 | ImGui::Text("Name: %s", sensor_->Descriptor().name);
42 | ImGui::Text("Vendor: %s", sensor_->Descriptor().vendor);
43 | ImGui::Separator();
44 | DisplayTopic("", publisher_);
45 | ImGui::Separator();
46 | ImGui::Text("Last measurement: %.2f Pa", last_msg_.fluid_pressure);
47 | ImGui::End();
48 | }
49 |
50 | std::string BarometerSensorController::PrettyName() const {
51 | return "Barometer Sensor";
52 | }
53 | } // namespace sensors_for_ros
54 |
--------------------------------------------------------------------------------
/src/controllers/gyroscope_sensor_controller.cc:
--------------------------------------------------------------------------------
1 | #include "controllers/gyroscope_sensor_controller.h"
2 |
3 | #include "display_topic.h"
4 | #include "imgui.h"
5 |
6 | namespace sensors_for_ros {
7 | GyroscopeSensorController::GyroscopeSensorController(GyroscopeSensor* sensor,
8 | RosInterface& ros)
9 | : sensor_(sensor),
10 | publisher_(ros),
11 | Controller(std::string(sensor->Descriptor().name) +
12 | sensor->Descriptor().vendor) {
13 | sensor->SetListener(std::bind(&GyroscopeSensorController::OnGyroReading, this,
14 | std::placeholders::_1));
15 |
16 | // TODO allow publisher topic to be set from GUI
17 | publisher_.SetTopic("gyroscope");
18 | // TODO allow publisher to be enabled/disabled from GUI
19 | publisher_.Enable();
20 |
21 | // TODO allow GUI to change topic and QoS
22 | }
23 |
24 | void GyroscopeSensorController::OnGyroReading(
25 | const geometry_msgs::msg::TwistStamped& msg) {
26 | last_msg_ = msg;
27 | publisher_.Publish(msg);
28 | }
29 |
30 | void GyroscopeSensorController::DrawFrame() {
31 | bool show_dialog = true;
32 | ImGui::Begin("Gyroscope", &show_dialog,
33 | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize |
34 | ImGuiWindowFlags_NoTitleBar);
35 | if (ImGui::Button("< Back")) {
36 | LOGI("Asked to go back");
37 | Emit(event::GuiNavigateBack{});
38 | }
39 | ImGui::Text("Gyroscope");
40 | ImGui::Separator();
41 | ImGui::Text("Name: %s", sensor_->Descriptor().name);
42 | ImGui::Text("Vendor: %s", sensor_->Descriptor().vendor);
43 | ImGui::Separator();
44 | DisplayTopic("", publisher_);
45 | ImGui::Separator();
46 | ImGui::Text("Last measurement: %.2f, %.2f, %.2f rad/s",
47 | last_msg_.twist.angular.x, last_msg_.twist.angular.y,
48 | last_msg_.twist.angular.z);
49 | ImGui::End();
50 | }
51 |
52 | std::string GyroscopeSensorController::PrettyName() const {
53 | return "Gyroscope Sensor";
54 | }
55 | } // namespace sensors_for_ros
56 |
--------------------------------------------------------------------------------
/src/controllers/accelerometer_sensor_controller.cc:
--------------------------------------------------------------------------------
1 | #include "controllers/accelerometer_sensor_controller.h"
2 |
3 | #include "display_topic.h"
4 | #include "imgui.h"
5 |
6 | namespace sensors_for_ros {
7 | AccelerometerSensorController::AccelerometerSensorController(
8 | AccelerometerSensor* sensor, RosInterface& ros)
9 | : sensor_(sensor),
10 | publisher_(ros),
11 | Controller(std::string(sensor->Descriptor().name) +
12 | sensor->Descriptor().vendor) {
13 | sensor->SetListener(std::bind(&AccelerometerSensorController::OnSensorReading,
14 | this, std::placeholders::_1));
15 |
16 | // TODO allow publisher topic to be set from GUI
17 | publisher_.SetTopic("accelerometer");
18 | // TODO allow publisher to be enabled/disabled from GUI
19 | publisher_.Enable();
20 |
21 | // TODO allow GUI to change topic and QoS
22 | }
23 |
24 | void AccelerometerSensorController::OnSensorReading(
25 | const geometry_msgs::msg::AccelStamped& msg) {
26 | last_msg_ = msg;
27 | publisher_.Publish(msg);
28 | }
29 |
30 | void AccelerometerSensorController::DrawFrame() {
31 | bool show_dialog = true;
32 | ImGui::Begin("Accelerometer", &show_dialog,
33 | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize |
34 | ImGuiWindowFlags_NoTitleBar);
35 | if (ImGui::Button("< Back")) {
36 | LOGI("Asked to go back");
37 | Emit(event::GuiNavigateBack{});
38 | }
39 | ImGui::Text("Accelerometer");
40 | ImGui::Separator();
41 | ImGui::Text("Name: %s", sensor_->Descriptor().name);
42 | ImGui::Text("Vendor: %s", sensor_->Descriptor().vendor);
43 | ImGui::Separator();
44 | DisplayTopic("", publisher_);
45 | ImGui::Separator();
46 | ImGui::Text("Last measurement: %.2f, %.2f, %.2f m/s^2",
47 | last_msg_.accel.linear.x, last_msg_.accel.linear.y,
48 | last_msg_.accel.linear.z);
49 | ImGui::End();
50 | }
51 |
52 | std::string AccelerometerSensorController::PrettyName() const {
53 | return "Accelerometer Sensor";
54 | }
55 | } // namespace sensors_for_ros
56 |
--------------------------------------------------------------------------------
/src/controllers/list_controller.cc:
--------------------------------------------------------------------------------
1 | #include "controllers/list_controller.h"
2 |
3 | #include "imgui.h"
4 |
5 | namespace sensors_for_ros {
6 | ListController::ListController() : Controller(kListControllerId) {}
7 |
8 | void ListController::AddController(const Controller* controller) {
9 | if (controllers_.end() ==
10 | std::find(controllers_.begin(), controllers_.end(), controller)) {
11 | controllers_.push_back(controller);
12 | }
13 | }
14 |
15 | void ListController::DrawFrame() {
16 | bool show_dialog = true;
17 | ImGui::Begin("Sensor List", &show_dialog,
18 | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize |
19 | ImGuiWindowFlags_NoTitleBar);
20 |
21 | // TODO(sloretz) re-enable when changing the network works at runtime.
22 | // if (ImGui::Button("< Back")) {
23 | // LOGI("Asked to go back");
24 | // Emit(event::GuiNavigateBack{});
25 | // }
26 |
27 | ImGui::Separator();
28 |
29 | // Which controller is selected
30 | static const Controller* selected_controller = nullptr;
31 |
32 | if (ImGui::BeginListBox("##list_box", ImVec2(-FLT_MIN, -FLT_MIN))) {
33 | for (const Controller* controller : controllers_) {
34 | const bool is_selected = (controller == selected_controller);
35 |
36 | // TODO Unique ID
37 | ImGui::PushID(controller->UniqueId());
38 |
39 | if (ImGui::Selectable("##selectable", is_selected)) {
40 | selected_controller = controller;
41 | auto event = event::GuiNavigateTo();
42 | event.unique_id = selected_controller->UniqueId();
43 | Emit(event);
44 | }
45 |
46 | ImGui::SameLine();
47 | std::string name = controller->PrettyName();
48 | ImGui::Text("%s", name.c_str());
49 |
50 | ImGui::PopID();
51 |
52 | // Set the initial focus when opening the combo (scrolling + keyboard
53 | // navigation focus)
54 | if (is_selected) {
55 | ImGui::SetItemDefaultFocus();
56 | }
57 | }
58 | ImGui::EndListBox();
59 | }
60 |
61 | ImGui::End();
62 | }
63 | } // namespace sensors_for_ros
64 |
--------------------------------------------------------------------------------
/src/controllers/magnetometer_sensor_controller.cc:
--------------------------------------------------------------------------------
1 | #include "controllers/magnetometer_sensor_controller.h"
2 |
3 | #include "display_topic.h"
4 | #include "imgui.h"
5 |
6 | namespace sensors_for_ros {
7 | MagnetometerSensorController::MagnetometerSensorController(
8 | MagnetometerSensor* sensor, RosInterface& ros)
9 | : sensor_(sensor),
10 | publisher_(ros),
11 | Controller(std::string(sensor->Descriptor().name) +
12 | sensor->Descriptor().vendor) {
13 | sensor->SetListener(std::bind(&MagnetometerSensorController::OnSensorReading,
14 | this, std::placeholders::_1));
15 |
16 | // TODO allow publisher topic to be set from GUI
17 | publisher_.SetTopic("magnetometer");
18 | // TODO allow publisher to be enabled/disabled from GUI
19 | publisher_.Enable();
20 |
21 | // TODO allow GUI to change topic and QoS
22 | }
23 |
24 | void MagnetometerSensorController::OnSensorReading(
25 | const sensor_msgs::msg::MagneticField& msg) {
26 | last_msg_ = msg;
27 | publisher_.Publish(msg);
28 | }
29 |
30 | void MagnetometerSensorController::DrawFrame() {
31 | bool show_dialog = true;
32 | ImGui::Begin("Magnetometer", &show_dialog,
33 | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize |
34 | ImGuiWindowFlags_NoTitleBar);
35 | if (ImGui::Button("< Back")) {
36 | LOGI("Asked to go back");
37 | Emit(event::GuiNavigateBack{});
38 | }
39 | ImGui::Text("Magnetometer");
40 | ImGui::Separator();
41 | ImGui::Text("Name: %s", sensor_->Descriptor().name);
42 | ImGui::Text("Vendor: %s", sensor_->Descriptor().vendor);
43 | ImGui::Separator();
44 | DisplayTopic("", publisher_);
45 | ImGui::Separator();
46 | ImGui::Text("Last measurement: %.2f, %.2f, %.2f microtesla",
47 | last_msg_.magnetic_field.x * kMicroTeslaPerTesla,
48 | last_msg_.magnetic_field.y * kMicroTeslaPerTesla,
49 | last_msg_.magnetic_field.z * kMicroTeslaPerTesla);
50 | ImGui::End();
51 | }
52 |
53 | std::string MagnetometerSensorController::PrettyName() const {
54 | return "Magnetometer Sensor";
55 | }
56 | } // namespace sensors_for_ros
57 |
--------------------------------------------------------------------------------
/src/camera_device.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 |
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 | #include
15 | #include
16 |
17 | #include "camera_descriptor.h"
18 | #include "events.h"
19 | #include "log.h"
20 |
21 | namespace sensors_for_ros {
22 | struct AImageDeleter {
23 | void operator()(AImage* image) { AImage_delete(image); }
24 | };
25 |
26 | using sensor_msgs::msg::CameraInfo;
27 | using sensor_msgs::msg::Image;
28 |
29 | class CameraDevice : public event::Emitter<
30 | std::pair> {
31 | public:
32 | static std::unique_ptr OpenCamera(
33 | ACameraManager* native_manager, const CameraDescriptor& desc);
34 |
35 | ~CameraDevice();
36 |
37 | const CameraDescriptor& GetDescriptor() const { return desc_; }
38 |
39 | // Internal
40 | void OnImage(std::unique_ptr image);
41 |
42 | std::tuple Resolution() const { return {width_, height_}; }
43 |
44 | private:
45 | CameraDevice();
46 |
47 | // TODO Open supported formats we queried with the descriptor
48 | int width_ = 640;
49 | int height_ = 480;
50 |
51 | // Get images from camera and convert them to sensor_msgs/msgs/Image
52 | void ProcessImages();
53 |
54 | CameraDescriptor desc_;
55 | ACameraDevice* native_device_ = nullptr;
56 | ACameraDevice_stateCallbacks state_callbacks_;
57 |
58 | AImageReader* reader_ = nullptr;
59 | AImageReader_ImageListener reader_callbacks_;
60 |
61 | ACameraOutputTarget* camera_output_target_ = nullptr;
62 | ACaptureSessionOutput* capture_session_output_ = nullptr;
63 |
64 | ACaptureSessionOutputContainer* output_container_ = nullptr;
65 |
66 | ACaptureRequest* capture_request_ = nullptr;
67 |
68 | ACameraCaptureSession* capture_session_ = nullptr;
69 |
70 | std::mutex mutex_;
71 | std::unique_ptr image_ = nullptr;
72 | std::atomic shutdown_;
73 | std::condition_variable wake_cv_;
74 | std::thread thread_;
75 | };
76 | } // namespace sensors_for_ros
77 |
--------------------------------------------------------------------------------
/src/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.13)
2 |
3 | project(hello_android)
4 |
5 | find_package(geometry_msgs REQUIRED)
6 | find_package(rclcpp REQUIRED)
7 | find_package(rclcpp_lifecycle REQUIRED)
8 | find_package(std_msgs REQUIRED)
9 | find_package(sensor_msgs REQUIRED)
10 |
11 | set(CMAKE_VERBOSE_MAKEFILE ON)
12 |
13 | add_library(imgui STATIC
14 | DearImGui/imgui.cpp
15 | DearImGui/imgui_demo.cpp
16 | DearImGui/imgui_draw.cpp
17 | DearImGui/imgui_tables.cpp
18 | DearImGui/imgui_widgets.cpp
19 | DearImGui/backends/imgui_impl_android.cpp
20 | DearImGui/backends/imgui_impl_opengl3.cpp)
21 | target_include_directories(imgui PUBLIC
22 | DearImGui
23 | DearImGui/backends)
24 | target_link_libraries(imgui PUBLIC
25 | EGL
26 | GLESv3)
27 |
28 | add_library(android-ros SHARED
29 | android_entry_point.cc
30 | camera_descriptor.cc
31 | camera_device.cc
32 | camera_manager.cc
33 | controllers/accelerometer_sensor_controller.cc
34 | controllers/barometer_sensor_controller.cc
35 | controllers/camera_controller.cc
36 | controllers/gyroscope_sensor_controller.cc
37 | controllers/illuminance_sensor_controller.cc
38 | controllers/list_controller.cc
39 | controllers/magnetometer_sensor_controller.cc
40 | controllers/ros_domain_id_controller.cc
41 | gui.cc
42 | jvm.cc
43 | ros_interface.cc
44 | sensor.cc
45 | sensor_descriptor.cc
46 | sensors.cc
47 | sensors/accelerometer_sensor.cc
48 | sensors/barometer_sensor.cc
49 | sensors/gyroscope_sensor.cc
50 | sensors/illuminance_sensor.cc
51 | sensors/magnetometer_sensor.cc
52 | )
53 | target_link_libraries(android-ros PRIVATE
54 | android
55 | camera2ndk
56 | mediandk
57 | log
58 | imgui
59 | rclcpp::rclcpp
60 | ${geometry_msgs_TARGETS}
61 | ${sensor_msgs_TARGETS})
62 | target_link_options(android-ros PRIVATE -u ANativeActivity_onCreate)
63 | target_include_directories(android-ros PUBLIC
64 | $)
65 | # LTO to reduce APK size
66 | set_property(TARGET android-ros PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE)
67 |
68 |
69 | # Workaround https://gitlab.kitware.com/cmake/cmake/-/issues/21772
70 | # TODO(sloretz) only do this if CMake version is less than 3.20
71 | STRING(REGEX REPLACE "-fuse-ld=gold" "" CMAKE_CXX_LINK_OPTIONS_IPO ${CMAKE_CXX_LINK_OPTIONS_IPO})
72 |
73 | install(TARGETS android-ros DESTINATION lib)
74 |
--------------------------------------------------------------------------------
/src/sensor.cc:
--------------------------------------------------------------------------------
1 | #include "sensor.h"
2 |
3 | #include
4 |
5 | #include "log.h"
6 |
7 | using sensors_for_ros::Sensor;
8 |
9 | /// Have event loop reading each sensor
10 | void Sensor::EventLoop() {
11 | const int kSensorIdent = 42;
12 | // Looper is thread specific, so must create it here.
13 | looper_ = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS);
14 | ASensorEventQueue* queue = ASensorManager_createEventQueue(
15 | manager_, looper_, kSensorIdent, nullptr, nullptr);
16 |
17 | // Ask for addional info events to have sensor position
18 | if (0 != ASensorEventQueue_requestAdditionalInfoEvents(queue, true)) {
19 | LOGW("Couldn't enable additional info events");
20 | }
21 |
22 | ASensorEventQueue_enableSensor(queue, descriptor_.sensor_ref);
23 |
24 | // Read all pending events.
25 | int ident;
26 | int events;
27 | void* data;
28 | while (not shutdown_.load()) {
29 | ident = ALooper_pollAll(-1, nullptr, &events, &data);
30 | if (kSensorIdent == ident) {
31 | ASensorEvent event;
32 | while (ASensorEventQueue_getEvents(queue, &event, 1) > 0) {
33 | // LOGI("Event from sensor handle %d", event.sensor);
34 | assert(event.sensor == descriptor_.handle);
35 | if (ASENSOR_TYPE_ADDITIONAL_INFO == event.type) {
36 | LOGI("Additional info type: %d", event.additional_info.type);
37 | if (ASENSOR_ADDITIONAL_INFO_SENSOR_PLACEMENT ==
38 | event.additional_info.type) {
39 | LOGI("Got position!");
40 | // TODO store this position somewhere
41 | }
42 | } else {
43 | OnEvent(event);
44 | }
45 | }
46 | } else if (ALOOPER_POLL_WAKE == ident) {
47 | LOGI("Looper was manually woken up");
48 | } else if (ALOOPER_POLL_CALLBACK == ident) {
49 | LOGI("Callbacks were called? I don't care");
50 | } else if (ALOOPER_POLL_TIMEOUT == ident) {
51 | LOGW("Loop with infinite timeout timed out");
52 | } else if (ALOOPER_POLL_ERROR == ident) {
53 | LOGW("Some error occured, shutting down");
54 | shutdown_.store(true);
55 | } else {
56 | LOGI("Uknown ident %d", ident);
57 | }
58 | }
59 | ASensorEventQueue_disableSensor(queue, descriptor_.sensor_ref);
60 | ASensorManager_destroyEventQueue(manager_, queue);
61 | ALooper_release(looper_);
62 | }
63 |
64 | void Sensor::Initialize() {
65 | shutdown_.store(false);
66 | queue_thread_ = std::thread(&Sensor::EventLoop, this);
67 | }
68 |
69 | void Sensor::Shutdown() {
70 | shutdown_.store(true);
71 | if (queue_thread_.joinable()) {
72 | // TODO(sloretz) Check looper_ isn't null
73 | ALooper_wake(looper_);
74 | queue_thread_.join();
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/sensors.cc:
--------------------------------------------------------------------------------
1 | #include "sensors.h"
2 |
3 | #include "jvm.h"
4 | #include "log.h"
5 | #include "sensors/accelerometer_sensor.h"
6 | #include "sensors/barometer_sensor.h"
7 | #include "sensors/gyroscope_sensor.h"
8 | #include "sensors/illuminance_sensor.h"
9 | #include "sensors/magnetometer_sensor.h"
10 |
11 | using sensors_for_ros::AccelerometerSensor;
12 | using sensors_for_ros::BarometerSensor;
13 | using sensors_for_ros::GyroscopeSensor;
14 | using sensors_for_ros::IlluminanceSensor;
15 | using sensors_for_ros::MagnetometerSensor;
16 | using sensors_for_ros::SensorDescriptor;
17 | using sensors_for_ros::Sensors;
18 |
19 | Sensors::Sensors(ANativeActivity* activity) {
20 | // TODO(sloretz) Query sensors
21 | std::string package_name = GetPackageName(activity);
22 | sensor_manager_ = ASensorManager_getInstanceForPackage(package_name.c_str());
23 |
24 | auto descriptors = QuerySensors();
25 | // Create wrapping classes
26 | for (const SensorDescriptor& desc : descriptors) {
27 | if (ASENSOR_TYPE_LIGHT == desc.type) {
28 | sensors_.push_back(std::move(
29 | std::make_unique(sensor_manager_, desc)));
30 | } else if (ASENSOR_TYPE_GYROSCOPE == desc.type) {
31 | sensors_.push_back(
32 | std::move(std::make_unique(sensor_manager_, desc)));
33 | } else if (ASENSOR_TYPE_ACCELEROMETER == desc.type) {
34 | sensors_.push_back(std::move(
35 | std::make_unique(sensor_manager_, desc)));
36 | } else if (ASENSOR_TYPE_PRESSURE == desc.type) {
37 | sensors_.push_back(
38 | std::move(std::make_unique(sensor_manager_, desc)));
39 | } else if (ASENSOR_TYPE_MAGNETIC_FIELD == desc.type) {
40 | sensors_.push_back(std::move(
41 | std::make_unique(sensor_manager_, desc)));
42 | }
43 | }
44 | }
45 |
46 | std::vector Sensors::QuerySensors() {
47 | std::vector descriptors;
48 | ASensorList sensor_list;
49 | int num_sensors = ASensorManager_getSensorList(sensor_manager_, &sensor_list);
50 | if (num_sensors < 0) {
51 | LOGW("Could not get sensor list: %d", num_sensors);
52 | return {};
53 | }
54 | LOGI("Got %d sensors", num_sensors);
55 |
56 | while (num_sensors > 0) {
57 | --num_sensors;
58 | ASensorRef sensor_ref = sensor_list[num_sensors];
59 | if (nullptr == sensor_ref) {
60 | LOGW("Got null sensor at position :%d", num_sensors);
61 | continue;
62 | }
63 | descriptors.emplace_back(sensor_ref);
64 | LOGI("Sensor %s name %s", descriptors.back().type_str,
65 | descriptors.back().name);
66 | }
67 | return descriptors;
68 | }
69 |
70 | void Sensors::Initialize() {
71 | for (auto& sensor : sensors_) {
72 | sensor->Initialize();
73 | }
74 | }
75 |
76 | void Sensors::Shutdown() {
77 | for (auto& sensor : sensors_) {
78 | sensor->Shutdown();
79 | }
80 | sensors_.clear();
81 | }
82 |
--------------------------------------------------------------------------------
/src/controllers/camera_controller.cc:
--------------------------------------------------------------------------------
1 | #include "camera_controller.h"
2 |
3 | #include
4 |
5 | #include "display_topic.h"
6 | #include "imgui.h"
7 |
8 | using sensors_for_ros::CameraController;
9 | using sensors_for_ros::CameraDevice;
10 |
11 | CameraController::CameraController(CameraManager* camera_manager,
12 | const CameraDescriptor& camera_descriptor,
13 | RosInterface& ros)
14 | : camera_manager_(camera_manager),
15 | camera_descriptor_(camera_descriptor),
16 | Controller(camera_descriptor.GetName()),
17 | info_pub_(ros),
18 | image_pub_(ros) {
19 | std::stringstream base_topic;
20 | // TODO(sloretz) what if id has invalid characters?
21 | base_topic << "camera/id_" << camera_descriptor_.id << "/";
22 |
23 | std::string info_topic = base_topic.str() + "camera_info";
24 | std::string image_topic = base_topic.str() + "image_color";
25 |
26 | // TODO allow publisher topic to be set from GUI
27 | info_pub_.SetTopic(info_topic.c_str());
28 |
29 | // TODO allow publisher topic to be set from GUI
30 | image_pub_.SetTopic(image_topic.c_str());
31 | image_pub_.SetQos(rclcpp::QoS(1).best_effort());
32 | }
33 |
34 | CameraController::~CameraController() {}
35 |
36 | void CameraController::EnableCamera() {
37 | image_pub_.Enable();
38 | info_pub_.Enable();
39 | device_ = camera_manager_->OpenCamera(camera_descriptor_);
40 | device_->SetListener(
41 | std::bind(&CameraController::OnImage, this, std::placeholders::_1));
42 | }
43 |
44 | void CameraController::DisableCamera() {
45 | image_pub_.Disable();
46 | info_pub_.Disable();
47 | device_.reset();
48 | }
49 |
50 | // Called by the GUI to draw a frame
51 | void CameraController::DrawFrame() {
52 | bool show_dialog = true;
53 | ImGui::Begin("Camera", &show_dialog,
54 | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize |
55 | ImGuiWindowFlags_NoTitleBar);
56 | if (ImGui::Button("< Back")) {
57 | Emit(event::GuiNavigateBack{});
58 | }
59 | ImGui::Text("%s", camera_descriptor_.GetName().c_str());
60 |
61 | ImGui::Separator();
62 |
63 | if (image_pub_.Enabled() && ImGui::Button("Disable")) {
64 | DisableCamera();
65 | } else if (!image_pub_.Enabled() && ImGui::Button("Enable")) {
66 | EnableCamera();
67 | }
68 |
69 | ImGui::Separator();
70 |
71 | DisplayTopic("Image", image_pub_);
72 |
73 | ImGui::Spacing();
74 |
75 | DisplayTopic("Camera Info", info_pub_);
76 |
77 | if (device_) {
78 | ImGui::Separator();
79 | auto [width, height] = device_->Resolution();
80 | ImGui::Text("Resolution: %dx%d", width, height);
81 | }
82 |
83 | ImGui::End();
84 | }
85 |
86 | std::string CameraController::PrettyName() const {
87 | std::string name{camera_descriptor_.GetName()};
88 | if (!device_) {
89 | name += " [disabled]";
90 | }
91 | return name;
92 | }
93 |
94 | void CameraController::OnImage(
95 | const std::pair& info_image) {
96 | LOGI("Controller has image?");
97 | // TODO move images if I ever need intraprocesses stuff - requires changes to
98 | // Emitter class
99 | info_pub_.Publish(*info_image.first.get());
100 | image_pub_.Publish(*info_image.second.get());
101 | }
102 |
--------------------------------------------------------------------------------
/dep_build.cmake:
--------------------------------------------------------------------------------
1 | # Inputs:
2 | # Name to give to the rule
3 | # path to the source code
4 | # packages it depends on
5 | # Extra CMake arguments
6 | # Append
7 |
8 | function(dep_build name)
9 | cmake_parse_arguments(ARG "CMAKE;PIP;NATIVE" "SOURCE_DIR" "CMAKE_ARGS;DEPENDENCIES" ${ARGN})
10 | if(ARG_UNPARSED_ARGUMENTS)
11 | message(FATAL_ERROR "dep_build() given unknown arguments: "
12 | "${ARG_UNPARSED_ARGUMENTS}")
13 | endif()
14 | if(NOT ARG_CMAKE AND NOT ARG_PIP)
15 | message(FATAL_ERROR "dep_build() must be given PIP or CMAKE")
16 | endif()
17 | if(ARG_CMAKE AND ARG_PIP)
18 | message(FATAL_ERROR "dep_build() must be given only one of PIP or CMAKE")
19 | endif()
20 |
21 | if (NOT ARG_SOURCE_DIR)
22 | message(FATAL_ERROR "SOURCE_DIR for dependency must be given")
23 | endif()
24 |
25 | # Assume path is relative to current source dir
26 | set(ARG_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/${ARG_SOURCE_DIR}")
27 |
28 | if (NOT EXISTS "${ARG_SOURCE_DIR}")
29 | message(FATAL_ERROR "SOURCE_DIR must exist: ${ARG_SOURCE_DIR}")
30 | endif()
31 |
32 | if(ARG_NATIVE)
33 | # Prefix with `native-` so find_package() can't normally find CycloneDDS here
34 | set(name "native-${name}")
35 | endif()
36 |
37 | set(dep_name "deps-${name}")
38 | # Place where CMake packges get installed
39 | set(cmake_install_dir "${CMAKE_CURRENT_BINARY_DIR}/deps")
40 | # Place where Python packages get installed
41 | set(pip_install_dir "${CMAKE_CURRENT_BINARY_DIR}/deps/_python_")
42 | if(ARG_NATIVE)
43 | set(cmake_install_dir "${CMAKE_CURRENT_BINARY_DIR}/native-deps")
44 | set(pip_install_dir "${CMAKE_CURRENT_BINARY_DIR}/native-deps/_python_")
45 | endif()
46 |
47 | set(ament_prefix_path)
48 | list(APPEND dependency_targets)
49 | foreach(_dependency ${ARG_DEPENDENCIES})
50 | # Assume each dependency was also called with `dep_build`
51 | set(_dep_target "deps-${_dependency}")
52 | if(NOT TARGET ${_dep_target})
53 | message(WARNING "Dependency target ${_dep_target} does not exist")
54 | else()
55 | list(APPEND dependency_targets ${_dep_target})
56 | endif()
57 | endforeach()
58 |
59 | if(ARG_CMAKE)
60 | set(cmake_with_env "${CMAKE_COMMAND}" -E
61 | env
62 | "PYTHONPATH=${pip_install_dir}"
63 | "AMENT_PREFIX_PATH=${cmake_install_dir}"
64 | "${CMAKE_COMMAND}")
65 |
66 | ExternalProject_Add(${dep_name}
67 | # BUILD_ALWAYS ON
68 | DOWNLOAD_COMMAND ""
69 | # Assume every CMake package might need access to installed python packages when building
70 | CMAKE_COMMAND ${cmake_with_env}
71 | BUILD_COMMAND ${cmake_with_env} --build .
72 | DEPENDS
73 | ${dependency_targets}
74 | SOURCE_DIR "${ARG_SOURCE_DIR}"
75 | CMAKE_ARGS
76 | "-DCMAKE_FIND_ROOT_PATH=${cmake_install_dir}"
77 | "-DCMAKE_INSTALL_PREFIX=${cmake_install_dir}"
78 | -DBUILD_TESTING=OFF
79 | "-DPYTHON_INSTALL_DIR=_python_"
80 | ${ARG_CMAKE_ARGS})
81 | elseif(ARG_PIP)
82 | ExternalProject_Add(${dep_name}
83 | # BUILD_ALWAYS ON
84 | DOWNLOAD_COMMAND ""
85 | CONFIGURE_COMMAND ""
86 | BUILD_COMMAND ""
87 | TEST_COMMAND ""
88 | DEPENDS
89 | ${dependency_targets}
90 | SOURCE_DIR "${ARG_SOURCE_DIR}"
91 | INSTALL_COMMAND
92 | pip install
93 | -t "${pip_install_dir}"
94 | --no-deps
95 | "${ARG_SOURCE_DIR}")
96 | endif()
97 | endfunction()
98 |
--------------------------------------------------------------------------------
/src/camera_manager.cc:
--------------------------------------------------------------------------------
1 | #include "camera_manager.h"
2 |
3 | #include
4 |
5 | #include
6 |
7 | #include "log.h"
8 |
9 | using sensors_for_ros::CameraDescriptor;
10 | using sensors_for_ros::CameraDevice;
11 | using sensors_for_ros::CameraManager;
12 |
13 | /// ***************** Camera manager stuff ********************
14 | CameraManager::CameraManager() {
15 | native_manager_ = ACameraManager_create();
16 |
17 | DiscoverCameras();
18 | }
19 |
20 | void CameraManager::DiscoverCameras() {
21 | cameras_.clear();
22 | // Get camera IDs and put them in RAII container
23 | std::unique_ptr
24 | camera_ids{nullptr, nullptr};
25 | {
26 | ACameraIdList* camera_ids_temp = nullptr;
27 | ACameraManager_getCameraIdList(native_manager_, &camera_ids_temp);
28 | camera_ids = {camera_ids_temp, &ACameraManager_deleteCameraIdList};
29 | }
30 |
31 | for (int i = 0; i < camera_ids->numCameras; ++i) {
32 | // Get camera's metadata in more convenient type
33 | CameraDescriptor cam_desc;
34 |
35 | cam_desc.id = camera_ids->cameraIds[i];
36 | std::unique_ptr metadata{
37 | nullptr, nullptr};
38 | {
39 | ACameraMetadata* metadata_temp;
40 | ACameraManager_getCameraCharacteristics(
41 | native_manager_, cam_desc.id.c_str(), &metadata_temp);
42 | metadata = {metadata_temp, &ACameraMetadata_free};
43 | }
44 |
45 | int32_t num_tags = 0;
46 | const uint32_t* tags = nullptr;
47 | ACameraMetadata_getAllTags(metadata.get(), &num_tags, &tags);
48 |
49 | ACameraMetadata_const_entry entry = {0};
50 |
51 | auto status = ACameraMetadata_getConstEntry(metadata.get(),
52 | ACAMERA_LENS_FACING, &entry);
53 | if (ACAMERA_OK != status) {
54 | LOGW("Unable to get ACAMERA_LENS_FACING from camera %d", i);
55 | continue;
56 | }
57 | cam_desc.lens_facing =
58 | static_cast(
59 | entry.data.u8[0]);
60 |
61 | LOGI("Looking up available stream configurations");
62 | // Figure out what kinds of data we can get from the camera
63 | ACameraMetadata_getConstEntry(
64 | metadata.get(), ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS, &entry);
65 | for (int i = 0; i < entry.count; i += 4) {
66 | // We are only interested in output streams, so skip input stream
67 | int32_t input = entry.data.i32[i + 3];
68 | if (input) {
69 | continue;
70 | }
71 |
72 | int32_t format = entry.data.i32[i + 0];
73 | if (format == AIMAGE_FORMAT_YUV_420_888) {
74 | // This one is always supported.
75 | // https://developer.android.com/ndk/reference/group/media
76 | // #group___media_1gga9c3dace30485a0f28163a882a5d65a19aea9797f9b5db5d26a2055a43d8491890
77 | int32_t width = entry.data.i32[i + 1];
78 | int32_t height = entry.data.i32[i + 2];
79 | LOGI("YUV_420_888 supported w: %d h: %d", width, height);
80 | }
81 | }
82 |
83 | cameras_.push_back(cam_desc);
84 | }
85 | }
86 |
87 | CameraManager::~CameraManager() {
88 | if (native_manager_) {
89 | ACameraManager_delete(native_manager_);
90 | }
91 | }
92 |
93 | std::unique_ptr CameraManager::OpenCamera(
94 | const CameraDescriptor& desc) const {
95 | return std::move(CameraDevice::OpenCamera(native_manager_, desc));
96 | }
97 |
--------------------------------------------------------------------------------
/scripts/generate_ros_superbuild.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | """ Generate a CMake superbuild from a directory of ROS packages. """
4 |
5 | CMAKE_TEMPLATE = """
6 | dep_build({package_name} CMAKE
7 | SOURCE_DIR "{path_to_source}"
8 | DEPENDENCIES {dependencies}
9 | CMAKE_ARGS ${{android_cmake_args}})
10 | """
11 |
12 | PIP_TEMPLATE = """
13 | dep_build({package_name} PIP
14 | SOURCE_DIR "{path_to_source}"
15 | DEPENDENCIES {dependencies})
16 | """
17 |
18 | import pathlib
19 | import sys
20 |
21 | import argparse
22 |
23 | from catkin_pkg.topological_order import topological_order
24 |
25 |
26 | def _export_depends(pkg):
27 | deps = []
28 | deps.extend(pkg.buildtool_export_depends)
29 | deps.extend(pkg.build_export_depends)
30 |
31 | return deps
32 |
33 |
34 | def _build_depends(pkg):
35 | deps = []
36 | deps.extend(pkg.buildtool_depends)
37 | deps.extend(pkg.build_depends)
38 | return deps
39 |
40 |
41 | def _dependencies_to_names(packages):
42 | just_names = set([pkg.name for pkg in packages])
43 | rendered = ' '.join(just_names)
44 | return rendered
45 |
46 |
47 | def _source_dir(pkg):
48 | package_xml_path = pathlib.Path(pkg.filename)
49 | return str(package_xml_path.parent)
50 |
51 |
52 | def _generate_cmake(pkg, dependencies, output_to):
53 | rendered = CMAKE_TEMPLATE.format(
54 | package_name=pkg.name,
55 | path_to_source=_source_dir(pkg),
56 | dependencies=_dependencies_to_names(dependencies))
57 | output_to.write(rendered)
58 |
59 |
60 | def _generate_pip(pkg, dependencies, output_to):
61 | rendered = PIP_TEMPLATE.format(
62 | package_name=pkg.name,
63 | path_to_source=_source_dir(pkg),
64 | dependencies=_dependencies_to_names(dependencies))
65 | output_to.write(rendered)
66 |
67 |
68 | def generate_ros_superbuild(source_dir, output_to=sys.stdout):
69 |
70 | export_lookup = {}
71 | packages = dict(topological_order(source_dir))
72 |
73 | for pkg in packages.values():
74 | export_lookup[pkg.name] = _export_depends(pkg)
75 |
76 | for pkg_path, pkg in packages.items():
77 | # Add exported dependencies transitively
78 | dependencies = set()
79 | to_crawl_deps = _build_depends(pkg)
80 | while to_crawl_deps:
81 | dep = to_crawl_deps.pop()
82 | dependencies.add(dep)
83 | if dep.name in export_lookup:
84 | for transitive_dep in export_lookup[dep.name]:
85 | if transitive_dep not in dependencies:
86 | to_crawl_deps.append(transitive_dep)
87 | # else:
88 | # output_to.write(f'# system dep? {dep.name}')
89 |
90 | build_type = pkg.get_build_type().lower()
91 |
92 | if build_type in ('cmake', 'ament_cmake'):
93 | _generate_cmake(pkg, dependencies, output_to)
94 | elif build_type in ('python', 'ament_python'):
95 | _generate_pip(pkg, dependencies, output_to)
96 | else:
97 | raise ValueError(f'Unknown build type: {build_type}')
98 |
99 |
100 | def main():
101 | parser = argparse.ArgumentParser(
102 | description='Generate a superbuild from a directory of ROS packages.')
103 | parser.add_argument(
104 | 'path', metavar='PATH', type=str,
105 | help='path to directory containing ROS packages')
106 |
107 | args = parser.parse_args()
108 |
109 | generate_ros_superbuild(args.path)
110 |
111 |
112 | if __name__ == '__main__':
113 | main()
114 |
--------------------------------------------------------------------------------
/src/controllers/ros_domain_id_controller.cc:
--------------------------------------------------------------------------------
1 | #include "controllers/ros_domain_id_controller.h"
2 |
3 | #include "imgui.h"
4 | #include "jvm.h"
5 |
6 | namespace sensors_for_ros {
7 |
8 | RosDomainIdController::RosDomainIdController(ANativeActivity* activity) : Controller(kRosDomainIdControllerId)
9 | {
10 | network_interfaces_ = sensors_for_ros::GetNetworkInterfaces(activity);
11 |
12 | if (network_interfaces_.size()) {
13 | preferred_interface_ = *network_interfaces_.begin();
14 | for (const auto& interface: network_interfaces_) {
15 | if (interface.rfind("wlan", 0) == 0) {
16 | preferred_interface_ = interface;
17 | break;
18 | }
19 | }
20 | }
21 | }
22 |
23 | void RosDomainIdController::DrawFrame() {
24 | static int32_t picked_ros_domain_id = -1;
25 |
26 | auto increase_id = [](int num) {
27 | if (picked_ros_domain_id < 0) {
28 | picked_ros_domain_id = num;
29 | } else {
30 | picked_ros_domain_id = picked_ros_domain_id * 10 + num;
31 | }
32 | };
33 |
34 | bool show_ros_domain_id_picker = true;
35 | ImGui::Begin("ROS_DOMAIN_ID", &show_ros_domain_id_picker,
36 | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize |
37 | ImGuiWindowFlags_NoTitleBar);
38 |
39 | ImGui::Spacing();
40 | ImGui::Text("ROS_DOMAIN_ID");
41 |
42 | // Disable buttons so that only domain ids between 0 and 232 are pickable
43 |
44 | bool disabled = false;
45 | #define DISABLE_IF(expr) \
46 | { \
47 | if (!disabled && (expr)) { \
48 | ImGui::BeginDisabled(); \
49 | disabled = true; \
50 | } \
51 | } \
52 | while (false)
53 |
54 | DISABLE_IF(picked_ros_domain_id >= 30);
55 | if (ImGui::Button("0")) {
56 | increase_id(0);
57 | }
58 | ImGui::SameLine();
59 | if (ImGui::Button("1")) {
60 | increase_id(1);
61 | }
62 | ImGui::SameLine();
63 | if (ImGui::Button("2")) {
64 | increase_id(2);
65 | }
66 | DISABLE_IF(picked_ros_domain_id == 23);
67 | if (ImGui::Button("3")) {
68 | increase_id(3);
69 | }
70 | ImGui::SameLine();
71 | if (ImGui::Button("4")) {
72 | increase_id(4);
73 | }
74 | ImGui::SameLine();
75 | if (ImGui::Button("5")) {
76 | increase_id(5);
77 | }
78 | if (ImGui::Button("6")) {
79 | increase_id(6);
80 | }
81 | ImGui::SameLine();
82 | if (ImGui::Button("7")) {
83 | increase_id(7);
84 | }
85 | ImGui::SameLine();
86 | if (ImGui::Button("8")) {
87 | increase_id(8);
88 | }
89 | if (ImGui::Button("9")) {
90 | increase_id(9);
91 | }
92 |
93 | if (disabled) {
94 | ImGui::EndDisabled();
95 | }
96 |
97 | ImGui::SameLine();
98 | if (ImGui::Button("Clear")) {
99 | picked_ros_domain_id = -1;
100 | }
101 |
102 | if (picked_ros_domain_id < 0) {
103 | ImGui::Text("---");
104 | } else {
105 | ImGui::Text("%d", picked_ros_domain_id);
106 | }
107 |
108 | ImGui::Separator();
109 |
110 | static std::string selected_interface = preferred_interface_;
111 | if (ImGui::BeginCombo("Network Interface", selected_interface.c_str(), 0))
112 | {
113 | for (const std::string& interface: network_interfaces_)
114 | {
115 | bool is_selected = (selected_interface == interface);
116 | if (ImGui::Selectable(interface.c_str(), is_selected)) {
117 | selected_interface = interface;
118 | }
119 | if (is_selected) {
120 | ImGui::SetItemDefaultFocus();
121 | }
122 | }
123 | ImGui::EndCombo();
124 | }
125 |
126 | ImGui::Separator();
127 |
128 | if (ImGui::Button("Start ROS")) {
129 | if (picked_ros_domain_id < 0) {
130 | // Default to 0
131 | picked_ros_domain_id = 0;
132 | }
133 | Emit(event::RosDomainIdChanged{picked_ros_domain_id, selected_interface});
134 | }
135 |
136 | ImGui::End();
137 | }
138 | } // namespace sensors_for_ros
139 |
--------------------------------------------------------------------------------
/src/ros_interface.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 |
7 | #include "events.h"
8 | #include "log.h"
9 |
10 | namespace sensors_for_ros {
11 | class RosInterface {
12 | public:
13 | RosInterface();
14 | ~RosInterface() = default;
15 |
16 | void Initialize(size_t ros_domain_id);
17 | void Shutdown();
18 |
19 | bool Initialized() const;
20 |
21 | rclcpp::Context::SharedPtr get_context() const;
22 | rclcpp::Node::SharedPtr get_node() const;
23 |
24 | void AddObserver(std::function init_or_shutdown);
25 |
26 | private:
27 | rclcpp::Context::SharedPtr context_;
28 | rclcpp::Node::SharedPtr node_;
29 | rclcpp::Executor::SharedPtr executor_;
30 |
31 | std::vector> observers_;
32 |
33 | std::thread executor_thread_;
34 |
35 | void NotifyInitChanged();
36 | };
37 |
38 | /// Interface to A ROS publisher
39 | // I want this class to insulate the Sensor interface from ROS going up or down
40 | // or publishers being created etc when settings change
41 | // This is to be used by the sensor interface to publish data
42 | template
43 | class Publisher {
44 | public:
45 | Publisher(RosInterface& ros) : ros_(ros) {}
46 |
47 | virtual ~Publisher() {}
48 |
49 | // No moves - Observer code has `this` pointer
50 | Publisher(Publisher&& other) = delete;
51 | Publisher& operator=(Publisher&& other) = delete;
52 | // No copies please
53 | Publisher(const Publisher& other) = delete;
54 | Publisher& operator=(const Publisher& other) = delete;
55 |
56 | void SetTopic(const char* topic) {
57 | LOGI("Setting topic for publisher");
58 | topic_ = topic;
59 | if (publisher_) {
60 | DestroyPublisher();
61 | CreatePublisher();
62 | }
63 | }
64 |
65 | void SetQos(const rclcpp::QoS& qos) {
66 | LOGI("Setting qos for publisher");
67 | qos_ = qos;
68 | if (publisher_) {
69 | DestroyPublisher();
70 | CreatePublisher();
71 | }
72 | }
73 |
74 | void CreatePublisher() {
75 | LOGI("Created publisher!");
76 | // Check just in case publisher became disabled
77 | if (enable_) {
78 | auto node = ros_.get_node();
79 | publisher_ = node->template create_publisher(topic_, qos_);
80 | // Tell ROS interface to destroy publisher when it's shutdown
81 | ros_.AddObserver(std::bind(&Publisher::DestroyPublisher, this));
82 | }
83 | }
84 |
85 | void DestroyPublisher() {
86 | LOGI("Destroyed publisher!");
87 | publisher_.reset();
88 | }
89 |
90 | void Enable() {
91 | LOGI("Asked to enable publisher");
92 | enable_ = true;
93 | if (!publisher_) {
94 | if (ros_.Initialized()) {
95 | CreatePublisher();
96 | } else {
97 | // Tell ROS interface to create publisher when it's initialized
98 | ros_.AddObserver(std::bind(&Publisher::CreatePublisher, this));
99 | }
100 | }
101 | }
102 |
103 | void Disable() {
104 | LOGI("Asked to disable publisher");
105 | enable_ = false;
106 | DestroyPublisher();
107 | }
108 |
109 | bool Enabled() const { return nullptr != publisher_.get(); }
110 |
111 | const char* Topic() const {
112 | if (publisher_) {
113 | return publisher_->get_topic_name();
114 | }
115 | return topic_.c_str();
116 | }
117 |
118 | const char* Type() const { return rosidl_generator_traits::name(); }
119 |
120 | // Big messages to avoid copies
121 | void Publish(std::unique_ptr msg) const {
122 | if (publisher_) {
123 | publisher_->template publish(std::move(msg));
124 | }
125 | }
126 |
127 | // Little messages to avoid heap allocation
128 | void Publish(const MsgT& msg) const {
129 | if (publisher_) {
130 | publisher_->publish(msg);
131 | }
132 | }
133 |
134 | private:
135 | bool enable_ = false;
136 | RosInterface& ros_;
137 | std::string topic_ = "default_topic";
138 | rclcpp::QoS qos_ = rclcpp::QoS(1);
139 | typename rclcpp::Publisher::SharedPtr publisher_;
140 | };
141 | } // namespace sensors_for_ros
142 |
--------------------------------------------------------------------------------
/ros.repos:
--------------------------------------------------------------------------------
1 | # First part is generated with
2 | # rosinstall_generator --format repos --upstream-devel --rosdistro humble --deps rclcpp sensor_msgs geometry_msgs --exclude rmw_fastrtps_cpp spdlog_vendor uncrustify_vendor rpyutils rosidl_generator_py fastrtps fastcdr rosidl_typesupport_fastrtps_c rosidl_typesupport_fastrtps_cpp rmw_fastrtps_dynamic_cpp performance_test_fixture iceoryx_binding_c google_benchmark_vendor rmw_connextdds rmw_connextdds_common rti_connext_dds_cmake_module rcl_logging_spdlog launch_testing_ament_cmake launch_testing launch iceoryx_posh ament_cmake_gmock ament_cmake_google_benchmark ament_cmake_gtest ament_cmake_pytest domain_coordinator ament_cmake_cpplint ament_cmake_flake8 ament_cmake_pep257 ament_cmake_uncrustify ament_cmake_xmllint ament_copyright ament_cppcheck ament_cpplint ament_flake8 ament_lint ament_lint_auto ament_lint_cmake ament_lint_common ament_pep257 gmock_vendor gtest_vendor iceoryx_hoofs
3 | repositories:
4 | ament_cmake:
5 | type: git
6 | url: https://github.com/ament/ament_cmake.git
7 | version: humble
8 | ament_cmake_ros:
9 | type: git
10 | url: https://github.com/ros2/ament_cmake_ros.git
11 | version: humble
12 | ament_index:
13 | type: git
14 | url: https://github.com/ament/ament_index.git
15 | version: humble
16 | ament_lint:
17 | type: git
18 | url: https://github.com/ament/ament_lint.git
19 | version: humble
20 | ament_package:
21 | type: git
22 | url: https://github.com/ament/ament_package.git
23 | version: humble
24 | common_interfaces:
25 | type: git
26 | url: https://github.com/ros2/common_interfaces.git
27 | version: humble
28 | cyclonedds:
29 | type: git
30 | url: https://github.com/eclipse-cyclonedds/cyclonedds.git
31 | version: releases/0.9.x
32 | libstatistics_collector:
33 | type: git
34 | url: https://github.com/ros-tooling/libstatistics_collector.git
35 | version: humble
36 | libyaml_vendor:
37 | type: git
38 | url: https://github.com/ros2/libyaml_vendor.git
39 | version: humble
40 | mimick_vendor:
41 | type: git
42 | url: https://github.com/ros2/mimick_vendor.git
43 | version: humble
44 | osrf_testing_tools_cpp:
45 | type: git
46 | url: https://github.com/osrf/osrf_testing_tools_cpp.git
47 | version: master
48 | rcl:
49 | type: git
50 | url: https://github.com/ros2/rcl.git
51 | version: humble
52 | rcl_interfaces:
53 | type: git
54 | url: https://github.com/ros2/rcl_interfaces.git
55 | version: humble
56 | rcl_logging:
57 | type: git
58 | url: https://github.com/ros2/rcl_logging.git
59 | version: humble
60 | rclcpp:
61 | type: git
62 | url: https://github.com/ros2/rclcpp.git
63 | version: humble
64 | rcpputils:
65 | type: git
66 | url: https://github.com/ros2/rcpputils.git
67 | version: humble
68 | rcutils:
69 | type: git
70 | url: https://github.com/ros2/rcutils.git
71 | version: humble
72 | rmw:
73 | type: git
74 | url: https://github.com/ros2/rmw.git
75 | version: humble
76 | rmw_cyclonedds:
77 | type: git
78 | url: https://github.com/ros2/rmw_cyclonedds.git
79 | version: humble
80 | rmw_dds_common:
81 | type: git
82 | url: https://github.com/ros2/rmw_dds_common.git
83 | version: humble
84 | rmw_implementation:
85 | type: git
86 | url: https://github.com/ros2/rmw_implementation.git
87 | version: humble
88 | ros2_tracing:
89 | type: git
90 | url: https://github.com/ros2/ros2_tracing.git
91 | version: humble
92 | rosidl:
93 | type: git
94 | url: https://github.com/ros2/rosidl.git
95 | version: humble
96 | rosidl_defaults:
97 | type: git
98 | url: https://github.com/ros2/rosidl_defaults.git
99 | version: humble
100 | rosidl_typesupport:
101 | type: git
102 | url: https://github.com/ros2/rosidl_typesupport.git
103 | version: humble
104 | test_interface_files:
105 | type: git
106 | url: https://github.com/ros2/test_interface_files.git
107 | version: humble
108 | unique_identifier_msgs:
109 | type: git
110 | url: https://github.com/ros2/unique_identifier_msgs.git
111 | version: humble
112 | # Hand made
113 | rcl_logging_android:
114 | type: git
115 | url: https://github.com/sloretz/rcl_logging_android.git
116 | version: main
117 |
--------------------------------------------------------------------------------
/res/drawable/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Sensors for ROS
2 |
3 | Sensors for ROS is an app that publishes sensor data from an Android device onto ROS 2 topics.
4 | Currently it supports ROS Humble.
5 |
6 | **Supported sensors**
7 | * Accelerometer
8 | * Barometer
9 | * Camera(s)
10 | * Gyroscope
11 | * Illuminance
12 | * Magnetometer
13 |
14 | This app is built using only CMake and C++.
15 | It does not use Java or Kotlin.
16 | ROS 2 packages up to `rclcpp` are cross-compiled.
17 | A successful build produces an `.apk` file called `sensors_for_ros.apk` in the build directory.
18 |
19 | ## Inspiration
20 |
21 | These projects were extremely helpful, and used as a reference for this one:
22 |
23 | * https://github.com/cnlohr/rawdrawandroid
24 | * https://github.com/ocornut/imgui/tree/master/examples/example_android_opengl3
25 | * https://www.sisik.eu/blog/android/ndk/camera
26 |
27 | ## How to install it
28 |
29 | Currently the only way to get **Sensors for ROS** is to build it from source.
30 | It is not yet available on Google's app store.
31 |
32 | ## How to build it from source
33 |
34 | You do not need ROS installed on your machine to build the **Sensors for ROS** app.
35 | However, it's needed to use the sensor data being published by your Android device.
36 | Follow [these instructions to install ROS Humble](https://docs.ros.org/en/humble/Installation.html).
37 |
38 | ### Computer setup
39 |
40 | Download the [Android SDK "Command-line tools only" version](https://developer.android.com/studio#command-tools).
41 | Other versions may work, but this is the minimum needed.
42 |
43 | Make a folder for the SDK and extract the archive.
44 |
45 | ```bash
46 | mkdir ~/android-sdk
47 | cd ~/android-sdk
48 | unzip ~/Downloads/commandlinetools-linux-8512546_latest.zip
49 | ```
50 |
51 | Install some Android SDK components
52 | (If it gives linkage error try installing `sudo apt install openjdk-17-jre-headless`)
53 | ```
54 | ./cmdline-tools/bin/sdkmanager --sdk_root=$HOME/android-sdk "build-tools;33.0.0" "platforms;android-30" "ndk;25.1.8937393"
55 | ```
56 |
57 | Install `adb`
58 |
59 | ```bash
60 | # If you're using Ubuntu
61 | sudo apt install adb android-sdk-platform-tools-common
62 | # If you're using Fedora
63 | sudo dnf install android-tools
64 | ```
65 |
66 | Install catkin-pkg, empy, and lark
67 |
68 | ```bash
69 | # If you're using ubuntu
70 | sudo apt install python3-catkin-pkg-modules python3-empy python3-lark-parser
71 | # If you're using Fedora
72 | sudo dnf install python3-catkin_pkg python3-empy python3-lark-parser
73 | ```
74 |
75 | You may need to do additional setup to use adb.
76 | Follow the [Set up a device for development](https://developer.android.com/studio/run/device#setting-up) instructions if you're using Ubuntu, or follow [the instructions in this thread](https://forums.fedoraforum.org/showthread.php?298965-HowTo-set-up-adb-(Android-Debug-Bridge)-on-Fedora-20) if you're using Fedora.
77 |
78 |
79 | ### Create debug keys
80 |
81 | You'll need to install openjdk to get access to `keytool`.
82 |
83 | ```bash
84 | sudo apt install openjdk-11-jre-headless
85 | ```
86 |
87 | Create a debug keystore
88 |
89 | ```bash
90 | mkdir ~/.android
91 | keytool -genkey -v -keystore ~/.android/debug.keystore -alias adb_debug_key -keyalg RSA -keysize 2048 -validity 10000 -storepass android -keypass android
92 | ```
93 |
94 | ### Clone the repo
95 |
96 | The official repo is [`sloretz/sensors_for_ros`](https://github.com/sloretz/sensors_for_ros).
97 |
98 | ```
99 | git clone https://github.com/sloretz/sensors_for_ros.git
100 | ```
101 |
102 | Next initialize the git submodules.
103 |
104 | ```bash
105 | git submodule init
106 | git submodule update
107 | ```
108 |
109 | ### Download ROS dependencies
110 |
111 | Use [vcstool](https://github.com/dirk-thomas/vcstool) to download the ROS packages we need to cross compile into the `deps` folder.
112 |
113 | ```
114 | vcs import --input ros.repos deps/
115 | ```
116 |
117 | ### Building the App
118 |
119 | Build the software
120 |
121 | ```
122 | mkdir build
123 | cd build
124 | cmake ../ -DANDROID_HOME=$HOME/android-sdk/
125 | make -j`nproc`
126 | ```
127 |
128 | ### Installing the App on your Android Device
129 |
130 | Install the APK in the build directory onto a device.
131 |
132 | ```
133 | adb install -r sensors_for_ros.apk
134 | ```
135 |
136 | ## Development tips
137 |
138 | Use logcat to view the logs from the app
139 | ```
140 | adb logcat
141 | ```
142 |
143 | Sometimes you may want to try out a permission without writing the code to request it.
144 | The app must be installed, but not running already for this command to work.
145 | ```
146 | adb shell pm grant com.github.sloretz.sensors_for_ros android.permission.CAMERA
147 | ```
148 |
149 | The main activity can be started directly from the CLI
150 | ```
151 | adb shell am start -n com.github.sloretz.sensors_for_ros/android.app.NativeActivity
152 | ```
153 |
154 | Getting stack traces
155 |
156 | ```
157 | adb logcat | $HOME/android-sdk/ndk/*/ndk-stack -sym lib/arm64-v8a/
158 | ```
159 |
160 | # Random lessons
161 |
162 | During development I documented problems I encountered and fixes for them in the [Problems Encountered](docs/problems_encountered.md) document.
163 |
--------------------------------------------------------------------------------
/src/gui.cc:
--------------------------------------------------------------------------------
1 | #include "gui.h"
2 |
3 | #include
4 |
5 | #include
6 |
7 | #include "imgui.h"
8 | #include "imgui_impl_android.h"
9 | #include "imgui_impl_opengl3.h"
10 | #include "log.h"
11 |
12 | using sensors_for_ros::GUI;
13 |
14 | GUI::GUI() {}
15 |
16 | GUI::~GUI() { Stop(); }
17 |
18 | void GUI::SetController(Controller* controller) {
19 | active_controller = controller;
20 | }
21 |
22 | void GUI::DrawingLoop(ANativeWindow* window,
23 | std::promise promise_first_frame) {
24 | LOGI("Entered DrawingLoop()");
25 | // Display initialization MUST happen in drawing thread
26 | InitializeDisplay(window);
27 | InitializeDearImGui(window);
28 | DrawFrame();
29 | promise_first_frame.set_value();
30 |
31 | while (not exit_loop_.load()) {
32 | CheckInput();
33 | DrawFrame();
34 | }
35 | TerminateDearImGui();
36 | TerminateDisplay();
37 | }
38 |
39 | void GUI::CheckInput() {
40 | std::unique_lock lock(iqueue_mtx_, std::defer_lock);
41 | // Try to get input events, but don't worry about if it doesn't happen
42 | if (!lock.try_lock()) {
43 | return;
44 | }
45 | if (iqueue_ == nullptr) {
46 | return;
47 | }
48 | AInputEvent* event = nullptr;
49 | while (AInputQueue_getEvent(iqueue_, &event) >= 0) {
50 | LOGI("New input event: type=%d\n", AInputEvent_getType(event));
51 | if (AInputQueue_preDispatchEvent(iqueue_, event)) {
52 | continue;
53 | }
54 | int32_t handled = 0;
55 | handled = ImGui_ImplAndroid_HandleInputEvent(event);
56 | AInputQueue_finishEvent(iqueue_, event, handled);
57 | }
58 | }
59 |
60 | void GUI::SetInputQueue(AInputQueue* queue) {
61 | std::lock_guard guard(iqueue_mtx_);
62 | iqueue_ = queue;
63 | }
64 |
65 | void GUI::RemoveInputQueue() {
66 | std::lock_guard guard(iqueue_mtx_);
67 | iqueue_ = nullptr;
68 | }
69 |
70 | void GUI::Start(ANativeActivity* activity, ANativeWindow* window) {
71 | activity_ = activity;
72 | exit_loop_.store(false);
73 | std::promise promise_first_frame;
74 | std::future first_frame_drawn = promise_first_frame.get_future();
75 | draw_thread_ = std::thread(&GUI::DrawingLoop, this, window,
76 | std::move(promise_first_frame));
77 | first_frame_drawn.wait();
78 | }
79 |
80 | void GUI::Stop() {
81 | if (draw_thread_.joinable()) {
82 | // Join drawing thread
83 | exit_loop_.store(true);
84 | draw_thread_.join();
85 | }
86 | activity_ = nullptr;
87 | }
88 |
89 | bool GUI::InitializeDisplay(ANativeWindow* window) {
90 | // Copied from
91 | // https://developer.android.com/ndk/samples/sample_na
92 | // and
93 | // https://github.com/ocornut/imgui/blob/e346059eef140c5a8611581f3e6c8b8816d6998e/examples/example_android_opengl3/main.cpp#L51-L65
94 |
95 | const EGLint egl_attributes[] = {EGL_BLUE_SIZE, 8,
96 | EGL_GREEN_SIZE, 8,
97 | EGL_RED_SIZE, 8,
98 | EGL_DEPTH_SIZE, 24,
99 | EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
100 | EGL_NONE};
101 | EGLint w, h, format;
102 | EGLint numConfigs;
103 | EGLConfig config = nullptr;
104 | EGLSurface surface;
105 | EGLContext context;
106 |
107 | EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
108 |
109 | eglInitialize(display, nullptr, nullptr);
110 |
111 | // Get the first matching config
112 | EGLConfig egl_config;
113 | eglChooseConfig(display, egl_attributes, &egl_config, 1, &numConfigs);
114 | EGLint egl_format;
115 | eglGetConfigAttrib(display, egl_config, EGL_NATIVE_VISUAL_ID, &egl_format);
116 | ANativeWindow_setBuffersGeometry(window, 0, 0, egl_format);
117 |
118 | const EGLint egl_context_attributes[] = {EGL_CONTEXT_CLIENT_VERSION, 3,
119 | EGL_NONE};
120 | context = eglCreateContext(display, egl_config, EGL_NO_CONTEXT,
121 | egl_context_attributes);
122 |
123 | if (context == EGL_NO_CONTEXT) {
124 | LOGW("eglCreateContext() returned EGL_NO_CONTEXT");
125 | }
126 |
127 | surface = eglCreateWindowSurface(display, egl_config, window, NULL);
128 | eglMakeCurrent(display, surface, surface, context);
129 |
130 | eglQuerySurface(display, surface, EGL_WIDTH, &w);
131 | eglQuerySurface(display, surface, EGL_HEIGHT, &h);
132 |
133 | display_ = display;
134 | context_ = context;
135 | surface_ = surface;
136 | width_ = w;
137 | height_ = h;
138 |
139 | LOGI("Display width %d height %d", width_, height_);
140 |
141 | // Check openGL on the system
142 | auto opengl_info = {GL_VENDOR, GL_RENDERER, GL_VERSION, GL_EXTENSIONS};
143 | for (auto name : opengl_info) {
144 | auto info = glGetString(name);
145 | LOGI("OpenGL Info: %s", info);
146 | }
147 |
148 | return true;
149 | }
150 |
151 | void GUI::InitializeDearImGui(ANativeWindow* window) {
152 | // Setup Dear ImGui context
153 | IMGUI_CHECKVERSION();
154 | ImGui::CreateContext();
155 | ImGuiIO& io = ImGui::GetIO();
156 |
157 | // Disable loading/saving of .ini file from disk.
158 | io.IniFilename = NULL;
159 |
160 | // Setup Dear ImGui style
161 | ImGui::StyleColorsDark();
162 |
163 | // Setup Platform/Renderer backends
164 | ImGui_ImplAndroid_Init(window);
165 | ImGui_ImplOpenGL3_Init("#version 300 es");
166 |
167 | // TODO(sloretz) DPI aware scaling
168 | ImFontConfig font_cfg;
169 | font_cfg.SizePixels = 50.0f;
170 | io.Fonts->AddFontDefault(&font_cfg);
171 | ImGui::GetStyle().ScaleAllSizes(4.0f);
172 | }
173 |
174 | void GUI::TerminateDearImGui() {
175 | ImGui_ImplOpenGL3_Shutdown();
176 | ImGui_ImplAndroid_Shutdown();
177 | ImGui::DestroyContext();
178 | }
179 |
180 | void GUI::DrawFrame() {
181 | ImGuiIO& io = ImGui::GetIO();
182 | if (display_ == EGL_NO_DISPLAY) {
183 | return;
184 | }
185 |
186 | // Start the Dear ImGui frame
187 | ImGui_ImplOpenGL3_NewFrame();
188 | ImGui_ImplAndroid_NewFrame();
189 | ImGui::NewFrame();
190 |
191 | ImGui::SetNextWindowPos(ImVec2(0.0f, 0.0f));
192 | if (io.DisplaySize.x <= 0 || io.DisplaySize.y <= 0) {
193 | LOGI("huyh, don't know size yet?");
194 | } else {
195 | // Each controller runs full screen
196 | ImGui::SetNextWindowSize(io.DisplaySize);
197 | // Add some padding
198 | ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding,
199 | ImVec2(io.DisplaySize.x / 10, io.DisplaySize.x / 10));
200 |
201 | if (active_controller) {
202 | active_controller->DrawFrame();
203 | }
204 |
205 | ImGui::PopStyleVar();
206 | }
207 |
208 | // Rendering
209 | ImGui::Render();
210 | glViewport(0, 0, (int)io.DisplaySize.x, (int)io.DisplaySize.y);
211 | glClearColor(0.8f, 0.8f, 0.8f, 1.0f);
212 | glClear(GL_COLOR_BUFFER_BIT);
213 | ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
214 | eglSwapBuffers(display_, surface_);
215 | }
216 |
217 | void GUI::TerminateDisplay() {
218 | // Copied from
219 | // https://developer.android.com/ndk/samples/sample_na
220 | if (display_ != EGL_NO_DISPLAY) {
221 | eglMakeCurrent(display_, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
222 | if (context_ != EGL_NO_CONTEXT) {
223 | eglDestroyContext(display_, context_);
224 | }
225 | if (surface_ != EGL_NO_SURFACE) {
226 | eglDestroySurface(display_, surface_);
227 | }
228 | eglTerminate(display_);
229 | }
230 | display_ = EGL_NO_DISPLAY;
231 | context_ = EGL_NO_CONTEXT;
232 | surface_ = EGL_NO_SURFACE;
233 | }
234 |
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.12)
2 |
3 | project(anroid_ros-superbuild)
4 |
5 | include(ExternalProject)
6 |
7 | set(minimum_target_sdk 30)
8 |
9 | if(NOT ANDROID_HOME)
10 | message(FATAL_ERROR "ANDROID_HOME must be given")
11 | endif()
12 | if(NOT ANDROID_HOME)
13 | message(FATAL_ERROR "ANDROID_HOME must be given")
14 | endif()
15 |
16 | if(NOT PATH_TO_KEYSTORE)
17 | set(PATH_TO_KEYSTORE "$ENV{HOME}/.android/debug.keystore")
18 | endif()
19 | message(STATUS "Given keystore ${PATH_TO_KEYSTORE}")
20 |
21 | file(GLOB android_toolchain
22 | LIST_DIRECTORIES false
23 | "${ANDROID_HOME}/ndk/*/build/cmake/android.toolchain.cmake"
24 | )
25 | if(NOT android_toolchain)
26 | message(FATAL_ERROR "Failed to find android toolchain file - is ANDROID_HOME correct?")
27 | endif()
28 |
29 | file(GLOB android_aapt2
30 | LIST_DIRECTORIES false
31 | "${ANDROID_HOME}/build-tools/*/aapt2"
32 | )
33 | if(NOT android_aapt2)
34 | message(FATAL_ERROR "Failed to find aapt2 - is ANDROID_HOME correct?")
35 | endif()
36 |
37 | file(GLOB android_jar
38 | LIST_DIRECTORIES false
39 | "${ANDROID_HOME}/platforms/android-${minimum_target_sdk}/android.jar"
40 | )
41 | if(NOT android_jar)
42 | message(FATAL_ERROR "Failed to find android.jar - is ANDROID_HOME correct?")
43 | endif()
44 |
45 | file(GLOB android_zipalign
46 | LIST_DIRECTORIES false
47 | "${ANDROID_HOME}/build-tools/*/zipalign"
48 | )
49 | if(NOT android_zipalign)
50 | message(FATAL_ERROR "Failed to find zipalign - is ANDROID_HOME correct?")
51 | endif()
52 |
53 | file(GLOB android_apksigner
54 | LIST_DIRECTORIES false
55 | "${ANDROID_HOME}/build-tools/*/apksigner"
56 | )
57 | if(NOT android_apksigner)
58 | message(FATAL_ERROR "Failed to find apksigner - is ANDROID_HOME correct?")
59 | endif()
60 |
61 | set(ANDROID_ABI arm64-v8a)
62 |
63 | set(android_cmake_args
64 | -DCMAKE_TOOLCHAIN_FILE=${android_toolchain}
65 | -DANDROID_ABI=${ANDROID_ABI}
66 | -DANDROID_PLATFORM=android-${minimum_target_sdk}
67 | )
68 |
69 | # Make place to install Python packages we depend on
70 | file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/deps/_python_)
71 |
72 | include(dependencies.cmake)
73 |
74 | build_native_dependencies()
75 |
76 | build_crosscompile_dependencies()
77 |
78 | ##################################
79 | # Android specific projects
80 | ##################################
81 |
82 |
83 | set(cmake_with_env "${CMAKE_COMMAND}" -E
84 | env
85 | "PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}/deps/_python_"
86 | "AMENT_PREFIX_PATH=${CMAKE_CURRENT_BINARY_DIR}/deps"
87 | "${CMAKE_COMMAND}")
88 |
89 | ExternalProject_Add(ros_for_android
90 | BUILD_ALWAYS ON
91 | DOWNLOAD_COMMAND ""
92 | CMAKE_COMMAND ${cmake_with_env} -DCMAKE_BUILD_TYPE=RELEASE # TODO(sloretz) better release/debug switching
93 | BUILD_COMMAND ${cmake_with_env} --build .
94 | SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/src"
95 | DEPENDS
96 | deps-rclcpp
97 | deps-rclcpp_lifecycle
98 | deps-std_msgs
99 | deps-sensor_msgs
100 | CMAKE_ARGS
101 | ${android_cmake_args}
102 | "-DCMAKE_FIND_ROOT_PATH=${CMAKE_CURRENT_BINARY_DIR}/deps"
103 | -DCMAKE_INSTALL_PREFIX=${CMAKE_CURRENT_BINARY_DIR}/ros_for_android
104 | # -DCMAKE_VERBOSE_MAKEFILE=ON
105 | )
106 |
107 | # Configure the manifest
108 | set(NAMESPACE "com.github.sloretz.sensors_for_ros")
109 | set(MIN_SDK_VERSION 30)
110 | set(TARGET_SDK_VERSION 30)
111 | set(LIB_NAME "android-ros")
112 | configure_file(
113 | AndroidManifest.xml.in
114 | AndroidManifest.xml
115 | @ONLY)
116 |
117 | # Make a place for compiled resources to go
118 | file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/compiled_resources)
119 | # Make a list of compiled resources for copying
120 | set(aapt2_compiled_resources)
121 | macro(aapt2_compile input output)
122 | add_custom_command(
123 | OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/compiled_resources/${output}"
124 | COMMENT "compiling ${input} -> ${output}"
125 | COMMAND ${android_aapt2} compile
126 | "${CMAKE_CURRENT_SOURCE_DIR}/${input}"
127 | -o "${CMAKE_CURRENT_BINARY_DIR}/compiled_resources"
128 | )
129 | list(APPEND aapt2_compiled_resources "${CMAKE_CURRENT_BINARY_DIR}/compiled_resources/${output}")
130 | endmacro()
131 |
132 | # Compile resources
133 | aapt2_compile(res/values/strings.xml values_strings.arsc.flat)
134 | aapt2_compile(res/values/colors.xml values_colors.arsc.flat)
135 | aapt2_compile(res/drawable/ic_launcher_foreground.xml drawable_ic_launcher_foreground.xml.flat)
136 | aapt2_compile(res/drawable/ic_launcher.xml drawable_ic_launcher.xml.flat)
137 |
138 | # Link resources into an APK that doesn't have the libs yet
139 | add_custom_command(
140 | OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/sensors_for_ros-nolibs.zip"
141 | COMMENT "linking sensors_for_ros-nolibs.zip"
142 | DEPENDS
143 | ${aapt2_compiled_resources}
144 | COMMAND ${android_aapt2} link
145 | -o "${CMAKE_CURRENT_BINARY_DIR}/sensors_for_ros-nolibs.zip"
146 | -I "${android_jar}"
147 | --manifest "${CMAKE_CURRENT_BINARY_DIR}/AndroidManifest.xml"
148 | -R
149 | ${aapt2_compiled_resources}
150 | --auto-add-overlay
151 | )
152 |
153 | ##############################
154 | # Add compiled libs to the APK
155 |
156 | file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/lib/${ANDROID_ABI})
157 |
158 | # Copy cross-compiled libraries to a folder to include in the zip
159 | set(copied_android_libs)
160 | macro(copy_android_lib from_target lib_path)
161 | if(NOT TARGET ${from_target})
162 | message(FATAL_ERROR "${from_target} isn't a target. There must be a typo")
163 | endif()
164 |
165 | get_filename_component(libname "${lib_path}" NAME)
166 | add_custom_command(
167 | OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/lib/${ANDROID_ABI}/${libname}"
168 | COMMENT "Copying ${lib_path} to lib/${ANDROID_ABI}/${libname}"
169 | DEPENDS
170 | "${from_target}"
171 | "${lib_path}"
172 | COMMAND
173 | ${CMAKE_COMMAND} -E copy_if_different
174 | "${lib_path}"
175 | "${CMAKE_CURRENT_BINARY_DIR}/lib/${ANDROID_ABI}/"
176 | )
177 | list(APPEND copied_android_libs "${CMAKE_CURRENT_BINARY_DIR}/lib/${ANDROID_ABI}/${libname}")
178 | endmacro()
179 |
180 | copy_android_lib(ros_for_android "ros_for_android/lib/libandroid-ros.so")
181 | copy_android_lib(deps-cyclonedds "deps/lib/libddsc.so")
182 | copy_android_lib(deps-rmw_dds_common "deps/lib/librmw_dds_common.so")
183 | copy_android_lib(deps-libyaml_vendor "deps/lib/libyaml.so")
184 |
185 | # Add all shared libraries to APK
186 | add_custom_command(
187 | OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/sensors_for_ros-unaligned-unsigned.zip
188 | COMMENT "Adding libs to sensors_for_ros-nolibs.zip to make sensors_for_ros-unaligned-unsigned.zip"
189 | DEPENDS
190 | ${copied_android_libs}
191 | ${CMAKE_CURRENT_BINARY_DIR}/sensors_for_ros-nolibs.zip
192 | WORKING_DIRECTORY
193 | "${CMAKE_CURRENT_BINARY_DIR}"
194 | COMMAND zip -D0ru
195 | ${CMAKE_CURRENT_BINARY_DIR}/sensors_for_ros-nolibs.zip
196 | --output-file ${CMAKE_CURRENT_BINARY_DIR}/sensors_for_ros-unaligned-unsigned.zip
197 | lib/
198 | )
199 |
200 | #############################
201 | # Align to 4KiB page boundary
202 | add_custom_command(
203 | OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/sensors_for_ros-unsigned.zip
204 | COMMENT "Aligning make sensors_for_ros-unaligned-unsigned.zip to produce sensors_for_ros-unsigned.zip"
205 | DEPENDS
206 | ${CMAKE_CURRENT_BINARY_DIR}/sensors_for_ros-unaligned-unsigned.zip
207 | COMMAND ${android_zipalign} -p -f -v 4
208 | ${CMAKE_CURRENT_BINARY_DIR}/sensors_for_ros-unaligned-unsigned.zip
209 | ${CMAKE_CURRENT_BINARY_DIR}/sensors_for_ros-unsigned.zip
210 | )
211 |
212 | # Sign the APK
213 | if(NOT DEFINED $ENV{ANDROID_KEY_PASS})
214 | set(ENV{ANDROID_KEY_PASS} android)
215 | endif()
216 | if(NOT DEFINED $ENV{ANDROID_KEYSTORE_PASS})
217 | set(ENV{ANDROID_KEYSTORE_PASS} android)
218 | endif()
219 |
220 | add_custom_command(
221 | OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/sensors_for_ros.apk
222 | COMMENT "Signing sensors_for_ros-unsigned.zip to make sensors_for_ros.apk"
223 | DEPENDS
224 | ${CMAKE_CURRENT_BINARY_DIR}/sensors_for_ros-unsigned.zip
225 | COMMAND "${CMAKE_COMMAND}" -E env
226 | "ANDROID_KEY_PASS=$ENV{ANDROID_KEY_PASS}"
227 | "ANDROID_KEYSTORE_PASS=$ENV{ANDROID_KEYSTORE_PASS}"
228 | "${android_apksigner}" sign
229 | --ks "${PATH_TO_KEYSTORE}"
230 | --ks-pass env:ANDROID_KEYSTORE_PASS
231 | --key-pass env:ANDROID_KEY_PASS
232 | --out "${CMAKE_CURRENT_BINARY_DIR}/sensors_for_ros.apk"
233 | "${CMAKE_CURRENT_BINARY_DIR}/sensors_for_ros-unsigned.zip"
234 | )
235 |
236 | add_custom_target(apk ALL
237 | DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/sensors_for_ros.apk)
238 |
--------------------------------------------------------------------------------
/src/jvm.cc:
--------------------------------------------------------------------------------
1 | #include "jvm.h"
2 |
3 | #include "log.h"
4 |
5 | std::string sensors_for_ros::GetPackageName(ANativeActivity* activity) {
6 | JNIEnv* env = nullptr;
7 | activity->vm->AttachCurrentThread(&env, nullptr);
8 |
9 | jclass android_content_Context = env->GetObjectClass(activity->clazz);
10 | jmethodID midGetPackageName = env->GetMethodID(
11 | android_content_Context, "getPackageName", "()Ljava/lang/String;");
12 | auto packageName =
13 | (jstring)env->CallObjectMethod(activity->clazz, midGetPackageName);
14 |
15 | return std::string(env->GetStringUTFChars(packageName, nullptr));
16 | }
17 |
18 | // https://stackoverflow.com/a/57656736
19 | /**
20 | * \brief Gets the internal name for an android permission.
21 | * \param[in] lJNIEnv a pointer to the JNI environment
22 | * \param[in] perm_name the name of the permission, e.g.,
23 | * "READ_EXTERNAL_STORAGE", "WRITE_EXTERNAL_STORAGE".
24 | * \return a jstring with the internal name of the permission,
25 | * to be used with android Java functions
26 | * Context.checkSelfPermission() or Activity.requestPermissions()
27 | */
28 | jstring android_permission_name(JNIEnv* lJNIEnv, const char* perm_name) {
29 | // nested class permission in class android.Manifest,
30 | // hence android 'slash' Manifest 'dollar' permission
31 | jclass ClassManifestpermission =
32 | lJNIEnv->FindClass("android/Manifest$permission");
33 | jfieldID lid_PERM = lJNIEnv->GetStaticFieldID(
34 | ClassManifestpermission, perm_name, "Ljava/lang/String;");
35 | jstring ls_PERM = (jstring)(
36 | lJNIEnv->GetStaticObjectField(ClassManifestpermission, lid_PERM));
37 | return ls_PERM;
38 | }
39 |
40 | /**
41 | * \brief Tests whether a permission is granted.
42 | * \param[in] app a pointer to the android app.
43 | * \param[in] perm_name the name of the permission, e.g.,
44 | * "READ_EXTERNAL_STORAGE", "WRITE_EXTERNAL_STORAGE".
45 | * \retval true if the permission is granted.
46 | * \retval false otherwise.
47 | * \note Requires Android API level 23 (Marshmallow, May 2015)
48 | */
49 | bool sensors_for_ros::HasPermission(struct ANativeActivity* activity,
50 | const char* perm_name) {
51 | JavaVM* lJavaVM = activity->vm;
52 | JNIEnv* lJNIEnv = nullptr;
53 | bool lThreadAttached = false;
54 |
55 | // Get JNIEnv from lJavaVM using GetEnv to test whether
56 | // thread is attached or not to the VM. If not, attach it
57 | // (and note that it will need to be detached at the end
58 | // of the function).
59 | switch (lJavaVM->GetEnv((void**)&lJNIEnv, JNI_VERSION_1_6)) {
60 | case JNI_OK:
61 | break;
62 | case JNI_EDETACHED: {
63 | jint lResult = lJavaVM->AttachCurrentThread(&lJNIEnv, nullptr);
64 | if (lResult == JNI_ERR) {
65 | throw std::runtime_error("Could not attach current thread");
66 | }
67 | lThreadAttached = true;
68 | } break;
69 | case JNI_EVERSION:
70 | throw std::runtime_error("Invalid java version");
71 | }
72 |
73 | bool result = false;
74 |
75 | jstring ls_PERM = android_permission_name(lJNIEnv, perm_name);
76 |
77 | jint PERMISSION_GRANTED = jint(-1);
78 | {
79 | jclass ClassPackageManager =
80 | lJNIEnv->FindClass("android/content/pm/PackageManager");
81 | jfieldID lid_PERMISSION_GRANTED = lJNIEnv->GetStaticFieldID(
82 | ClassPackageManager, "PERMISSION_GRANTED", "I");
83 | PERMISSION_GRANTED =
84 | lJNIEnv->GetStaticIntField(ClassPackageManager, lid_PERMISSION_GRANTED);
85 | }
86 | {
87 | jclass ClassContext = lJNIEnv->FindClass("android/content/Context");
88 | jmethodID MethodcheckSelfPermission = lJNIEnv->GetMethodID(
89 | ClassContext, "checkSelfPermission", "(Ljava/lang/String;)I");
90 | jint int_result = lJNIEnv->CallIntMethod(
91 | activity->clazz, MethodcheckSelfPermission, ls_PERM);
92 | result = (int_result == PERMISSION_GRANTED);
93 | }
94 |
95 | if (lThreadAttached) {
96 | lJavaVM->DetachCurrentThread();
97 | }
98 |
99 | return result;
100 | }
101 |
102 | /**
103 | * \brief Query file permissions.
104 | * \details This opens the system dialog that lets the user
105 | * grant (or deny) the permission.
106 | * \param[in] app a pointer to the android app.
107 | * \note Requires Android API level 23 (Marshmallow, May 2015)
108 | */
109 | void sensors_for_ros::RequestPermission(ANativeActivity* activity,
110 | const char* permission) {
111 | LOGI("Requesting permission %s", permission);
112 | JavaVM* lJavaVM = activity->vm;
113 | JNIEnv* lJNIEnv = nullptr;
114 | bool lThreadAttached = false;
115 |
116 | // Get JNIEnv from lJavaVM using GetEnv to test whether
117 | // thread is attached or not to the VM. If not, attach it
118 | // (and note that it will need to be detached at the end
119 | // of the function).
120 | switch (lJavaVM->GetEnv((void**)&lJNIEnv, JNI_VERSION_1_6)) {
121 | case JNI_OK:
122 | break;
123 | case JNI_EDETACHED: {
124 | jint lResult = lJavaVM->AttachCurrentThread(&lJNIEnv, nullptr);
125 | if (lResult == JNI_ERR) {
126 | throw std::runtime_error("Could not attach current thread");
127 | }
128 | lThreadAttached = true;
129 | } break;
130 | case JNI_EVERSION:
131 | throw std::runtime_error("Invalid java version");
132 | }
133 |
134 | LOGI("Seem to be attached?");
135 |
136 | jobjectArray perm_array = lJNIEnv->NewObjectArray(
137 | 1, lJNIEnv->FindClass("java/lang/String"), lJNIEnv->NewStringUTF(""));
138 |
139 | LOGI("Created an array for permissions");
140 |
141 | lJNIEnv->SetObjectArrayElement(perm_array, 0,
142 | android_permission_name(lJNIEnv, permission));
143 |
144 | LOGI("Set the first permission on the array");
145 |
146 | jclass ClassActivity = lJNIEnv->FindClass("android/app/Activity");
147 |
148 | jmethodID MethodrequestPermissions = lJNIEnv->GetMethodID(
149 | ClassActivity, "requestPermissions", "([Ljava/lang/String;I)V");
150 |
151 | // TODO before requesting permissions use RegisterNatives to
152 | // make a callback for the result of the permissions request
153 | // https://stackoverflow.com/a/32919074
154 | LOGI("Got the requestPermissions method, about to call it");
155 |
156 | // Last arg (0) given as requestCode to onRequestPermissionsResult
157 | lJNIEnv->CallVoidMethod(activity->clazz, MethodrequestPermissions, perm_array,
158 | 0);
159 | if (lJNIEnv->ExceptionCheck()) {
160 | LOGI("An Exception occurred when requestion permissions");
161 | } else {
162 | LOGI("No Exception occurred when requestion permissions");
163 | }
164 |
165 | LOGI("About to detach thread");
166 |
167 | if (lThreadAttached) {
168 | lJavaVM->DetachCurrentThread();
169 | }
170 | LOGI("All done requesting permissions");
171 | }
172 |
173 | std::vector sensors_for_ros::GetNetworkInterfaces(ANativeActivity* activity) {
174 | JNIEnv* env = nullptr;
175 | activity->vm->AttachCurrentThread(&env, nullptr);
176 |
177 | jclass classNetworkInterface = env->FindClass("java/net/NetworkInterface");
178 | jmethodID midGetByIndex = env->GetStaticMethodID(
179 | classNetworkInterface, "getByIndex", "(I)Ljava/net/NetworkInterface;");
180 | jmethodID midGetName = env->GetMethodID(
181 | classNetworkInterface, "getName", "()Ljava/lang/String;");
182 |
183 |
184 | std::vector interfaces;
185 |
186 | // Couldn't figure out how to iterate generic in JNI, so assume all indexes
187 | // are between 0 and 256
188 | LOGI("Looking at network interfaces");
189 | for (int i = 0; i < 256; ++i) {
190 | jint jindex = jint(i);
191 | jobject networkInterface =
192 | env->CallStaticObjectMethod(
193 | classNetworkInterface, midGetByIndex, jindex);
194 |
195 | if (env->IsSameObject(networkInterface, NULL)) {
196 | continue;
197 | }
198 |
199 | jmethodID midGetInterfaceName = env->GetMethodID(
200 | classNetworkInterface, "getName", "()Ljava/lang/String;");
201 | auto interfaceName =
202 | (jstring)env->CallObjectMethod(networkInterface, midGetInterfaceName);
203 | interfaces.emplace_back(env->GetStringUTFChars(interfaceName, nullptr));
204 | LOGI("Found an interface %s %d", interfaces.back().c_str(), i);
205 | }
206 | return interfaces;
207 | }
208 |
209 | // https://stackoverflow.com/a/28120447
210 | std::string sensors_for_ros::GetCacheDir(ANativeActivity* activity) {
211 | JNIEnv* env;
212 | activity->vm->AttachCurrentThread( &env, NULL );
213 |
214 | jclass activityClass = env->FindClass("android/app/Activity");
215 | jmethodID getCacheDir = env->GetMethodID(activityClass, "getCacheDir", "()Ljava/io/File;");
216 | jobject cache_dir = env->CallObjectMethod(activity->clazz, getCacheDir);
217 |
218 | jclass fileClass = env->FindClass( "java/io/File" );
219 | jmethodID getPath = env->GetMethodID(fileClass, "getPath", "()Ljava/lang/String;");
220 | jstring path_string = (jstring)env->CallObjectMethod(cache_dir, getPath);
221 |
222 | const char *path_chars = env->GetStringUTFChars(path_string, NULL);
223 | std::string temp_folder(path_chars);
224 |
225 | env->ReleaseStringUTFChars(path_string, path_chars);
226 | // activity->vm->DetachCurrentThread();
227 | return temp_folder;
228 | }
229 |
--------------------------------------------------------------------------------
/docs/problems_encountered.md:
--------------------------------------------------------------------------------
1 | # Problems encountered
2 |
3 | This document is place for problems encountered while developing **ROS for Android** and their solutions.
4 |
5 | ## Native dependencies
6 | bison
7 |
8 | ## Can't build iceoryx
9 |
10 | Android [intentionally doesn't impelement the necessary APIs](https://stackoverflow.com/a/38637565).
11 |
12 | ```
13 | [ 74%] Performing build step for 'deps-iceoryx_hoofs'
14 | [ 2%] Building CXX object platform/CMakeFiles/iceoryx_platform.dir/unix/source/file.cpp.o
15 | [ 4%] Building CXX object platform/CMakeFiles/iceoryx_platform.dir/unix/source/fnctl.cpp.o
16 | [ 6%] Building CXX object platform/CMakeFiles/iceoryx_platform.dir/unix/source/grp.cpp.o
17 | [ 8%] Building CXX object platform/CMakeFiles/iceoryx_platform.dir/unix/source/mman.cpp.o
18 | /home/sloretz/android_ros/deps/iceoryx/iceoryx_hoofs/platform/unix/source/mman.cpp:22:12: error: use of undeclared identifier 'shm_open'
19 | return shm_open(name, oflag, mode);
20 | ^
21 | /home/sloretz/android_ros/deps/iceoryx/iceoryx_hoofs/platform/unix/source/mman.cpp:28:12: error: use of undeclared identifier 'shm_unlink'
22 | return shm_unlink(name);
23 | ```
24 |
25 | ## Cyclonedds needs native ddsconf tool
26 |
27 | In the 0.8.x release it has a tool [`ddsconf` that needs to exist on the native machine](https://github.com/eclipse-cyclonedds/cyclonedds/blob/93fdedcf0c99988147b04c22e13cb24dd2e8f74f/src/tools/ddsconf/CMakeLists.txt#L58-L60).
28 | I think this means two projects: one building the ddsconf executable locally.
29 |
30 | ## Cyclonedds Native and Cross Compiled need matching ENABLE_SSL settings
31 |
32 | They take a CMake argument `-DENABLE_SSL=OFF` to turn off SSL support.
33 | If the native build supports SSL, but the cross compiled does not, then compile erros result because the native ddsconf tool assumes SSL support
34 |
35 | ```
36 |
37 | [ 43%] Building C object src/core/CMakeFiles/ddsc.dir/defconfig.c.o
38 | /home/sloretz/android_ros/build/deps-cyclonedds-prefix/src/deps-cyclonedds-build/src/core/defconfig.c:83:8: error: no member named 'ssl_verify' in 'struct ddsi_config'
39 | cfg->ssl_verify = INT32_C (1);
40 | ~~~ ^
41 | /home/sloretz/android_ros/build/deps-cyclonedds-prefix/src/deps-cyclonedds-build/src/core/defconfig.c:84:8: error: no member named 'ssl_verify_client' in 'struct ddsi_config'
42 | cfg->ssl_verify_client = INT32_C (1);
43 | ~~~ ^
44 | /home/sloretz/android_ros/build/deps-cyclonedds-prefix/src/deps-cyclonedds-build/src/core/defconfig.c:85:8: error: no member named 'ssl_keystore' in 'struct ddsi_config'
45 | cfg->ssl_keystore = "keystore";
46 | ~~~ ^
47 | /home/sloretz/android_ros/build/deps-cyclonedds-prefix/src/deps-cyclonedds-build/src/core/defconfig.c:86:8: error: no member named 'ssl_key_pass' in 'struct ddsi_config'
48 | cfg->ssl_key_pass = "secret";
49 | ~~~ ^
50 | /home/sloretz/android_ros/build/deps-cyclonedds-prefix/src/deps-cyclonedds-build/src/core/defconfig.c:87:8: error: no member named 'ssl_ciphers' in 'struct ddsi_config'
51 | cfg->ssl_ciphers = "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH";
52 | ~~~ ^
53 | /home/sloretz/android_ros/build/deps-cyclonedds-prefix/src/deps-cyclonedds-build/src/core/defconfig.c:88:8: error: no member named 'ssl_rand_file' in 'struct ddsi_config'
54 | cfg->ssl_rand_file = "";
55 | ~~~ ^
56 | /home/sloretz/android_ros/build/deps-cyclonedds-prefix/src/deps-cyclonedds-build/src/core/defconfig.c:89:8: error: no member named 'ssl_min_version' in 'struct ddsi_config'
57 | cfg->ssl_min_version.major = 1;
58 | ~~~ ^
59 | /home/sloretz/android_ros/build/deps-cyclonedds-prefix/src/deps-cyclonedds-build/src/core/defconfig.c:90:8: error: no member named 'ssl_min_version' in 'struct ddsi_config'
60 | cfg->ssl_min_version.minor = 3;
61 | ~~~ ^
62 | ```
63 |
64 | ## catkin_pkg won't give transitive build dependencies
65 |
66 | The `catkin_pkg.topological_order.topological_order` function does seem to order by exported dependencies, but the exported package objects don't have that information built in.
67 | I had to make the `generate_ros_superbuild.py` script transitively lookup exported dependencies so`make -jN` on the superbuild would work.
68 |
69 | ## Vendor package is inconvenient when scripting a superbuild
70 |
71 | `rcl_yaml_param_parser` can't find `yamlConfig.cmake` because of the way I've written `dep_build` to install packages to separate folders.
72 | CMake can find a package with `/*/(cmake|CMake)/`, but the `` here is `libyaml_vendor` instead of `yaml`.
73 |
74 | I worked around it by passing `-Dyaml_DIR` to all downstream packages.
75 |
76 | ## Typesupports are found using the ament index
77 |
78 | ```
79 | CMake Error at /home/sloretz/android_ros/build/deps/rosidl_typesupport_c/share/rosidl_typesupport_c/cmake/get_used_typesupports.cmake:35 (message):
80 | No 'rosidl_typesupport_c' found
81 | Call Stack (most recent call first):
82 | /home/sloretz/android_ros/build/deps/rosidl_typesupport_c/share/rosidl_typesupport_c/cmake/rosidl_typesupport_c-extras.cmake:8 (get_used_typesupports)
83 | /home/sloretz/android_ros/build/deps/rosidl_typesupport_c/share/rosidl_typesupport_c/cmake/rosidl_typesupport_cConfig.cmake:41 (include)
84 | /home/sloretz/android_ros/build/deps/rosidl_default_generators/share/rosidl_default_generators/cmake/rosidl_default_generators-extras.cmake:21 (find_package)
85 | /home/sloretz/android_ros/build/deps/rosidl_default_generators/share/rosidl_default_generators/cmake/rosidl_default_generatorsConfig.cmake:41 (include)
86 | CMakeLists.txt:14 (find_package)
87 | ```
88 |
89 | I'll work around it by setting `AMENT_PREFIX_PATH` in dep-build.
90 | I could also work around this by using a "merged" install space.
91 |
92 | ## Need PYTHONPATH to be set for CMake projects
93 |
94 | I'm installing Python packages with PIP into `deps/_python_`, but CMake packages are installed to `deps/${name}`
95 | How do I update PYTHONPATH for CMake packages that install Python modules?
96 | Ament feature to override python install dir?
97 | It looks like [`PYTHON_INSTALL_DIR`](https://github.com/ament/ament_cmake/blob/b84cf9e6f2a61d8f9fc5a90c02dc2b5cb63e7f76/ament_cmake_python/ament_cmake_python-extras.cmake#L44) will do what I want.
98 |
99 | ```
100 | CMake Error at /home/sloretz/android_ros/build/deps/rosidl_adapter/share/rosidl_adapter/cmake/rosidl_adapt_interfaces.cmake:59 (message):
101 | execute_process(/usr/bin/python3.8 -m rosidl_adapter --package-name
102 | builtin_interfaces --arguments-file
103 | /home/sloretz/android_ros/build/deps-builtin_interfaces-prefix/src/deps-builtin_interfaces-build/rosidl_adapter__arguments__builtin_interfaces.json
104 | --output-dir
105 | /home/sloretz/android_ros/build/deps-builtin_interfaces-prefix/src/deps-builtin_interfaces-build/rosidl_adapter/builtin_interfaces
106 | --output-file
107 | /home/sloretz/android_ros/build/deps-builtin_interfaces-prefix/src/deps-builtin_interfaces-build/rosidl_adapter/builtin_interfaces.idls)
108 | returned error code 1:
109 |
110 | /usr/bin/python3.8: No module named rosidl_adapter
111 |
112 | Call Stack (most recent call first):
113 | /home/sloretz/android_ros/build/deps/rosidl_cmake/share/rosidl_cmake/cmake/rosidl_generate_interfaces.cmake:130 (rosidl_adapt_interfaces)
114 | CMakeLists.txt:16 (rosidl_generate_interfaces)
115 | ```
116 |
117 | ## rosidl_generator_c expects relative PYTHON_INSTALL_DIR
118 |
119 | ```
120 | CMake Error at /home/sloretz/android_ros/build/deps/rosidl_generator_c/share/rosidl_generator_c/cmake/rosidl_generator_c_generate_interfaces.cmake:69 (message):
121 | Target dependency
122 | '/home/sloretz/android_ros/build/deps/rosidl_generator_c/share/rosidl_generator_c/cmake/../../..//home/sloretz/android_ros/build/deps/_python_/rosidl_generator_c/__init__.py'
123 | does not exist
124 | Call Stack (most recent call first):
125 | /home/sloretz/android_ros/build/deps/ament_cmake_core/share/ament_cmake_core/cmake/core/ament_execute_extensions.cmake:48 (include)
126 | /home/sloretz/android_ros/build/deps/rosidl_cmake/share/rosidl_cmake/cmake/rosidl_generate_interfaces.cmake:286 (ament_execute_extensions)
127 | CMakeLists.txt:16 (rosidl_generate_interfaces)
128 | ```
129 |
130 | ## rosidl generators use add_custom_command, but PYTHONPATH not available to them
131 |
132 | I'm not sure how the rosidl generator inputs are supposed to work.
133 | `add_custom_command` runs at build time, so the PYTHONPATH given to the CMake invocation
134 | is not necessarily available to the make invocation.
135 |
136 | I'll work around this by making `dep_build` set `PYTHONPATH` in the build command.
137 |
138 | Maybe the rosidl generators should pass the ENV at CMake time to the Python process?
139 |
140 | ```
141 | [ 5%] Generating C code for ROS interfaces SLORETZ /home/sloretz/android_ros/build/deps/_python_
142 | [ 11%] Generating C++ code for ROS interfaces
143 | ['/home/sloretz/android_ros/build/deps/rosidl_generator_c/lib/rosidl_generator_c', '/usr/lib/python38.zip', '/usr/lib/python3.8', '/usr/lib/python3.8/lib-dynload', '/usr/local/lib/python3.8/dist-packages', '/usr/lib/python3/dist-packages']
144 | ['/home/sloretz/android_ros/build/deps/rosidl_generator_c/share/rosidl_generator_c/cmake/../../../lib/rosidl_generator_c/rosidl_generator_c', '--generator-arguments-file', '/home/sloretz/android_ros/build/deps-builtin_interfaces-prefix/src/deps-builtin_interfaces-build/rosidl_generator_c__arguments.json']
145 | Traceback (most recent call last):
146 | File "/home/sloretz/android_ros/build/deps/rosidl_generator_c/share/rosidl_generator_c/cmake/../../../lib/rosidl_generator_c/rosidl_generator_c", line 11, in
147 | from rosidl_generator_c import generate_c
148 | ModuleNotFoundError: No module named 'rosidl_generator_c'
149 | CMakeFiles/builtin_interfaces__rosidl_generator_c.dir/build.make:87: recipe for target 'rosidl_generator_c/builtin_interfaces/msg/duration.h' failed
150 | ```
151 |
152 | ## rosidl_typesupport_cpp sneakily depends on rosidl_typesupport_introspection_c
153 |
154 | `rosidl_typesupport_c` when found tries to find other typesupports that use it.
155 | If not, it has a fatal error.
156 | The only package in this workspace that uses it is `rosidl_typesupport_introspection_c`.
157 | This means `rosidl_typesupport_cpp` can't `find_package(rosidl_typesupport_c` until after `rosidl_typesupport_introspection_c` is built.
158 |
159 | ```
160 | CMake Error at /home/sloretz/android_ros/build/deps/rosidl_typesupport_c/share/rosidl_typesupport_c/cmake/get_used_typesupports.cmake:35 (message):
161 | No 'rosidl_typesupport_c' found
162 | Call Stack (most recent call first):
163 | /home/sloretz/android_ros/build/deps/rosidl_typesupport_c/share/rosidl_typesupport_c/cmake/rosidl_typesupport_c-extras.cmake:8 (get_used_typesupports)
164 | /home/sloretz/android_ros/build/deps/rosidl_typesupport_c/share/rosidl_typesupport_c/cmake/rosidl_typesupport_cConfig.cmake:41 (include)
165 | CMakeLists.txt:20 (find_package)
166 |
167 |
168 | -- Configuring incomplete, errors occurred!
169 | See also "/home/sloretz/android_ros/build/deps-rosidl_typesupport_cpp-prefix/src/deps-rosidl_typesupport_cpp-build/CMakeFiles/CMakeOutput.log".
170 | ```
171 |
172 | ## Failed to configure logging
173 |
174 | Might need an android specific logger.
175 | I'll use the noop logging for now.
176 | It looks like the CMake variable `RCL_LOGGING_IMPLEMENTATION` is the one I need to set.
177 |
178 |
179 | ```
180 | 05-07 15:15:43.986 2980 3006 E libc++abi: terminating with uncaught exception of type rclcpp::exceptions::RCLError: failed to configure logging: Failed to get logging directory, at /home/sloretz/android_ros/deps/rcl_logging/rcl_logging_spdlog/src/rcl_logging_spdlog.cpp:83
181 | ```
182 |
183 | ## rmw implementation invalid - can't create node
184 |
185 |
186 | ```
187 | 05-07 15:29:28.641 3452 3486 E libc++abi: terminating with uncaught exception of type rclcpp::exceptions::RCLError: failed to initialize rcl node: rcl node's rmw handle is invalid, at /home/sloretz/android_ros/deps/rcl/rcl/src/rcl/node.c:416
188 | ```
189 |
190 | This was caused by not having the permissions needed to access the network.
191 | Adding those permissions to the manifest fixed it.
192 |
193 | ```xml
194 |
195 |
196 | ```
197 |
198 |
--------------------------------------------------------------------------------
/src/camera_device.cc:
--------------------------------------------------------------------------------
1 | #include "camera_device.h"
2 |
3 | #include // Debugging processing time
4 |
5 | using sensors_for_ros::CameraDevice;
6 |
7 | /// ***************** Android OpenCamera callbacks ********************
8 |
9 | // Android camera state callback called when a camera is no longer available
10 | void OnCameraDisconnected(void* context, ACameraDevice* device) {
11 | LOGI("Camera Disconnected (context pointer %p, camera device pointer %p",
12 | context, device);
13 | }
14 |
15 | // Android camera state callback called when a camera is no longer available
16 | // because of an error
17 | void OnCameraError(void* context, ACameraDevice* device, int error) {
18 | LOGI("Camera error (context pointer %p, camera device pointer %p, error %d)",
19 | context, device, error);
20 | }
21 |
22 | static const ACameraDevice_stateCallbacks kCameraStateCallbacks = {
23 | .context = nullptr,
24 | .onDisconnected = OnCameraDisconnected,
25 | .onError = OnCameraError,
26 | };
27 |
28 | /// ***************** Android AImageReader callbacks ********************
29 |
30 | void OnImage(void* context, AImageReader* reader) {
31 | AImage* cimage = nullptr;
32 | auto status = AImageReader_acquireNextImage(reader, &cimage);
33 |
34 | // TODO Check status here ...
35 |
36 | std::unique_ptr image(cimage);
37 |
38 | auto* camera_device = static_cast(context);
39 | camera_device->OnImage(std::move(image));
40 | }
41 |
42 | static const AImageReader_ImageListener kImageListenerCallbacks = {
43 | .context = nullptr,
44 | .onImageAvailable = OnImage,
45 | };
46 |
47 | /// ***************** Android capture session state callbacks
48 | /// ********************
49 | // TODO implement these -they're still copy/pasted
50 | static void onSessionActive(void* context, ACameraCaptureSession* session) {}
51 |
52 | static void onSessionReady(void* context, ACameraCaptureSession* session) {}
53 |
54 | static void onSessionClosed(void* context, ACameraCaptureSession* session) {}
55 |
56 | static ACameraCaptureSession_stateCallbacks sessionStateCallbacks{
57 | .context = nullptr,
58 | .onClosed = onSessionClosed,
59 | .onReady = onSessionReady,
60 | .onActive = onSessionActive,
61 | };
62 |
63 | /// ***************** Android capture callbacks ********************
64 | // TODO implement these -they're still copy/pasted
65 | void onCaptureFailed(void* context, ACameraCaptureSession* session,
66 | ACaptureRequest* request, ACameraCaptureFailure* failure) {
67 | }
68 |
69 | void onCaptureSequenceCompleted(void* context, ACameraCaptureSession* session,
70 | int sequenceId, int64_t frameNumber) {}
71 |
72 | void onCaptureSequenceAborted(void* context, ACameraCaptureSession* session,
73 | int sequenceId) {}
74 |
75 | void onCaptureCompleted(void* context, ACameraCaptureSession* session,
76 | ACaptureRequest* request,
77 | const ACameraMetadata* result) {}
78 |
79 | static ACameraCaptureSession_captureCallbacks captureCallbacks{
80 | .context = nullptr,
81 | .onCaptureStarted = nullptr,
82 | .onCaptureProgressed = nullptr,
83 | .onCaptureCompleted = onCaptureCompleted,
84 | .onCaptureFailed = onCaptureFailed,
85 | .onCaptureSequenceCompleted = onCaptureSequenceCompleted,
86 | .onCaptureSequenceAborted = onCaptureSequenceAborted,
87 | .onCaptureBufferLost = nullptr,
88 | };
89 |
90 | /// ***************** CameraDevice ********************
91 | std::unique_ptr CameraDevice::OpenCamera(
92 | ACameraManager* native_manager, const CameraDescriptor& desc) {
93 | const char* camera_id = desc.id.c_str();
94 | auto camera_device = std::unique_ptr(new CameraDevice);
95 | camera_device->desc_ = desc;
96 |
97 | auto result = ACameraManager_openCamera(native_manager, camera_id,
98 | &(camera_device->state_callbacks_),
99 | &(camera_device->native_device_));
100 |
101 | if (ACAMERA_OK != result) {
102 | LOGW("Failed to open camera %s, %d", camera_id, result);
103 | return nullptr;
104 | }
105 | LOGI("XXX I opened a camera!");
106 |
107 | // Open image reader to get camera data
108 | constexpr int max_simultaneous_images = 1;
109 | media_status_t status = AImageReader_new(
110 | camera_device->width_, camera_device->height_, AIMAGE_FORMAT_YUV_420_888,
111 | max_simultaneous_images, &(camera_device->reader_));
112 |
113 | // TODO handle errors
114 | // if (status != AMEDIA_OK)
115 | // Handle errors here
116 |
117 | AImageReader_setImageListener(camera_device->reader_,
118 | &(camera_device->reader_callbacks_));
119 |
120 | ACaptureSessionOutputContainer_create(&(camera_device->output_container_));
121 |
122 | ACameraDevice_createCaptureRequest(camera_device->native_device_,
123 | TEMPLATE_RECORD,
124 | &(camera_device->capture_request_));
125 |
126 | ANativeWindow* native_window;
127 | AImageReader_getWindow(camera_device->reader_, &native_window);
128 | ANativeWindow_acquire(native_window);
129 | ACameraOutputTarget_create(native_window,
130 | &(camera_device->camera_output_target_));
131 | ACaptureRequest_addTarget(camera_device->capture_request_,
132 | camera_device->camera_output_target_);
133 | ACaptureSessionOutput_create(native_window,
134 | &(camera_device->capture_session_output_));
135 | ACaptureSessionOutputContainer_add(camera_device->output_container_,
136 | camera_device->capture_session_output_);
137 |
138 | ACameraDevice_createCaptureSession(camera_device->native_device_,
139 | camera_device->output_container_,
140 | &sessionStateCallbacks, // TODO
141 | &(camera_device->capture_session_));
142 |
143 | // Start Recording
144 | ACameraCaptureSession_setRepeatingRequest(
145 | camera_device->capture_session_,
146 | &captureCallbacks, // TODO
147 | 1, &(camera_device->capture_request_), nullptr);
148 |
149 | return camera_device;
150 | }
151 |
152 | CameraDevice::CameraDevice()
153 | : state_callbacks_(kCameraStateCallbacks),
154 | reader_callbacks_(kImageListenerCallbacks) {
155 | state_callbacks_.context = this;
156 | reader_callbacks_.context = this;
157 |
158 | shutdown_.store(false);
159 | thread_ = std::thread(&CameraDevice::ProcessImages, this);
160 | }
161 |
162 | void CameraDevice::ProcessImages() {
163 | while (!shutdown_.load()) {
164 | auto dbg_start = std::chrono::high_resolution_clock::now();
165 | std::unique_ptr image;
166 | {
167 | // Wait for next image, or shutdown
168 | std::unique_lock guard(mutex_);
169 | wake_cv_.wait(guard, [&image, this] {
170 | image = std::move(image_);
171 | image_.reset();
172 | return nullptr != image.get() || shutdown_.load();
173 | });
174 | }
175 | if (nullptr != image.get()) {
176 | auto image_msg = std::make_unique();
177 | // Plane 0: Y
178 | // Plane 1: U (Cb)
179 | // Plane 2: V (Cr)
180 | // U/V planes guaranteed to have same row and pixel stride
181 |
182 | // Y plane guaranteed stride of 1
183 | constexpr int32_t y_pixel_stride = 1;
184 | const int32_t y_row_stride = width_;
185 |
186 | int32_t uv_pixel_stride;
187 | int32_t uv_row_stride;
188 |
189 | if (AMEDIA_OK !=
190 | AImage_getPlanePixelStride(image.get(), 1, &uv_pixel_stride)) {
191 | LOGW("Unable to get U/V plane pixel stride");
192 | continue;
193 | }
194 | if (AMEDIA_OK !=
195 | AImage_getPlaneRowStride(image.get(), 1, &uv_row_stride)) {
196 | LOGW("Unable to get U/V plane row stride");
197 | continue;
198 | }
199 |
200 | uint8_t* y_data = nullptr;
201 | uint8_t* u_data = nullptr;
202 | uint8_t* v_data = nullptr;
203 | int y_len = -1;
204 | int u_len = -1;
205 | int v_len = -1;
206 |
207 | if (AMEDIA_OK != AImage_getPlaneData(image.get(), 0, &y_data, &y_len)) {
208 | LOGW("Unable to get Y plane data");
209 | continue;
210 | }
211 | if (AMEDIA_OK != AImage_getPlaneData(image.get(), 1, &u_data, &u_len)) {
212 | LOGW("Unable to get U plane data");
213 | continue;
214 | }
215 | if (AMEDIA_OK != AImage_getPlaneData(image.get(), 2, &v_data, &v_len)) {
216 | LOGW("Unable to get V plane data");
217 | continue;
218 | }
219 |
220 | image_msg->width = width_;
221 | image_msg->height = height_;
222 | image_msg->encoding = "rgb8";
223 | image_msg->step = width_ * 3;
224 | image_msg->data.resize(image_msg->step * image_msg->height);
225 |
226 | // YUV420 to RGB888
227 | // https://blog.minhazav.dev/how-to-convert-yuv-420-sp-android.media.Image-to-Bitmap-or-jpeg
228 | size_t byte = 0;
229 | for (int h = 0; h < height_; ++h) {
230 | // U/V are subsampled
231 | const int uvh = h / 2;
232 | for (int w = 0; w < width_; ++w) {
233 | // U/V are subsampled
234 | const int uvw = w / 2;
235 | const size_t y_idx = h * y_row_stride + w * y_pixel_stride;
236 | const size_t uv_idx = uvh * uv_row_stride + uvw * uv_pixel_stride;
237 | // LOGI("y_idx %lu uv_idx %lu y_len %d, u_len %d, v_len %d", y_idx,
238 | // uv_idx, y_len, u_len, v_len); continue; // XXX
239 |
240 | const uint8_t y = y_data[y_idx];
241 | const uint8_t u = u_data[uv_idx];
242 | const uint8_t v = v_data[uv_idx];
243 |
244 | int r = y + (1.370705 * (v - 128));
245 | int g = y - (0.698001 * (v - 128)) - (0.337633 * (u - 128));
246 | ;
247 | int b = y + (1.732446 * (u - 128));
248 |
249 | if (r < 0) {
250 | r = 0;
251 | } else if (r > 255) {
252 | r = 255;
253 | }
254 | if (g < 0) {
255 | g = 0;
256 | } else if (g > 255) {
257 | g = 255;
258 | }
259 | if (b < 0) {
260 | b = 0;
261 | } else if (b > 255) {
262 | b = 255;
263 | }
264 | image_msg->data[byte++] = r;
265 | image_msg->data[byte++] = g;
266 | image_msg->data[byte++] = b;
267 | }
268 | }
269 |
270 | // TODO Emit data for publisher
271 | auto dbg_process_end = std::chrono::high_resolution_clock::now();
272 | Emit({std::make_unique(), std::move(image_msg)});
273 | auto dbg_emit_end = std::chrono::high_resolution_clock::now();
274 | auto dbg_process = std::chrono::duration_cast(
275 | dbg_process_end - dbg_start);
276 | auto dbg_emit = std::chrono::duration_cast(
277 | dbg_emit_end - dbg_process_end);
278 | LOGI("Image processing time: %lld emit time: %lld", dbg_process.count(),
279 | dbg_emit.count());
280 | }
281 | }
282 | LOGI("Camera device ProcessImages shutting down");
283 | }
284 |
285 | CameraDevice::~CameraDevice() {
286 | // Shut down processing thread
287 | shutdown_.store(true);
288 | if (thread_.joinable()) {
289 | wake_cv_.notify_one();
290 | thread_.join();
291 | }
292 | if (capture_session_) {
293 | ACameraCaptureSession_stopRepeating(capture_session_);
294 | ACameraCaptureSession_close(capture_session_);
295 | }
296 |
297 | if (output_container_) {
298 | ACaptureSessionOutputContainer_free(output_container_);
299 | }
300 |
301 | if (capture_session_output_) {
302 | ACaptureSessionOutput_free(capture_session_output_);
303 | }
304 |
305 | if (capture_request_) {
306 | ACaptureRequest_free(capture_request_);
307 | }
308 |
309 | if (reader_) {
310 | AImageReader_delete(reader_);
311 | }
312 | if (native_device_) {
313 | ACameraDevice_close(native_device_);
314 | }
315 | }
316 |
317 | void CameraDevice::OnImage(std::unique_ptr image) {
318 | // Hand off image data to processing thread
319 | std::unique_lock lock(mutex_);
320 | image_ = std::move(image);
321 | wake_cv_.notify_one();
322 | }
323 |
--------------------------------------------------------------------------------
/src/android_entry_point.cc:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 |
8 | #include "camera_descriptor.h"
9 | #include "camera_manager.h"
10 | #include "controller.h"
11 | #include "controllers/accelerometer_sensor_controller.h"
12 | #include "controllers/barometer_sensor_controller.h"
13 | #include "controllers/camera_controller.h"
14 | #include "controllers/gyroscope_sensor_controller.h"
15 | #include "controllers/illuminance_sensor_controller.h"
16 | #include "controllers/list_controller.h"
17 | #include "controllers/magnetometer_sensor_controller.h"
18 | #include "controllers/ros_domain_id_controller.h"
19 | #include "events.h"
20 | #include "gui.h"
21 | #include "jvm.h"
22 | #include "log.h"
23 | #include "ros_interface.h"
24 | #include "sensors.h"
25 |
26 | // Controller class links all the parts
27 | class AndroidApp {
28 | public:
29 | AndroidApp(ANativeActivity* activity)
30 | : activity_(activity), sensors_(activity), ros_domain_id_controller_(activity) {
31 | ros_domain_id_controller_.SetListener(std::bind(
32 | &AndroidApp::OnRosDomainIdChanged, this, std::placeholders::_1));
33 | PushController(&ros_domain_id_controller_);
34 |
35 | list_controller_.SetListener(
36 | std::bind(&AndroidApp::OnNavigateBack, this, std::placeholders::_1));
37 | list_controller_.SetListener(
38 | std::bind(&AndroidApp::OnNavigateTo, this, std::placeholders::_1));
39 |
40 | LOGI("Initalizing Sensors");
41 | sensors_.Initialize();
42 |
43 | // Create sensor-specific controllers
44 | for (auto& sensor : sensors_.GetSensors()) {
45 | if (ASENSOR_TYPE_LIGHT == sensor->Descriptor().type) {
46 | auto controller =
47 | std::make_unique(
48 | static_cast(sensor.get()),
49 | ros_);
50 | // Listen to go-back-to-the-last-window GUI events from this controller
51 | controller->SetListener(std::bind(&AndroidApp::OnNavigateBack, this,
52 | std::placeholders::_1));
53 | controllers_.emplace_back(std::move(controller));
54 | LOGI("Sensor controller with handle %d added",
55 | sensor->Descriptor().handle);
56 | } else if (ASENSOR_TYPE_GYROSCOPE == sensor->Descriptor().type) {
57 | auto controller =
58 | std::make_unique(
59 | static_cast(sensor.get()),
60 | ros_);
61 | // Listen to go-back-to-the-last-window GUI events from this controller
62 | controller->SetListener(std::bind(&AndroidApp::OnNavigateBack, this,
63 | std::placeholders::_1));
64 | controllers_.emplace_back(std::move(controller));
65 | LOGI("Sensor controller with handle %d added",
66 | sensor->Descriptor().handle);
67 | } else if (ASENSOR_TYPE_ACCELEROMETER == sensor->Descriptor().type) {
68 | auto controller =
69 | std::make_unique(
70 | static_cast(
71 | sensor.get()),
72 | ros_);
73 | // Listen to go-back-to-the-last-window GUI events from this controller
74 | controller->SetListener(std::bind(&AndroidApp::OnNavigateBack, this,
75 | std::placeholders::_1));
76 | controllers_.emplace_back(std::move(controller));
77 | LOGI("Sensor controller with handle %d added",
78 | sensor->Descriptor().handle);
79 | } else if (ASENSOR_TYPE_PRESSURE == sensor->Descriptor().type) {
80 | auto controller =
81 | std::make_unique(
82 | static_cast(sensor.get()),
83 | ros_);
84 | // Listen to go-back-to-the-last-window GUI events from this controller
85 | controller->SetListener(std::bind(&AndroidApp::OnNavigateBack, this,
86 | std::placeholders::_1));
87 | controllers_.emplace_back(std::move(controller));
88 | LOGI("Sensor controller with handle %d added",
89 | sensor->Descriptor().handle);
90 | } else if (ASENSOR_TYPE_MAGNETIC_FIELD == sensor->Descriptor().type) {
91 | auto controller =
92 | std::make_unique(
93 | static_cast(sensor.get()),
94 | ros_);
95 | // Listen to go-back-to-the-last-window GUI events from this controller
96 | controller->SetListener(std::bind(&AndroidApp::OnNavigateBack, this,
97 | std::placeholders::_1));
98 | controllers_.emplace_back(std::move(controller));
99 | LOGI("Sensor controller with handle %d added",
100 | sensor->Descriptor().handle);
101 | }
102 | }
103 |
104 | for (const auto& controller : controllers_) {
105 | list_controller_.AddController(controller.get());
106 | }
107 |
108 | // Create camera specific controllers
109 | if (camera_manager_.HasCameras()) {
110 | if (!sensors_for_ros::HasPermission(activity_, "CAMERA")) {
111 | LOGI("Requesting Camera Permission");
112 | sensors_for_ros::RequestPermission(activity_, "CAMERA");
113 | } else {
114 | StartCameras();
115 | }
116 | }
117 | }
118 |
119 | ~AndroidApp() = default;
120 |
121 | // Camera permission has to be requested at runtime, and we're not sure when
122 | // we'll get it.
123 | // Try to start the cameras.
124 | void StartCameras() {
125 | if (started_cameras_) {
126 | return;
127 | }
128 | if (camera_manager_.HasCameras() &&
129 | sensors_for_ros::HasPermission(activity_, "CAMERA")) {
130 | started_cameras_ = true;
131 | // Create camera specific controllers
132 | LOGI("Starting cameras");
133 | std::vector cameras =
134 | camera_manager_.GetCameras();
135 | for (auto cam_desc : cameras) {
136 | LOGI("Camera: %s", cam_desc.GetName().c_str());
137 | std::unique_ptr camera_controller(
138 | new sensors_for_ros::CameraController(&camera_manager_, cam_desc,
139 | ros_));
140 | camera_controller->SetListener(std::bind(&AndroidApp::OnNavigateBack,
141 | this, std::placeholders::_1));
142 | controllers_.emplace_back(std::move(camera_controller));
143 | list_controller_.AddController(controllers_.back().get());
144 | }
145 | }
146 | }
147 |
148 | ANativeActivity* activity_;
149 | sensors_for_ros::RosInterface ros_;
150 | sensors_for_ros::Sensors sensors_;
151 | sensors_for_ros::GUI gui_;
152 |
153 | // Special controller: ROS_DOMAIN_ID picker shown at startup
154 | sensors_for_ros::RosDomainIdController ros_domain_id_controller_;
155 | // Special controller: Show list of sensors and cameras
156 | sensors_for_ros::ListController list_controller_;
157 | // Controllers that can be nativated to by unique id
158 | std::vector> controllers_;
159 |
160 | // Stack of controllers for navigation windows
161 | std::vector controller_stack_;
162 |
163 | // Manager for working for cameras
164 | sensors_for_ros::CameraManager camera_manager_;
165 | bool started_cameras_ = false;
166 |
167 | private:
168 | void PushController(sensors_for_ros::Controller* controller) {
169 | if (controller) {
170 | controller_stack_.push_back(controller);
171 | gui_.SetController(controller);
172 | }
173 | }
174 |
175 | void PopController() {
176 | // Don't allow popping past the first controller
177 | if (controller_stack_.size() > 1) {
178 | controller_stack_.pop_back();
179 | gui_.SetController(controller_stack_.back());
180 | }
181 | }
182 |
183 | void OnNavigateBack(const sensors_for_ros::event::GuiNavigateBack&) {
184 | LOGI("Poping controller!");
185 | PopController();
186 | }
187 |
188 | void OnNavigateTo(const sensors_for_ros::event::GuiNavigateTo& event) {
189 | auto cit = std::find_if(controllers_.begin(), controllers_.end(),
190 | [&event](const auto& other) {
191 | return event.unique_id == other->UniqueId();
192 | });
193 | if (cit != controllers_.end()) {
194 | PushController(cit->get());
195 | }
196 | }
197 |
198 | void OnRosDomainIdChanged(
199 | const sensors_for_ros::event::RosDomainIdChanged& event) {
200 | StartRos(event.id, event.interface);
201 | PushController(&list_controller_);
202 | }
203 |
204 | void StartRos(int32_t ros_domain_id, std::string network_interface) {
205 | // Write a config file to pass to cyclonedds
206 | std::string cyclone_uri;
207 | cyclone_uri += sensors_for_ros::GetCacheDir(activity_);
208 | if (cyclone_uri.back() != '/') {
209 | cyclone_uri += '/';
210 | }
211 | cyclone_uri += "cyclonedds.xml";
212 | LOGI("Cyclonedds URI: %s", cyclone_uri.c_str());
213 | setenv("CYCLONEDDS_URI", cyclone_uri.c_str(), 1);
214 |
215 | std::ofstream config_file(cyclone_uri.c_str(), std::ofstream::trunc);
216 | config_file << "\n";
217 | config_file << "";
218 | config_file << "";
219 | config_file << "";
220 | config_file << "";
221 | config_file.close();
222 |
223 | if (ros_.Initialized()) {
224 | LOGI("Shutting down ROS");
225 | ros_.Shutdown();
226 | }
227 | LOGI("Initalizing ROS");
228 | ros_.Initialize(ros_domain_id);
229 | }
230 | };
231 |
232 | inline AndroidApp* GetApp(ANativeActivity* activity) {
233 | return static_cast(activity->instance);
234 | }
235 |
236 | /// The current device AConfiguration has changed.
237 | static void onConfigurationChanged(ANativeActivity* activity) {
238 | LOGI("ConfigurationChanged: %p\n", activity);
239 | }
240 |
241 | /// The rectangle in the window in which content should be placed has changed.
242 | static void onContentRectChanged(ANativeActivity* activity, const ARect* rect) {
243 | LOGI("ContentRectChanged: %p\n", activity);
244 | }
245 |
246 | /// NativeActivity is being destroyed.
247 | static void onDestroy(ANativeActivity* activity) {
248 | LOGI("Destroy: %p\n", activity);
249 | GetApp(activity)->sensors_.Shutdown();
250 | }
251 |
252 | /// The input queue for this native activity's window has been created.
253 | static void onInputQueueCreated(ANativeActivity* activity, AInputQueue* queue) {
254 | LOGI("InputQueueCreated: %p -- %p\n", activity, queue);
255 | GetApp(activity)->gui_.SetInputQueue(queue);
256 | }
257 |
258 | /// The input queue for this native activity's window is being destroyed.
259 | static void onInputQueueDestroyed(ANativeActivity* activity,
260 | AInputQueue* queue) {
261 | LOGI("InputQueueDestroyed: %p -- %p\n", activity, queue);
262 | GetApp(activity)->gui_.RemoveInputQueue();
263 | }
264 |
265 | /// The system is running low on memory.
266 | static void onLowMemory(ANativeActivity* activity) {
267 | LOGI("LowMemory: %p\n", activity);
268 | }
269 |
270 | /// The drawing window for this native activity has been created.
271 | static void onNativeWindowCreated(ANativeActivity* activity,
272 | ANativeWindow* window) {
273 | LOGI("NativeWindowCreated: %p -- %p\n", activity, window);
274 | GetApp(activity)->gui_.Start(activity, window);
275 | }
276 |
277 | /// The drawing window for this native activity is going to be destroyed.
278 | static void onNativeWindowDestroyed(ANativeActivity* activity,
279 | ANativeWindow* window) {
280 | LOGI("NativeWindowDestroyed: %p -- %p\n", activity, window);
281 | GetApp(activity)->gui_.Stop();
282 | }
283 |
284 | /// The drawing window for this native activity needs to be redrawn.
285 | static void onNativeWindowRedrawNeeded(ANativeActivity* activity,
286 | ANativeWindow* window) {
287 | LOGI("NativeWindowRedrawNeeded: %p -- %p\n", activity, window);
288 | }
289 |
290 | /// The drawing window for this native activity has been resized.
291 | static void onNativeWindowResized(ANativeActivity* activity,
292 | ANativeWindow* window) {
293 | LOGI("NativeWindowResized: %p -- %p\n", activity, window);
294 | }
295 |
296 | /// NativeActivity has paused.
297 | static void onPause(ANativeActivity* activity) {
298 | LOGI("Pause: %p\n", activity);
299 | }
300 |
301 | /// NativeActivity has resumed.
302 | static void onResume(ANativeActivity* activity) {
303 | LOGI("Resume: %p\n", activity);
304 | GetApp(activity)->StartCameras();
305 | }
306 |
307 | /// Framework is asking NativeActivity to save its current instance state.
308 | static void* onSaveInstanceState(ANativeActivity* activity, size_t* outLen) {
309 | LOGI("SaveInstanceState: %p\n", activity);
310 | return NULL;
311 | }
312 |
313 | /// NativeActivity has started.
314 | static void onStart(ANativeActivity* activity) {
315 | LOGI("Start: %p\n", activity);
316 | }
317 |
318 | /// NativeActivity has stopped.
319 | static void onStop(ANativeActivity* activity) { LOGI("Stop: %p\n", activity); }
320 |
321 | /// Focus has changed in this NativeActivity's window.
322 | static void onWindowFocusChanged(ANativeActivity* activity, int focused) {
323 | LOGI("WindowFocusChanged: %p -- %d\n", activity, focused);
324 | }
325 |
326 | /// Entry point called by Android to start this app
327 | void ANativeActivity_onCreate(ANativeActivity* activity, void* savedState,
328 | size_t savedStateSize) {
329 | // TODO(sloretz) support saved state - things like ROS domain id and
330 | // settings
331 | (void)savedState;
332 | (void)savedStateSize;
333 |
334 | // https://developer.android.com/ndk/reference/struct/a-native-activity-callbacks
335 | activity->callbacks->onConfigurationChanged = onConfigurationChanged;
336 | activity->callbacks->onContentRectChanged = onContentRectChanged;
337 | activity->callbacks->onDestroy = onDestroy;
338 | activity->callbacks->onStart = onStart;
339 | activity->callbacks->onResume = onResume;
340 | activity->callbacks->onSaveInstanceState = onSaveInstanceState;
341 | activity->callbacks->onPause = onPause;
342 | activity->callbacks->onStop = onStop;
343 | activity->callbacks->onLowMemory = onLowMemory;
344 | activity->callbacks->onWindowFocusChanged = onWindowFocusChanged;
345 | activity->callbacks->onNativeWindowCreated = onNativeWindowCreated;
346 | activity->callbacks->onNativeWindowDestroyed = onNativeWindowDestroyed;
347 | activity->callbacks->onNativeWindowRedrawNeeded = onNativeWindowRedrawNeeded;
348 | activity->callbacks->onNativeWindowResized = onNativeWindowResized;
349 | activity->callbacks->onInputQueueCreated = onInputQueueCreated;
350 | activity->callbacks->onInputQueueDestroyed = onInputQueueDestroyed;
351 |
352 | // User data
353 | activity->instance = new AndroidApp(activity);
354 | }
355 |
--------------------------------------------------------------------------------
/dependencies.cmake:
--------------------------------------------------------------------------------
1 | include(dep_build.cmake)
2 |
3 | macro(build_native_dependencies)
4 | dep_build(cyclonedds CMAKE
5 | NATIVE
6 | SOURCE_DIR "deps/cyclonedds"
7 | CMAKE_ARGS "-DBUILD_DDSCONF=ON"
8 | # TODO(sloretz) is SSL required for sros2? if so, figure out how to enable
9 | "-DENABLE_SSL=OFF")
10 | endmacro()
11 |
12 | macro(build_crosscompile_dependencies)
13 | set(extra_cmake_args ${android_cmake_args})
14 | list(APPEND extra_cmake_args -DBUILD_SHARED_LIBS=OFF)
15 |
16 | dep_build(ament_index_python PIP
17 | SOURCE_DIR "deps/ament_index/ament_index_python"
18 | DEPENDENCIES )
19 |
20 | dep_build(ament_package PIP
21 | SOURCE_DIR "deps/ament_package")
22 |
23 | dep_build(ament_cmake_core CMAKE
24 | SOURCE_DIR "deps/ament_cmake/ament_cmake_core"
25 | DEPENDENCIES ament_package
26 | CMAKE_ARGS ${extra_cmake_args})
27 |
28 | dep_build(ament_cmake_export_definitions CMAKE
29 | SOURCE_DIR "deps/ament_cmake/ament_cmake_export_definitions"
30 | DEPENDENCIES ament_cmake_core ament_package
31 | CMAKE_ARGS ${extra_cmake_args})
32 |
33 | dep_build(ament_cmake_export_dependencies CMAKE
34 | SOURCE_DIR "deps/ament_cmake/ament_cmake_export_dependencies"
35 | DEPENDENCIES ament_cmake_core ament_package
36 | CMAKE_ARGS ${extra_cmake_args})
37 |
38 | dep_build(ament_cmake CMAKE
39 | SOURCE_DIR "deps/ament_cmake/ament_cmake"
40 | DEPENDENCIES ament_cmake_core ament_cmake_export_dependencies ament_package
41 | CMAKE_ARGS ${extra_cmake_args})
42 |
43 | dep_build(ament_cmake_export_include_directories CMAKE
44 | SOURCE_DIR "deps/ament_cmake/ament_cmake_export_include_directories"
45 | DEPENDENCIES ament_cmake_core ament_package
46 | CMAKE_ARGS ${extra_cmake_args})
47 |
48 | dep_build(ament_cmake_export_interfaces CMAKE
49 | SOURCE_DIR "deps/ament_cmake/ament_cmake_export_interfaces"
50 | DEPENDENCIES ament_cmake_core ament_package
51 | CMAKE_ARGS ${extra_cmake_args})
52 |
53 | dep_build(ament_cmake_export_libraries CMAKE
54 | SOURCE_DIR "deps/ament_cmake/ament_cmake_export_libraries"
55 | DEPENDENCIES ament_cmake_core ament_package
56 | CMAKE_ARGS ${extra_cmake_args})
57 |
58 | dep_build(ament_cmake_export_link_flags CMAKE
59 | SOURCE_DIR "deps/ament_cmake/ament_cmake_export_link_flags"
60 | DEPENDENCIES ament_cmake_core ament_package
61 | CMAKE_ARGS ${extra_cmake_args})
62 |
63 | dep_build(ament_cmake_export_targets CMAKE
64 | SOURCE_DIR "deps/ament_cmake/ament_cmake_export_targets"
65 | DEPENDENCIES ament_cmake_core ament_package
66 | CMAKE_ARGS ${extra_cmake_args})
67 |
68 | dep_build(ament_cmake_gmock CMAKE
69 | SOURCE_DIR "deps/ament_cmake/ament_cmake_gmock"
70 | DEPENDENCIES ament_cmake_core ament_package
71 | CMAKE_ARGS ${extra_cmake_args})
72 |
73 | dep_build(ament_cmake_gtest CMAKE
74 | SOURCE_DIR "deps/ament_cmake/ament_cmake_gtest"
75 | DEPENDENCIES ament_cmake_core ament_package
76 | CMAKE_ARGS ${extra_cmake_args})
77 |
78 | dep_build(ament_cmake_gen_version_h CMAKE
79 | SOURCE_DIR "deps/ament_cmake/ament_cmake_gen_version_h"
80 | DEPENDENCIES ament_cmake_core ament_package
81 | CMAKE_ARGS ${extra_cmake_args})
82 |
83 | dep_build(ament_cmake_include_directories CMAKE
84 | SOURCE_DIR "deps/ament_cmake/ament_cmake_include_directories"
85 | DEPENDENCIES ament_cmake_core ament_package
86 | CMAKE_ARGS ${extra_cmake_args})
87 |
88 | dep_build(ament_cmake_libraries CMAKE
89 | SOURCE_DIR "deps/ament_cmake/ament_cmake_libraries"
90 | DEPENDENCIES ament_cmake_core ament_package
91 | CMAKE_ARGS ${extra_cmake_args})
92 |
93 | dep_build(ament_cmake_nose CMAKE
94 | SOURCE_DIR "deps/ament_cmake/ament_cmake_nose"
95 | DEPENDENCIES ament_cmake_core ament_package
96 | CMAKE_ARGS ${extra_cmake_args})
97 |
98 | dep_build(ament_cmake_pytest CMAKE
99 | SOURCE_DIR "deps/ament_cmake/ament_cmake_pytest"
100 | DEPENDENCIES ament_cmake_core ament_package
101 | CMAKE_ARGS ${extra_cmake_args})
102 |
103 | dep_build(ament_cmake_python CMAKE
104 | SOURCE_DIR "deps/ament_cmake/ament_cmake_python"
105 | DEPENDENCIES ament_cmake_core ament_package
106 | CMAKE_ARGS ${extra_cmake_args})
107 |
108 | dep_build(ament_cmake_google_benchmark CMAKE
109 | SOURCE_DIR "deps/ament_cmake/ament_cmake_google_benchmark"
110 | DEPENDENCIES ament_cmake_core ament_cmake_python ament_cmake_export_dependencies ament_package
111 | CMAKE_ARGS ${extra_cmake_args})
112 |
113 | dep_build(ament_cmake_target_dependencies CMAKE
114 | SOURCE_DIR "deps/ament_cmake/ament_cmake_target_dependencies"
115 | DEPENDENCIES ament_cmake_core ament_package
116 | CMAKE_ARGS ${extra_cmake_args})
117 |
118 | dep_build(ament_cmake_test CMAKE
119 | SOURCE_DIR "deps/ament_cmake/ament_cmake_test"
120 | DEPENDENCIES ament_cmake_core ament_cmake_python ament_package
121 | CMAKE_ARGS ${extra_cmake_args})
122 |
123 | dep_build(ament_cmake_version CMAKE
124 | SOURCE_DIR "deps/ament_cmake/ament_cmake_version"
125 | DEPENDENCIES ament_cmake_core ament_package
126 | CMAKE_ARGS ${extra_cmake_args})
127 |
128 | dep_build(ament_cmake_auto CMAKE
129 | SOURCE_DIR "deps/ament_cmake/ament_cmake_auto"
130 | DEPENDENCIES ament_cmake_export_include_directories ament_cmake_export_link_flags ament_cmake_include_directories ament_cmake_export_interfaces ament_cmake_export_libraries ament_cmake_core ament_cmake_version ament_cmake_test ament_cmake_python ament_cmake_export_dependencies ament_cmake_target_dependencies ament_cmake_gtest ament_cmake_gen_version_h ament_cmake ament_cmake_export_definitions ament_cmake_export_targets ament_package
131 | CMAKE_ARGS ${extra_cmake_args})
132 |
133 | dep_build(ament_index_cpp CMAKE
134 | SOURCE_DIR "deps/ament_index/ament_index_cpp"
135 | DEPENDENCIES ament_cmake_export_include_directories ament_cmake_export_link_flags ament_cmake_include_directories ament_cmake_export_interfaces ament_cmake_export_libraries ament_cmake_core ament_cmake_version ament_cmake_test ament_cmake_python ament_cmake_export_dependencies ament_cmake_target_dependencies ament_cmake_gen_version_h ament_cmake ament_cmake_export_definitions ament_cmake_export_targets ament_package
136 | CMAKE_ARGS ${extra_cmake_args})
137 |
138 | dep_build(common_interfaces CMAKE
139 | SOURCE_DIR "deps/common_interfaces/common_interfaces"
140 | DEPENDENCIES ament_cmake_export_include_directories ament_cmake_export_link_flags ament_cmake_include_directories ament_cmake_export_interfaces ament_cmake_export_libraries ament_cmake_core ament_cmake_version ament_cmake_test ament_cmake_python ament_cmake_export_dependencies ament_cmake_target_dependencies ament_cmake_gen_version_h ament_cmake ament_cmake_export_definitions ament_cmake_export_targets ament_package
141 | CMAKE_ARGS ${extra_cmake_args})
142 |
143 | dep_build(domain_coordinator PIP
144 | SOURCE_DIR "deps/ament_cmake_ros/domain_coordinator"
145 | DEPENDENCIES )
146 |
147 | dep_build(ament_cmake_ros CMAKE
148 | SOURCE_DIR "deps/ament_cmake_ros/ament_cmake_ros"
149 | DEPENDENCIES ament_cmake_export_include_directories ament_cmake_export_link_flags ament_cmake_include_directories ament_cmake_export_interfaces ament_cmake_export_libraries ament_cmake_core ament_cmake_version ament_cmake_test ament_cmake_python ament_cmake_export_dependencies ament_cmake_target_dependencies ament_cmake_gen_version_h domain_coordinator ament_cmake ament_cmake_export_definitions ament_cmake_export_targets ament_package
150 | CMAKE_ARGS ${extra_cmake_args})
151 |
152 | dep_build(cyclonedds CMAKE
153 | SOURCE_DIR "deps/cyclonedds"
154 | DEPENDENCIES native-cyclonedds
155 | CMAKE_ARGS
156 | # ${extra_cmake_args} # Can't build this one statically
157 | ${android_cmake_args}
158 | # allow finding native ddsconf tool
159 | "-DCMAKE_PREFIX_PATH=${CMAKE_CURRENT_BINARY_DIR}/native-deps/native-cyclonedds"
160 | # TODO(sloretz) is SSL required for sros2? if so, figure out how to enable
161 | "-DENABLE_SSL=OFF")
162 |
163 | dep_build(rcutils CMAKE
164 | SOURCE_DIR "deps/rcutils"
165 | DEPENDENCIES ament_cmake_pytest ament_cmake_export_include_directories ament_cmake_export_link_flags ament_cmake_include_directories ament_cmake_export_interfaces ament_cmake_export_libraries ament_cmake_core ament_cmake_version ament_cmake_test ament_cmake_python ament_cmake_ros ament_cmake_export_dependencies ament_cmake_target_dependencies ament_cmake_gtest ament_cmake_gen_version_h domain_coordinator ament_cmake ament_cmake_gmock ament_cmake_export_definitions ament_cmake_export_targets ament_package
166 | CMAKE_ARGS ${extra_cmake_args})
167 |
168 | dep_build(rcpputils CMAKE
169 | SOURCE_DIR "deps/rcpputils"
170 | DEPENDENCIES ament_cmake_pytest ament_cmake_export_include_directories ament_cmake_export_link_flags ament_cmake_include_directories ament_cmake_export_interfaces ament_cmake_export_libraries ament_cmake_core ament_cmake_version ament_cmake_test ament_cmake_python rcutils ament_cmake_ros ament_cmake_export_dependencies ament_cmake_target_dependencies ament_cmake_gtest ament_cmake_gen_version_h domain_coordinator ament_cmake ament_cmake_gmock ament_cmake_export_definitions ament_cmake_export_targets ament_package
171 | CMAKE_ARGS ${extra_cmake_args})
172 |
173 | dep_build(libyaml_vendor CMAKE
174 | SOURCE_DIR "deps/libyaml_vendor"
175 | DEPENDENCIES ament_cmake_export_include_directories ament_cmake_export_link_flags ament_cmake_include_directories ament_cmake_export_interfaces ament_cmake_export_libraries ament_cmake_core ament_cmake_version ament_cmake_test ament_cmake_python ament_cmake_export_dependencies ament_cmake_target_dependencies ament_cmake_gen_version_h ament_cmake ament_cmake_export_definitions ament_cmake_export_targets ament_package
176 | CMAKE_ARGS ${extra_cmake_args})
177 |
178 | dep_build(rcl_logging_interface CMAKE
179 | SOURCE_DIR "deps/rcl_logging/rcl_logging_interface"
180 | DEPENDENCIES ament_cmake_pytest ament_cmake_export_include_directories ament_cmake_export_link_flags ament_cmake_include_directories ament_cmake_export_interfaces ament_cmake_export_libraries ament_cmake_core ament_cmake_version ament_cmake_test ament_cmake_python rcutils ament_cmake_ros ament_cmake_export_dependencies ament_cmake_target_dependencies ament_cmake_gtest ament_cmake_gen_version_h domain_coordinator ament_cmake ament_cmake_gmock ament_cmake_export_definitions ament_cmake_export_targets ament_package
181 | CMAKE_ARGS ${extra_cmake_args})
182 |
183 | dep_build(rcl_logging_android CMAKE
184 | SOURCE_DIR "deps/rcl_logging_android"
185 | DEPENDENCIES ament_cmake_pytest ament_cmake_export_include_directories ament_cmake_export_link_flags ament_cmake_include_directories ament_cmake_export_interfaces ament_cmake_export_libraries ament_cmake_core ament_cmake_version ament_cmake_test ament_cmake_python rcutils ament_cmake_ros ament_cmake_export_dependencies ament_cmake_target_dependencies ament_cmake_gtest ament_cmake_gen_version_h domain_coordinator ament_cmake ament_cmake_gmock ament_cmake_export_definitions ament_cmake_export_targets rcl_logging_interface ament_package
186 | CMAKE_ARGS ${extra_cmake_args})
187 |
188 | dep_build(rmw_implementation_cmake CMAKE
189 | SOURCE_DIR "deps/rmw/rmw_implementation_cmake"
190 | DEPENDENCIES ament_cmake_export_include_directories ament_cmake_export_link_flags ament_cmake_include_directories ament_cmake_export_interfaces ament_cmake_export_libraries ament_cmake_core ament_cmake_version ament_cmake_test ament_cmake_python ament_cmake_export_dependencies ament_cmake_target_dependencies ament_cmake_gen_version_h ament_cmake ament_cmake_export_definitions ament_cmake_export_targets ament_package
191 | CMAKE_ARGS ${extra_cmake_args})
192 |
193 | dep_build(rosidl_adapter CMAKE
194 | SOURCE_DIR "deps/rosidl/rosidl_adapter"
195 | DEPENDENCIES ament_cmake_export_include_directories ament_cmake_export_link_flags ament_cmake_include_directories ament_cmake_export_interfaces ament_cmake_export_libraries ament_cmake_core ament_cmake_version ament_cmake_test ament_cmake_python ament_cmake_export_dependencies ament_cmake_target_dependencies ament_cmake_gen_version_h ament_cmake ament_cmake_export_definitions ament_cmake_export_targets ament_package
196 | CMAKE_ARGS ${extra_cmake_args})
197 |
198 | dep_build(rosidl_cli PIP
199 | SOURCE_DIR "deps/rosidl/rosidl_cli"
200 | DEPENDENCIES )
201 |
202 | dep_build(rosidl_cmake CMAKE
203 | SOURCE_DIR "deps/rosidl/rosidl_cmake"
204 | DEPENDENCIES ament_cmake_export_include_directories ament_cmake_export_link_flags ament_cmake_include_directories ament_cmake_export_interfaces ament_cmake_export_libraries ament_cmake_core ament_cmake_version ament_cmake_test ament_cmake_python ament_cmake_export_dependencies ament_cmake_target_dependencies ament_cmake_gen_version_h ament_cmake ament_cmake_export_definitions ament_cmake_export_targets ament_package
205 | CMAKE_ARGS ${extra_cmake_args})
206 |
207 | dep_build(rosidl_parser CMAKE
208 | SOURCE_DIR "deps/rosidl/rosidl_parser"
209 | DEPENDENCIES ament_cmake_export_include_directories ament_cmake_export_link_flags ament_cmake_include_directories ament_cmake_export_interfaces ament_cmake_export_libraries ament_cmake_core ament_cmake_version ament_cmake_test ament_cmake_python ament_cmake_export_dependencies ament_cmake_target_dependencies ament_cmake_gen_version_h ament_cmake ament_cmake_export_definitions ament_cmake_export_targets ament_package
210 | CMAKE_ARGS ${extra_cmake_args})
211 |
212 | dep_build(rosidl_typesupport_interface CMAKE
213 | SOURCE_DIR "deps/rosidl/rosidl_typesupport_interface"
214 | DEPENDENCIES ament_cmake_export_include_directories ament_cmake_export_link_flags ament_cmake_include_directories ament_cmake_export_interfaces ament_cmake_export_libraries ament_cmake_core ament_cmake_version ament_cmake_test ament_cmake_python ament_cmake_export_dependencies ament_cmake_target_dependencies ament_cmake_gen_version_h ament_cmake ament_cmake_export_definitions ament_cmake_export_targets ament_package
215 | CMAKE_ARGS ${extra_cmake_args})
216 |
217 | dep_build(rosidl_runtime_c CMAKE
218 | SOURCE_DIR "deps/rosidl/rosidl_runtime_c"
219 | DEPENDENCIES rosidl_typesupport_interface ament_cmake_pytest ament_cmake_export_include_directories ament_cmake_export_link_flags ament_cmake_include_directories ament_cmake_export_interfaces ament_cmake_export_libraries ament_cmake_core ament_cmake_version ament_cmake_test ament_cmake_python rcutils ament_cmake_ros ament_cmake_export_dependencies ament_cmake_target_dependencies ament_cmake_gtest ament_cmake_gen_version_h domain_coordinator ament_cmake ament_cmake_gmock ament_cmake_export_definitions ament_cmake_export_targets ament_package
220 | CMAKE_ARGS ${extra_cmake_args})
221 |
222 | dep_build(rmw CMAKE
223 | SOURCE_DIR "deps/rmw/rmw"
224 | DEPENDENCIES rosidl_typesupport_interface ament_cmake_pytest ament_cmake_export_include_directories ament_cmake_export_link_flags ament_cmake_include_directories ament_cmake_export_interfaces ament_cmake_export_libraries ament_cmake_core ament_cmake_version ament_cmake_test rcutils ament_cmake_python rosidl_runtime_c ament_cmake_ros ament_cmake_export_dependencies ament_cmake_gtest ament_cmake_target_dependencies ament_cmake_gen_version_h domain_coordinator ament_cmake ament_cmake_gmock ament_cmake_export_definitions ament_cmake_export_targets ament_package
225 | CMAKE_ARGS ${extra_cmake_args})
226 |
227 | dep_build(rcl_yaml_param_parser CMAKE
228 | SOURCE_DIR "deps/rcl/rcl_yaml_param_parser"
229 | DEPENDENCIES rosidl_typesupport_interface ament_cmake_pytest ament_cmake_export_include_directories ament_cmake_export_link_flags ament_cmake_include_directories ament_cmake_export_interfaces ament_cmake_export_libraries ament_cmake_core ament_cmake_version ament_cmake_test rcutils ament_cmake_python libyaml_vendor rosidl_runtime_c ament_cmake_ros ament_cmake_export_dependencies ament_cmake_target_dependencies ament_cmake_gtest ament_cmake_gen_version_h domain_coordinator ament_cmake ament_cmake_gmock ament_cmake_export_definitions rmw ament_cmake_export_targets ament_package
230 | CMAKE_ARGS ${extra_cmake_args})
231 |
232 | dep_build(rosidl_generator_c CMAKE
233 | SOURCE_DIR "deps/rosidl/rosidl_generator_c"
234 | DEPENDENCIES ament_cmake_pytest ament_cmake_export_include_directories ament_cmake_export_link_flags ament_cmake_include_directories ament_cmake_export_interfaces ament_cmake_export_libraries ament_cmake_core ament_cmake_version ament_cmake_test ament_cmake_python ament_cmake_ros ament_cmake_export_dependencies ament_cmake_target_dependencies ament_cmake_gtest ament_cmake_gen_version_h domain_coordinator ament_cmake ament_cmake_gmock ament_cmake_export_definitions ament_cmake_export_targets ament_package
235 | CMAKE_ARGS ${extra_cmake_args})
236 |
237 | dep_build(rosidl_runtime_cpp CMAKE
238 | SOURCE_DIR "deps/rosidl/rosidl_runtime_cpp"
239 | DEPENDENCIES rosidl_typesupport_interface ament_cmake_export_include_directories ament_cmake_export_link_flags ament_cmake_include_directories ament_cmake_export_interfaces ament_cmake_export_libraries ament_cmake_core ament_cmake_version ament_cmake_test rcutils ament_cmake_python rosidl_runtime_c ament_cmake_export_dependencies ament_cmake_target_dependencies ament_cmake_gen_version_h ament_cmake ament_cmake_export_definitions ament_cmake_export_targets ament_package
240 | CMAKE_ARGS ${extra_cmake_args})
241 |
242 | dep_build(rosidl_generator_cpp CMAKE
243 | SOURCE_DIR "deps/rosidl/rosidl_generator_cpp"
244 | DEPENDENCIES ament_cmake_export_include_directories ament_cmake_export_link_flags ament_cmake_include_directories ament_cmake_export_interfaces ament_cmake_export_libraries ament_cmake_core ament_cmake_version ament_cmake_test ament_cmake_python ament_cmake_export_dependencies ament_cmake_target_dependencies ament_cmake_gen_version_h ament_cmake ament_cmake_export_definitions ament_cmake_export_targets ament_package
245 | CMAKE_ARGS ${extra_cmake_args})
246 |
247 | dep_build(rosidl_typesupport_introspection_c CMAKE
248 | SOURCE_DIR "deps/rosidl/rosidl_typesupport_introspection_c"
249 | DEPENDENCIES ament_cmake_pytest ament_cmake_export_include_directories ament_cmake_export_link_flags ament_cmake_include_directories ament_cmake_export_interfaces ament_cmake_export_libraries ament_cmake_core ament_cmake_version ament_cmake_test ament_cmake_python ament_cmake_ros ament_cmake_export_dependencies ament_cmake_target_dependencies ament_cmake_gtest ament_cmake_gen_version_h domain_coordinator ament_cmake ament_cmake_gmock ament_cmake_export_definitions ament_cmake_export_targets ament_package
250 | CMAKE_ARGS ${extra_cmake_args})
251 |
252 | dep_build(rosidl_typesupport_introspection_cpp CMAKE
253 | SOURCE_DIR "deps/rosidl/rosidl_typesupport_introspection_cpp"
254 | DEPENDENCIES rosidl_typesupport_interface rosidl_runtime_cpp ament_cmake_pytest ament_cmake_export_include_directories ament_cmake_export_link_flags ament_cmake_include_directories ament_cmake_export_interfaces ament_cmake_export_libraries ament_cmake_core ament_cmake_version ament_cmake_test rcutils ament_cmake_python rosidl_runtime_c ament_cmake_ros ament_cmake_export_dependencies ament_cmake_gtest ament_cmake_target_dependencies ament_cmake_gen_version_h domain_coordinator ament_cmake ament_cmake_gmock ament_cmake_export_definitions rosidl_cmake rosidl_typesupport_introspection_c ament_cmake_export_targets ament_package
255 | CMAKE_ARGS ${extra_cmake_args})
256 |
257 | dep_build(rosidl_typesupport_c CMAKE
258 | SOURCE_DIR "deps/rosidl_typesupport/rosidl_typesupport_c"
259 | DEPENDENCIES rosidl_typesupport_interface ament_cmake_pytest ament_cmake_export_include_directories ament_cmake_export_link_flags ament_cmake_include_directories ament_cmake_export_interfaces ament_cmake_export_libraries ament_cmake_core ament_cmake_version ament_cmake_test rcutils ament_cmake_python rosidl_runtime_c ament_cmake_ros ament_cmake_export_dependencies ament_cmake_gtest ament_cmake_target_dependencies ament_cmake_gen_version_h domain_coordinator ament_cmake ament_cmake_gmock ament_cmake_export_definitions rosidl_cmake rosidl_typesupport_introspection_c ament_cmake_export_targets rcpputils ament_package
260 | CMAKE_ARGS ${extra_cmake_args})
261 |
262 | dep_build(rosidl_typesupport_cpp CMAKE
263 | SOURCE_DIR "deps/rosidl_typesupport/rosidl_typesupport_cpp"
264 | DEPENDENCIES rosidl_runtime_cpp rosidl_typesupport_interface ament_cmake_pytest ament_cmake_export_include_directories ament_cmake_export_link_flags ament_cmake_include_directories ament_cmake_export_interfaces ament_cmake_export_libraries ament_cmake_core rosidl_typesupport_introspection_cpp ament_cmake_version ament_cmake_test rcutils ament_cmake_python rosidl_runtime_c ament_cmake_ros ament_cmake_export_dependencies ament_cmake_target_dependencies ament_cmake_gen_version_h domain_coordinator ament_cmake_gtest ament_cmake ament_cmake_gmock ament_cmake_export_definitions rosidl_typesupport_c rosidl_cmake ament_cmake_export_targets rcpputils ament_package
265 | # Hack, need a typesupport using rosidl_typesupport_c to exist before cpp typesupport can be built
266 | # rosidl_typesupport_introspection_c
267 | CMAKE_ARGS ${extra_cmake_args})
268 |
269 | dep_build(rosidl_default_generators CMAKE
270 | SOURCE_DIR "deps/rosidl_defaults/rosidl_default_generators"
271 | DEPENDENCIES ament_cmake_export_include_directories ament_cmake_export_link_flags ament_cmake_include_directories ament_cmake_export_interfaces ament_cmake_export_libraries ament_cmake_core ament_cmake_version ament_cmake_test ament_cmake_python ament_cmake_export_dependencies ament_cmake_target_dependencies ament_cmake_gen_version_h ament_cmake ament_cmake_export_definitions ament_cmake_export_targets ament_package
272 | CMAKE_ARGS ${extra_cmake_args})
273 |
274 | dep_build(builtin_interfaces CMAKE
275 | SOURCE_DIR "deps/rcl_interfaces/builtin_interfaces"
276 | DEPENDENCIES rosidl_runtime_cpp rosidl_typesupport_interface ament_cmake_export_include_directories ament_cmake_export_link_flags ament_cmake_include_directories ament_cmake_export_interfaces ament_cmake_export_libraries ament_cmake_core rosidl_typesupport_introspection_cpp ament_cmake_version ament_cmake_test rcutils ament_cmake_python rosidl_runtime_c ament_cmake_export_dependencies ament_cmake_target_dependencies ament_cmake_gen_version_h ament_cmake ament_cmake_export_definitions rosidl_typesupport_c rosidl_cmake rosidl_typesupport_introspection_c rosidl_generator_cpp ament_cmake_export_targets rosidl_typesupport_cpp rcpputils rosidl_generator_c rosidl_default_generators ament_package
277 | CMAKE_ARGS ${extra_cmake_args})
278 |
279 | dep_build(lifecycle_msgs CMAKE
280 | SOURCE_DIR "deps/rcl_interfaces/lifecycle_msgs"
281 | DEPENDENCIES rosidl_runtime_cpp rosidl_typesupport_interface ament_cmake_export_include_directories ament_cmake_export_link_flags ament_cmake_include_directories ament_cmake_export_interfaces ament_cmake_export_libraries ament_cmake_core rosidl_typesupport_introspection_cpp ament_cmake_version ament_cmake_test rcutils ament_cmake_python rosidl_runtime_c ament_cmake_export_dependencies ament_cmake_target_dependencies ament_cmake_gen_version_h ament_cmake ament_cmake_export_definitions rosidl_typesupport_c rosidl_cmake rosidl_typesupport_introspection_c rosidl_generator_cpp ament_cmake_export_targets rosidl_typesupport_cpp rcpputils rosidl_generator_c rosidl_default_generators ament_package
282 | CMAKE_ARGS ${extra_cmake_args})
283 |
284 | dep_build(rmw_dds_common CMAKE
285 | SOURCE_DIR "deps/rmw_dds_common/rmw_dds_common"
286 | DEPENDENCIES rosidl_runtime_cpp rosidl_typesupport_interface ament_cmake_export_include_directories ament_cmake_export_link_flags ament_cmake_include_directories ament_cmake_export_interfaces ament_cmake_export_libraries ament_cmake_core ament_cmake_version rosidl_typesupport_introspection_cpp ament_cmake_test rcutils ament_cmake_python rosidl_runtime_c ament_cmake_export_dependencies ament_cmake_target_dependencies ament_cmake_gen_version_h ament_cmake ament_cmake_export_definitions rosidl_typesupport_c rosidl_cmake rosidl_typesupport_introspection_c rmw rosidl_generator_cpp ament_cmake_export_targets rosidl_typesupport_cpp rcpputils rosidl_generator_c rosidl_default_generators ament_package
287 | CMAKE_ARGS ${extra_cmake_args})
288 |
289 | dep_build(rosidl_default_runtime CMAKE
290 | SOURCE_DIR "deps/rosidl_defaults/rosidl_default_runtime"
291 | DEPENDENCIES ament_cmake_export_include_directories ament_cmake_export_link_flags ament_cmake_include_directories ament_cmake_export_interfaces ament_cmake_export_libraries ament_cmake_core ament_cmake_version ament_cmake_test ament_cmake_python ament_cmake_export_dependencies ament_cmake_target_dependencies ament_cmake_gen_version_h ament_cmake ament_cmake_export_definitions ament_cmake_export_targets ament_package
292 | CMAKE_ARGS ${extra_cmake_args})
293 |
294 | dep_build(rcl_interfaces CMAKE
295 | SOURCE_DIR "deps/rcl_interfaces/rcl_interfaces"
296 | DEPENDENCIES rosidl_runtime_cpp rosidl_typesupport_interface ament_cmake_export_include_directories ament_cmake_export_link_flags ament_cmake_include_directories ament_cmake_export_interfaces ament_cmake_export_libraries ament_cmake_core rosidl_typesupport_introspection_cpp ament_cmake_version ament_cmake_python ament_cmake_test rcutils rosidl_runtime_c ament_cmake_export_dependencies ament_cmake_target_dependencies ament_cmake_gen_version_h builtin_interfaces ament_cmake ament_cmake_export_definitions rosidl_typesupport_c rosidl_cmake rosidl_typesupport_introspection_c rosidl_generator_cpp ament_cmake_export_targets rosidl_typesupport_cpp rcpputils rosidl_generator_c rosidl_default_generators ament_package
297 | CMAKE_ARGS ${extra_cmake_args})
298 |
299 | dep_build(composition_interfaces CMAKE
300 | SOURCE_DIR "deps/rcl_interfaces/composition_interfaces"
301 | DEPENDENCIES rosidl_runtime_cpp rosidl_typesupport_interface ament_cmake_export_include_directories ament_cmake_export_link_flags ament_cmake_include_directories ament_cmake_export_interfaces ament_cmake_export_libraries ament_cmake_core rosidl_typesupport_introspection_cpp ament_cmake_version ament_cmake_python ament_cmake_test rcutils rcl_interfaces rosidl_runtime_c ament_cmake_export_dependencies ament_cmake_target_dependencies ament_cmake_gen_version_h builtin_interfaces ament_cmake ament_cmake_export_definitions rosidl_typesupport_c rosidl_cmake rosidl_typesupport_introspection_c rosidl_generator_cpp ament_cmake_export_targets rosidl_typesupport_cpp rcpputils rosidl_generator_c rosidl_default_generators ament_package
302 | CMAKE_ARGS ${extra_cmake_args})
303 |
304 | dep_build(rosgraph_msgs CMAKE
305 | SOURCE_DIR "deps/rcl_interfaces/rosgraph_msgs"
306 | DEPENDENCIES rosidl_runtime_cpp rosidl_typesupport_interface ament_cmake_export_include_directories ament_cmake_export_link_flags ament_cmake_include_directories ament_cmake_export_interfaces ament_cmake_export_libraries ament_cmake_core rosidl_typesupport_introspection_cpp ament_cmake_version ament_cmake_python ament_cmake_test rcutils rosidl_runtime_c ament_cmake_export_dependencies ament_cmake_target_dependencies ament_cmake_gen_version_h builtin_interfaces ament_cmake ament_cmake_export_definitions rosidl_typesupport_c rosidl_cmake rosidl_typesupport_introspection_c rosidl_generator_cpp ament_cmake_export_targets rosidl_typesupport_cpp rcpputils rosidl_generator_c rosidl_default_generators ament_package
307 | CMAKE_ARGS ${extra_cmake_args})
308 |
309 | dep_build(statistics_msgs CMAKE
310 | SOURCE_DIR "deps/rcl_interfaces/statistics_msgs"
311 | DEPENDENCIES rosidl_runtime_cpp rosidl_typesupport_interface ament_cmake_export_include_directories ament_cmake_export_link_flags ament_cmake_include_directories ament_cmake_export_interfaces ament_cmake_export_libraries ament_cmake_core rosidl_typesupport_introspection_cpp ament_cmake_version ament_cmake_python ament_cmake_test rcutils rosidl_runtime_c ament_cmake_export_dependencies ament_cmake_target_dependencies ament_cmake_gen_version_h builtin_interfaces ament_cmake ament_cmake_export_definitions rosidl_typesupport_c rosidl_cmake rosidl_typesupport_introspection_c rosidl_generator_cpp ament_cmake_export_targets rosidl_typesupport_cpp rcpputils rosidl_generator_c rosidl_default_generators ament_package
312 | CMAKE_ARGS ${extra_cmake_args})
313 |
314 | dep_build(std_msgs CMAKE
315 | SOURCE_DIR "deps/common_interfaces/std_msgs"
316 | DEPENDENCIES rosidl_runtime_cpp rosidl_typesupport_interface ament_cmake_export_include_directories ament_cmake_export_link_flags ament_cmake_include_directories ament_cmake_export_interfaces ament_cmake_export_libraries ament_cmake_core rosidl_typesupport_introspection_cpp ament_cmake_version ament_cmake_python ament_cmake_test rcutils rosidl_runtime_c ament_cmake_export_dependencies ament_cmake_target_dependencies ament_cmake_gen_version_h builtin_interfaces ament_cmake ament_cmake_export_definitions rosidl_typesupport_c rosidl_cmake rosidl_typesupport_introspection_c rosidl_generator_cpp ament_cmake_export_targets rosidl_typesupport_cpp rcpputils rosidl_generator_c rosidl_default_generators ament_package
317 | CMAKE_ARGS ${extra_cmake_args})
318 |
319 | dep_build(actionlib_msgs CMAKE
320 | SOURCE_DIR "deps/common_interfaces/actionlib_msgs"
321 | DEPENDENCIES rosidl_runtime_cpp rosidl_typesupport_interface ament_cmake_export_include_directories ament_cmake_export_link_flags ament_cmake_include_directories ament_cmake_export_interfaces std_msgs ament_cmake_export_libraries ament_cmake_core rosidl_typesupport_introspection_cpp ament_cmake_version ament_cmake_python ament_cmake_test rcutils rosidl_runtime_c ament_cmake_export_dependencies ament_cmake_target_dependencies ament_cmake_gen_version_h builtin_interfaces ament_cmake ament_cmake_export_definitions rosidl_typesupport_c rosidl_cmake rosidl_typesupport_introspection_c rosidl_generator_cpp ament_cmake_export_targets rosidl_typesupport_cpp rcpputils rosidl_generator_c rosidl_default_generators ament_package
322 | CMAKE_ARGS ${extra_cmake_args})
323 |
324 | dep_build(geometry_msgs CMAKE
325 | SOURCE_DIR "deps/common_interfaces/geometry_msgs"
326 | DEPENDENCIES rosidl_runtime_cpp rosidl_typesupport_interface ament_cmake_export_include_directories ament_cmake_export_link_flags ament_cmake_include_directories ament_cmake_export_interfaces std_msgs ament_cmake_export_libraries ament_cmake_core rosidl_typesupport_introspection_cpp ament_cmake_version ament_cmake_python ament_cmake_test rcutils rosidl_runtime_c ament_cmake_export_dependencies ament_cmake_target_dependencies ament_cmake_gen_version_h builtin_interfaces ament_cmake ament_cmake_export_definitions rosidl_typesupport_c rosidl_cmake rosidl_typesupport_introspection_c rosidl_generator_cpp ament_cmake_export_targets rosidl_typesupport_cpp rcpputils rosidl_generator_c rosidl_default_generators ament_package
327 | CMAKE_ARGS ${extra_cmake_args})
328 |
329 | dep_build(diagnostic_msgs CMAKE
330 | SOURCE_DIR "deps/common_interfaces/diagnostic_msgs"
331 | DEPENDENCIES rosidl_runtime_cpp rosidl_typesupport_interface geometry_msgs ament_cmake_export_include_directories ament_cmake_export_link_flags ament_cmake_include_directories ament_cmake_export_interfaces std_msgs ament_cmake_export_libraries ament_cmake_core rosidl_typesupport_introspection_cpp ament_cmake_version ament_cmake_python ament_cmake_test rcutils rosidl_runtime_c ament_cmake_export_dependencies ament_cmake_target_dependencies ament_cmake_gen_version_h builtin_interfaces ament_cmake ament_cmake_export_definitions rosidl_typesupport_c rosidl_cmake rosidl_typesupport_introspection_c rosidl_generator_cpp ament_cmake_export_targets rosidl_typesupport_cpp rcpputils rosidl_generator_c rosidl_default_generators ament_package
332 | CMAKE_ARGS ${extra_cmake_args})
333 |
334 | dep_build(nav_msgs CMAKE
335 | SOURCE_DIR "deps/common_interfaces/nav_msgs"
336 | DEPENDENCIES rosidl_runtime_cpp rosidl_typesupport_interface geometry_msgs ament_cmake_export_include_directories ament_cmake_export_link_flags ament_cmake_include_directories ament_cmake_export_interfaces std_msgs ament_cmake_export_libraries ament_cmake_core rosidl_typesupport_introspection_cpp ament_cmake_version ament_cmake_python ament_cmake_test rcutils rosidl_runtime_c ament_cmake_export_dependencies ament_cmake_target_dependencies ament_cmake_gen_version_h builtin_interfaces ament_cmake ament_cmake_export_definitions rosidl_typesupport_c rosidl_cmake rosidl_typesupport_introspection_c rosidl_generator_cpp ament_cmake_export_targets rosidl_typesupport_cpp rcpputils rosidl_generator_c rosidl_default_generators ament_package
337 | CMAKE_ARGS ${extra_cmake_args})
338 |
339 | dep_build(sensor_msgs CMAKE
340 | SOURCE_DIR "deps/common_interfaces/sensor_msgs"
341 | DEPENDENCIES rosidl_runtime_cpp rosidl_typesupport_interface geometry_msgs ament_cmake_export_include_directories ament_cmake_export_link_flags ament_cmake_include_directories ament_cmake_export_interfaces std_msgs ament_cmake_export_libraries ament_cmake_core rosidl_typesupport_introspection_cpp ament_cmake_version ament_cmake_python ament_cmake_test rcutils rosidl_runtime_c ament_cmake_export_dependencies ament_cmake_target_dependencies ament_cmake_gen_version_h builtin_interfaces ament_cmake ament_cmake_export_definitions rosidl_typesupport_c rosidl_cmake rosidl_typesupport_introspection_c rosidl_generator_cpp ament_cmake_export_targets rosidl_typesupport_cpp rcpputils rosidl_generator_c rosidl_default_generators ament_package
342 | CMAKE_ARGS ${extra_cmake_args})
343 |
344 | dep_build(shape_msgs CMAKE
345 | SOURCE_DIR "deps/common_interfaces/shape_msgs"
346 | DEPENDENCIES rosidl_runtime_cpp rosidl_typesupport_interface geometry_msgs ament_cmake_export_include_directories ament_cmake_export_link_flags ament_cmake_include_directories ament_cmake_export_interfaces std_msgs ament_cmake_export_libraries ament_cmake_core rosidl_typesupport_introspection_cpp ament_cmake_version ament_cmake_python ament_cmake_test rcutils rosidl_runtime_c ament_cmake_export_dependencies ament_cmake_target_dependencies ament_cmake_gen_version_h builtin_interfaces ament_cmake ament_cmake_export_definitions rosidl_typesupport_c rosidl_cmake rosidl_typesupport_introspection_c rosidl_generator_cpp ament_cmake_export_targets rosidl_typesupport_cpp rcpputils rosidl_generator_c rosidl_default_generators ament_package
347 | CMAKE_ARGS ${extra_cmake_args})
348 |
349 | dep_build(std_srvs CMAKE
350 | SOURCE_DIR "deps/common_interfaces/std_srvs"
351 | DEPENDENCIES rosidl_runtime_cpp rosidl_typesupport_interface ament_cmake_export_include_directories ament_cmake_export_link_flags ament_cmake_include_directories ament_cmake_export_interfaces ament_cmake_export_libraries ament_cmake_core rosidl_typesupport_introspection_cpp ament_cmake_version ament_cmake_test rcutils ament_cmake_python rosidl_runtime_c ament_cmake_export_dependencies ament_cmake_target_dependencies ament_cmake_gen_version_h ament_cmake ament_cmake_export_definitions rosidl_typesupport_c rosidl_cmake rosidl_typesupport_introspection_c rosidl_generator_cpp ament_cmake_export_targets rosidl_typesupport_cpp rcpputils rosidl_generator_c rosidl_default_generators ament_package
352 | CMAKE_ARGS ${extra_cmake_args})
353 |
354 | dep_build(stereo_msgs CMAKE
355 | SOURCE_DIR "deps/common_interfaces/stereo_msgs"
356 | DEPENDENCIES rosidl_runtime_cpp rosidl_typesupport_interface geometry_msgs ament_cmake_export_include_directories ament_cmake_export_link_flags ament_cmake_include_directories ament_cmake_export_interfaces std_msgs ament_cmake_export_libraries sensor_msgs ament_cmake_core rosidl_typesupport_introspection_cpp ament_cmake_version ament_cmake_python ament_cmake_test rcutils rosidl_runtime_c ament_cmake_export_dependencies ament_cmake_target_dependencies ament_cmake_gen_version_h builtin_interfaces ament_cmake ament_cmake_export_definitions rosidl_typesupport_c rosidl_cmake rosidl_typesupport_introspection_c rosidl_generator_cpp ament_cmake_export_targets rosidl_typesupport_cpp rcpputils rosidl_generator_c rosidl_default_generators ament_package
357 | CMAKE_ARGS ${extra_cmake_args})
358 |
359 | dep_build(tracetools CMAKE
360 | SOURCE_DIR "deps/ros2_tracing/tracetools"
361 | DEPENDENCIES ament_cmake_pytest ament_cmake_export_include_directories ament_cmake_export_link_flags ament_cmake_include_directories ament_cmake_export_interfaces ament_cmake_export_libraries ament_cmake_core ament_cmake_version ament_cmake_test ament_cmake_python ament_cmake_ros ament_cmake_export_dependencies ament_cmake_target_dependencies ament_cmake_gtest ament_cmake_gen_version_h domain_coordinator ament_cmake ament_cmake_gmock ament_cmake_export_definitions ament_cmake_export_targets ament_package
362 | CMAKE_ARGS ${extra_cmake_args})
363 |
364 | dep_build(rmw_cyclonedds_cpp CMAKE
365 | SOURCE_DIR "deps/rmw_cyclonedds/rmw_cyclonedds_cpp"
366 | DEPENDENCIES rosidl_runtime_cpp rosidl_typesupport_interface ament_cmake_pytest ament_cmake_export_include_directories tracetools ament_cmake_export_link_flags ament_cmake_include_directories ament_cmake_export_interfaces ament_cmake_export_libraries ament_cmake_core rosidl_typesupport_introspection_cpp ament_cmake_version ament_cmake_test rcutils ament_cmake_python rosidl_runtime_c ament_cmake_ros ament_cmake_export_dependencies ament_cmake_target_dependencies ament_cmake_gen_version_h domain_coordinator ament_cmake_gtest ament_cmake ament_cmake_gmock ament_cmake_export_definitions rosidl_cmake rosidl_typesupport_introspection_c rmw ament_cmake_export_targets rcpputils rmw_dds_common cyclonedds ament_package
367 | CMAKE_ARGS ${extra_cmake_args})
368 |
369 | dep_build(rmw_implementation CMAKE
370 | SOURCE_DIR "deps/rmw_implementation/rmw_implementation"
371 | DEPENDENCIES rosidl_runtime_cpp rosidl_typesupport_interface ament_cmake_export_include_directories tracetools ament_cmake_export_link_flags ament_cmake_include_directories ament_cmake_export_interfaces ament_cmake_export_libraries rmw_implementation_cmake ament_index_cpp ament_cmake_core ament_cmake_version rosidl_typesupport_introspection_cpp ament_cmake_test rcutils ament_cmake_python rosidl_runtime_c ament_cmake_export_dependencies ament_cmake_target_dependencies ament_cmake_gen_version_h ament_cmake ament_cmake_export_definitions rmw_cyclonedds_cpp rosidl_cmake rosidl_typesupport_introspection_c rmw ament_cmake_export_targets rcpputils rmw_dds_common cyclonedds ament_package
372 | CMAKE_ARGS ${extra_cmake_args})
373 |
374 | dep_build(rcl CMAKE
375 | SOURCE_DIR "deps/rcl/rcl"
376 | DEPENDENCIES rosidl_typesupport_interface rcl_yaml_param_parser ament_cmake_pytest ament_cmake_export_include_directories tracetools ament_cmake_export_link_flags ament_cmake_include_directories ament_cmake_export_interfaces ament_cmake_export_libraries rmw_implementation_cmake rmw_implementation ament_index_cpp ament_cmake_core ament_cmake_version ament_cmake_test rcutils ament_cmake_python rcl_interfaces libyaml_vendor rosidl_runtime_c ament_cmake_ros ament_cmake_export_dependencies ament_cmake_target_dependencies ament_cmake_gen_version_h domain_coordinator builtin_interfaces ament_cmake_gtest ament_cmake ament_cmake_gmock ament_cmake_export_definitions rmw ament_cmake_export_targets rcpputils rcl_logging_interface ament_package
377 | rcl_logging_android
378 | CMAKE_ARGS ${extra_cmake_args}
379 | -DRCL_LOGGING_IMPLEMENTATION=rcl_logging_android
380 | )
381 |
382 | dep_build(libstatistics_collector CMAKE
383 | SOURCE_DIR "deps/libstatistics_collector"
384 | DEPENDENCIES rosidl_typesupport_interface rcl_yaml_param_parser ament_cmake_pytest rosidl_runtime_cpp ament_cmake_export_include_directories tracetools ament_cmake_export_link_flags ament_cmake_include_directories ament_cmake_export_interfaces statistics_msgs std_msgs ament_cmake_export_libraries rmw_implementation_cmake rmw_implementation ament_index_cpp ament_cmake_core ament_cmake_version rosidl_typesupport_introspection_cpp ament_cmake_python ament_cmake_test rcutils rcl_interfaces libyaml_vendor rosidl_runtime_c ament_cmake_ros ament_cmake_export_dependencies ament_cmake_target_dependencies ament_cmake_gen_version_h domain_coordinator builtin_interfaces rcl ament_cmake_gtest ament_cmake ament_cmake_gmock ament_cmake_export_definitions rosidl_typesupport_c rosidl_cmake rosidl_typesupport_introspection_c rmw rosidl_generator_cpp ament_cmake_export_targets rosidl_typesupport_cpp rcpputils rosidl_generator_c rosidl_default_generators rcl_logging_interface ament_package
385 | CMAKE_ARGS ${extra_cmake_args})
386 |
387 | dep_build(rcl_lifecycle CMAKE
388 | SOURCE_DIR "deps/rcl/rcl_lifecycle"
389 | DEPENDENCIES rosidl_typesupport_interface rcl_yaml_param_parser ament_cmake_pytest lifecycle_msgs ament_cmake_export_include_directories tracetools ament_cmake_export_link_flags ament_cmake_include_directories ament_cmake_export_interfaces ament_cmake_export_libraries rmw_implementation_cmake rmw_implementation ament_index_cpp ament_cmake_core ament_cmake_version ament_cmake_test rcutils ament_cmake_python rcl_interfaces libyaml_vendor rosidl_runtime_c ament_cmake_ros ament_cmake_export_dependencies ament_cmake_target_dependencies ament_cmake_gen_version_h domain_coordinator builtin_interfaces rcl ament_cmake_gtest ament_cmake ament_cmake_gmock ament_cmake_export_definitions rmw ament_cmake_export_targets rcpputils rcl_logging_interface ament_package
390 | CMAKE_ARGS ${extra_cmake_args})
391 |
392 | dep_build(rclcpp CMAKE
393 | SOURCE_DIR "deps/rclcpp/rclcpp"
394 | DEPENDENCIES rosidl_typesupport_interface rcl_yaml_param_parser rosidl_runtime_cpp ament_cmake_pytest ament_cmake_export_include_directories tracetools rosgraph_msgs ament_cmake_export_link_flags ament_cmake_include_directories ament_cmake_export_interfaces statistics_msgs ament_cmake_export_libraries rmw_implementation_cmake rmw_implementation ament_index_cpp ament_cmake_core ament_cmake_version ament_cmake_test ament_cmake_python rcutils rcl_interfaces libyaml_vendor rosidl_runtime_c ament_cmake_ros ament_cmake_export_dependencies ament_cmake_target_dependencies ament_cmake_gen_version_h domain_coordinator builtin_interfaces rcl ament_cmake_gtest ament_cmake ament_cmake_gmock ament_cmake_export_definitions rosidl_typesupport_c rmw ament_cmake_export_targets rosidl_typesupport_cpp libstatistics_collector rcpputils rcl_logging_interface ament_package
395 | CMAKE_ARGS ${extra_cmake_args})
396 |
397 | dep_build(rclcpp_lifecycle CMAKE
398 | SOURCE_DIR "deps/rclcpp/rclcpp_lifecycle"
399 | DEPENDENCIES rclcpp rosidl_typesupport_interface rcl_yaml_param_parser rosidl_runtime_cpp ament_cmake_pytest lifecycle_msgs ament_cmake_export_include_directories tracetools rosgraph_msgs ament_cmake_export_link_flags ament_cmake_include_directories ament_cmake_export_interfaces statistics_msgs rcl_lifecycle ament_cmake_export_libraries rmw_implementation_cmake rmw_implementation ament_index_cpp ament_cmake_core ament_cmake_version ament_cmake_test rcutils ament_cmake_python rcl_interfaces libyaml_vendor rosidl_runtime_c ament_cmake_ros ament_cmake_export_dependencies ament_cmake_target_dependencies ament_cmake_gen_version_h domain_coordinator builtin_interfaces rcl ament_cmake_gtest ament_cmake ament_cmake_gmock ament_cmake_export_definitions rosidl_typesupport_c rmw ament_cmake_export_targets rosidl_typesupport_cpp libstatistics_collector rcpputils rcl_logging_interface ament_package
400 | CMAKE_ARGS ${extra_cmake_args})
401 |
402 | dep_build(trajectory_msgs CMAKE
403 | SOURCE_DIR "deps/common_interfaces/trajectory_msgs"
404 | DEPENDENCIES rosidl_runtime_cpp rosidl_typesupport_interface geometry_msgs ament_cmake_export_include_directories ament_cmake_export_link_flags ament_cmake_include_directories ament_cmake_export_interfaces std_msgs ament_cmake_export_libraries ament_cmake_core rosidl_typesupport_introspection_cpp ament_cmake_version ament_cmake_python ament_cmake_test rcutils rosidl_runtime_c ament_cmake_export_dependencies ament_cmake_target_dependencies ament_cmake_gen_version_h builtin_interfaces ament_cmake ament_cmake_export_definitions rosidl_typesupport_c rosidl_cmake rosidl_typesupport_introspection_c rosidl_generator_cpp ament_cmake_export_targets rosidl_typesupport_cpp rcpputils rosidl_generator_c rosidl_default_generators ament_package
405 | CMAKE_ARGS ${extra_cmake_args})
406 |
407 | dep_build(visualization_msgs CMAKE
408 | SOURCE_DIR "deps/common_interfaces/visualization_msgs"
409 | DEPENDENCIES rosidl_runtime_cpp rosidl_typesupport_interface geometry_msgs ament_cmake_export_include_directories ament_cmake_export_link_flags ament_cmake_include_directories ament_cmake_export_interfaces std_msgs ament_cmake_export_libraries sensor_msgs ament_cmake_core rosidl_typesupport_introspection_cpp ament_cmake_version ament_cmake_python ament_cmake_test rcutils rosidl_runtime_c ament_cmake_export_dependencies ament_cmake_target_dependencies ament_cmake_gen_version_h builtin_interfaces ament_cmake ament_cmake_export_definitions rosidl_typesupport_c rosidl_cmake rosidl_typesupport_introspection_c rosidl_generator_cpp ament_cmake_export_targets rosidl_typesupport_cpp rcpputils rosidl_generator_c rosidl_default_generators ament_package
410 | CMAKE_ARGS ${extra_cmake_args})
411 | endmacro()
412 |
--------------------------------------------------------------------------------