CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
Ardupilot

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.

GitHub Repository: Ardupilot/ardupilot
Path: blob/master/libraries/AP_DDS/AP_DDS_Client.cpp
Views: 1798
1
#include <AP_HAL/AP_HAL_Boards.h>
2
3
#include "AP_DDS_config.h"
4
#if AP_DDS_ENABLED
5
#include <uxr/client/util/ping.h>
6
7
#include <AP_GPS/AP_GPS.h>
8
#include <AP_HAL/AP_HAL.h>
9
#include <RC_Channel/RC_Channel.h>
10
#include <AP_RTC/AP_RTC.h>
11
#include <AP_Math/AP_Math.h>
12
#include <AP_InertialSensor/AP_InertialSensor.h>
13
#include <GCS_MAVLink/GCS.h>
14
#include <AP_BattMonitor/AP_BattMonitor.h>
15
#include <AP_AHRS/AP_AHRS.h>
16
#if AP_DDS_ARM_SERVER_ENABLED
17
#include <AP_Arming/AP_Arming.h>
18
# endif // AP_DDS_ARM_SERVER_ENABLED
19
#include <AP_Vehicle/AP_Vehicle.h>
20
#include <AP_Common/AP_FWVersion.h>
21
#include <AP_ExternalControl/AP_ExternalControl_config.h>
22
23
#if AP_DDS_ARM_SERVER_ENABLED
24
#include "ardupilot_msgs/srv/ArmMotors.h"
25
#endif // AP_DDS_ARM_SERVER_ENABLED
26
#if AP_DDS_MODE_SWITCH_SERVER_ENABLED
27
#include "ardupilot_msgs/srv/ModeSwitch.h"
28
#endif // AP_DDS_MODE_SWITCH_SERVER_ENABLED
29
#if AP_DDS_ARM_CHECK_SERVER_ENABLED
30
#include "std_srvs/srv/Trigger.h"
31
#endif // AP_DDS_ARM_CHECK_SERVER_ENABLED
32
#if AP_DDS_VTOL_TAKEOFF_SERVER_ENABLED
33
#include "ardupilot_msgs/srv/Takeoff.h"
34
#endif // AP_DDS_VTOL_TAKEOFF_SERVER_ENABLED
35
36
#if AP_EXTERNAL_CONTROL_ENABLED
37
#include "AP_DDS_ExternalControl.h"
38
#endif // AP_EXTERNAL_CONTROL_ENABLED
39
#include "AP_DDS_Frames.h"
40
41
#include "AP_DDS_Client.h"
42
#include "AP_DDS_Topic_Table.h"
43
#include "AP_DDS_Service_Table.h"
44
#include "AP_DDS_External_Odom.h"
45
46
#define STRCPY(D,S) strncpy(D, S, ARRAY_SIZE(D))
47
48
// Enable DDS at runtime by default
49
static constexpr uint8_t ENABLED_BY_DEFAULT = 1;
50
#if AP_DDS_TIME_PUB_ENABLED
51
static constexpr uint16_t DELAY_TIME_TOPIC_MS = AP_DDS_DELAY_TIME_TOPIC_MS;
52
#endif // AP_DDS_TIME_PUB_ENABLED
53
#if AP_DDS_BATTERY_STATE_PUB_ENABLED
54
static constexpr uint16_t DELAY_BATTERY_STATE_TOPIC_MS = AP_DDS_DELAY_BATTERY_STATE_TOPIC_MS;
55
#endif // AP_DDS_BATTERY_STATE_PUB_ENABLED
56
#if AP_DDS_IMU_PUB_ENABLED
57
static constexpr uint16_t DELAY_IMU_TOPIC_MS = AP_DDS_DELAY_IMU_TOPIC_MS;
58
#endif // AP_DDS_IMU_PUB_ENABLED
59
#if AP_DDS_LOCAL_POSE_PUB_ENABLED
60
static constexpr uint16_t DELAY_LOCAL_POSE_TOPIC_MS = AP_DDS_DELAY_LOCAL_POSE_TOPIC_MS;
61
#endif // AP_DDS_LOCAL_POSE_PUB_ENABLED
62
#if AP_DDS_LOCAL_VEL_PUB_ENABLED
63
static constexpr uint16_t DELAY_LOCAL_VELOCITY_TOPIC_MS = AP_DDS_DELAY_LOCAL_VELOCITY_TOPIC_MS;
64
#endif // AP_DDS_LOCAL_VEL_PUB_ENABLED
65
#if AP_DDS_AIRSPEED_PUB_ENABLED
66
static constexpr uint16_t DELAY_AIRSPEED_TOPIC_MS = AP_DDS_DELAY_AIRSPEED_TOPIC_MS;
67
#endif // AP_DDS_AIRSPEED_PUB_ENABLED
68
#if AP_DDS_GEOPOSE_PUB_ENABLED
69
static constexpr uint16_t DELAY_GEO_POSE_TOPIC_MS = AP_DDS_DELAY_GEO_POSE_TOPIC_MS;
70
#endif // AP_DDS_GEOPOSE_PUB_ENABLED
71
#if AP_DDS_GOAL_PUB_ENABLED
72
static constexpr uint16_t DELAY_GOAL_TOPIC_MS = AP_DDS_DELAY_GOAL_TOPIC_MS ;
73
#endif // AP_DDS_GOAL_PUB_ENABLED
74
#if AP_DDS_CLOCK_PUB_ENABLED
75
static constexpr uint16_t DELAY_CLOCK_TOPIC_MS =AP_DDS_DELAY_CLOCK_TOPIC_MS;
76
#endif // AP_DDS_CLOCK_PUB_ENABLED
77
#if AP_DDS_GPS_GLOBAL_ORIGIN_PUB_ENABLED
78
static constexpr uint16_t DELAY_GPS_GLOBAL_ORIGIN_TOPIC_MS = AP_DDS_DELAY_GPS_GLOBAL_ORIGIN_TOPIC_MS;
79
#endif // AP_DDS_GPS_GLOBAL_ORIGIN_PUB_ENABLED
80
static constexpr uint16_t DELAY_PING_MS = 500;
81
#if AP_DDS_STATUS_PUB_ENABLED
82
static constexpr uint16_t DELAY_STATUS_TOPIC_MS = AP_DDS_DELAY_STATUS_TOPIC_MS;
83
#endif // AP_DDS_STATUS_PUB_ENABLED
84
85
// Define the subscriber data members, which are static class scope.
86
// If these are created on the stack in the subscriber,
87
// the AP_DDS_Client::on_topic frame size is exceeded.
88
#if AP_DDS_JOY_SUB_ENABLED
89
sensor_msgs_msg_Joy AP_DDS_Client::rx_joy_topic {};
90
#endif // AP_DDS_JOY_SUB_ENABLED
91
#if AP_DDS_DYNAMIC_TF_SUB_ENABLED
92
tf2_msgs_msg_TFMessage AP_DDS_Client::rx_dynamic_transforms_topic {};
93
#endif // AP_DDS_DYNAMIC_TF_SUB_ENABLED
94
#if AP_DDS_VEL_CTRL_ENABLED
95
geometry_msgs_msg_TwistStamped AP_DDS_Client::rx_velocity_control_topic {};
96
#endif // AP_DDS_VEL_CTRL_ENABLED
97
#if AP_DDS_GLOBAL_POS_CTRL_ENABLED
98
ardupilot_msgs_msg_GlobalPosition AP_DDS_Client::rx_global_position_control_topic {};
99
#endif // AP_DDS_GLOBAL_POS_CTRL_ENABLED
100
101
// Define the parameter server data members, which are static class scope.
102
// If these are created on the stack, then the AP_DDS_Client::on_request
103
// frame size is exceeded.
104
#if AP_DDS_PARAMETER_SERVER_ENABLED
105
rcl_interfaces_srv_SetParameters_Request AP_DDS_Client::set_parameter_request {};
106
rcl_interfaces_srv_SetParameters_Response AP_DDS_Client::set_parameter_response {};
107
rcl_interfaces_srv_GetParameters_Request AP_DDS_Client::get_parameters_request {};
108
rcl_interfaces_srv_GetParameters_Response AP_DDS_Client::get_parameters_response {};
109
rcl_interfaces_msg_Parameter AP_DDS_Client::param {};
110
#endif
111
112
const AP_Param::GroupInfo AP_DDS_Client::var_info[] {
113
114
// @Param: _ENABLE
115
// @DisplayName: DDS enable
116
// @Description: Enable DDS subsystem
117
// @Values: 0:Disabled,1:Enabled
118
// @RebootRequired: True
119
// @User: Advanced
120
AP_GROUPINFO_FLAGS("_ENABLE", 1, AP_DDS_Client, enabled, ENABLED_BY_DEFAULT, AP_PARAM_FLAG_ENABLE),
121
122
#if AP_DDS_UDP_ENABLED
123
// @Param: _UDP_PORT
124
// @DisplayName: DDS UDP port
125
// @Description: UDP port number for DDS
126
// @Range: 1 65535
127
// @RebootRequired: True
128
// @User: Standard
129
AP_GROUPINFO("_UDP_PORT", 2, AP_DDS_Client, udp.port, 2019),
130
131
// @Group: _IP
132
// @Path: ../AP_Networking/AP_Networking_address.cpp
133
AP_SUBGROUPINFO(udp.ip, "_IP", 3, AP_DDS_Client, AP_Networking_IPV4),
134
135
#endif
136
137
// @Param: _DOMAIN_ID
138
// @DisplayName: DDS DOMAIN ID
139
// @Description: Set the ROS_DOMAIN_ID
140
// @Range: 0 232
141
// @RebootRequired: True
142
// @User: Standard
143
AP_GROUPINFO("_DOMAIN_ID", 4, AP_DDS_Client, domain_id, 0),
144
145
// @Param: _TIMEOUT_MS
146
// @DisplayName: DDS ping timeout
147
// @Description: The time in milliseconds the DDS client will wait for a response from the XRCE agent before reattempting.
148
// @Units: ms
149
// @Range: 1 10000
150
// @RebootRequired: True
151
// @Increment: 1
152
// @User: Standard
153
AP_GROUPINFO("_TIMEOUT_MS", 5, AP_DDS_Client, ping_timeout_ms, 1000),
154
155
// @Param: _MAX_RETRY
156
// @DisplayName: DDS ping max attempts
157
// @Description: The maximum number of times the DDS client will attempt to ping the XRCE agent before exiting.
158
// @Range: 1 100
159
// @RebootRequired: True
160
// @Increment: 1
161
// @User: Standard
162
AP_GROUPINFO("_MAX_RETRY", 6, AP_DDS_Client, ping_max_retry, 10),
163
164
AP_GROUPEND
165
};
166
167
#if AP_DDS_STATIC_TF_PUB_ENABLED | AP_DDS_LOCAL_POSE_PUB_ENABLED | AP_DDS_GEOPOSE_PUB_ENABLED | AP_DDS_IMU_PUB_ENABLED
168
static void initialize(geometry_msgs_msg_Quaternion& q)
169
{
170
q.x = 0.0;
171
q.y = 0.0;
172
q.z = 0.0;
173
q.w = 1.0;
174
}
175
#endif // AP_DDS_STATIC_TF_PUB_ENABLED | AP_DDS_LOCAL_POSE_PUB_ENABLED | AP_DDS_GEOPOSE_PUB_ENABLED | AP_DDS_IMU_PUB_ENABLED
176
177
AP_DDS_Client::~AP_DDS_Client()
178
{
179
// close transport
180
if (is_using_serial) {
181
uxr_close_custom_transport(&serial.transport);
182
} else {
183
#if AP_DDS_UDP_ENABLED
184
uxr_close_custom_transport(&udp.transport);
185
#endif
186
}
187
}
188
189
#if AP_DDS_TIME_PUB_ENABLED
190
void AP_DDS_Client::update_topic(builtin_interfaces_msg_Time& msg)
191
{
192
uint64_t utc_usec;
193
if (!AP::rtc().get_utc_usec(utc_usec)) {
194
utc_usec = AP_HAL::micros64();
195
}
196
msg.sec = utc_usec / 1000000ULL;
197
msg.nanosec = (utc_usec % 1000000ULL) * 1000UL;
198
199
}
200
#endif // AP_DDS_TIME_PUB_ENABLED
201
202
#if AP_DDS_NAVSATFIX_PUB_ENABLED
203
bool AP_DDS_Client::update_topic(sensor_msgs_msg_NavSatFix& msg, const uint8_t instance)
204
{
205
// Add a lambda that takes in navsatfix msg and populates the cov
206
// Make it constexpr if possible
207
// https://www.fluentcpp.com/2021/12/13/the-evolutions-of-lambdas-in-c14-c17-and-c20/
208
// constexpr auto times2 = [] (sensor_msgs_msg_NavSatFix* msg) { return n * 2; };
209
210
auto &gps = AP::gps();
211
WITH_SEMAPHORE(gps.get_semaphore());
212
213
if (!gps.is_healthy(instance)) {
214
msg.status.status = -1; // STATUS_NO_FIX
215
msg.status.service = 0; // No services supported
216
msg.position_covariance_type = 0; // COVARIANCE_TYPE_UNKNOWN
217
return false;
218
}
219
220
// No update is needed
221
const auto last_fix_time_ms = gps.last_fix_time_ms(instance);
222
if (last_nav_sat_fix_time_ms == last_fix_time_ms) {
223
return false;
224
} else {
225
last_nav_sat_fix_time_ms = last_fix_time_ms;
226
}
227
228
229
update_topic(msg.header.stamp);
230
static_assert(GPS_MAX_RECEIVERS <= 9, "GPS_MAX_RECEIVERS is greater than 9");
231
hal.util->snprintf(msg.header.frame_id, 2, "%u", instance);
232
msg.status.service = 0; // SERVICE_GPS
233
msg.status.status = -1; // STATUS_NO_FIX
234
235
236
//! @todo What about glonass, compass, galileo?
237
//! This will be properly designed and implemented to spec in #23277
238
msg.status.service = 1; // SERVICE_GPS
239
240
const auto status = gps.status(instance);
241
switch (status) {
242
case AP_GPS::NO_GPS:
243
case AP_GPS::NO_FIX:
244
msg.status.status = -1; // STATUS_NO_FIX
245
msg.position_covariance_type = 0; // COVARIANCE_TYPE_UNKNOWN
246
return true;
247
case AP_GPS::GPS_OK_FIX_2D:
248
case AP_GPS::GPS_OK_FIX_3D:
249
msg.status.status = 0; // STATUS_FIX
250
break;
251
case AP_GPS::GPS_OK_FIX_3D_DGPS:
252
msg.status.status = 1; // STATUS_SBAS_FIX
253
break;
254
case AP_GPS::GPS_OK_FIX_3D_RTK_FLOAT:
255
case AP_GPS::GPS_OK_FIX_3D_RTK_FIXED:
256
msg.status.status = 2; // STATUS_SBAS_FIX
257
break;
258
default:
259
//! @todo Can we not just use an enum class and not worry about this condition?
260
break;
261
}
262
const auto loc = gps.location(instance);
263
msg.latitude = loc.lat * 1E-7;
264
msg.longitude = loc.lng * 1E-7;
265
266
int32_t alt_cm;
267
if (!loc.get_alt_cm(Location::AltFrame::ABSOLUTE, alt_cm)) {
268
// With absolute frame, this condition is unlikely
269
msg.status.status = -1; // STATUS_NO_FIX
270
msg.position_covariance_type = 0; // COVARIANCE_TYPE_UNKNOWN
271
return true;
272
}
273
msg.altitude = alt_cm * 0.01;
274
275
// ROS allows double precision, ArduPilot exposes float precision today
276
Matrix3f cov;
277
msg.position_covariance_type = (uint8_t)gps.position_covariance(instance, cov);
278
msg.position_covariance[0] = cov[0][0];
279
msg.position_covariance[1] = cov[0][1];
280
msg.position_covariance[2] = cov[0][2];
281
msg.position_covariance[3] = cov[1][0];
282
msg.position_covariance[4] = cov[1][1];
283
msg.position_covariance[5] = cov[1][2];
284
msg.position_covariance[6] = cov[2][0];
285
msg.position_covariance[7] = cov[2][1];
286
msg.position_covariance[8] = cov[2][2];
287
288
return true;
289
}
290
#endif // AP_DDS_NAVSATFIX_PUB_ENABLED
291
292
#if AP_DDS_STATIC_TF_PUB_ENABLED
293
void AP_DDS_Client::populate_static_transforms(tf2_msgs_msg_TFMessage& msg)
294
{
295
msg.transforms_size = 0;
296
297
auto &gps = AP::gps();
298
for (uint8_t i = 0; i < GPS_MAX_RECEIVERS; i++) {
299
const auto gps_type = gps.get_type(i);
300
if (gps_type == AP_GPS::GPS_Type::GPS_TYPE_NONE) {
301
continue;
302
}
303
update_topic(msg.transforms[i].header.stamp);
304
char gps_frame_id[16];
305
//! @todo should GPS frame ID's be 0 or 1 indexed in ROS?
306
hal.util->snprintf(gps_frame_id, sizeof(gps_frame_id), "GPS_%u", i);
307
STRCPY(msg.transforms[i].header.frame_id, BASE_LINK_FRAME_ID);
308
STRCPY(msg.transforms[i].child_frame_id, gps_frame_id);
309
// The body-frame offsets
310
// X - Forward
311
// Y - Right
312
// Z - Down
313
// https://ardupilot.org/copter/docs/common-sensor-offset-compensation.html#sensor-position-offset-compensation
314
315
const auto offset = gps.get_antenna_offset(i);
316
317
// In ROS REP 103, it follows this convention
318
// X - Forward
319
// Y - Left
320
// Z - Up
321
// https://www.ros.org/reps/rep-0103.html#axis-orientation
322
323
msg.transforms[i].transform.translation.x = offset[0];
324
msg.transforms[i].transform.translation.y = -1 * offset[1];
325
msg.transforms[i].transform.translation.z = -1 * offset[2];
326
327
// Ensure rotation is initialized.
328
initialize(msg.transforms[i].transform.rotation);
329
330
msg.transforms_size++;
331
}
332
333
}
334
#endif // AP_DDS_STATIC_TF_PUB_ENABLED
335
336
#if AP_DDS_BATTERY_STATE_PUB_ENABLED
337
void AP_DDS_Client::update_topic(sensor_msgs_msg_BatteryState& msg, const uint8_t instance)
338
{
339
if (instance >= AP_BATT_MONITOR_MAX_INSTANCES) {
340
return;
341
}
342
static_assert(AP_BATT_MONITOR_MAX_INSTANCES <= 99, "AP_BATT_MONITOR_MAX_INSTANCES is greater than 99");
343
344
update_topic(msg.header.stamp);
345
hal.util->snprintf(msg.header.frame_id, 2, "%u", instance);
346
347
auto &battery = AP::battery();
348
349
if (!battery.healthy(instance)) {
350
msg.power_supply_status = 3; //POWER_SUPPLY_HEALTH_DEAD
351
msg.present = false;
352
return;
353
}
354
msg.present = true;
355
356
msg.voltage = battery.voltage(instance);
357
358
float temperature;
359
msg.temperature = (battery.get_temperature(temperature, instance)) ? temperature : NAN;
360
361
float current;
362
msg.current = (battery.current_amps(current, instance)) ? -1 * current : NAN;
363
364
const float design_capacity = (float)battery.pack_capacity_mah(instance) * 0.001;
365
msg.design_capacity = design_capacity;
366
367
uint8_t percentage;
368
if (battery.capacity_remaining_pct(percentage, instance)) {
369
msg.percentage = percentage * 0.01;
370
msg.charge = (percentage * design_capacity) * 0.01;
371
} else {
372
msg.percentage = NAN;
373
msg.charge = NAN;
374
}
375
376
msg.capacity = NAN;
377
378
if (battery.current_amps(current, instance)) {
379
if (percentage == 100) {
380
msg.power_supply_status = 4; //POWER_SUPPLY_STATUS_FULL
381
} else if (is_negative(current)) {
382
msg.power_supply_status = 1; //POWER_SUPPLY_STATUS_CHARGING
383
} else if (is_positive(current)) {
384
msg.power_supply_status = 2; //POWER_SUPPLY_STATUS_DISCHARGING
385
} else {
386
msg.power_supply_status = 3; //POWER_SUPPLY_STATUS_NOT_CHARGING
387
}
388
} else {
389
msg.power_supply_status = 0; //POWER_SUPPLY_STATUS_UNKNOWN
390
}
391
392
msg.power_supply_health = (battery.overpower_detected(instance)) ? 4 : 1; //POWER_SUPPLY_HEALTH_OVERVOLTAGE or POWER_SUPPLY_HEALTH_GOOD
393
394
msg.power_supply_technology = 0; //POWER_SUPPLY_TECHNOLOGY_UNKNOWN
395
396
if (battery.has_cell_voltages(instance)) {
397
const auto &cells = battery.get_cell_voltages(instance);
398
const uint8_t ncells_max = MIN(ARRAY_SIZE(msg.cell_voltage), ARRAY_SIZE(cells.cells));
399
for (uint8_t i=0; i< ncells_max; i++) {
400
msg.cell_voltage[i] = cells.cells[i] * 0.001;
401
}
402
}
403
}
404
#endif // AP_DDS_BATTERY_STATE_PUB_ENABLED
405
406
#if AP_DDS_LOCAL_POSE_PUB_ENABLED
407
void AP_DDS_Client::update_topic(geometry_msgs_msg_PoseStamped& msg)
408
{
409
update_topic(msg.header.stamp);
410
STRCPY(msg.header.frame_id, BASE_LINK_FRAME_ID);
411
412
auto &ahrs = AP::ahrs();
413
WITH_SEMAPHORE(ahrs.get_semaphore());
414
415
// ROS REP 103 uses the ENU convention:
416
// X - East
417
// Y - North
418
// Z - Up
419
// https://www.ros.org/reps/rep-0103.html#axis-orientation
420
// AP_AHRS uses the NED convention
421
// X - North
422
// Y - East
423
// Z - Down
424
// As a consequence, to follow ROS REP 103, it is necessary to switch X and Y,
425
// as well as invert Z
426
427
Vector3f position;
428
if (ahrs.get_relative_position_NED_home(position)) {
429
msg.pose.position.x = position[1];
430
msg.pose.position.y = position[0];
431
msg.pose.position.z = -position[2];
432
}
433
434
// In ROS REP 103, axis orientation uses the following convention:
435
// X - Forward
436
// Y - Left
437
// Z - Up
438
// https://www.ros.org/reps/rep-0103.html#axis-orientation
439
// As a consequence, to follow ROS REP 103, it is necessary to switch X and Y,
440
// as well as invert Z (NED to ENU conversion) as well as a 90 degree rotation in the Z axis
441
// for x to point forward
442
Quaternion orientation;
443
if (ahrs.get_quaternion(orientation)) {
444
Quaternion aux(orientation[0], orientation[2], orientation[1], -orientation[3]); //NED to ENU transformation
445
Quaternion transformation (sqrtF(2) * 0.5,0,0,sqrtF(2) * 0.5); // Z axis 90 degree rotation
446
orientation = aux * transformation;
447
msg.pose.orientation.w = orientation[0];
448
msg.pose.orientation.x = orientation[1];
449
msg.pose.orientation.y = orientation[2];
450
msg.pose.orientation.z = orientation[3];
451
} else {
452
initialize(msg.pose.orientation);
453
}
454
}
455
#endif // AP_DDS_LOCAL_POSE_PUB_ENABLED
456
457
#if AP_DDS_LOCAL_VEL_PUB_ENABLED
458
void AP_DDS_Client::update_topic(geometry_msgs_msg_TwistStamped& msg)
459
{
460
update_topic(msg.header.stamp);
461
STRCPY(msg.header.frame_id, BASE_LINK_FRAME_ID);
462
463
auto &ahrs = AP::ahrs();
464
WITH_SEMAPHORE(ahrs.get_semaphore());
465
466
// ROS REP 103 uses the ENU convention:
467
// X - East
468
// Y - North
469
// Z - Up
470
// https://www.ros.org/reps/rep-0103.html#axis-orientation
471
// AP_AHRS uses the NED convention
472
// X - North
473
// Y - East
474
// Z - Down
475
// As a consequence, to follow ROS REP 103, it is necessary to switch X and Y,
476
// as well as invert Z
477
Vector3f velocity;
478
if (ahrs.get_velocity_NED(velocity)) {
479
msg.twist.linear.x = velocity[1];
480
msg.twist.linear.y = velocity[0];
481
msg.twist.linear.z = -velocity[2];
482
}
483
484
// In ROS REP 103, axis orientation uses the following convention:
485
// X - Forward
486
// Y - Left
487
// Z - Up
488
// https://www.ros.org/reps/rep-0103.html#axis-orientation
489
// The gyro data is received from AP_AHRS in body-frame
490
// X - Forward
491
// Y - Right
492
// Z - Down
493
// As a consequence, to follow ROS REP 103, it is necessary to invert Y and Z
494
Vector3f angular_velocity = ahrs.get_gyro();
495
msg.twist.angular.x = angular_velocity[0];
496
msg.twist.angular.y = -angular_velocity[1];
497
msg.twist.angular.z = -angular_velocity[2];
498
}
499
#endif // AP_DDS_LOCAL_VEL_PUB_ENABLED
500
#if AP_DDS_AIRSPEED_PUB_ENABLED
501
bool AP_DDS_Client::update_topic(geometry_msgs_msg_Vector3Stamped& msg)
502
{
503
update_topic(msg.header.stamp);
504
STRCPY(msg.header.frame_id, BASE_LINK_FRAME_ID);
505
auto &ahrs = AP::ahrs();
506
WITH_SEMAPHORE(ahrs.get_semaphore());
507
// In ROS REP 103, axis orientation uses the following convention:
508
// X - Forward
509
// Y - Left
510
// Z - Up
511
// https://www.ros.org/reps/rep-0103.html#axis-orientation
512
// The true airspeed data is received from AP_AHRS in body-frame
513
// X - Forward
514
// Y - Right
515
// Z - Down
516
// As a consequence, to follow ROS REP 103, it is necessary to invert Y and Z
517
Vector3f true_airspeed_vec_bf;
518
bool is_airspeed_available {false};
519
if (ahrs.airspeed_vector_true(true_airspeed_vec_bf)) {
520
msg.vector.x = true_airspeed_vec_bf[0];
521
msg.vector.y = -true_airspeed_vec_bf[1];
522
msg.vector.z = -true_airspeed_vec_bf[2];
523
is_airspeed_available = true;
524
}
525
return is_airspeed_available;
526
}
527
#endif // AP_DDS_AIRSPEED_PUB_ENABLED
528
529
#if AP_DDS_GEOPOSE_PUB_ENABLED
530
void AP_DDS_Client::update_topic(geographic_msgs_msg_GeoPoseStamped& msg)
531
{
532
update_topic(msg.header.stamp);
533
STRCPY(msg.header.frame_id, BASE_LINK_FRAME_ID);
534
535
auto &ahrs = AP::ahrs();
536
WITH_SEMAPHORE(ahrs.get_semaphore());
537
538
Location loc;
539
if (ahrs.get_location(loc)) {
540
msg.pose.position.latitude = loc.lat * 1E-7;
541
msg.pose.position.longitude = loc.lng * 1E-7;
542
// TODO this is assumed to be absolute frame in WGS-84 as per the GeoPose message definition in ROS.
543
// Use loc.get_alt_frame() to convert if necessary.
544
msg.pose.position.altitude = loc.alt * 0.01; // Transform from cm to m
545
}
546
547
// In ROS REP 103, axis orientation uses the following convention:
548
// X - Forward
549
// Y - Left
550
// Z - Up
551
// https://www.ros.org/reps/rep-0103.html#axis-orientation
552
// As a consequence, to follow ROS REP 103, it is necessary to switch X and Y,
553
// as well as invert Z (NED to ENU conversion) as well as a 90 degree rotation in the Z axis
554
// for x to point forward
555
Quaternion orientation;
556
if (ahrs.get_quaternion(orientation)) {
557
Quaternion aux(orientation[0], orientation[2], orientation[1], -orientation[3]); //NED to ENU transformation
558
Quaternion transformation(sqrtF(2) * 0.5, 0, 0, sqrtF(2) * 0.5); // Z axis 90 degree rotation
559
orientation = aux * transformation;
560
msg.pose.orientation.w = orientation[0];
561
msg.pose.orientation.x = orientation[1];
562
msg.pose.orientation.y = orientation[2];
563
msg.pose.orientation.z = orientation[3];
564
} else {
565
initialize(msg.pose.orientation);
566
}
567
}
568
#endif // AP_DDS_GEOPOSE_PUB_ENABLED
569
570
#if AP_DDS_GOAL_PUB_ENABLED
571
bool AP_DDS_Client::update_topic_goal(geographic_msgs_msg_GeoPointStamped& msg)
572
{
573
const auto &vehicle = AP::vehicle();
574
update_topic(msg.header.stamp);
575
Location target_loc;
576
// Exit if no target is available.
577
if (!vehicle->get_target_location(target_loc)) {
578
return false;
579
}
580
target_loc.change_alt_frame(Location::AltFrame::ABSOLUTE);
581
msg.position.latitude = target_loc.lat * 1e-7;
582
msg.position.longitude = target_loc.lng * 1e-7;
583
msg.position.altitude = target_loc.alt * 1e-2;
584
585
// Check whether the goal has changed or if the topic has never been published.
586
const double tolerance_lat_lon = 1e-8; // One order of magnitude smaller than the target's resolution.
587
const double distance_alt = 1e-3;
588
if (abs(msg.position.latitude - prev_goal_msg.position.latitude) > tolerance_lat_lon ||
589
abs(msg.position.longitude - prev_goal_msg.position.longitude) > tolerance_lat_lon ||
590
abs(msg.position.altitude - prev_goal_msg.position.altitude) > distance_alt ||
591
prev_goal_msg.header.stamp.sec == 0 ) {
592
update_topic(prev_goal_msg.header.stamp);
593
prev_goal_msg.position.latitude = msg.position.latitude;
594
prev_goal_msg.position.longitude = msg.position.longitude;
595
prev_goal_msg.position.altitude = msg.position.altitude;
596
return true;
597
} else {
598
return false;
599
}
600
}
601
#endif // AP_DDS_GOAL_PUB_ENABLED
602
603
#if AP_DDS_IMU_PUB_ENABLED
604
void AP_DDS_Client::update_topic(sensor_msgs_msg_Imu& msg)
605
{
606
update_topic(msg.header.stamp);
607
STRCPY(msg.header.frame_id, BASE_LINK_NED_FRAME_ID);
608
609
auto &imu = AP::ins();
610
auto &ahrs = AP::ahrs();
611
612
WITH_SEMAPHORE(ahrs.get_semaphore());
613
614
Quaternion orientation;
615
if (ahrs.get_quaternion(orientation)) {
616
msg.orientation.x = orientation[0];
617
msg.orientation.y = orientation[1];
618
msg.orientation.z = orientation[2];
619
msg.orientation.w = orientation[3];
620
} else {
621
initialize(msg.orientation);
622
}
623
msg.orientation_covariance[0] = -1;
624
625
uint8_t accel_index = ahrs.get_primary_accel_index();
626
uint8_t gyro_index = ahrs.get_primary_gyro_index();
627
const Vector3f accel_data = imu.get_accel(accel_index);
628
const Vector3f gyro_data = imu.get_gyro(gyro_index);
629
630
// Populate the message fields
631
msg.linear_acceleration.x = accel_data.x;
632
msg.linear_acceleration.y = accel_data.y;
633
msg.linear_acceleration.z = accel_data.z;
634
635
msg.angular_velocity.x = gyro_data.x;
636
msg.angular_velocity.y = gyro_data.y;
637
msg.angular_velocity.z = gyro_data.z;
638
msg.angular_velocity_covariance[0] = -1;
639
msg.linear_acceleration_covariance[0] = -1;
640
}
641
#endif // AP_DDS_IMU_PUB_ENABLED
642
643
#if AP_DDS_CLOCK_PUB_ENABLED
644
void AP_DDS_Client::update_topic(rosgraph_msgs_msg_Clock& msg)
645
{
646
update_topic(msg.clock);
647
}
648
#endif // AP_DDS_CLOCK_PUB_ENABLED
649
650
#if AP_DDS_GPS_GLOBAL_ORIGIN_PUB_ENABLED
651
void AP_DDS_Client::update_topic(geographic_msgs_msg_GeoPointStamped& msg)
652
{
653
update_topic(msg.header.stamp);
654
STRCPY(msg.header.frame_id, BASE_LINK_FRAME_ID);
655
656
auto &ahrs = AP::ahrs();
657
WITH_SEMAPHORE(ahrs.get_semaphore());
658
659
Location ekf_origin;
660
// LLA is WGS-84 geodetic coordinate.
661
// Altitude converted from cm to m.
662
if (ahrs.get_origin(ekf_origin)) {
663
msg.position.latitude = ekf_origin.lat * 1E-7;
664
msg.position.longitude = ekf_origin.lng * 1E-7;
665
msg.position.altitude = ekf_origin.alt * 0.01;
666
}
667
}
668
#endif // AP_DDS_GPS_GLOBAL_ORIGIN_PUB_ENABLED
669
670
#if AP_DDS_STATUS_PUB_ENABLED
671
bool AP_DDS_Client::update_topic(ardupilot_msgs_msg_Status& msg)
672
{
673
// Fill the new message.
674
const auto &vehicle = AP::vehicle();
675
const auto &battery = AP::battery();
676
msg.vehicle_type = static_cast<uint8_t>(AP::fwversion().vehicle_type);
677
msg.armed = hal.util->get_soft_armed();
678
msg.mode = vehicle->get_mode();
679
msg.flying = vehicle->get_likely_flying();
680
msg.external_control = true; // Always true for now. To be filled after PR#28429.
681
uint8_t fs_iter = 0;
682
msg.failsafe_size = 0;
683
if (rc().in_rc_failsafe()) {
684
msg.failsafe[fs_iter++] = FS_RADIO;
685
}
686
if (battery.has_failsafed()) {
687
msg.failsafe[fs_iter++] = FS_BATTERY;
688
}
689
// TODO: replace flag with function.
690
if (AP_Notify::flags.failsafe_gcs) {
691
msg.failsafe[fs_iter++] = FS_GCS;
692
}
693
// TODO: replace flag with function.
694
if (AP_Notify::flags.failsafe_ekf) {
695
msg.failsafe[fs_iter++] = FS_EKF;
696
}
697
msg.failsafe_size = fs_iter;
698
699
// Compare with the previous one.
700
bool is_message_changed {false};
701
is_message_changed |= (last_status_msg_.flying != msg.flying);
702
is_message_changed |= (last_status_msg_.armed != msg.armed);
703
is_message_changed |= (last_status_msg_.mode != msg.mode);
704
is_message_changed |= (last_status_msg_.vehicle_type != msg.vehicle_type);
705
is_message_changed |= (last_status_msg_.failsafe_size != msg.failsafe_size);
706
is_message_changed |= (last_status_msg_.external_control != msg.external_control);
707
708
if ( is_message_changed ) {
709
last_status_msg_.flying = msg.flying;
710
last_status_msg_.armed = msg.armed;
711
last_status_msg_.mode = msg.mode;
712
last_status_msg_.vehicle_type = msg.vehicle_type;
713
last_status_msg_.failsafe_size = msg.failsafe_size;
714
last_status_msg_.external_control = msg.external_control;
715
update_topic(msg.header.stamp);
716
return true;
717
} else {
718
return false;
719
}
720
}
721
#endif // AP_DDS_STATUS_PUB_ENABLED
722
/*
723
start the DDS thread
724
*/
725
bool AP_DDS_Client::start(void)
726
{
727
AP_Param::setup_object_defaults(this, var_info);
728
AP_Param::load_object_from_eeprom(this, var_info);
729
730
if (enabled == 0) {
731
return true;
732
}
733
734
if (!hal.scheduler->thread_create(FUNCTOR_BIND_MEMBER(&AP_DDS_Client::main_loop, void),
735
"DDS",
736
8192, AP_HAL::Scheduler::PRIORITY_IO, 1)) {
737
GCS_SEND_TEXT(MAV_SEVERITY_ERROR, "%s Thread create failed", msg_prefix);
738
return false;
739
}
740
return true;
741
}
742
743
// read function triggered at every subscription callback
744
void 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,
745
void* args)
746
{
747
AP_DDS_Client *dds = (AP_DDS_Client *)args;
748
dds->on_topic(uxr_session, object_id, request_id, stream_id, ub, length);
749
}
750
751
void AP_DDS_Client::on_topic(uxrSession* uxr_session, uxrObjectId object_id, uint16_t request_id, uxrStreamId stream_id, struct ucdrBuffer* ub, uint16_t length)
752
{
753
/*
754
TEMPLATE for reading to the subscribed topics
755
1) Store the read contents into the ucdr buffer
756
2) Deserialize the said contents into the topic instance
757
*/
758
(void) uxr_session;
759
(void) request_id;
760
(void) stream_id;
761
(void) length;
762
switch (object_id.id) {
763
#if AP_DDS_JOY_SUB_ENABLED
764
case topics[to_underlying(TopicIndex::JOY_SUB)].dr_id.id: {
765
const bool success = sensor_msgs_msg_Joy_deserialize_topic(ub, &rx_joy_topic);
766
767
if (success == false) {
768
break;
769
}
770
771
if (rx_joy_topic.axes_size >= 4) {
772
const uint32_t t_now = AP_HAL::millis();
773
774
for (uint8_t i = 0; i < MIN(8U, rx_joy_topic.axes_size); i++) {
775
// Ignore channel override if NaN.
776
if (std::isnan(rx_joy_topic.axes[i])) {
777
// Setting the RC override to 0U releases the channel back to the RC.
778
RC_Channels::set_override(i, 0U, t_now);
779
} else {
780
const uint16_t mapped_data = static_cast<uint16_t>(
781
linear_interpolate(rc().channel(i)->get_radio_min(),
782
rc().channel(i)->get_radio_max(),
783
rx_joy_topic.axes[i],
784
-1.0, 1.0));
785
RC_Channels::set_override(i, mapped_data, t_now);
786
}
787
}
788
789
}
790
break;
791
}
792
#endif // AP_DDS_JOY_SUB_ENABLED
793
#if AP_DDS_DYNAMIC_TF_SUB_ENABLED
794
case topics[to_underlying(TopicIndex::DYNAMIC_TRANSFORMS_SUB)].dr_id.id: {
795
const bool success = tf2_msgs_msg_TFMessage_deserialize_topic(ub, &rx_dynamic_transforms_topic);
796
if (success == false) {
797
break;
798
}
799
800
if (rx_dynamic_transforms_topic.transforms_size > 0) {
801
#if AP_DDS_VISUALODOM_ENABLED
802
AP_DDS_External_Odom::handle_external_odom(rx_dynamic_transforms_topic);
803
#endif // AP_DDS_VISUALODOM_ENABLED
804
805
} else {
806
GCS_SEND_TEXT(MAV_SEVERITY_INFO, "%s Received tf2_msgs/TFMessage: TF is empty", msg_prefix);
807
}
808
break;
809
}
810
#endif // AP_DDS_DYNAMIC_TF_SUB_ENABLED
811
#if AP_DDS_VEL_CTRL_ENABLED
812
case topics[to_underlying(TopicIndex::VELOCITY_CONTROL_SUB)].dr_id.id: {
813
const bool success = geometry_msgs_msg_TwistStamped_deserialize_topic(ub, &rx_velocity_control_topic);
814
if (success == false) {
815
break;
816
}
817
#if AP_EXTERNAL_CONTROL_ENABLED
818
if (!AP_DDS_External_Control::handle_velocity_control(rx_velocity_control_topic)) {
819
// TODO #23430 handle velocity control failure through rosout, throttled.
820
}
821
#endif // AP_EXTERNAL_CONTROL_ENABLED
822
break;
823
}
824
#endif // AP_DDS_VEL_CTRL_ENABLED
825
#if AP_DDS_GLOBAL_POS_CTRL_ENABLED
826
case topics[to_underlying(TopicIndex::GLOBAL_POSITION_SUB)].dr_id.id: {
827
const bool success = ardupilot_msgs_msg_GlobalPosition_deserialize_topic(ub, &rx_global_position_control_topic);
828
if (success == false) {
829
break;
830
}
831
832
#if AP_EXTERNAL_CONTROL_ENABLED
833
if (!AP_DDS_External_Control::handle_global_position_control(rx_global_position_control_topic)) {
834
// TODO #23430 handle global position control failure through rosout, throttled.
835
}
836
#endif // AP_EXTERNAL_CONTROL_ENABLED
837
break;
838
}
839
#endif // AP_DDS_GLOBAL_POS_CTRL_ENABLED
840
}
841
842
}
843
844
/*
845
callback on request completion
846
*/
847
void 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)
848
{
849
AP_DDS_Client *dds = (AP_DDS_Client *)args;
850
dds->on_request(uxr_session, object_id, request_id, sample_id, ub, length);
851
}
852
853
void AP_DDS_Client::on_request(uxrSession* uxr_session, uxrObjectId object_id, uint16_t request_id, SampleIdentity* sample_id, ucdrBuffer* ub, uint16_t length)
854
{
855
(void) request_id;
856
(void) length;
857
switch (object_id.id) {
858
#if AP_DDS_ARM_SERVER_ENABLED
859
case services[to_underlying(ServiceIndex::ARMING_MOTORS)].rep_id: {
860
ardupilot_msgs_srv_ArmMotors_Request arm_motors_request;
861
ardupilot_msgs_srv_ArmMotors_Response arm_motors_response;
862
const bool deserialize_success = ardupilot_msgs_srv_ArmMotors_Request_deserialize_topic(ub, &arm_motors_request);
863
if (deserialize_success == false) {
864
break;
865
}
866
867
GCS_SEND_TEXT(MAV_SEVERITY_INFO, "%s Request for %sing received", msg_prefix, arm_motors_request.arm ? "arm" : "disarm");
868
#if AP_EXTERNAL_CONTROL_ENABLED
869
const bool do_checks = true;
870
arm_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);
871
if (!arm_motors_response.result) {
872
// TODO #23430 handle arm failure through rosout, throttled.
873
}
874
#endif // AP_EXTERNAL_CONTROL_ENABLED
875
876
const uxrObjectId replier_id = {
877
.id = services[to_underlying(ServiceIndex::ARMING_MOTORS)].rep_id,
878
.type = UXR_REPLIER_ID
879
};
880
881
uint8_t reply_buffer[8] {};
882
ucdrBuffer reply_ub;
883
884
ucdr_init_buffer(&reply_ub, reply_buffer, sizeof(reply_buffer));
885
const bool serialize_success = ardupilot_msgs_srv_ArmMotors_Response_serialize_topic(&reply_ub, &arm_motors_response);
886
if (serialize_success == false) {
887
break;
888
}
889
890
uxr_buffer_reply(uxr_session, reliable_out, replier_id, sample_id, reply_buffer, ucdr_buffer_length(&reply_ub));
891
GCS_SEND_TEXT(MAV_SEVERITY_INFO, "%s Request for Arming/Disarming : %s", msg_prefix, arm_motors_response.result ? "SUCCESS" : "FAIL");
892
break;
893
}
894
#endif // AP_DDS_ARM_SERVER_ENABLED
895
#if AP_DDS_MODE_SWITCH_SERVER_ENABLED
896
case services[to_underlying(ServiceIndex::MODE_SWITCH)].rep_id: {
897
ardupilot_msgs_srv_ModeSwitch_Request mode_switch_request;
898
ardupilot_msgs_srv_ModeSwitch_Response mode_switch_response;
899
const bool deserialize_success = ardupilot_msgs_srv_ModeSwitch_Request_deserialize_topic(ub, &mode_switch_request);
900
if (deserialize_success == false) {
901
break;
902
}
903
mode_switch_response.status = AP::vehicle()->set_mode(mode_switch_request.mode, ModeReason::DDS_COMMAND);
904
mode_switch_response.curr_mode = AP::vehicle()->get_mode();
905
906
const uxrObjectId replier_id = {
907
.id = services[to_underlying(ServiceIndex::MODE_SWITCH)].rep_id,
908
.type = UXR_REPLIER_ID
909
};
910
911
uint8_t reply_buffer[8] {};
912
ucdrBuffer reply_ub;
913
914
ucdr_init_buffer(&reply_ub, reply_buffer, sizeof(reply_buffer));
915
const bool serialize_success = ardupilot_msgs_srv_ModeSwitch_Response_serialize_topic(&reply_ub, &mode_switch_response);
916
if (serialize_success == false) {
917
break;
918
}
919
920
uxr_buffer_reply(uxr_session, reliable_out, replier_id, sample_id, reply_buffer, ucdr_buffer_length(&reply_ub));
921
GCS_SEND_TEXT(MAV_SEVERITY_INFO, "%s Request for Mode Switch : %s", msg_prefix, mode_switch_response.status ? "SUCCESS" : "FAIL");
922
break;
923
}
924
#endif // AP_DDS_MODE_SWITCH_SERVER_ENABLED
925
#if AP_DDS_VTOL_TAKEOFF_SERVER_ENABLED
926
case services[to_underlying(ServiceIndex::TAKEOFF)].rep_id: {
927
ardupilot_msgs_srv_Takeoff_Request takeoff_request;
928
ardupilot_msgs_srv_Takeoff_Response takeoff_response;
929
const bool deserialize_success = ardupilot_msgs_srv_Takeoff_Request_deserialize_topic(ub, &takeoff_request);
930
if (deserialize_success == false) {
931
break;
932
}
933
takeoff_response.status = AP::vehicle()->start_takeoff(takeoff_request.alt);
934
935
const uxrObjectId replier_id = {
936
.id = services[to_underlying(ServiceIndex::TAKEOFF)].rep_id,
937
.type = UXR_REPLIER_ID
938
};
939
940
uint8_t reply_buffer[8] {};
941
ucdrBuffer reply_ub;
942
943
ucdr_init_buffer(&reply_ub, reply_buffer, sizeof(reply_buffer));
944
const bool serialize_success = ardupilot_msgs_srv_Takeoff_Response_serialize_topic(&reply_ub, &takeoff_response);
945
if (serialize_success == false) {
946
break;
947
}
948
949
uxr_buffer_reply(uxr_session, reliable_out, replier_id, sample_id, reply_buffer, ucdr_buffer_length(&reply_ub));
950
GCS_SEND_TEXT(MAV_SEVERITY_INFO, "%s Request for Takeoff : %s", msg_prefix, takeoff_response.status ? "SUCCESS" : "FAIL");
951
break;
952
}
953
#endif // AP_DDS_VTOL_TAKEOFF_SERVER_ENABLED
954
#if AP_DDS_ARM_CHECK_SERVER_ENABLED
955
case services[to_underlying(ServiceIndex::PREARM_CHECK)].rep_id: {
956
std_srvs_srv_Trigger_Request prearm_check_request;
957
std_srvs_srv_Trigger_Response prearm_check_response;
958
const bool deserialize_success = std_srvs_srv_Trigger_Request_deserialize_topic(ub, &prearm_check_request);
959
if (deserialize_success == false) {
960
break;
961
}
962
prearm_check_response.success = AP::arming().pre_arm_checks(false);
963
STRCPY(prearm_check_response.message, prearm_check_response.success ? "Vehicle is Armable" : "Vehicle is Not Armable");
964
965
const uxrObjectId replier_id = {
966
.id = services[to_underlying(ServiceIndex::PREARM_CHECK)].rep_id,
967
.type = UXR_REPLIER_ID
968
};
969
970
uint8_t reply_buffer[sizeof(prearm_check_response.message) + 1] {};
971
ucdrBuffer reply_ub;
972
973
ucdr_init_buffer(&reply_ub, reply_buffer, sizeof(reply_buffer));
974
const bool serialize_success = std_srvs_srv_Trigger_Response_serialize_topic(&reply_ub, &prearm_check_response);
975
if (serialize_success == false) {
976
break;
977
}
978
979
uxr_buffer_reply(uxr_session, reliable_out, replier_id, sample_id, reply_buffer, ucdr_buffer_length(&reply_ub));
980
break;
981
}
982
#endif //AP_DDS_ARM_CHECK_SERVER_ENABLED
983
#if AP_DDS_PARAMETER_SERVER_ENABLED
984
case services[to_underlying(ServiceIndex::SET_PARAMETERS)].rep_id: {
985
const bool deserialize_success = rcl_interfaces_srv_SetParameters_Request_deserialize_topic(ub, &set_parameter_request);
986
if (deserialize_success == false) {
987
GCS_SEND_TEXT(MAV_SEVERITY_ERROR, "%s Set Parameters Request : Failed to deserialize request.", msg_prefix);
988
break;
989
}
990
991
if (set_parameter_request.parameters_size > 8U) {
992
break;
993
}
994
995
// Set parameters and responses for each one requested
996
set_parameter_response.results_size = set_parameter_request.parameters_size;
997
for (size_t i = 0; i < set_parameter_request.parameters_size; i++) {
998
param = set_parameter_request.parameters[i];
999
1000
enum ap_var_type var_type;
1001
1002
// set parameter
1003
AP_Param *vp;
1004
char param_key[AP_MAX_NAME_SIZE + 1];
1005
strncpy(param_key, (char *)param.name, AP_MAX_NAME_SIZE);
1006
param_key[AP_MAX_NAME_SIZE] = 0;
1007
1008
// Currently only integer and double value types can be set.
1009
// The following parameter value types are not handled:
1010
// bool, string, byte_array, bool_array, integer_array, double_array and string_array
1011
bool param_isnan = true;
1012
bool param_isinf = true;
1013
float param_value = 0.0f;
1014
switch (param.value.type) {
1015
case PARAMETER_INTEGER: {
1016
param_isnan = isnan(param.value.integer_value);
1017
param_isinf = isinf(param.value.integer_value);
1018
param_value = float(param.value.integer_value);
1019
break;
1020
}
1021
case PARAMETER_DOUBLE: {
1022
param_isnan = isnan(param.value.double_value);
1023
param_isinf = isinf(param.value.double_value);
1024
param_value = float(param.value.double_value);
1025
break;
1026
}
1027
default: {
1028
break;
1029
}
1030
}
1031
1032
// find existing param to get the old value
1033
uint16_t parameter_flags = 0;
1034
vp = AP_Param::find(param_key, &var_type, &parameter_flags);
1035
if (vp == nullptr || param_isnan || param_isinf) {
1036
set_parameter_response.results[i].successful = false;
1037
strncpy(set_parameter_response.results[i].reason, "Parameter not found", sizeof(set_parameter_response.results[i].reason));
1038
continue;
1039
}
1040
1041
// Add existing parameter checks used in GCS_Param.cpp
1042
if (parameter_flags & AP_PARAM_FLAG_INTERNAL_USE_ONLY) {
1043
// The user can set BRD_OPTIONS to enable set of internal
1044
// parameters, for developer testing or unusual use cases
1045
if (AP_BoardConfig::allow_set_internal_parameters()) {
1046
parameter_flags &= ~AP_PARAM_FLAG_INTERNAL_USE_ONLY;
1047
}
1048
}
1049
1050
if ((parameter_flags & AP_PARAM_FLAG_INTERNAL_USE_ONLY) || vp->is_read_only()) {
1051
set_parameter_response.results[i].successful = false;
1052
strncpy(set_parameter_response.results[i].reason, "Parameter is read only",sizeof(set_parameter_response.results[i].reason));
1053
continue;
1054
}
1055
1056
// Set and save the value if it changed
1057
bool force_save = vp->set_and_save_by_name_ifchanged(param_key, param_value);
1058
1059
if (force_save && (parameter_flags & AP_PARAM_FLAG_ENABLE)) {
1060
AP_Param::invalidate_count();
1061
}
1062
1063
set_parameter_response.results[i].successful = true;
1064
strncpy(set_parameter_response.results[i].reason, "Parameter accepted", sizeof(set_parameter_response.results[i].reason));
1065
}
1066
1067
const uxrObjectId replier_id = {
1068
.id = services[to_underlying(ServiceIndex::SET_PARAMETERS)].rep_id,
1069
.type = UXR_REPLIER_ID
1070
};
1071
1072
const uint32_t reply_size = rcl_interfaces_srv_SetParameters_Response_size_of_topic(&set_parameter_response, 0U);
1073
uint8_t reply_buffer[reply_size];
1074
memset(reply_buffer, 0, reply_size * sizeof(uint8_t));
1075
ucdrBuffer reply_ub;
1076
1077
ucdr_init_buffer(&reply_ub, reply_buffer, reply_size);
1078
const bool serialize_success = rcl_interfaces_srv_SetParameters_Response_serialize_topic(&reply_ub, &set_parameter_response);
1079
if (serialize_success == false) {
1080
break;
1081
}
1082
1083
uxr_buffer_reply(uxr_session, reliable_out, replier_id, sample_id, reply_buffer, ucdr_buffer_length(&reply_ub));
1084
bool successful_params = true;
1085
for (size_t i = 0; i < set_parameter_response.results_size; i++) {
1086
// Check that all the parameters were set successfully
1087
successful_params &= set_parameter_response.results[i].successful;
1088
}
1089
GCS_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" );
1090
break;
1091
}
1092
case services[to_underlying(ServiceIndex::GET_PARAMETERS)].rep_id: {
1093
const bool deserialize_success = rcl_interfaces_srv_GetParameters_Request_deserialize_topic(ub, &get_parameters_request);
1094
if (deserialize_success == false) {
1095
GCS_SEND_TEXT(MAV_SEVERITY_ERROR, "%s Get Parameters Request : Failed to deserialize request.", msg_prefix);
1096
break;
1097
}
1098
1099
if (get_parameters_request.names_size > 8U) {
1100
break;
1101
}
1102
1103
bool successful_read = true;
1104
get_parameters_response.values_size = get_parameters_request.names_size;
1105
for (size_t i = 0; i < get_parameters_request.names_size; i++) {
1106
enum ap_var_type var_type;
1107
1108
AP_Param *vp;
1109
char param_key[AP_MAX_NAME_SIZE + 1];
1110
strncpy(param_key, (char *)get_parameters_request.names[i], AP_MAX_NAME_SIZE);
1111
param_key[AP_MAX_NAME_SIZE] = 0;
1112
1113
vp = AP_Param::find(param_key, &var_type);
1114
if (vp == nullptr) {
1115
get_parameters_response.values[i].type = PARAMETER_NOT_SET;
1116
successful_read &= false;
1117
continue;
1118
}
1119
1120
switch (var_type) {
1121
case AP_PARAM_INT8: {
1122
get_parameters_response.values[i].type = PARAMETER_INTEGER;
1123
get_parameters_response.values[i].integer_value = ((AP_Int8 *)vp)->get();
1124
successful_read &= true;
1125
break;
1126
}
1127
case AP_PARAM_INT16: {
1128
get_parameters_response.values[i].type = PARAMETER_INTEGER;
1129
get_parameters_response.values[i].integer_value = ((AP_Int16 *)vp)->get();
1130
successful_read &= true;
1131
break;
1132
}
1133
case AP_PARAM_INT32: {
1134
get_parameters_response.values[i].type = PARAMETER_INTEGER;
1135
get_parameters_response.values[i].integer_value = ((AP_Int32 *)vp)->get();
1136
successful_read &= true;
1137
break;
1138
}
1139
case AP_PARAM_FLOAT: {
1140
get_parameters_response.values[i].type = PARAMETER_DOUBLE;
1141
get_parameters_response.values[i].double_value = vp->cast_to_float(var_type);
1142
successful_read &= true;
1143
break;
1144
}
1145
default: {
1146
get_parameters_response.values[i].type = PARAMETER_NOT_SET;
1147
successful_read &= false;
1148
break;
1149
}
1150
}
1151
}
1152
1153
const uxrObjectId replier_id = {
1154
.id = services[to_underlying(ServiceIndex::GET_PARAMETERS)].rep_id,
1155
.type = UXR_REPLIER_ID
1156
};
1157
1158
const uint32_t reply_size = rcl_interfaces_srv_GetParameters_Response_size_of_topic(&get_parameters_response, 0U);
1159
uint8_t reply_buffer[reply_size];
1160
memset(reply_buffer, 0, reply_size * sizeof(uint8_t));
1161
ucdrBuffer reply_ub;
1162
1163
ucdr_init_buffer(&reply_ub, reply_buffer, reply_size);
1164
const bool serialize_success = rcl_interfaces_srv_GetParameters_Response_serialize_topic(&reply_ub, &get_parameters_response);
1165
if (serialize_success == false) {
1166
break;
1167
}
1168
1169
uxr_buffer_reply(uxr_session, reliable_out, replier_id, sample_id, reply_buffer, ucdr_buffer_length(&reply_ub));
1170
1171
GCS_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");
1172
break;
1173
}
1174
#endif // AP_DDS_PARAMETER_SERVER_ENABLED
1175
}
1176
}
1177
1178
/*
1179
main loop for DDS thread
1180
*/
1181
void AP_DDS_Client::main_loop(void)
1182
{
1183
if (!init_transport()) {
1184
return;
1185
}
1186
1187
//! @todo check for request to stop task
1188
while (true) {
1189
if (comm == nullptr) {
1190
GCS_SEND_TEXT(MAV_SEVERITY_ERROR, "%s transport invalid, exiting", msg_prefix);
1191
return;
1192
}
1193
1194
// check ping
1195
if (!uxr_ping_agent_attempts(comm, ping_timeout_ms, ping_max_retry)) {
1196
GCS_SEND_TEXT(MAV_SEVERITY_ERROR, "%s No ping response, exiting", msg_prefix);
1197
return;
1198
}
1199
1200
// create session
1201
if (!init_session() || !create()) {
1202
GCS_SEND_TEXT(MAV_SEVERITY_ERROR, "%s Creation Requests failed", msg_prefix);
1203
return;
1204
}
1205
connected = true;
1206
GCS_SEND_TEXT(MAV_SEVERITY_INFO, "%s Initialization passed", msg_prefix);
1207
1208
#if AP_DDS_STATIC_TF_PUB_ENABLED
1209
populate_static_transforms(tx_static_transforms_topic);
1210
write_static_transforms();
1211
#endif // AP_DDS_STATIC_TF_PUB_ENABLED
1212
1213
uint64_t last_ping_ms{0};
1214
uint8_t num_pings_missed{0};
1215
bool had_ping_reply{false};
1216
while (connected) {
1217
hal.scheduler->delay(1);
1218
1219
// publish topics
1220
update();
1221
1222
// check ping response
1223
if (session.on_pong_flag == PONG_IN_SESSION_STATUS) {
1224
had_ping_reply = true;
1225
}
1226
1227
const auto cur_time_ms = AP_HAL::millis64();
1228
if (cur_time_ms - last_ping_ms > DELAY_PING_MS) {
1229
last_ping_ms = cur_time_ms;
1230
1231
if (had_ping_reply) {
1232
num_pings_missed = 0;
1233
1234
} else {
1235
++num_pings_missed;
1236
}
1237
1238
const int ping_agent_timeout_ms{0};
1239
const uint8_t ping_agent_attempts{1};
1240
uxr_ping_agent_session(&session, ping_agent_timeout_ms, ping_agent_attempts);
1241
1242
had_ping_reply = false;
1243
}
1244
1245
if (num_pings_missed > 2) {
1246
GCS_SEND_TEXT(MAV_SEVERITY_ERROR,
1247
"%s No ping response, disconnecting", msg_prefix);
1248
connected = false;
1249
}
1250
}
1251
1252
// delete session if connected
1253
if (connected) {
1254
uxr_delete_session(&session);
1255
}
1256
}
1257
}
1258
1259
bool AP_DDS_Client::init_transport()
1260
{
1261
// serial init will fail if the SERIALn_PROTOCOL is not setup
1262
bool initTransportStatus = ddsSerialInit();
1263
is_using_serial = initTransportStatus;
1264
1265
#if AP_DDS_UDP_ENABLED
1266
// fallback to UDP if available
1267
if (!initTransportStatus) {
1268
initTransportStatus = ddsUdpInit();
1269
}
1270
#endif
1271
1272
if (!initTransportStatus) {
1273
GCS_SEND_TEXT(MAV_SEVERITY_INFO, "%s Transport initialization failed", msg_prefix);
1274
return false;
1275
}
1276
1277
return true;
1278
}
1279
1280
bool AP_DDS_Client::init_session()
1281
{
1282
// init session
1283
uxr_init_session(&session, comm, key);
1284
1285
// Register topic callbacks
1286
uxr_set_topic_callback(&session, AP_DDS_Client::on_topic_trampoline, this);
1287
1288
// ROS-2 Service : Register service request callbacks
1289
uxr_set_request_callback(&session, AP_DDS_Client::on_request_trampoline, this);
1290
1291
while (!uxr_create_session(&session)) {
1292
GCS_SEND_TEXT(MAV_SEVERITY_INFO, "%s Initialization waiting...", msg_prefix);
1293
hal.scheduler->delay(1000);
1294
}
1295
1296
// setup reliable stream buffers
1297
input_reliable_stream = NEW_NOTHROW uint8_t[DDS_BUFFER_SIZE];
1298
output_reliable_stream = NEW_NOTHROW uint8_t[DDS_BUFFER_SIZE];
1299
if (input_reliable_stream == nullptr || output_reliable_stream == nullptr) {
1300
GCS_SEND_TEXT(MAV_SEVERITY_ERROR, "%s Allocation failed", msg_prefix);
1301
return false;
1302
}
1303
1304
reliable_in = uxr_create_input_reliable_stream(&session, input_reliable_stream, DDS_BUFFER_SIZE, DDS_STREAM_HISTORY);
1305
reliable_out = uxr_create_output_reliable_stream(&session, output_reliable_stream, DDS_BUFFER_SIZE, DDS_STREAM_HISTORY);
1306
1307
GCS_SEND_TEXT(MAV_SEVERITY_INFO, "%s Init complete", msg_prefix);
1308
1309
return true;
1310
}
1311
1312
bool AP_DDS_Client::create()
1313
{
1314
WITH_SEMAPHORE(csem);
1315
1316
// Participant
1317
const uxrObjectId participant_id = {
1318
.id = 0x01,
1319
.type = UXR_PARTICIPANT_ID
1320
};
1321
const char* participant_name = AP_DDS_PARTICIPANT_NAME;
1322
const auto participant_req_id = uxr_buffer_create_participant_bin(&session, reliable_out, participant_id,
1323
static_cast<uint16_t>(domain_id), participant_name, UXR_REPLACE);
1324
1325
//Participant requests
1326
constexpr uint8_t nRequestsParticipant = 1;
1327
const uint16_t requestsParticipant[nRequestsParticipant] = {participant_req_id};
1328
1329
constexpr uint16_t maxTimeMsPerRequestMs = 500;
1330
constexpr uint16_t requestTimeoutParticipantMs = (uint16_t) nRequestsParticipant * maxTimeMsPerRequestMs;
1331
uint8_t statusParticipant[nRequestsParticipant];
1332
if (!uxr_run_session_until_all_status(&session, requestTimeoutParticipantMs, requestsParticipant, statusParticipant, nRequestsParticipant)) {
1333
GCS_SEND_TEXT(MAV_SEVERITY_ERROR, "%s Participant session request failure", msg_prefix);
1334
// TODO add a failure log message sharing the status results
1335
return false;
1336
}
1337
1338
for (uint16_t i = 0 ; i < ARRAY_SIZE(topics); i++) {
1339
// Topic
1340
const uxrObjectId topic_id = {
1341
.id = topics[i].topic_id,
1342
.type = UXR_TOPIC_ID
1343
};
1344
const auto topic_req_id = uxr_buffer_create_topic_bin(&session, reliable_out, topic_id,
1345
participant_id, topics[i].topic_name, topics[i].type_name, UXR_REPLACE);
1346
1347
// Status requests
1348
constexpr uint8_t nRequests = 3;
1349
uint16_t requests[nRequests];
1350
constexpr uint16_t requestTimeoutMs = nRequests * maxTimeMsPerRequestMs;
1351
uint8_t status[nRequests];
1352
1353
if (topics[i].topic_rw == Topic_rw::DataWriter) {
1354
// Publisher
1355
const uxrObjectId pub_id = {
1356
.id = topics[i].pub_id,
1357
.type = UXR_PUBLISHER_ID
1358
};
1359
const auto pub_req_id = uxr_buffer_create_publisher_bin(&session, reliable_out, pub_id,
1360
participant_id, UXR_REPLACE);
1361
1362
// Data Writer
1363
const auto dwriter_req_id = uxr_buffer_create_datawriter_bin(&session, reliable_out, topics[i].dw_id,
1364
pub_id, topic_id, topics[i].qos, UXR_REPLACE);
1365
1366
// save the request statuses
1367
requests[0] = topic_req_id;
1368
requests[1] = pub_req_id;
1369
requests[2] = dwriter_req_id;
1370
1371
if (!uxr_run_session_until_all_status(&session, requestTimeoutMs, requests, status, nRequests)) {
1372
GCS_SEND_TEXT(MAV_SEVERITY_ERROR, "%s Topic/Pub/Writer session request failure for index '%u'", msg_prefix, i);
1373
for (uint8_t s = 0 ; s < nRequests; s++) {
1374
GCS_SEND_TEXT(MAV_SEVERITY_ERROR, "%s Status '%d' result '%u'", msg_prefix, s, status[s]);
1375
}
1376
// TODO add a failure log message sharing the status results
1377
return false;
1378
} else {
1379
GCS_SEND_TEXT(MAV_SEVERITY_INFO, "%s Topic/Pub/Writer session pass for index '%u'", msg_prefix, i);
1380
}
1381
} else if (topics[i].topic_rw == Topic_rw::DataReader) {
1382
// Subscriber
1383
const uxrObjectId sub_id = {
1384
.id = topics[i].sub_id,
1385
.type = UXR_SUBSCRIBER_ID
1386
};
1387
const auto sub_req_id = uxr_buffer_create_subscriber_bin(&session, reliable_out, sub_id,
1388
participant_id, UXR_REPLACE);
1389
1390
// Data Reader
1391
const auto dreader_req_id = uxr_buffer_create_datareader_bin(&session, reliable_out, topics[i].dr_id,
1392
sub_id, topic_id, topics[i].qos, UXR_REPLACE);
1393
1394
// save the request statuses
1395
requests[0] = topic_req_id;
1396
requests[1] = sub_req_id;
1397
requests[2] = dreader_req_id;
1398
1399
if (!uxr_run_session_until_all_status(&session, requestTimeoutMs, requests, status, nRequests)) {
1400
GCS_SEND_TEXT(MAV_SEVERITY_ERROR, "%s Topic/Sub/Reader session request failure for index '%u'", msg_prefix, i);
1401
for (uint8_t s = 0 ; s < nRequests; s++) {
1402
GCS_SEND_TEXT(MAV_SEVERITY_ERROR, "%s Status '%d' result '%u'", msg_prefix, s, status[s]);
1403
}
1404
// TODO add a failure log message sharing the status results
1405
return false;
1406
} else {
1407
GCS_SEND_TEXT(MAV_SEVERITY_INFO, "%s Topic/Sub/Reader session pass for index '%u'", msg_prefix, i);
1408
uxr_buffer_request_data(&session, reliable_out, topics[i].dr_id, reliable_in, &delivery_control);
1409
}
1410
}
1411
}
1412
1413
// ROS-2 Service : else case for service requests
1414
1415
for (uint16_t i = 0; i < ARRAY_SIZE(services); i++) {
1416
1417
constexpr uint16_t requestTimeoutMs = maxTimeMsPerRequestMs;
1418
1419
if (services[i].service_rr == Service_rr::Replier) {
1420
const uxrObjectId rep_id = {
1421
.id = services[i].rep_id,
1422
.type = UXR_REPLIER_ID
1423
};
1424
const auto replier_req_id = uxr_buffer_create_replier_bin(&session, reliable_out, rep_id,
1425
participant_id, services[i].service_name, services[i].request_type, services[i].reply_type,
1426
services[i].request_topic_name, services[i].reply_topic_name, services[i].qos, UXR_REPLACE);
1427
1428
uint16_t request = replier_req_id;
1429
uint8_t status;
1430
1431
if (!uxr_run_session_until_all_status(&session, requestTimeoutMs, &request, &status, 1)) {
1432
GCS_SEND_TEXT(MAV_SEVERITY_ERROR, "%s Service/Replier session request failure for index '%u'", msg_prefix, i);
1433
GCS_SEND_TEXT(MAV_SEVERITY_ERROR, "%s Status result '%u'", msg_prefix, status);
1434
// TODO add a failure log message sharing the status results
1435
return false;
1436
} else {
1437
GCS_SEND_TEXT(MAV_SEVERITY_INFO, "%s Service/Replier session pass for index '%u'", msg_prefix, i);
1438
uxr_buffer_request_data(&session, reliable_out, rep_id, reliable_in, &delivery_control);
1439
}
1440
1441
} else if (services[i].service_rr == Service_rr::Requester) {
1442
// TODO : Add Similar Code for Requester Profile
1443
}
1444
}
1445
1446
return true;
1447
}
1448
1449
void AP_DDS_Client::write_time_topic()
1450
{
1451
WITH_SEMAPHORE(csem);
1452
if (connected) {
1453
ucdrBuffer ub {};
1454
const uint32_t topic_size = builtin_interfaces_msg_Time_size_of_topic(&time_topic, 0);
1455
uxr_prepare_output_stream(&session, reliable_out, topics[to_underlying(TopicIndex::TIME_PUB)].dw_id, &ub, topic_size);
1456
const bool success = builtin_interfaces_msg_Time_serialize_topic(&ub, &time_topic);
1457
if (!success) {
1458
// TODO sometimes serialization fails on bootup. Determine why.
1459
// AP_HAL::panic("FATAL: XRCE_Client failed to serialize");
1460
}
1461
}
1462
}
1463
1464
#if AP_DDS_NAVSATFIX_PUB_ENABLED
1465
void AP_DDS_Client::write_nav_sat_fix_topic()
1466
{
1467
WITH_SEMAPHORE(csem);
1468
if (connected) {
1469
ucdrBuffer ub {};
1470
const uint32_t topic_size = sensor_msgs_msg_NavSatFix_size_of_topic(&nav_sat_fix_topic, 0);
1471
uxr_prepare_output_stream(&session, reliable_out, topics[to_underlying(TopicIndex::NAV_SAT_FIX_PUB)].dw_id, &ub, topic_size);
1472
const bool success = sensor_msgs_msg_NavSatFix_serialize_topic(&ub, &nav_sat_fix_topic);
1473
if (!success) {
1474
// TODO sometimes serialization fails on bootup. Determine why.
1475
// AP_HAL::panic("FATAL: DDS_Client failed to serialize");
1476
}
1477
}
1478
}
1479
#endif // AP_DDS_NAVSATFIX_PUB_ENABLED
1480
1481
#if AP_DDS_STATIC_TF_PUB_ENABLED
1482
void AP_DDS_Client::write_static_transforms()
1483
{
1484
WITH_SEMAPHORE(csem);
1485
if (connected) {
1486
ucdrBuffer ub {};
1487
const uint32_t topic_size = tf2_msgs_msg_TFMessage_size_of_topic(&tx_static_transforms_topic, 0);
1488
uxr_prepare_output_stream(&session, reliable_out, topics[to_underlying(TopicIndex::STATIC_TRANSFORMS_PUB)].dw_id, &ub, topic_size);
1489
const bool success = tf2_msgs_msg_TFMessage_serialize_topic(&ub, &tx_static_transforms_topic);
1490
if (!success) {
1491
// TODO sometimes serialization fails on bootup. Determine why.
1492
// AP_HAL::panic("FATAL: DDS_Client failed to serialize");
1493
}
1494
}
1495
}
1496
#endif // AP_DDS_STATIC_TF_PUB_ENABLED
1497
1498
#if AP_DDS_BATTERY_STATE_PUB_ENABLED
1499
void AP_DDS_Client::write_battery_state_topic()
1500
{
1501
WITH_SEMAPHORE(csem);
1502
if (connected) {
1503
ucdrBuffer ub {};
1504
const uint32_t topic_size = sensor_msgs_msg_BatteryState_size_of_topic(&battery_state_topic, 0);
1505
uxr_prepare_output_stream(&session, reliable_out, topics[to_underlying(TopicIndex::BATTERY_STATE_PUB)].dw_id, &ub, topic_size);
1506
const bool success = sensor_msgs_msg_BatteryState_serialize_topic(&ub, &battery_state_topic);
1507
if (!success) {
1508
// TODO sometimes serialization fails on bootup. Determine why.
1509
// AP_HAL::panic("FATAL: DDS_Client failed to serialize");
1510
}
1511
}
1512
}
1513
#endif // AP_DDS_BATTERY_STATE_PUB_ENABLED
1514
1515
#if AP_DDS_LOCAL_POSE_PUB_ENABLED
1516
void AP_DDS_Client::write_local_pose_topic()
1517
{
1518
WITH_SEMAPHORE(csem);
1519
if (connected) {
1520
ucdrBuffer ub {};
1521
const uint32_t topic_size = geometry_msgs_msg_PoseStamped_size_of_topic(&local_pose_topic, 0);
1522
uxr_prepare_output_stream(&session, reliable_out, topics[to_underlying(TopicIndex::LOCAL_POSE_PUB)].dw_id, &ub, topic_size);
1523
const bool success = geometry_msgs_msg_PoseStamped_serialize_topic(&ub, &local_pose_topic);
1524
if (!success) {
1525
// TODO sometimes serialization fails on bootup. Determine why.
1526
// AP_HAL::panic("FATAL: DDS_Client failed to serialize");
1527
}
1528
}
1529
}
1530
#endif // AP_DDS_LOCAL_POSE_PUB_ENABLED
1531
1532
#if AP_DDS_LOCAL_VEL_PUB_ENABLED
1533
void AP_DDS_Client::write_tx_local_velocity_topic()
1534
{
1535
WITH_SEMAPHORE(csem);
1536
if (connected) {
1537
ucdrBuffer ub {};
1538
const uint32_t topic_size = geometry_msgs_msg_TwistStamped_size_of_topic(&tx_local_velocity_topic, 0);
1539
uxr_prepare_output_stream(&session, reliable_out, topics[to_underlying(TopicIndex::LOCAL_VELOCITY_PUB)].dw_id, &ub, topic_size);
1540
const bool success = geometry_msgs_msg_TwistStamped_serialize_topic(&ub, &tx_local_velocity_topic);
1541
if (!success) {
1542
// TODO sometimes serialization fails on bootup. Determine why.
1543
// AP_HAL::panic("FATAL: DDS_Client failed to serialize");
1544
}
1545
}
1546
}
1547
#endif // AP_DDS_LOCAL_VEL_PUB_ENABLED
1548
#if AP_DDS_AIRSPEED_PUB_ENABLED
1549
void AP_DDS_Client::write_tx_local_airspeed_topic()
1550
{
1551
WITH_SEMAPHORE(csem);
1552
if (connected) {
1553
ucdrBuffer ub {};
1554
const uint32_t topic_size = geometry_msgs_msg_Vector3Stamped_size_of_topic(&tx_local_airspeed_topic, 0);
1555
uxr_prepare_output_stream(&session, reliable_out, topics[to_underlying(TopicIndex::LOCAL_AIRSPEED_PUB)].dw_id, &ub, topic_size);
1556
const bool success = geometry_msgs_msg_Vector3Stamped_serialize_topic(&ub, &tx_local_airspeed_topic);
1557
if (!success) {
1558
// TODO sometimes serialization fails on bootup. Determine why.
1559
// AP_HAL::panic("FATAL: DDS_Client failed to serialize");
1560
}
1561
}
1562
}
1563
#endif // AP_DDS_AIRSPEED_PUB_ENABLED
1564
#if AP_DDS_IMU_PUB_ENABLED
1565
void AP_DDS_Client::write_imu_topic()
1566
{
1567
WITH_SEMAPHORE(csem);
1568
if (connected) {
1569
ucdrBuffer ub {};
1570
const uint32_t topic_size = sensor_msgs_msg_Imu_size_of_topic(&imu_topic, 0);
1571
uxr_prepare_output_stream(&session, reliable_out, topics[to_underlying(TopicIndex::IMU_PUB)].dw_id, &ub, topic_size);
1572
const bool success = sensor_msgs_msg_Imu_serialize_topic(&ub, &imu_topic);
1573
if (!success) {
1574
// TODO sometimes serialization fails on bootup. Determine why.
1575
// AP_HAL::panic("FATAL: DDS_Client failed to serialize");
1576
}
1577
}
1578
}
1579
#endif // AP_DDS_IMU_PUB_ENABLED
1580
1581
#if AP_DDS_GEOPOSE_PUB_ENABLED
1582
void AP_DDS_Client::write_geo_pose_topic()
1583
{
1584
WITH_SEMAPHORE(csem);
1585
if (connected) {
1586
ucdrBuffer ub {};
1587
const uint32_t topic_size = geographic_msgs_msg_GeoPoseStamped_size_of_topic(&geo_pose_topic, 0);
1588
uxr_prepare_output_stream(&session, reliable_out, topics[to_underlying(TopicIndex::GEOPOSE_PUB)].dw_id, &ub, topic_size);
1589
const bool success = geographic_msgs_msg_GeoPoseStamped_serialize_topic(&ub, &geo_pose_topic);
1590
if (!success) {
1591
// TODO sometimes serialization fails on bootup. Determine why.
1592
// AP_HAL::panic("FATAL: DDS_Client failed to serialize");
1593
}
1594
}
1595
}
1596
#endif // AP_DDS_GEOPOSE_PUB_ENABLED
1597
1598
#if AP_DDS_CLOCK_PUB_ENABLED
1599
void AP_DDS_Client::write_clock_topic()
1600
{
1601
WITH_SEMAPHORE(csem);
1602
if (connected) {
1603
ucdrBuffer ub {};
1604
const uint32_t topic_size = rosgraph_msgs_msg_Clock_size_of_topic(&clock_topic, 0);
1605
uxr_prepare_output_stream(&session, reliable_out, topics[to_underlying(TopicIndex::CLOCK_PUB)].dw_id, &ub, topic_size);
1606
const bool success = rosgraph_msgs_msg_Clock_serialize_topic(&ub, &clock_topic);
1607
if (!success) {
1608
// TODO sometimes serialization fails on bootup. Determine why.
1609
// AP_HAL::panic("FATAL: DDS_Client failed to serialize");
1610
}
1611
}
1612
}
1613
#endif // AP_DDS_CLOCK_PUB_ENABLED
1614
1615
#if AP_DDS_GPS_GLOBAL_ORIGIN_PUB_ENABLED
1616
void AP_DDS_Client::write_gps_global_origin_topic()
1617
{
1618
WITH_SEMAPHORE(csem);
1619
if (connected) {
1620
ucdrBuffer ub {};
1621
const uint32_t topic_size = geographic_msgs_msg_GeoPointStamped_size_of_topic(&gps_global_origin_topic, 0);
1622
uxr_prepare_output_stream(&session, reliable_out, topics[to_underlying(TopicIndex::GPS_GLOBAL_ORIGIN_PUB)].dw_id, &ub, topic_size);
1623
const bool success = geographic_msgs_msg_GeoPointStamped_serialize_topic(&ub, &gps_global_origin_topic);
1624
if (!success) {
1625
// AP_HAL::panic("FATAL: DDS_Client failed to serialize");
1626
}
1627
}
1628
}
1629
#endif // AP_DDS_GPS_GLOBAL_ORIGIN_PUB_ENABLED
1630
1631
#if AP_DDS_GOAL_PUB_ENABLED
1632
void AP_DDS_Client::write_goal_topic()
1633
{
1634
WITH_SEMAPHORE(csem);
1635
if (connected) {
1636
ucdrBuffer ub {};
1637
const uint32_t topic_size = geographic_msgs_msg_GeoPointStamped_size_of_topic(&goal_topic, 0);
1638
uxr_prepare_output_stream(&session, reliable_out, topics[to_underlying(TopicIndex::GOAL_PUB)].dw_id, &ub, topic_size);
1639
const bool success = geographic_msgs_msg_GeoPointStamped_serialize_topic(&ub, &goal_topic);
1640
if (!success) {
1641
// AP_HAL::panic("FATAL: DDS_Client failed to serialize");
1642
}
1643
}
1644
}
1645
#endif // AP_DDS_GOAL_PUB_ENABLED
1646
1647
#if AP_DDS_STATUS_PUB_ENABLED
1648
void AP_DDS_Client::write_status_topic()
1649
{
1650
WITH_SEMAPHORE(csem);
1651
if (connected) {
1652
ucdrBuffer ub {};
1653
const uint32_t topic_size = ardupilot_msgs_msg_Status_size_of_topic(&status_topic, 0);
1654
uxr_prepare_output_stream(&session, reliable_out, topics[to_underlying(TopicIndex::STATUS_PUB)].dw_id, &ub, topic_size);
1655
const bool success = ardupilot_msgs_msg_Status_serialize_topic(&ub, &status_topic);
1656
if (!success) {
1657
// TODO sometimes serialization fails on bootup. Determine why.
1658
// AP_HAL::panic("FATAL: DDS_Client failed to serialize");
1659
}
1660
}
1661
}
1662
#endif // AP_DDS_STATUS_PUB_ENABLED
1663
1664
void AP_DDS_Client::update()
1665
{
1666
WITH_SEMAPHORE(csem);
1667
const auto cur_time_ms = AP_HAL::millis64();
1668
1669
#if AP_DDS_TIME_PUB_ENABLED
1670
if (cur_time_ms - last_time_time_ms > DELAY_TIME_TOPIC_MS) {
1671
update_topic(time_topic);
1672
last_time_time_ms = cur_time_ms;
1673
write_time_topic();
1674
}
1675
#endif // AP_DDS_TIME_PUB_ENABLED
1676
#if AP_DDS_NAVSATFIX_PUB_ENABLED
1677
constexpr uint8_t gps_instance = 0;
1678
if (update_topic(nav_sat_fix_topic, gps_instance)) {
1679
write_nav_sat_fix_topic();
1680
}
1681
#endif // AP_DDS_NAVSATFIX_PUB_ENABLED
1682
#if AP_DDS_BATTERY_STATE_PUB_ENABLED
1683
if (cur_time_ms - last_battery_state_time_ms > DELAY_BATTERY_STATE_TOPIC_MS) {
1684
for (uint8_t battery_instance = 0; battery_instance < AP_BATT_MONITOR_MAX_INSTANCES; battery_instance++) {
1685
update_topic(battery_state_topic, battery_instance);
1686
if (battery_state_topic.present) {
1687
write_battery_state_topic();
1688
}
1689
}
1690
last_battery_state_time_ms = cur_time_ms;
1691
}
1692
#endif // AP_DDS_BATTERY_STATE_PUB_ENABLED
1693
#if AP_DDS_LOCAL_POSE_PUB_ENABLED
1694
if (cur_time_ms - last_local_pose_time_ms > DELAY_LOCAL_POSE_TOPIC_MS) {
1695
update_topic(local_pose_topic);
1696
last_local_pose_time_ms = cur_time_ms;
1697
write_local_pose_topic();
1698
}
1699
#endif // AP_DDS_LOCAL_POSE_PUB_ENABLED
1700
#if AP_DDS_LOCAL_VEL_PUB_ENABLED
1701
if (cur_time_ms - last_local_velocity_time_ms > DELAY_LOCAL_VELOCITY_TOPIC_MS) {
1702
update_topic(tx_local_velocity_topic);
1703
last_local_velocity_time_ms = cur_time_ms;
1704
write_tx_local_velocity_topic();
1705
}
1706
#endif // AP_DDS_LOCAL_VEL_PUB_ENABLED
1707
#if AP_DDS_AIRSPEED_PUB_ENABLED
1708
if (cur_time_ms - last_airspeed_time_ms > DELAY_AIRSPEED_TOPIC_MS) {
1709
last_airspeed_time_ms = cur_time_ms;
1710
if (update_topic(tx_local_airspeed_topic)) {
1711
write_tx_local_airspeed_topic();
1712
}
1713
}
1714
#endif // AP_DDS_AIRSPEED_PUB_ENABLED
1715
#if AP_DDS_IMU_PUB_ENABLED
1716
if (cur_time_ms - last_imu_time_ms > DELAY_IMU_TOPIC_MS) {
1717
update_topic(imu_topic);
1718
last_imu_time_ms = cur_time_ms;
1719
write_imu_topic();
1720
}
1721
#endif // AP_DDS_IMU_PUB_ENABLED
1722
#if AP_DDS_GEOPOSE_PUB_ENABLED
1723
if (cur_time_ms - last_geo_pose_time_ms > DELAY_GEO_POSE_TOPIC_MS) {
1724
update_topic(geo_pose_topic);
1725
last_geo_pose_time_ms = cur_time_ms;
1726
write_geo_pose_topic();
1727
}
1728
#endif // AP_DDS_GEOPOSE_PUB_ENABLED
1729
#if AP_DDS_CLOCK_PUB_ENABLED
1730
if (cur_time_ms - last_clock_time_ms > DELAY_CLOCK_TOPIC_MS) {
1731
update_topic(clock_topic);
1732
last_clock_time_ms = cur_time_ms;
1733
write_clock_topic();
1734
}
1735
#endif // AP_DDS_CLOCK_PUB_ENABLED
1736
#if AP_DDS_GPS_GLOBAL_ORIGIN_PUB_ENABLED
1737
if (cur_time_ms - last_gps_global_origin_time_ms > DELAY_GPS_GLOBAL_ORIGIN_TOPIC_MS) {
1738
update_topic(gps_global_origin_topic);
1739
last_gps_global_origin_time_ms = cur_time_ms;
1740
write_gps_global_origin_topic();
1741
}
1742
#endif // AP_DDS_GPS_GLOBAL_ORIGIN_PUB_ENABLED
1743
#if AP_DDS_GOAL_PUB_ENABLED
1744
if (cur_time_ms - last_goal_time_ms > DELAY_GOAL_TOPIC_MS) {
1745
if (update_topic_goal(goal_topic)) {
1746
write_goal_topic();
1747
}
1748
last_goal_time_ms = cur_time_ms;
1749
}
1750
#endif // AP_DDS_GOAL_PUB_ENABLED
1751
#if AP_DDS_STATUS_PUB_ENABLED
1752
if (cur_time_ms - last_status_check_time_ms > DELAY_STATUS_TOPIC_MS) {
1753
if (update_topic(status_topic)) {
1754
write_status_topic();
1755
}
1756
last_status_check_time_ms = cur_time_ms;
1757
}
1758
#endif // AP_DDS_STATUS_PUB_ENABLED
1759
1760
status_ok = uxr_run_session_time(&session, 1);
1761
}
1762
1763
#if CONFIG_HAL_BOARD != HAL_BOARD_SITL && CONFIG_HAL_BOARD != HAL_BOARD_ESP32
1764
extern "C" {
1765
int clock_gettime(clockid_t clockid, struct timespec *ts);
1766
}
1767
1768
int clock_gettime(clockid_t clockid, struct timespec *ts)
1769
{
1770
//! @todo the value of clockid is ignored here.
1771
//! A fallback mechanism is employed against the caller's choice of clock.
1772
uint64_t utc_usec;
1773
if (!AP::rtc().get_utc_usec(utc_usec)) {
1774
utc_usec = AP_HAL::micros64();
1775
}
1776
ts->tv_sec = utc_usec / 1000000ULL;
1777
ts->tv_nsec = (utc_usec % 1000000ULL) * 1000UL;
1778
return 0;
1779
}
1780
#endif // CONFIG_HAL_BOARD != HAL_BOARD_SITL && CONFIG_HAL_BOARD != HAL_BOARD_ESP32
1781
1782
#endif // AP_DDS_ENABLED
1783
1784