Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.
Path: blob/master/libraries/AP_DDS/AP_DDS_Client.cpp
Views: 1798
#include <AP_HAL/AP_HAL_Boards.h>12#include "AP_DDS_config.h"3#if AP_DDS_ENABLED4#include <uxr/client/util/ping.h>56#include <AP_GPS/AP_GPS.h>7#include <AP_HAL/AP_HAL.h>8#include <RC_Channel/RC_Channel.h>9#include <AP_RTC/AP_RTC.h>10#include <AP_Math/AP_Math.h>11#include <AP_InertialSensor/AP_InertialSensor.h>12#include <GCS_MAVLink/GCS.h>13#include <AP_BattMonitor/AP_BattMonitor.h>14#include <AP_AHRS/AP_AHRS.h>15#if AP_DDS_ARM_SERVER_ENABLED16#include <AP_Arming/AP_Arming.h>17# endif // AP_DDS_ARM_SERVER_ENABLED18#include <AP_Vehicle/AP_Vehicle.h>19#include <AP_Common/AP_FWVersion.h>20#include <AP_ExternalControl/AP_ExternalControl_config.h>2122#if AP_DDS_ARM_SERVER_ENABLED23#include "ardupilot_msgs/srv/ArmMotors.h"24#endif // AP_DDS_ARM_SERVER_ENABLED25#if AP_DDS_MODE_SWITCH_SERVER_ENABLED26#include "ardupilot_msgs/srv/ModeSwitch.h"27#endif // AP_DDS_MODE_SWITCH_SERVER_ENABLED28#if AP_DDS_ARM_CHECK_SERVER_ENABLED29#include "std_srvs/srv/Trigger.h"30#endif // AP_DDS_ARM_CHECK_SERVER_ENABLED31#if AP_DDS_VTOL_TAKEOFF_SERVER_ENABLED32#include "ardupilot_msgs/srv/Takeoff.h"33#endif // AP_DDS_VTOL_TAKEOFF_SERVER_ENABLED3435#if AP_EXTERNAL_CONTROL_ENABLED36#include "AP_DDS_ExternalControl.h"37#endif // AP_EXTERNAL_CONTROL_ENABLED38#include "AP_DDS_Frames.h"3940#include "AP_DDS_Client.h"41#include "AP_DDS_Topic_Table.h"42#include "AP_DDS_Service_Table.h"43#include "AP_DDS_External_Odom.h"4445#define STRCPY(D,S) strncpy(D, S, ARRAY_SIZE(D))4647// Enable DDS at runtime by default48static constexpr uint8_t ENABLED_BY_DEFAULT = 1;49#if AP_DDS_TIME_PUB_ENABLED50static constexpr uint16_t DELAY_TIME_TOPIC_MS = AP_DDS_DELAY_TIME_TOPIC_MS;51#endif // AP_DDS_TIME_PUB_ENABLED52#if AP_DDS_BATTERY_STATE_PUB_ENABLED53static constexpr uint16_t DELAY_BATTERY_STATE_TOPIC_MS = AP_DDS_DELAY_BATTERY_STATE_TOPIC_MS;54#endif // AP_DDS_BATTERY_STATE_PUB_ENABLED55#if AP_DDS_IMU_PUB_ENABLED56static constexpr uint16_t DELAY_IMU_TOPIC_MS = AP_DDS_DELAY_IMU_TOPIC_MS;57#endif // AP_DDS_IMU_PUB_ENABLED58#if AP_DDS_LOCAL_POSE_PUB_ENABLED59static constexpr uint16_t DELAY_LOCAL_POSE_TOPIC_MS = AP_DDS_DELAY_LOCAL_POSE_TOPIC_MS;60#endif // AP_DDS_LOCAL_POSE_PUB_ENABLED61#if AP_DDS_LOCAL_VEL_PUB_ENABLED62static constexpr uint16_t DELAY_LOCAL_VELOCITY_TOPIC_MS = AP_DDS_DELAY_LOCAL_VELOCITY_TOPIC_MS;63#endif // AP_DDS_LOCAL_VEL_PUB_ENABLED64#if AP_DDS_AIRSPEED_PUB_ENABLED65static constexpr uint16_t DELAY_AIRSPEED_TOPIC_MS = AP_DDS_DELAY_AIRSPEED_TOPIC_MS;66#endif // AP_DDS_AIRSPEED_PUB_ENABLED67#if AP_DDS_GEOPOSE_PUB_ENABLED68static constexpr uint16_t DELAY_GEO_POSE_TOPIC_MS = AP_DDS_DELAY_GEO_POSE_TOPIC_MS;69#endif // AP_DDS_GEOPOSE_PUB_ENABLED70#if AP_DDS_GOAL_PUB_ENABLED71static constexpr uint16_t DELAY_GOAL_TOPIC_MS = AP_DDS_DELAY_GOAL_TOPIC_MS ;72#endif // AP_DDS_GOAL_PUB_ENABLED73#if AP_DDS_CLOCK_PUB_ENABLED74static constexpr uint16_t DELAY_CLOCK_TOPIC_MS =AP_DDS_DELAY_CLOCK_TOPIC_MS;75#endif // AP_DDS_CLOCK_PUB_ENABLED76#if AP_DDS_GPS_GLOBAL_ORIGIN_PUB_ENABLED77static constexpr uint16_t DELAY_GPS_GLOBAL_ORIGIN_TOPIC_MS = AP_DDS_DELAY_GPS_GLOBAL_ORIGIN_TOPIC_MS;78#endif // AP_DDS_GPS_GLOBAL_ORIGIN_PUB_ENABLED79static constexpr uint16_t DELAY_PING_MS = 500;80#if AP_DDS_STATUS_PUB_ENABLED81static constexpr uint16_t DELAY_STATUS_TOPIC_MS = AP_DDS_DELAY_STATUS_TOPIC_MS;82#endif // AP_DDS_STATUS_PUB_ENABLED8384// Define the subscriber data members, which are static class scope.85// If these are created on the stack in the subscriber,86// the AP_DDS_Client::on_topic frame size is exceeded.87#if AP_DDS_JOY_SUB_ENABLED88sensor_msgs_msg_Joy AP_DDS_Client::rx_joy_topic {};89#endif // AP_DDS_JOY_SUB_ENABLED90#if AP_DDS_DYNAMIC_TF_SUB_ENABLED91tf2_msgs_msg_TFMessage AP_DDS_Client::rx_dynamic_transforms_topic {};92#endif // AP_DDS_DYNAMIC_TF_SUB_ENABLED93#if AP_DDS_VEL_CTRL_ENABLED94geometry_msgs_msg_TwistStamped AP_DDS_Client::rx_velocity_control_topic {};95#endif // AP_DDS_VEL_CTRL_ENABLED96#if AP_DDS_GLOBAL_POS_CTRL_ENABLED97ardupilot_msgs_msg_GlobalPosition AP_DDS_Client::rx_global_position_control_topic {};98#endif // AP_DDS_GLOBAL_POS_CTRL_ENABLED99100// Define the parameter server data members, which are static class scope.101// If these are created on the stack, then the AP_DDS_Client::on_request102// frame size is exceeded.103#if AP_DDS_PARAMETER_SERVER_ENABLED104rcl_interfaces_srv_SetParameters_Request AP_DDS_Client::set_parameter_request {};105rcl_interfaces_srv_SetParameters_Response AP_DDS_Client::set_parameter_response {};106rcl_interfaces_srv_GetParameters_Request AP_DDS_Client::get_parameters_request {};107rcl_interfaces_srv_GetParameters_Response AP_DDS_Client::get_parameters_response {};108rcl_interfaces_msg_Parameter AP_DDS_Client::param {};109#endif110111const AP_Param::GroupInfo AP_DDS_Client::var_info[] {112113// @Param: _ENABLE114// @DisplayName: DDS enable115// @Description: Enable DDS subsystem116// @Values: 0:Disabled,1:Enabled117// @RebootRequired: True118// @User: Advanced119AP_GROUPINFO_FLAGS("_ENABLE", 1, AP_DDS_Client, enabled, ENABLED_BY_DEFAULT, AP_PARAM_FLAG_ENABLE),120121#if AP_DDS_UDP_ENABLED122// @Param: _UDP_PORT123// @DisplayName: DDS UDP port124// @Description: UDP port number for DDS125// @Range: 1 65535126// @RebootRequired: True127// @User: Standard128AP_GROUPINFO("_UDP_PORT", 2, AP_DDS_Client, udp.port, 2019),129130// @Group: _IP131// @Path: ../AP_Networking/AP_Networking_address.cpp132AP_SUBGROUPINFO(udp.ip, "_IP", 3, AP_DDS_Client, AP_Networking_IPV4),133134#endif135136// @Param: _DOMAIN_ID137// @DisplayName: DDS DOMAIN ID138// @Description: Set the ROS_DOMAIN_ID139// @Range: 0 232140// @RebootRequired: True141// @User: Standard142AP_GROUPINFO("_DOMAIN_ID", 4, AP_DDS_Client, domain_id, 0),143144// @Param: _TIMEOUT_MS145// @DisplayName: DDS ping timeout146// @Description: The time in milliseconds the DDS client will wait for a response from the XRCE agent before reattempting.147// @Units: ms148// @Range: 1 10000149// @RebootRequired: True150// @Increment: 1151// @User: Standard152AP_GROUPINFO("_TIMEOUT_MS", 5, AP_DDS_Client, ping_timeout_ms, 1000),153154// @Param: _MAX_RETRY155// @DisplayName: DDS ping max attempts156// @Description: The maximum number of times the DDS client will attempt to ping the XRCE agent before exiting.157// @Range: 1 100158// @RebootRequired: True159// @Increment: 1160// @User: Standard161AP_GROUPINFO("_MAX_RETRY", 6, AP_DDS_Client, ping_max_retry, 10),162163AP_GROUPEND164};165166#if AP_DDS_STATIC_TF_PUB_ENABLED | AP_DDS_LOCAL_POSE_PUB_ENABLED | AP_DDS_GEOPOSE_PUB_ENABLED | AP_DDS_IMU_PUB_ENABLED167static void initialize(geometry_msgs_msg_Quaternion& q)168{169q.x = 0.0;170q.y = 0.0;171q.z = 0.0;172q.w = 1.0;173}174#endif // AP_DDS_STATIC_TF_PUB_ENABLED | AP_DDS_LOCAL_POSE_PUB_ENABLED | AP_DDS_GEOPOSE_PUB_ENABLED | AP_DDS_IMU_PUB_ENABLED175176AP_DDS_Client::~AP_DDS_Client()177{178// close transport179if (is_using_serial) {180uxr_close_custom_transport(&serial.transport);181} else {182#if AP_DDS_UDP_ENABLED183uxr_close_custom_transport(&udp.transport);184#endif185}186}187188#if AP_DDS_TIME_PUB_ENABLED189void AP_DDS_Client::update_topic(builtin_interfaces_msg_Time& msg)190{191uint64_t utc_usec;192if (!AP::rtc().get_utc_usec(utc_usec)) {193utc_usec = AP_HAL::micros64();194}195msg.sec = utc_usec / 1000000ULL;196msg.nanosec = (utc_usec % 1000000ULL) * 1000UL;197198}199#endif // AP_DDS_TIME_PUB_ENABLED200201#if AP_DDS_NAVSATFIX_PUB_ENABLED202bool AP_DDS_Client::update_topic(sensor_msgs_msg_NavSatFix& msg, const uint8_t instance)203{204// Add a lambda that takes in navsatfix msg and populates the cov205// Make it constexpr if possible206// https://www.fluentcpp.com/2021/12/13/the-evolutions-of-lambdas-in-c14-c17-and-c20/207// constexpr auto times2 = [] (sensor_msgs_msg_NavSatFix* msg) { return n * 2; };208209auto &gps = AP::gps();210WITH_SEMAPHORE(gps.get_semaphore());211212if (!gps.is_healthy(instance)) {213msg.status.status = -1; // STATUS_NO_FIX214msg.status.service = 0; // No services supported215msg.position_covariance_type = 0; // COVARIANCE_TYPE_UNKNOWN216return false;217}218219// No update is needed220const auto last_fix_time_ms = gps.last_fix_time_ms(instance);221if (last_nav_sat_fix_time_ms == last_fix_time_ms) {222return false;223} else {224last_nav_sat_fix_time_ms = last_fix_time_ms;225}226227228update_topic(msg.header.stamp);229static_assert(GPS_MAX_RECEIVERS <= 9, "GPS_MAX_RECEIVERS is greater than 9");230hal.util->snprintf(msg.header.frame_id, 2, "%u", instance);231msg.status.service = 0; // SERVICE_GPS232msg.status.status = -1; // STATUS_NO_FIX233234235//! @todo What about glonass, compass, galileo?236//! This will be properly designed and implemented to spec in #23277237msg.status.service = 1; // SERVICE_GPS238239const auto status = gps.status(instance);240switch (status) {241case AP_GPS::NO_GPS:242case AP_GPS::NO_FIX:243msg.status.status = -1; // STATUS_NO_FIX244msg.position_covariance_type = 0; // COVARIANCE_TYPE_UNKNOWN245return true;246case AP_GPS::GPS_OK_FIX_2D:247case AP_GPS::GPS_OK_FIX_3D:248msg.status.status = 0; // STATUS_FIX249break;250case AP_GPS::GPS_OK_FIX_3D_DGPS:251msg.status.status = 1; // STATUS_SBAS_FIX252break;253case AP_GPS::GPS_OK_FIX_3D_RTK_FLOAT:254case AP_GPS::GPS_OK_FIX_3D_RTK_FIXED:255msg.status.status = 2; // STATUS_SBAS_FIX256break;257default:258//! @todo Can we not just use an enum class and not worry about this condition?259break;260}261const auto loc = gps.location(instance);262msg.latitude = loc.lat * 1E-7;263msg.longitude = loc.lng * 1E-7;264265int32_t alt_cm;266if (!loc.get_alt_cm(Location::AltFrame::ABSOLUTE, alt_cm)) {267// With absolute frame, this condition is unlikely268msg.status.status = -1; // STATUS_NO_FIX269msg.position_covariance_type = 0; // COVARIANCE_TYPE_UNKNOWN270return true;271}272msg.altitude = alt_cm * 0.01;273274// ROS allows double precision, ArduPilot exposes float precision today275Matrix3f cov;276msg.position_covariance_type = (uint8_t)gps.position_covariance(instance, cov);277msg.position_covariance[0] = cov[0][0];278msg.position_covariance[1] = cov[0][1];279msg.position_covariance[2] = cov[0][2];280msg.position_covariance[3] = cov[1][0];281msg.position_covariance[4] = cov[1][1];282msg.position_covariance[5] = cov[1][2];283msg.position_covariance[6] = cov[2][0];284msg.position_covariance[7] = cov[2][1];285msg.position_covariance[8] = cov[2][2];286287return true;288}289#endif // AP_DDS_NAVSATFIX_PUB_ENABLED290291#if AP_DDS_STATIC_TF_PUB_ENABLED292void AP_DDS_Client::populate_static_transforms(tf2_msgs_msg_TFMessage& msg)293{294msg.transforms_size = 0;295296auto &gps = AP::gps();297for (uint8_t i = 0; i < GPS_MAX_RECEIVERS; i++) {298const auto gps_type = gps.get_type(i);299if (gps_type == AP_GPS::GPS_Type::GPS_TYPE_NONE) {300continue;301}302update_topic(msg.transforms[i].header.stamp);303char gps_frame_id[16];304//! @todo should GPS frame ID's be 0 or 1 indexed in ROS?305hal.util->snprintf(gps_frame_id, sizeof(gps_frame_id), "GPS_%u", i);306STRCPY(msg.transforms[i].header.frame_id, BASE_LINK_FRAME_ID);307STRCPY(msg.transforms[i].child_frame_id, gps_frame_id);308// The body-frame offsets309// X - Forward310// Y - Right311// Z - Down312// https://ardupilot.org/copter/docs/common-sensor-offset-compensation.html#sensor-position-offset-compensation313314const auto offset = gps.get_antenna_offset(i);315316// In ROS REP 103, it follows this convention317// X - Forward318// Y - Left319// Z - Up320// https://www.ros.org/reps/rep-0103.html#axis-orientation321322msg.transforms[i].transform.translation.x = offset[0];323msg.transforms[i].transform.translation.y = -1 * offset[1];324msg.transforms[i].transform.translation.z = -1 * offset[2];325326// Ensure rotation is initialized.327initialize(msg.transforms[i].transform.rotation);328329msg.transforms_size++;330}331332}333#endif // AP_DDS_STATIC_TF_PUB_ENABLED334335#if AP_DDS_BATTERY_STATE_PUB_ENABLED336void AP_DDS_Client::update_topic(sensor_msgs_msg_BatteryState& msg, const uint8_t instance)337{338if (instance >= AP_BATT_MONITOR_MAX_INSTANCES) {339return;340}341static_assert(AP_BATT_MONITOR_MAX_INSTANCES <= 99, "AP_BATT_MONITOR_MAX_INSTANCES is greater than 99");342343update_topic(msg.header.stamp);344hal.util->snprintf(msg.header.frame_id, 2, "%u", instance);345346auto &battery = AP::battery();347348if (!battery.healthy(instance)) {349msg.power_supply_status = 3; //POWER_SUPPLY_HEALTH_DEAD350msg.present = false;351return;352}353msg.present = true;354355msg.voltage = battery.voltage(instance);356357float temperature;358msg.temperature = (battery.get_temperature(temperature, instance)) ? temperature : NAN;359360float current;361msg.current = (battery.current_amps(current, instance)) ? -1 * current : NAN;362363const float design_capacity = (float)battery.pack_capacity_mah(instance) * 0.001;364msg.design_capacity = design_capacity;365366uint8_t percentage;367if (battery.capacity_remaining_pct(percentage, instance)) {368msg.percentage = percentage * 0.01;369msg.charge = (percentage * design_capacity) * 0.01;370} else {371msg.percentage = NAN;372msg.charge = NAN;373}374375msg.capacity = NAN;376377if (battery.current_amps(current, instance)) {378if (percentage == 100) {379msg.power_supply_status = 4; //POWER_SUPPLY_STATUS_FULL380} else if (is_negative(current)) {381msg.power_supply_status = 1; //POWER_SUPPLY_STATUS_CHARGING382} else if (is_positive(current)) {383msg.power_supply_status = 2; //POWER_SUPPLY_STATUS_DISCHARGING384} else {385msg.power_supply_status = 3; //POWER_SUPPLY_STATUS_NOT_CHARGING386}387} else {388msg.power_supply_status = 0; //POWER_SUPPLY_STATUS_UNKNOWN389}390391msg.power_supply_health = (battery.overpower_detected(instance)) ? 4 : 1; //POWER_SUPPLY_HEALTH_OVERVOLTAGE or POWER_SUPPLY_HEALTH_GOOD392393msg.power_supply_technology = 0; //POWER_SUPPLY_TECHNOLOGY_UNKNOWN394395if (battery.has_cell_voltages(instance)) {396const auto &cells = battery.get_cell_voltages(instance);397const uint8_t ncells_max = MIN(ARRAY_SIZE(msg.cell_voltage), ARRAY_SIZE(cells.cells));398for (uint8_t i=0; i< ncells_max; i++) {399msg.cell_voltage[i] = cells.cells[i] * 0.001;400}401}402}403#endif // AP_DDS_BATTERY_STATE_PUB_ENABLED404405#if AP_DDS_LOCAL_POSE_PUB_ENABLED406void AP_DDS_Client::update_topic(geometry_msgs_msg_PoseStamped& msg)407{408update_topic(msg.header.stamp);409STRCPY(msg.header.frame_id, BASE_LINK_FRAME_ID);410411auto &ahrs = AP::ahrs();412WITH_SEMAPHORE(ahrs.get_semaphore());413414// ROS REP 103 uses the ENU convention:415// X - East416// Y - North417// Z - Up418// https://www.ros.org/reps/rep-0103.html#axis-orientation419// AP_AHRS uses the NED convention420// X - North421// Y - East422// Z - Down423// As a consequence, to follow ROS REP 103, it is necessary to switch X and Y,424// as well as invert Z425426Vector3f position;427if (ahrs.get_relative_position_NED_home(position)) {428msg.pose.position.x = position[1];429msg.pose.position.y = position[0];430msg.pose.position.z = -position[2];431}432433// In ROS REP 103, axis orientation uses the following convention:434// X - Forward435// Y - Left436// Z - Up437// https://www.ros.org/reps/rep-0103.html#axis-orientation438// As a consequence, to follow ROS REP 103, it is necessary to switch X and Y,439// as well as invert Z (NED to ENU conversion) as well as a 90 degree rotation in the Z axis440// for x to point forward441Quaternion orientation;442if (ahrs.get_quaternion(orientation)) {443Quaternion aux(orientation[0], orientation[2], orientation[1], -orientation[3]); //NED to ENU transformation444Quaternion transformation (sqrtF(2) * 0.5,0,0,sqrtF(2) * 0.5); // Z axis 90 degree rotation445orientation = aux * transformation;446msg.pose.orientation.w = orientation[0];447msg.pose.orientation.x = orientation[1];448msg.pose.orientation.y = orientation[2];449msg.pose.orientation.z = orientation[3];450} else {451initialize(msg.pose.orientation);452}453}454#endif // AP_DDS_LOCAL_POSE_PUB_ENABLED455456#if AP_DDS_LOCAL_VEL_PUB_ENABLED457void AP_DDS_Client::update_topic(geometry_msgs_msg_TwistStamped& msg)458{459update_topic(msg.header.stamp);460STRCPY(msg.header.frame_id, BASE_LINK_FRAME_ID);461462auto &ahrs = AP::ahrs();463WITH_SEMAPHORE(ahrs.get_semaphore());464465// ROS REP 103 uses the ENU convention:466// X - East467// Y - North468// Z - Up469// https://www.ros.org/reps/rep-0103.html#axis-orientation470// AP_AHRS uses the NED convention471// X - North472// Y - East473// Z - Down474// As a consequence, to follow ROS REP 103, it is necessary to switch X and Y,475// as well as invert Z476Vector3f velocity;477if (ahrs.get_velocity_NED(velocity)) {478msg.twist.linear.x = velocity[1];479msg.twist.linear.y = velocity[0];480msg.twist.linear.z = -velocity[2];481}482483// In ROS REP 103, axis orientation uses the following convention:484// X - Forward485// Y - Left486// Z - Up487// https://www.ros.org/reps/rep-0103.html#axis-orientation488// The gyro data is received from AP_AHRS in body-frame489// X - Forward490// Y - Right491// Z - Down492// As a consequence, to follow ROS REP 103, it is necessary to invert Y and Z493Vector3f angular_velocity = ahrs.get_gyro();494msg.twist.angular.x = angular_velocity[0];495msg.twist.angular.y = -angular_velocity[1];496msg.twist.angular.z = -angular_velocity[2];497}498#endif // AP_DDS_LOCAL_VEL_PUB_ENABLED499#if AP_DDS_AIRSPEED_PUB_ENABLED500bool AP_DDS_Client::update_topic(geometry_msgs_msg_Vector3Stamped& msg)501{502update_topic(msg.header.stamp);503STRCPY(msg.header.frame_id, BASE_LINK_FRAME_ID);504auto &ahrs = AP::ahrs();505WITH_SEMAPHORE(ahrs.get_semaphore());506// In ROS REP 103, axis orientation uses the following convention:507// X - Forward508// Y - Left509// Z - Up510// https://www.ros.org/reps/rep-0103.html#axis-orientation511// The true airspeed data is received from AP_AHRS in body-frame512// X - Forward513// Y - Right514// Z - Down515// As a consequence, to follow ROS REP 103, it is necessary to invert Y and Z516Vector3f true_airspeed_vec_bf;517bool is_airspeed_available {false};518if (ahrs.airspeed_vector_true(true_airspeed_vec_bf)) {519msg.vector.x = true_airspeed_vec_bf[0];520msg.vector.y = -true_airspeed_vec_bf[1];521msg.vector.z = -true_airspeed_vec_bf[2];522is_airspeed_available = true;523}524return is_airspeed_available;525}526#endif // AP_DDS_AIRSPEED_PUB_ENABLED527528#if AP_DDS_GEOPOSE_PUB_ENABLED529void AP_DDS_Client::update_topic(geographic_msgs_msg_GeoPoseStamped& msg)530{531update_topic(msg.header.stamp);532STRCPY(msg.header.frame_id, BASE_LINK_FRAME_ID);533534auto &ahrs = AP::ahrs();535WITH_SEMAPHORE(ahrs.get_semaphore());536537Location loc;538if (ahrs.get_location(loc)) {539msg.pose.position.latitude = loc.lat * 1E-7;540msg.pose.position.longitude = loc.lng * 1E-7;541// TODO this is assumed to be absolute frame in WGS-84 as per the GeoPose message definition in ROS.542// Use loc.get_alt_frame() to convert if necessary.543msg.pose.position.altitude = loc.alt * 0.01; // Transform from cm to m544}545546// In ROS REP 103, axis orientation uses the following convention:547// X - Forward548// Y - Left549// Z - Up550// https://www.ros.org/reps/rep-0103.html#axis-orientation551// As a consequence, to follow ROS REP 103, it is necessary to switch X and Y,552// as well as invert Z (NED to ENU conversion) as well as a 90 degree rotation in the Z axis553// for x to point forward554Quaternion orientation;555if (ahrs.get_quaternion(orientation)) {556Quaternion aux(orientation[0], orientation[2], orientation[1], -orientation[3]); //NED to ENU transformation557Quaternion transformation(sqrtF(2) * 0.5, 0, 0, sqrtF(2) * 0.5); // Z axis 90 degree rotation558orientation = aux * transformation;559msg.pose.orientation.w = orientation[0];560msg.pose.orientation.x = orientation[1];561msg.pose.orientation.y = orientation[2];562msg.pose.orientation.z = orientation[3];563} else {564initialize(msg.pose.orientation);565}566}567#endif // AP_DDS_GEOPOSE_PUB_ENABLED568569#if AP_DDS_GOAL_PUB_ENABLED570bool AP_DDS_Client::update_topic_goal(geographic_msgs_msg_GeoPointStamped& msg)571{572const auto &vehicle = AP::vehicle();573update_topic(msg.header.stamp);574Location target_loc;575// Exit if no target is available.576if (!vehicle->get_target_location(target_loc)) {577return false;578}579target_loc.change_alt_frame(Location::AltFrame::ABSOLUTE);580msg.position.latitude = target_loc.lat * 1e-7;581msg.position.longitude = target_loc.lng * 1e-7;582msg.position.altitude = target_loc.alt * 1e-2;583584// Check whether the goal has changed or if the topic has never been published.585const double tolerance_lat_lon = 1e-8; // One order of magnitude smaller than the target's resolution.586const double distance_alt = 1e-3;587if (abs(msg.position.latitude - prev_goal_msg.position.latitude) > tolerance_lat_lon ||588abs(msg.position.longitude - prev_goal_msg.position.longitude) > tolerance_lat_lon ||589abs(msg.position.altitude - prev_goal_msg.position.altitude) > distance_alt ||590prev_goal_msg.header.stamp.sec == 0 ) {591update_topic(prev_goal_msg.header.stamp);592prev_goal_msg.position.latitude = msg.position.latitude;593prev_goal_msg.position.longitude = msg.position.longitude;594prev_goal_msg.position.altitude = msg.position.altitude;595return true;596} else {597return false;598}599}600#endif // AP_DDS_GOAL_PUB_ENABLED601602#if AP_DDS_IMU_PUB_ENABLED603void AP_DDS_Client::update_topic(sensor_msgs_msg_Imu& msg)604{605update_topic(msg.header.stamp);606STRCPY(msg.header.frame_id, BASE_LINK_NED_FRAME_ID);607608auto &imu = AP::ins();609auto &ahrs = AP::ahrs();610611WITH_SEMAPHORE(ahrs.get_semaphore());612613Quaternion orientation;614if (ahrs.get_quaternion(orientation)) {615msg.orientation.x = orientation[0];616msg.orientation.y = orientation[1];617msg.orientation.z = orientation[2];618msg.orientation.w = orientation[3];619} else {620initialize(msg.orientation);621}622msg.orientation_covariance[0] = -1;623624uint8_t accel_index = ahrs.get_primary_accel_index();625uint8_t gyro_index = ahrs.get_primary_gyro_index();626const Vector3f accel_data = imu.get_accel(accel_index);627const Vector3f gyro_data = imu.get_gyro(gyro_index);628629// Populate the message fields630msg.linear_acceleration.x = accel_data.x;631msg.linear_acceleration.y = accel_data.y;632msg.linear_acceleration.z = accel_data.z;633634msg.angular_velocity.x = gyro_data.x;635msg.angular_velocity.y = gyro_data.y;636msg.angular_velocity.z = gyro_data.z;637msg.angular_velocity_covariance[0] = -1;638msg.linear_acceleration_covariance[0] = -1;639}640#endif // AP_DDS_IMU_PUB_ENABLED641642#if AP_DDS_CLOCK_PUB_ENABLED643void AP_DDS_Client::update_topic(rosgraph_msgs_msg_Clock& msg)644{645update_topic(msg.clock);646}647#endif // AP_DDS_CLOCK_PUB_ENABLED648649#if AP_DDS_GPS_GLOBAL_ORIGIN_PUB_ENABLED650void AP_DDS_Client::update_topic(geographic_msgs_msg_GeoPointStamped& msg)651{652update_topic(msg.header.stamp);653STRCPY(msg.header.frame_id, BASE_LINK_FRAME_ID);654655auto &ahrs = AP::ahrs();656WITH_SEMAPHORE(ahrs.get_semaphore());657658Location ekf_origin;659// LLA is WGS-84 geodetic coordinate.660// Altitude converted from cm to m.661if (ahrs.get_origin(ekf_origin)) {662msg.position.latitude = ekf_origin.lat * 1E-7;663msg.position.longitude = ekf_origin.lng * 1E-7;664msg.position.altitude = ekf_origin.alt * 0.01;665}666}667#endif // AP_DDS_GPS_GLOBAL_ORIGIN_PUB_ENABLED668669#if AP_DDS_STATUS_PUB_ENABLED670bool AP_DDS_Client::update_topic(ardupilot_msgs_msg_Status& msg)671{672// Fill the new message.673const auto &vehicle = AP::vehicle();674const auto &battery = AP::battery();675msg.vehicle_type = static_cast<uint8_t>(AP::fwversion().vehicle_type);676msg.armed = hal.util->get_soft_armed();677msg.mode = vehicle->get_mode();678msg.flying = vehicle->get_likely_flying();679msg.external_control = true; // Always true for now. To be filled after PR#28429.680uint8_t fs_iter = 0;681msg.failsafe_size = 0;682if (rc().in_rc_failsafe()) {683msg.failsafe[fs_iter++] = FS_RADIO;684}685if (battery.has_failsafed()) {686msg.failsafe[fs_iter++] = FS_BATTERY;687}688// TODO: replace flag with function.689if (AP_Notify::flags.failsafe_gcs) {690msg.failsafe[fs_iter++] = FS_GCS;691}692// TODO: replace flag with function.693if (AP_Notify::flags.failsafe_ekf) {694msg.failsafe[fs_iter++] = FS_EKF;695}696msg.failsafe_size = fs_iter;697698// Compare with the previous one.699bool is_message_changed {false};700is_message_changed |= (last_status_msg_.flying != msg.flying);701is_message_changed |= (last_status_msg_.armed != msg.armed);702is_message_changed |= (last_status_msg_.mode != msg.mode);703is_message_changed |= (last_status_msg_.vehicle_type != msg.vehicle_type);704is_message_changed |= (last_status_msg_.failsafe_size != msg.failsafe_size);705is_message_changed |= (last_status_msg_.external_control != msg.external_control);706707if ( is_message_changed ) {708last_status_msg_.flying = msg.flying;709last_status_msg_.armed = msg.armed;710last_status_msg_.mode = msg.mode;711last_status_msg_.vehicle_type = msg.vehicle_type;712last_status_msg_.failsafe_size = msg.failsafe_size;713last_status_msg_.external_control = msg.external_control;714update_topic(msg.header.stamp);715return true;716} else {717return false;718}719}720#endif // AP_DDS_STATUS_PUB_ENABLED721/*722start the DDS thread723*/724bool AP_DDS_Client::start(void)725{726AP_Param::setup_object_defaults(this, var_info);727AP_Param::load_object_from_eeprom(this, var_info);728729if (enabled == 0) {730return true;731}732733if (!hal.scheduler->thread_create(FUNCTOR_BIND_MEMBER(&AP_DDS_Client::main_loop, void),734"DDS",7358192, AP_HAL::Scheduler::PRIORITY_IO, 1)) {736GCS_SEND_TEXT(MAV_SEVERITY_ERROR, "%s Thread create failed", msg_prefix);737return false;738}739return true;740}741742// read function triggered at every subscription callback743void AP_DDS_Client::on_topic_trampoline(uxrSession* uxr_session, uxrObjectId object_id, uint16_t request_id, uxrStreamId stream_id, struct ucdrBuffer* ub, uint16_t length,744void* args)745{746AP_DDS_Client *dds = (AP_DDS_Client *)args;747dds->on_topic(uxr_session, object_id, request_id, stream_id, ub, length);748}749750void AP_DDS_Client::on_topic(uxrSession* uxr_session, uxrObjectId object_id, uint16_t request_id, uxrStreamId stream_id, struct ucdrBuffer* ub, uint16_t length)751{752/*753TEMPLATE for reading to the subscribed topics7541) Store the read contents into the ucdr buffer7552) Deserialize the said contents into the topic instance756*/757(void) uxr_session;758(void) request_id;759(void) stream_id;760(void) length;761switch (object_id.id) {762#if AP_DDS_JOY_SUB_ENABLED763case topics[to_underlying(TopicIndex::JOY_SUB)].dr_id.id: {764const bool success = sensor_msgs_msg_Joy_deserialize_topic(ub, &rx_joy_topic);765766if (success == false) {767break;768}769770if (rx_joy_topic.axes_size >= 4) {771const uint32_t t_now = AP_HAL::millis();772773for (uint8_t i = 0; i < MIN(8U, rx_joy_topic.axes_size); i++) {774// Ignore channel override if NaN.775if (std::isnan(rx_joy_topic.axes[i])) {776// Setting the RC override to 0U releases the channel back to the RC.777RC_Channels::set_override(i, 0U, t_now);778} else {779const uint16_t mapped_data = static_cast<uint16_t>(780linear_interpolate(rc().channel(i)->get_radio_min(),781rc().channel(i)->get_radio_max(),782rx_joy_topic.axes[i],783-1.0, 1.0));784RC_Channels::set_override(i, mapped_data, t_now);785}786}787788}789break;790}791#endif // AP_DDS_JOY_SUB_ENABLED792#if AP_DDS_DYNAMIC_TF_SUB_ENABLED793case topics[to_underlying(TopicIndex::DYNAMIC_TRANSFORMS_SUB)].dr_id.id: {794const bool success = tf2_msgs_msg_TFMessage_deserialize_topic(ub, &rx_dynamic_transforms_topic);795if (success == false) {796break;797}798799if (rx_dynamic_transforms_topic.transforms_size > 0) {800#if AP_DDS_VISUALODOM_ENABLED801AP_DDS_External_Odom::handle_external_odom(rx_dynamic_transforms_topic);802#endif // AP_DDS_VISUALODOM_ENABLED803804} else {805GCS_SEND_TEXT(MAV_SEVERITY_INFO, "%s Received tf2_msgs/TFMessage: TF is empty", msg_prefix);806}807break;808}809#endif // AP_DDS_DYNAMIC_TF_SUB_ENABLED810#if AP_DDS_VEL_CTRL_ENABLED811case topics[to_underlying(TopicIndex::VELOCITY_CONTROL_SUB)].dr_id.id: {812const bool success = geometry_msgs_msg_TwistStamped_deserialize_topic(ub, &rx_velocity_control_topic);813if (success == false) {814break;815}816#if AP_EXTERNAL_CONTROL_ENABLED817if (!AP_DDS_External_Control::handle_velocity_control(rx_velocity_control_topic)) {818// TODO #23430 handle velocity control failure through rosout, throttled.819}820#endif // AP_EXTERNAL_CONTROL_ENABLED821break;822}823#endif // AP_DDS_VEL_CTRL_ENABLED824#if AP_DDS_GLOBAL_POS_CTRL_ENABLED825case topics[to_underlying(TopicIndex::GLOBAL_POSITION_SUB)].dr_id.id: {826const bool success = ardupilot_msgs_msg_GlobalPosition_deserialize_topic(ub, &rx_global_position_control_topic);827if (success == false) {828break;829}830831#if AP_EXTERNAL_CONTROL_ENABLED832if (!AP_DDS_External_Control::handle_global_position_control(rx_global_position_control_topic)) {833// TODO #23430 handle global position control failure through rosout, throttled.834}835#endif // AP_EXTERNAL_CONTROL_ENABLED836break;837}838#endif // AP_DDS_GLOBAL_POS_CTRL_ENABLED839}840841}842843/*844callback on request completion845*/846void AP_DDS_Client::on_request_trampoline(uxrSession* uxr_session, uxrObjectId object_id, uint16_t request_id, SampleIdentity* sample_id, ucdrBuffer* ub, uint16_t length, void* args)847{848AP_DDS_Client *dds = (AP_DDS_Client *)args;849dds->on_request(uxr_session, object_id, request_id, sample_id, ub, length);850}851852void AP_DDS_Client::on_request(uxrSession* uxr_session, uxrObjectId object_id, uint16_t request_id, SampleIdentity* sample_id, ucdrBuffer* ub, uint16_t length)853{854(void) request_id;855(void) length;856switch (object_id.id) {857#if AP_DDS_ARM_SERVER_ENABLED858case services[to_underlying(ServiceIndex::ARMING_MOTORS)].rep_id: {859ardupilot_msgs_srv_ArmMotors_Request arm_motors_request;860ardupilot_msgs_srv_ArmMotors_Response arm_motors_response;861const bool deserialize_success = ardupilot_msgs_srv_ArmMotors_Request_deserialize_topic(ub, &arm_motors_request);862if (deserialize_success == false) {863break;864}865866GCS_SEND_TEXT(MAV_SEVERITY_INFO, "%s Request for %sing received", msg_prefix, arm_motors_request.arm ? "arm" : "disarm");867#if AP_EXTERNAL_CONTROL_ENABLED868const bool do_checks = true;869arm_motors_response.result = arm_motors_request.arm ? AP_DDS_External_Control::arm(AP_Arming::Method::DDS, do_checks) : AP_DDS_External_Control::disarm(AP_Arming::Method::DDS, do_checks);870if (!arm_motors_response.result) {871// TODO #23430 handle arm failure through rosout, throttled.872}873#endif // AP_EXTERNAL_CONTROL_ENABLED874875const uxrObjectId replier_id = {876.id = services[to_underlying(ServiceIndex::ARMING_MOTORS)].rep_id,877.type = UXR_REPLIER_ID878};879880uint8_t reply_buffer[8] {};881ucdrBuffer reply_ub;882883ucdr_init_buffer(&reply_ub, reply_buffer, sizeof(reply_buffer));884const bool serialize_success = ardupilot_msgs_srv_ArmMotors_Response_serialize_topic(&reply_ub, &arm_motors_response);885if (serialize_success == false) {886break;887}888889uxr_buffer_reply(uxr_session, reliable_out, replier_id, sample_id, reply_buffer, ucdr_buffer_length(&reply_ub));890GCS_SEND_TEXT(MAV_SEVERITY_INFO, "%s Request for Arming/Disarming : %s", msg_prefix, arm_motors_response.result ? "SUCCESS" : "FAIL");891break;892}893#endif // AP_DDS_ARM_SERVER_ENABLED894#if AP_DDS_MODE_SWITCH_SERVER_ENABLED895case services[to_underlying(ServiceIndex::MODE_SWITCH)].rep_id: {896ardupilot_msgs_srv_ModeSwitch_Request mode_switch_request;897ardupilot_msgs_srv_ModeSwitch_Response mode_switch_response;898const bool deserialize_success = ardupilot_msgs_srv_ModeSwitch_Request_deserialize_topic(ub, &mode_switch_request);899if (deserialize_success == false) {900break;901}902mode_switch_response.status = AP::vehicle()->set_mode(mode_switch_request.mode, ModeReason::DDS_COMMAND);903mode_switch_response.curr_mode = AP::vehicle()->get_mode();904905const uxrObjectId replier_id = {906.id = services[to_underlying(ServiceIndex::MODE_SWITCH)].rep_id,907.type = UXR_REPLIER_ID908};909910uint8_t reply_buffer[8] {};911ucdrBuffer reply_ub;912913ucdr_init_buffer(&reply_ub, reply_buffer, sizeof(reply_buffer));914const bool serialize_success = ardupilot_msgs_srv_ModeSwitch_Response_serialize_topic(&reply_ub, &mode_switch_response);915if (serialize_success == false) {916break;917}918919uxr_buffer_reply(uxr_session, reliable_out, replier_id, sample_id, reply_buffer, ucdr_buffer_length(&reply_ub));920GCS_SEND_TEXT(MAV_SEVERITY_INFO, "%s Request for Mode Switch : %s", msg_prefix, mode_switch_response.status ? "SUCCESS" : "FAIL");921break;922}923#endif // AP_DDS_MODE_SWITCH_SERVER_ENABLED924#if AP_DDS_VTOL_TAKEOFF_SERVER_ENABLED925case services[to_underlying(ServiceIndex::TAKEOFF)].rep_id: {926ardupilot_msgs_srv_Takeoff_Request takeoff_request;927ardupilot_msgs_srv_Takeoff_Response takeoff_response;928const bool deserialize_success = ardupilot_msgs_srv_Takeoff_Request_deserialize_topic(ub, &takeoff_request);929if (deserialize_success == false) {930break;931}932takeoff_response.status = AP::vehicle()->start_takeoff(takeoff_request.alt);933934const uxrObjectId replier_id = {935.id = services[to_underlying(ServiceIndex::TAKEOFF)].rep_id,936.type = UXR_REPLIER_ID937};938939uint8_t reply_buffer[8] {};940ucdrBuffer reply_ub;941942ucdr_init_buffer(&reply_ub, reply_buffer, sizeof(reply_buffer));943const bool serialize_success = ardupilot_msgs_srv_Takeoff_Response_serialize_topic(&reply_ub, &takeoff_response);944if (serialize_success == false) {945break;946}947948uxr_buffer_reply(uxr_session, reliable_out, replier_id, sample_id, reply_buffer, ucdr_buffer_length(&reply_ub));949GCS_SEND_TEXT(MAV_SEVERITY_INFO, "%s Request for Takeoff : %s", msg_prefix, takeoff_response.status ? "SUCCESS" : "FAIL");950break;951}952#endif // AP_DDS_VTOL_TAKEOFF_SERVER_ENABLED953#if AP_DDS_ARM_CHECK_SERVER_ENABLED954case services[to_underlying(ServiceIndex::PREARM_CHECK)].rep_id: {955std_srvs_srv_Trigger_Request prearm_check_request;956std_srvs_srv_Trigger_Response prearm_check_response;957const bool deserialize_success = std_srvs_srv_Trigger_Request_deserialize_topic(ub, &prearm_check_request);958if (deserialize_success == false) {959break;960}961prearm_check_response.success = AP::arming().pre_arm_checks(false);962STRCPY(prearm_check_response.message, prearm_check_response.success ? "Vehicle is Armable" : "Vehicle is Not Armable");963964const uxrObjectId replier_id = {965.id = services[to_underlying(ServiceIndex::PREARM_CHECK)].rep_id,966.type = UXR_REPLIER_ID967};968969uint8_t reply_buffer[sizeof(prearm_check_response.message) + 1] {};970ucdrBuffer reply_ub;971972ucdr_init_buffer(&reply_ub, reply_buffer, sizeof(reply_buffer));973const bool serialize_success = std_srvs_srv_Trigger_Response_serialize_topic(&reply_ub, &prearm_check_response);974if (serialize_success == false) {975break;976}977978uxr_buffer_reply(uxr_session, reliable_out, replier_id, sample_id, reply_buffer, ucdr_buffer_length(&reply_ub));979break;980}981#endif //AP_DDS_ARM_CHECK_SERVER_ENABLED982#if AP_DDS_PARAMETER_SERVER_ENABLED983case services[to_underlying(ServiceIndex::SET_PARAMETERS)].rep_id: {984const bool deserialize_success = rcl_interfaces_srv_SetParameters_Request_deserialize_topic(ub, &set_parameter_request);985if (deserialize_success == false) {986GCS_SEND_TEXT(MAV_SEVERITY_ERROR, "%s Set Parameters Request : Failed to deserialize request.", msg_prefix);987break;988}989990if (set_parameter_request.parameters_size > 8U) {991break;992}993994// Set parameters and responses for each one requested995set_parameter_response.results_size = set_parameter_request.parameters_size;996for (size_t i = 0; i < set_parameter_request.parameters_size; i++) {997param = set_parameter_request.parameters[i];998999enum ap_var_type var_type;10001001// set parameter1002AP_Param *vp;1003char param_key[AP_MAX_NAME_SIZE + 1];1004strncpy(param_key, (char *)param.name, AP_MAX_NAME_SIZE);1005param_key[AP_MAX_NAME_SIZE] = 0;10061007// Currently only integer and double value types can be set.1008// The following parameter value types are not handled:1009// bool, string, byte_array, bool_array, integer_array, double_array and string_array1010bool param_isnan = true;1011bool param_isinf = true;1012float param_value = 0.0f;1013switch (param.value.type) {1014case PARAMETER_INTEGER: {1015param_isnan = isnan(param.value.integer_value);1016param_isinf = isinf(param.value.integer_value);1017param_value = float(param.value.integer_value);1018break;1019}1020case PARAMETER_DOUBLE: {1021param_isnan = isnan(param.value.double_value);1022param_isinf = isinf(param.value.double_value);1023param_value = float(param.value.double_value);1024break;1025}1026default: {1027break;1028}1029}10301031// find existing param to get the old value1032uint16_t parameter_flags = 0;1033vp = AP_Param::find(param_key, &var_type, ¶meter_flags);1034if (vp == nullptr || param_isnan || param_isinf) {1035set_parameter_response.results[i].successful = false;1036strncpy(set_parameter_response.results[i].reason, "Parameter not found", sizeof(set_parameter_response.results[i].reason));1037continue;1038}10391040// Add existing parameter checks used in GCS_Param.cpp1041if (parameter_flags & AP_PARAM_FLAG_INTERNAL_USE_ONLY) {1042// The user can set BRD_OPTIONS to enable set of internal1043// parameters, for developer testing or unusual use cases1044if (AP_BoardConfig::allow_set_internal_parameters()) {1045parameter_flags &= ~AP_PARAM_FLAG_INTERNAL_USE_ONLY;1046}1047}10481049if ((parameter_flags & AP_PARAM_FLAG_INTERNAL_USE_ONLY) || vp->is_read_only()) {1050set_parameter_response.results[i].successful = false;1051strncpy(set_parameter_response.results[i].reason, "Parameter is read only",sizeof(set_parameter_response.results[i].reason));1052continue;1053}10541055// Set and save the value if it changed1056bool force_save = vp->set_and_save_by_name_ifchanged(param_key, param_value);10571058if (force_save && (parameter_flags & AP_PARAM_FLAG_ENABLE)) {1059AP_Param::invalidate_count();1060}10611062set_parameter_response.results[i].successful = true;1063strncpy(set_parameter_response.results[i].reason, "Parameter accepted", sizeof(set_parameter_response.results[i].reason));1064}10651066const uxrObjectId replier_id = {1067.id = services[to_underlying(ServiceIndex::SET_PARAMETERS)].rep_id,1068.type = UXR_REPLIER_ID1069};10701071const uint32_t reply_size = rcl_interfaces_srv_SetParameters_Response_size_of_topic(&set_parameter_response, 0U);1072uint8_t reply_buffer[reply_size];1073memset(reply_buffer, 0, reply_size * sizeof(uint8_t));1074ucdrBuffer reply_ub;10751076ucdr_init_buffer(&reply_ub, reply_buffer, reply_size);1077const bool serialize_success = rcl_interfaces_srv_SetParameters_Response_serialize_topic(&reply_ub, &set_parameter_response);1078if (serialize_success == false) {1079break;1080}10811082uxr_buffer_reply(uxr_session, reliable_out, replier_id, sample_id, reply_buffer, ucdr_buffer_length(&reply_ub));1083bool successful_params = true;1084for (size_t i = 0; i < set_parameter_response.results_size; i++) {1085// Check that all the parameters were set successfully1086successful_params &= set_parameter_response.results[i].successful;1087}1088GCS_SEND_TEXT(successful_params ? MAV_SEVERITY_INFO : MAV_SEVERITY_WARNING, "%s Set Parameters Request : %s", msg_prefix, successful_params ? "SUCCESSFUL" : "ONE OR MORE PARAMS FAILED" );1089break;1090}1091case services[to_underlying(ServiceIndex::GET_PARAMETERS)].rep_id: {1092const bool deserialize_success = rcl_interfaces_srv_GetParameters_Request_deserialize_topic(ub, &get_parameters_request);1093if (deserialize_success == false) {1094GCS_SEND_TEXT(MAV_SEVERITY_ERROR, "%s Get Parameters Request : Failed to deserialize request.", msg_prefix);1095break;1096}10971098if (get_parameters_request.names_size > 8U) {1099break;1100}11011102bool successful_read = true;1103get_parameters_response.values_size = get_parameters_request.names_size;1104for (size_t i = 0; i < get_parameters_request.names_size; i++) {1105enum ap_var_type var_type;11061107AP_Param *vp;1108char param_key[AP_MAX_NAME_SIZE + 1];1109strncpy(param_key, (char *)get_parameters_request.names[i], AP_MAX_NAME_SIZE);1110param_key[AP_MAX_NAME_SIZE] = 0;11111112vp = AP_Param::find(param_key, &var_type);1113if (vp == nullptr) {1114get_parameters_response.values[i].type = PARAMETER_NOT_SET;1115successful_read &= false;1116continue;1117}11181119switch (var_type) {1120case AP_PARAM_INT8: {1121get_parameters_response.values[i].type = PARAMETER_INTEGER;1122get_parameters_response.values[i].integer_value = ((AP_Int8 *)vp)->get();1123successful_read &= true;1124break;1125}1126case AP_PARAM_INT16: {1127get_parameters_response.values[i].type = PARAMETER_INTEGER;1128get_parameters_response.values[i].integer_value = ((AP_Int16 *)vp)->get();1129successful_read &= true;1130break;1131}1132case AP_PARAM_INT32: {1133get_parameters_response.values[i].type = PARAMETER_INTEGER;1134get_parameters_response.values[i].integer_value = ((AP_Int32 *)vp)->get();1135successful_read &= true;1136break;1137}1138case AP_PARAM_FLOAT: {1139get_parameters_response.values[i].type = PARAMETER_DOUBLE;1140get_parameters_response.values[i].double_value = vp->cast_to_float(var_type);1141successful_read &= true;1142break;1143}1144default: {1145get_parameters_response.values[i].type = PARAMETER_NOT_SET;1146successful_read &= false;1147break;1148}1149}1150}11511152const uxrObjectId replier_id = {1153.id = services[to_underlying(ServiceIndex::GET_PARAMETERS)].rep_id,1154.type = UXR_REPLIER_ID1155};11561157const uint32_t reply_size = rcl_interfaces_srv_GetParameters_Response_size_of_topic(&get_parameters_response, 0U);1158uint8_t reply_buffer[reply_size];1159memset(reply_buffer, 0, reply_size * sizeof(uint8_t));1160ucdrBuffer reply_ub;11611162ucdr_init_buffer(&reply_ub, reply_buffer, reply_size);1163const bool serialize_success = rcl_interfaces_srv_GetParameters_Response_serialize_topic(&reply_ub, &get_parameters_response);1164if (serialize_success == false) {1165break;1166}11671168uxr_buffer_reply(uxr_session, reliable_out, replier_id, sample_id, reply_buffer, ucdr_buffer_length(&reply_ub));11691170GCS_SEND_TEXT(successful_read ? MAV_SEVERITY_INFO : MAV_SEVERITY_WARNING, "%s Get Parameters Request : %s", msg_prefix, successful_read ? "SUCCESSFUL" : "ONE OR MORE PARAM NOT FOUND");1171break;1172}1173#endif // AP_DDS_PARAMETER_SERVER_ENABLED1174}1175}11761177/*1178main loop for DDS thread1179*/1180void AP_DDS_Client::main_loop(void)1181{1182if (!init_transport()) {1183return;1184}11851186//! @todo check for request to stop task1187while (true) {1188if (comm == nullptr) {1189GCS_SEND_TEXT(MAV_SEVERITY_ERROR, "%s transport invalid, exiting", msg_prefix);1190return;1191}11921193// check ping1194if (!uxr_ping_agent_attempts(comm, ping_timeout_ms, ping_max_retry)) {1195GCS_SEND_TEXT(MAV_SEVERITY_ERROR, "%s No ping response, exiting", msg_prefix);1196return;1197}11981199// create session1200if (!init_session() || !create()) {1201GCS_SEND_TEXT(MAV_SEVERITY_ERROR, "%s Creation Requests failed", msg_prefix);1202return;1203}1204connected = true;1205GCS_SEND_TEXT(MAV_SEVERITY_INFO, "%s Initialization passed", msg_prefix);12061207#if AP_DDS_STATIC_TF_PUB_ENABLED1208populate_static_transforms(tx_static_transforms_topic);1209write_static_transforms();1210#endif // AP_DDS_STATIC_TF_PUB_ENABLED12111212uint64_t last_ping_ms{0};1213uint8_t num_pings_missed{0};1214bool had_ping_reply{false};1215while (connected) {1216hal.scheduler->delay(1);12171218// publish topics1219update();12201221// check ping response1222if (session.on_pong_flag == PONG_IN_SESSION_STATUS) {1223had_ping_reply = true;1224}12251226const auto cur_time_ms = AP_HAL::millis64();1227if (cur_time_ms - last_ping_ms > DELAY_PING_MS) {1228last_ping_ms = cur_time_ms;12291230if (had_ping_reply) {1231num_pings_missed = 0;12321233} else {1234++num_pings_missed;1235}12361237const int ping_agent_timeout_ms{0};1238const uint8_t ping_agent_attempts{1};1239uxr_ping_agent_session(&session, ping_agent_timeout_ms, ping_agent_attempts);12401241had_ping_reply = false;1242}12431244if (num_pings_missed > 2) {1245GCS_SEND_TEXT(MAV_SEVERITY_ERROR,1246"%s No ping response, disconnecting", msg_prefix);1247connected = false;1248}1249}12501251// delete session if connected1252if (connected) {1253uxr_delete_session(&session);1254}1255}1256}12571258bool AP_DDS_Client::init_transport()1259{1260// serial init will fail if the SERIALn_PROTOCOL is not setup1261bool initTransportStatus = ddsSerialInit();1262is_using_serial = initTransportStatus;12631264#if AP_DDS_UDP_ENABLED1265// fallback to UDP if available1266if (!initTransportStatus) {1267initTransportStatus = ddsUdpInit();1268}1269#endif12701271if (!initTransportStatus) {1272GCS_SEND_TEXT(MAV_SEVERITY_INFO, "%s Transport initialization failed", msg_prefix);1273return false;1274}12751276return true;1277}12781279bool AP_DDS_Client::init_session()1280{1281// init session1282uxr_init_session(&session, comm, key);12831284// Register topic callbacks1285uxr_set_topic_callback(&session, AP_DDS_Client::on_topic_trampoline, this);12861287// ROS-2 Service : Register service request callbacks1288uxr_set_request_callback(&session, AP_DDS_Client::on_request_trampoline, this);12891290while (!uxr_create_session(&session)) {1291GCS_SEND_TEXT(MAV_SEVERITY_INFO, "%s Initialization waiting...", msg_prefix);1292hal.scheduler->delay(1000);1293}12941295// setup reliable stream buffers1296input_reliable_stream = NEW_NOTHROW uint8_t[DDS_BUFFER_SIZE];1297output_reliable_stream = NEW_NOTHROW uint8_t[DDS_BUFFER_SIZE];1298if (input_reliable_stream == nullptr || output_reliable_stream == nullptr) {1299GCS_SEND_TEXT(MAV_SEVERITY_ERROR, "%s Allocation failed", msg_prefix);1300return false;1301}13021303reliable_in = uxr_create_input_reliable_stream(&session, input_reliable_stream, DDS_BUFFER_SIZE, DDS_STREAM_HISTORY);1304reliable_out = uxr_create_output_reliable_stream(&session, output_reliable_stream, DDS_BUFFER_SIZE, DDS_STREAM_HISTORY);13051306GCS_SEND_TEXT(MAV_SEVERITY_INFO, "%s Init complete", msg_prefix);13071308return true;1309}13101311bool AP_DDS_Client::create()1312{1313WITH_SEMAPHORE(csem);13141315// Participant1316const uxrObjectId participant_id = {1317.id = 0x01,1318.type = UXR_PARTICIPANT_ID1319};1320const char* participant_name = AP_DDS_PARTICIPANT_NAME;1321const auto participant_req_id = uxr_buffer_create_participant_bin(&session, reliable_out, participant_id,1322static_cast<uint16_t>(domain_id), participant_name, UXR_REPLACE);13231324//Participant requests1325constexpr uint8_t nRequestsParticipant = 1;1326const uint16_t requestsParticipant[nRequestsParticipant] = {participant_req_id};13271328constexpr uint16_t maxTimeMsPerRequestMs = 500;1329constexpr uint16_t requestTimeoutParticipantMs = (uint16_t) nRequestsParticipant * maxTimeMsPerRequestMs;1330uint8_t statusParticipant[nRequestsParticipant];1331if (!uxr_run_session_until_all_status(&session, requestTimeoutParticipantMs, requestsParticipant, statusParticipant, nRequestsParticipant)) {1332GCS_SEND_TEXT(MAV_SEVERITY_ERROR, "%s Participant session request failure", msg_prefix);1333// TODO add a failure log message sharing the status results1334return false;1335}13361337for (uint16_t i = 0 ; i < ARRAY_SIZE(topics); i++) {1338// Topic1339const uxrObjectId topic_id = {1340.id = topics[i].topic_id,1341.type = UXR_TOPIC_ID1342};1343const auto topic_req_id = uxr_buffer_create_topic_bin(&session, reliable_out, topic_id,1344participant_id, topics[i].topic_name, topics[i].type_name, UXR_REPLACE);13451346// Status requests1347constexpr uint8_t nRequests = 3;1348uint16_t requests[nRequests];1349constexpr uint16_t requestTimeoutMs = nRequests * maxTimeMsPerRequestMs;1350uint8_t status[nRequests];13511352if (topics[i].topic_rw == Topic_rw::DataWriter) {1353// Publisher1354const uxrObjectId pub_id = {1355.id = topics[i].pub_id,1356.type = UXR_PUBLISHER_ID1357};1358const auto pub_req_id = uxr_buffer_create_publisher_bin(&session, reliable_out, pub_id,1359participant_id, UXR_REPLACE);13601361// Data Writer1362const auto dwriter_req_id = uxr_buffer_create_datawriter_bin(&session, reliable_out, topics[i].dw_id,1363pub_id, topic_id, topics[i].qos, UXR_REPLACE);13641365// save the request statuses1366requests[0] = topic_req_id;1367requests[1] = pub_req_id;1368requests[2] = dwriter_req_id;13691370if (!uxr_run_session_until_all_status(&session, requestTimeoutMs, requests, status, nRequests)) {1371GCS_SEND_TEXT(MAV_SEVERITY_ERROR, "%s Topic/Pub/Writer session request failure for index '%u'", msg_prefix, i);1372for (uint8_t s = 0 ; s < nRequests; s++) {1373GCS_SEND_TEXT(MAV_SEVERITY_ERROR, "%s Status '%d' result '%u'", msg_prefix, s, status[s]);1374}1375// TODO add a failure log message sharing the status results1376return false;1377} else {1378GCS_SEND_TEXT(MAV_SEVERITY_INFO, "%s Topic/Pub/Writer session pass for index '%u'", msg_prefix, i);1379}1380} else if (topics[i].topic_rw == Topic_rw::DataReader) {1381// Subscriber1382const uxrObjectId sub_id = {1383.id = topics[i].sub_id,1384.type = UXR_SUBSCRIBER_ID1385};1386const auto sub_req_id = uxr_buffer_create_subscriber_bin(&session, reliable_out, sub_id,1387participant_id, UXR_REPLACE);13881389// Data Reader1390const auto dreader_req_id = uxr_buffer_create_datareader_bin(&session, reliable_out, topics[i].dr_id,1391sub_id, topic_id, topics[i].qos, UXR_REPLACE);13921393// save the request statuses1394requests[0] = topic_req_id;1395requests[1] = sub_req_id;1396requests[2] = dreader_req_id;13971398if (!uxr_run_session_until_all_status(&session, requestTimeoutMs, requests, status, nRequests)) {1399GCS_SEND_TEXT(MAV_SEVERITY_ERROR, "%s Topic/Sub/Reader session request failure for index '%u'", msg_prefix, i);1400for (uint8_t s = 0 ; s < nRequests; s++) {1401GCS_SEND_TEXT(MAV_SEVERITY_ERROR, "%s Status '%d' result '%u'", msg_prefix, s, status[s]);1402}1403// TODO add a failure log message sharing the status results1404return false;1405} else {1406GCS_SEND_TEXT(MAV_SEVERITY_INFO, "%s Topic/Sub/Reader session pass for index '%u'", msg_prefix, i);1407uxr_buffer_request_data(&session, reliable_out, topics[i].dr_id, reliable_in, &delivery_control);1408}1409}1410}14111412// ROS-2 Service : else case for service requests14131414for (uint16_t i = 0; i < ARRAY_SIZE(services); i++) {14151416constexpr uint16_t requestTimeoutMs = maxTimeMsPerRequestMs;14171418if (services[i].service_rr == Service_rr::Replier) {1419const uxrObjectId rep_id = {1420.id = services[i].rep_id,1421.type = UXR_REPLIER_ID1422};1423const auto replier_req_id = uxr_buffer_create_replier_bin(&session, reliable_out, rep_id,1424participant_id, services[i].service_name, services[i].request_type, services[i].reply_type,1425services[i].request_topic_name, services[i].reply_topic_name, services[i].qos, UXR_REPLACE);14261427uint16_t request = replier_req_id;1428uint8_t status;14291430if (!uxr_run_session_until_all_status(&session, requestTimeoutMs, &request, &status, 1)) {1431GCS_SEND_TEXT(MAV_SEVERITY_ERROR, "%s Service/Replier session request failure for index '%u'", msg_prefix, i);1432GCS_SEND_TEXT(MAV_SEVERITY_ERROR, "%s Status result '%u'", msg_prefix, status);1433// TODO add a failure log message sharing the status results1434return false;1435} else {1436GCS_SEND_TEXT(MAV_SEVERITY_INFO, "%s Service/Replier session pass for index '%u'", msg_prefix, i);1437uxr_buffer_request_data(&session, reliable_out, rep_id, reliable_in, &delivery_control);1438}14391440} else if (services[i].service_rr == Service_rr::Requester) {1441// TODO : Add Similar Code for Requester Profile1442}1443}14441445return true;1446}14471448void AP_DDS_Client::write_time_topic()1449{1450WITH_SEMAPHORE(csem);1451if (connected) {1452ucdrBuffer ub {};1453const uint32_t topic_size = builtin_interfaces_msg_Time_size_of_topic(&time_topic, 0);1454uxr_prepare_output_stream(&session, reliable_out, topics[to_underlying(TopicIndex::TIME_PUB)].dw_id, &ub, topic_size);1455const bool success = builtin_interfaces_msg_Time_serialize_topic(&ub, &time_topic);1456if (!success) {1457// TODO sometimes serialization fails on bootup. Determine why.1458// AP_HAL::panic("FATAL: XRCE_Client failed to serialize");1459}1460}1461}14621463#if AP_DDS_NAVSATFIX_PUB_ENABLED1464void AP_DDS_Client::write_nav_sat_fix_topic()1465{1466WITH_SEMAPHORE(csem);1467if (connected) {1468ucdrBuffer ub {};1469const uint32_t topic_size = sensor_msgs_msg_NavSatFix_size_of_topic(&nav_sat_fix_topic, 0);1470uxr_prepare_output_stream(&session, reliable_out, topics[to_underlying(TopicIndex::NAV_SAT_FIX_PUB)].dw_id, &ub, topic_size);1471const bool success = sensor_msgs_msg_NavSatFix_serialize_topic(&ub, &nav_sat_fix_topic);1472if (!success) {1473// TODO sometimes serialization fails on bootup. Determine why.1474// AP_HAL::panic("FATAL: DDS_Client failed to serialize");1475}1476}1477}1478#endif // AP_DDS_NAVSATFIX_PUB_ENABLED14791480#if AP_DDS_STATIC_TF_PUB_ENABLED1481void AP_DDS_Client::write_static_transforms()1482{1483WITH_SEMAPHORE(csem);1484if (connected) {1485ucdrBuffer ub {};1486const uint32_t topic_size = tf2_msgs_msg_TFMessage_size_of_topic(&tx_static_transforms_topic, 0);1487uxr_prepare_output_stream(&session, reliable_out, topics[to_underlying(TopicIndex::STATIC_TRANSFORMS_PUB)].dw_id, &ub, topic_size);1488const bool success = tf2_msgs_msg_TFMessage_serialize_topic(&ub, &tx_static_transforms_topic);1489if (!success) {1490// TODO sometimes serialization fails on bootup. Determine why.1491// AP_HAL::panic("FATAL: DDS_Client failed to serialize");1492}1493}1494}1495#endif // AP_DDS_STATIC_TF_PUB_ENABLED14961497#if AP_DDS_BATTERY_STATE_PUB_ENABLED1498void AP_DDS_Client::write_battery_state_topic()1499{1500WITH_SEMAPHORE(csem);1501if (connected) {1502ucdrBuffer ub {};1503const uint32_t topic_size = sensor_msgs_msg_BatteryState_size_of_topic(&battery_state_topic, 0);1504uxr_prepare_output_stream(&session, reliable_out, topics[to_underlying(TopicIndex::BATTERY_STATE_PUB)].dw_id, &ub, topic_size);1505const bool success = sensor_msgs_msg_BatteryState_serialize_topic(&ub, &battery_state_topic);1506if (!success) {1507// TODO sometimes serialization fails on bootup. Determine why.1508// AP_HAL::panic("FATAL: DDS_Client failed to serialize");1509}1510}1511}1512#endif // AP_DDS_BATTERY_STATE_PUB_ENABLED15131514#if AP_DDS_LOCAL_POSE_PUB_ENABLED1515void AP_DDS_Client::write_local_pose_topic()1516{1517WITH_SEMAPHORE(csem);1518if (connected) {1519ucdrBuffer ub {};1520const uint32_t topic_size = geometry_msgs_msg_PoseStamped_size_of_topic(&local_pose_topic, 0);1521uxr_prepare_output_stream(&session, reliable_out, topics[to_underlying(TopicIndex::LOCAL_POSE_PUB)].dw_id, &ub, topic_size);1522const bool success = geometry_msgs_msg_PoseStamped_serialize_topic(&ub, &local_pose_topic);1523if (!success) {1524// TODO sometimes serialization fails on bootup. Determine why.1525// AP_HAL::panic("FATAL: DDS_Client failed to serialize");1526}1527}1528}1529#endif // AP_DDS_LOCAL_POSE_PUB_ENABLED15301531#if AP_DDS_LOCAL_VEL_PUB_ENABLED1532void AP_DDS_Client::write_tx_local_velocity_topic()1533{1534WITH_SEMAPHORE(csem);1535if (connected) {1536ucdrBuffer ub {};1537const uint32_t topic_size = geometry_msgs_msg_TwistStamped_size_of_topic(&tx_local_velocity_topic, 0);1538uxr_prepare_output_stream(&session, reliable_out, topics[to_underlying(TopicIndex::LOCAL_VELOCITY_PUB)].dw_id, &ub, topic_size);1539const bool success = geometry_msgs_msg_TwistStamped_serialize_topic(&ub, &tx_local_velocity_topic);1540if (!success) {1541// TODO sometimes serialization fails on bootup. Determine why.1542// AP_HAL::panic("FATAL: DDS_Client failed to serialize");1543}1544}1545}1546#endif // AP_DDS_LOCAL_VEL_PUB_ENABLED1547#if AP_DDS_AIRSPEED_PUB_ENABLED1548void AP_DDS_Client::write_tx_local_airspeed_topic()1549{1550WITH_SEMAPHORE(csem);1551if (connected) {1552ucdrBuffer ub {};1553const uint32_t topic_size = geometry_msgs_msg_Vector3Stamped_size_of_topic(&tx_local_airspeed_topic, 0);1554uxr_prepare_output_stream(&session, reliable_out, topics[to_underlying(TopicIndex::LOCAL_AIRSPEED_PUB)].dw_id, &ub, topic_size);1555const bool success = geometry_msgs_msg_Vector3Stamped_serialize_topic(&ub, &tx_local_airspeed_topic);1556if (!success) {1557// TODO sometimes serialization fails on bootup. Determine why.1558// AP_HAL::panic("FATAL: DDS_Client failed to serialize");1559}1560}1561}1562#endif // AP_DDS_AIRSPEED_PUB_ENABLED1563#if AP_DDS_IMU_PUB_ENABLED1564void AP_DDS_Client::write_imu_topic()1565{1566WITH_SEMAPHORE(csem);1567if (connected) {1568ucdrBuffer ub {};1569const uint32_t topic_size = sensor_msgs_msg_Imu_size_of_topic(&imu_topic, 0);1570uxr_prepare_output_stream(&session, reliable_out, topics[to_underlying(TopicIndex::IMU_PUB)].dw_id, &ub, topic_size);1571const bool success = sensor_msgs_msg_Imu_serialize_topic(&ub, &imu_topic);1572if (!success) {1573// TODO sometimes serialization fails on bootup. Determine why.1574// AP_HAL::panic("FATAL: DDS_Client failed to serialize");1575}1576}1577}1578#endif // AP_DDS_IMU_PUB_ENABLED15791580#if AP_DDS_GEOPOSE_PUB_ENABLED1581void AP_DDS_Client::write_geo_pose_topic()1582{1583WITH_SEMAPHORE(csem);1584if (connected) {1585ucdrBuffer ub {};1586const uint32_t topic_size = geographic_msgs_msg_GeoPoseStamped_size_of_topic(&geo_pose_topic, 0);1587uxr_prepare_output_stream(&session, reliable_out, topics[to_underlying(TopicIndex::GEOPOSE_PUB)].dw_id, &ub, topic_size);1588const bool success = geographic_msgs_msg_GeoPoseStamped_serialize_topic(&ub, &geo_pose_topic);1589if (!success) {1590// TODO sometimes serialization fails on bootup. Determine why.1591// AP_HAL::panic("FATAL: DDS_Client failed to serialize");1592}1593}1594}1595#endif // AP_DDS_GEOPOSE_PUB_ENABLED15961597#if AP_DDS_CLOCK_PUB_ENABLED1598void AP_DDS_Client::write_clock_topic()1599{1600WITH_SEMAPHORE(csem);1601if (connected) {1602ucdrBuffer ub {};1603const uint32_t topic_size = rosgraph_msgs_msg_Clock_size_of_topic(&clock_topic, 0);1604uxr_prepare_output_stream(&session, reliable_out, topics[to_underlying(TopicIndex::CLOCK_PUB)].dw_id, &ub, topic_size);1605const bool success = rosgraph_msgs_msg_Clock_serialize_topic(&ub, &clock_topic);1606if (!success) {1607// TODO sometimes serialization fails on bootup. Determine why.1608// AP_HAL::panic("FATAL: DDS_Client failed to serialize");1609}1610}1611}1612#endif // AP_DDS_CLOCK_PUB_ENABLED16131614#if AP_DDS_GPS_GLOBAL_ORIGIN_PUB_ENABLED1615void AP_DDS_Client::write_gps_global_origin_topic()1616{1617WITH_SEMAPHORE(csem);1618if (connected) {1619ucdrBuffer ub {};1620const uint32_t topic_size = geographic_msgs_msg_GeoPointStamped_size_of_topic(&gps_global_origin_topic, 0);1621uxr_prepare_output_stream(&session, reliable_out, topics[to_underlying(TopicIndex::GPS_GLOBAL_ORIGIN_PUB)].dw_id, &ub, topic_size);1622const bool success = geographic_msgs_msg_GeoPointStamped_serialize_topic(&ub, &gps_global_origin_topic);1623if (!success) {1624// AP_HAL::panic("FATAL: DDS_Client failed to serialize");1625}1626}1627}1628#endif // AP_DDS_GPS_GLOBAL_ORIGIN_PUB_ENABLED16291630#if AP_DDS_GOAL_PUB_ENABLED1631void AP_DDS_Client::write_goal_topic()1632{1633WITH_SEMAPHORE(csem);1634if (connected) {1635ucdrBuffer ub {};1636const uint32_t topic_size = geographic_msgs_msg_GeoPointStamped_size_of_topic(&goal_topic, 0);1637uxr_prepare_output_stream(&session, reliable_out, topics[to_underlying(TopicIndex::GOAL_PUB)].dw_id, &ub, topic_size);1638const bool success = geographic_msgs_msg_GeoPointStamped_serialize_topic(&ub, &goal_topic);1639if (!success) {1640// AP_HAL::panic("FATAL: DDS_Client failed to serialize");1641}1642}1643}1644#endif // AP_DDS_GOAL_PUB_ENABLED16451646#if AP_DDS_STATUS_PUB_ENABLED1647void AP_DDS_Client::write_status_topic()1648{1649WITH_SEMAPHORE(csem);1650if (connected) {1651ucdrBuffer ub {};1652const uint32_t topic_size = ardupilot_msgs_msg_Status_size_of_topic(&status_topic, 0);1653uxr_prepare_output_stream(&session, reliable_out, topics[to_underlying(TopicIndex::STATUS_PUB)].dw_id, &ub, topic_size);1654const bool success = ardupilot_msgs_msg_Status_serialize_topic(&ub, &status_topic);1655if (!success) {1656// TODO sometimes serialization fails on bootup. Determine why.1657// AP_HAL::panic("FATAL: DDS_Client failed to serialize");1658}1659}1660}1661#endif // AP_DDS_STATUS_PUB_ENABLED16621663void AP_DDS_Client::update()1664{1665WITH_SEMAPHORE(csem);1666const auto cur_time_ms = AP_HAL::millis64();16671668#if AP_DDS_TIME_PUB_ENABLED1669if (cur_time_ms - last_time_time_ms > DELAY_TIME_TOPIC_MS) {1670update_topic(time_topic);1671last_time_time_ms = cur_time_ms;1672write_time_topic();1673}1674#endif // AP_DDS_TIME_PUB_ENABLED1675#if AP_DDS_NAVSATFIX_PUB_ENABLED1676constexpr uint8_t gps_instance = 0;1677if (update_topic(nav_sat_fix_topic, gps_instance)) {1678write_nav_sat_fix_topic();1679}1680#endif // AP_DDS_NAVSATFIX_PUB_ENABLED1681#if AP_DDS_BATTERY_STATE_PUB_ENABLED1682if (cur_time_ms - last_battery_state_time_ms > DELAY_BATTERY_STATE_TOPIC_MS) {1683for (uint8_t battery_instance = 0; battery_instance < AP_BATT_MONITOR_MAX_INSTANCES; battery_instance++) {1684update_topic(battery_state_topic, battery_instance);1685if (battery_state_topic.present) {1686write_battery_state_topic();1687}1688}1689last_battery_state_time_ms = cur_time_ms;1690}1691#endif // AP_DDS_BATTERY_STATE_PUB_ENABLED1692#if AP_DDS_LOCAL_POSE_PUB_ENABLED1693if (cur_time_ms - last_local_pose_time_ms > DELAY_LOCAL_POSE_TOPIC_MS) {1694update_topic(local_pose_topic);1695last_local_pose_time_ms = cur_time_ms;1696write_local_pose_topic();1697}1698#endif // AP_DDS_LOCAL_POSE_PUB_ENABLED1699#if AP_DDS_LOCAL_VEL_PUB_ENABLED1700if (cur_time_ms - last_local_velocity_time_ms > DELAY_LOCAL_VELOCITY_TOPIC_MS) {1701update_topic(tx_local_velocity_topic);1702last_local_velocity_time_ms = cur_time_ms;1703write_tx_local_velocity_topic();1704}1705#endif // AP_DDS_LOCAL_VEL_PUB_ENABLED1706#if AP_DDS_AIRSPEED_PUB_ENABLED1707if (cur_time_ms - last_airspeed_time_ms > DELAY_AIRSPEED_TOPIC_MS) {1708last_airspeed_time_ms = cur_time_ms;1709if (update_topic(tx_local_airspeed_topic)) {1710write_tx_local_airspeed_topic();1711}1712}1713#endif // AP_DDS_AIRSPEED_PUB_ENABLED1714#if AP_DDS_IMU_PUB_ENABLED1715if (cur_time_ms - last_imu_time_ms > DELAY_IMU_TOPIC_MS) {1716update_topic(imu_topic);1717last_imu_time_ms = cur_time_ms;1718write_imu_topic();1719}1720#endif // AP_DDS_IMU_PUB_ENABLED1721#if AP_DDS_GEOPOSE_PUB_ENABLED1722if (cur_time_ms - last_geo_pose_time_ms > DELAY_GEO_POSE_TOPIC_MS) {1723update_topic(geo_pose_topic);1724last_geo_pose_time_ms = cur_time_ms;1725write_geo_pose_topic();1726}1727#endif // AP_DDS_GEOPOSE_PUB_ENABLED1728#if AP_DDS_CLOCK_PUB_ENABLED1729if (cur_time_ms - last_clock_time_ms > DELAY_CLOCK_TOPIC_MS) {1730update_topic(clock_topic);1731last_clock_time_ms = cur_time_ms;1732write_clock_topic();1733}1734#endif // AP_DDS_CLOCK_PUB_ENABLED1735#if AP_DDS_GPS_GLOBAL_ORIGIN_PUB_ENABLED1736if (cur_time_ms - last_gps_global_origin_time_ms > DELAY_GPS_GLOBAL_ORIGIN_TOPIC_MS) {1737update_topic(gps_global_origin_topic);1738last_gps_global_origin_time_ms = cur_time_ms;1739write_gps_global_origin_topic();1740}1741#endif // AP_DDS_GPS_GLOBAL_ORIGIN_PUB_ENABLED1742#if AP_DDS_GOAL_PUB_ENABLED1743if (cur_time_ms - last_goal_time_ms > DELAY_GOAL_TOPIC_MS) {1744if (update_topic_goal(goal_topic)) {1745write_goal_topic();1746}1747last_goal_time_ms = cur_time_ms;1748}1749#endif // AP_DDS_GOAL_PUB_ENABLED1750#if AP_DDS_STATUS_PUB_ENABLED1751if (cur_time_ms - last_status_check_time_ms > DELAY_STATUS_TOPIC_MS) {1752if (update_topic(status_topic)) {1753write_status_topic();1754}1755last_status_check_time_ms = cur_time_ms;1756}1757#endif // AP_DDS_STATUS_PUB_ENABLED17581759status_ok = uxr_run_session_time(&session, 1);1760}17611762#if CONFIG_HAL_BOARD != HAL_BOARD_SITL && CONFIG_HAL_BOARD != HAL_BOARD_ESP321763extern "C" {1764int clock_gettime(clockid_t clockid, struct timespec *ts);1765}17661767int clock_gettime(clockid_t clockid, struct timespec *ts)1768{1769//! @todo the value of clockid is ignored here.1770//! A fallback mechanism is employed against the caller's choice of clock.1771uint64_t utc_usec;1772if (!AP::rtc().get_utc_usec(utc_usec)) {1773utc_usec = AP_HAL::micros64();1774}1775ts->tv_sec = utc_usec / 1000000ULL;1776ts->tv_nsec = (utc_usec % 1000000ULL) * 1000UL;1777return 0;1778}1779#endif // CONFIG_HAL_BOARD != HAL_BOARD_SITL && CONFIG_HAL_BOARD != HAL_BOARD_ESP3217801781#endif // AP_DDS_ENABLED178217831784