├── 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 | --------------------------------------------------------------------------------