00001
00009
00010
00011
00012
00013
00014
00015
00016
00017 #include <chrono>
00018 #include <condition_variable>
00019 #include <mavros/mavros_plugin.h>
00020 #include <pluginlib/class_list_macros.h>
00021
00022 #include <mavros_msgs/CommandLong.h>
00023 #include <mavros_msgs/CommandInt.h>
00024 #include <mavros_msgs/CommandBool.h>
00025 #include <mavros_msgs/CommandHome.h>
00026 #include <mavros_msgs/CommandTOL.h>
00027 #include <mavros_msgs/CommandTriggerControl.h>
00028
00029 namespace mavplugin {
00030 class CommandTransaction {
00031 public:
00032 std::mutex cond_mutex;
00033 std::condition_variable ack;
00034 uint16_t expected_command;
00035 uint8_t result;
00036
00037 explicit CommandTransaction(uint16_t command) :
00038 ack(),
00039 expected_command(command),
00040 result(MAV_RESULT_FAILED)
00041 { }
00042 };
00043
00049 class CommandPlugin : public MavRosPlugin {
00050 public:
00051 CommandPlugin() :
00052 uas(nullptr),
00053 cmd_nh("~cmd"),
00054 use_comp_id_system_control(false),
00055 ACK_TIMEOUT_DT(ACK_TIMEOUT_MS / 1000.0)
00056 { };
00057
00058 void initialize(UAS &uas_)
00059 {
00060 uas = &uas_;
00061
00062 cmd_nh.param("use_comp_id_system_control", use_comp_id_system_control, false);
00063
00064 command_long_srv = cmd_nh.advertiseService("command", &CommandPlugin::command_long_cb, this);
00065 command_int_srv = cmd_nh.advertiseService("command_int", &CommandPlugin::command_int_cb, this);
00066 arming_srv = cmd_nh.advertiseService("arming", &CommandPlugin::arming_cb, this);
00067 set_home_srv = cmd_nh.advertiseService("set_home", &CommandPlugin::set_home_cb, this);
00068 takeoff_srv = cmd_nh.advertiseService("takeoff", &CommandPlugin::takeoff_cb, this);
00069 land_srv = cmd_nh.advertiseService("land", &CommandPlugin::land_cb, this);
00070 trigger_srv = cmd_nh.advertiseService("trigger_control", &CommandPlugin::trigger_control_cb, this);
00071 }
00072
00073 const message_map get_rx_handlers() {
00074 return {
00075 MESSAGE_HANDLER(MAVLINK_MSG_ID_COMMAND_ACK, &CommandPlugin::handle_command_ack)
00076 };
00077 }
00078
00079 private:
00080 std::recursive_mutex mutex;
00081 UAS *uas;
00082
00083 ros::NodeHandle cmd_nh;
00084 ros::ServiceServer command_long_srv;
00085 ros::ServiceServer command_int_srv;
00086 ros::ServiceServer arming_srv;
00087 ros::ServiceServer set_home_srv;
00088 ros::ServiceServer takeoff_srv;
00089 ros::ServiceServer land_srv;
00090 ros::ServiceServer trigger_srv;
00091
00092 bool use_comp_id_system_control;
00093
00094 std::list<CommandTransaction *> ack_waiting_list;
00095 static constexpr int ACK_TIMEOUT_MS = 5000;
00096
00097 const ros::Duration ACK_TIMEOUT_DT;
00098
00099
00100
00101 void handle_command_ack(const mavlink_message_t *msg, uint8_t sysid, uint8_t compid) {
00102 mavlink_command_ack_t ack;
00103 mavlink_msg_command_ack_decode(msg, &ack);
00104
00105 lock_guard lock(mutex);
00106 for (auto it = ack_waiting_list.cbegin();
00107 it != ack_waiting_list.cend(); it++)
00108 if ((*it)->expected_command == ack.command) {
00109 (*it)->result = ack.result;
00110 (*it)->ack.notify_all();
00111 return;
00112 }
00113
00114 ROS_WARN_THROTTLE_NAMED(10, "cmd", "CMD: Unexpected command %u, result %u",
00115 ack.command, ack.result);
00116 }
00117
00118
00119
00120 bool wait_ack_for(CommandTransaction *tr) {
00121 std::unique_lock<std::mutex> lock(tr->cond_mutex);
00122
00123 return tr->ack.wait_for(lock, std::chrono::nanoseconds(ACK_TIMEOUT_DT.toNSec()))
00124 == std::cv_status::no_timeout;
00125 }
00126
00132 bool send_command_long_and_wait(bool broadcast,
00133 uint16_t command, uint8_t confirmation,
00134 float param1, float param2,
00135 float param3, float param4,
00136 float param5, float param6,
00137 float param7,
00138 unsigned char &success, uint8_t &result)
00139 {
00140 unique_lock lock(mutex);
00141
00142
00143 for (auto it = ack_waiting_list.cbegin();
00144 it != ack_waiting_list.cend(); it++)
00145 if ((*it)->expected_command == command) {
00146 ROS_WARN_THROTTLE_NAMED(10, "cmd", "CMD: Command %u alredy in progress", command);
00147 return false;
00148 }
00149
00154 bool is_ack_required = (confirmation != 0 || uas->is_ardupilotmega()) && !uas->is_px4();
00155 if (is_ack_required && !broadcast)
00156 ack_waiting_list.push_back(new CommandTransaction(command));
00157
00158 command_long(broadcast,
00159 command, confirmation,
00160 param1, param2,
00161 param3, param4,
00162 param5, param6,
00163 param7);
00164
00165 if (is_ack_required && !broadcast) {
00166 auto it = ack_waiting_list.begin();
00167 for (; it != ack_waiting_list.end(); it++)
00168 if ((*it)->expected_command == command)
00169 break;
00170
00171 if (it == ack_waiting_list.end()) {
00172 ROS_ERROR_NAMED("cmd", "CMD: CommandTransaction not found for %u", command);
00173 return false;
00174 }
00175
00176 lock.unlock();
00177 bool is_not_timeout = wait_ack_for(*it);
00178 lock.lock();
00179
00180 success = is_not_timeout && (*it)->result == MAV_RESULT_ACCEPTED;
00181 result = (*it)->result;
00182
00183 delete *it;
00184 ack_waiting_list.erase(it);
00185 }
00186 else {
00187 success = true;
00188 result = MAV_RESULT_ACCEPTED;
00189 }
00190
00191 return true;
00192 }
00193
00197 bool send_command_int(bool broadcast,
00198 uint8_t frame, uint16_t command,
00199 uint8_t current, uint8_t autocontinue,
00200 float param1, float param2,
00201 float param3, float param4,
00202 int32_t x, int32_t y,
00203 float z,
00204 unsigned char &success) {
00205
00206
00207
00208 command_int(broadcast,
00209 frame, command, current, autocontinue,
00210 param1, param2,
00211 param3, param4,
00212 x, y, z);
00213
00214 success = true;
00215 return true;
00216 }
00217
00218
00219
00220 void command_long(bool broadcast,
00221 uint16_t command, uint8_t confirmation,
00222 float param1, float param2,
00223 float param3, float param4,
00224 float param5, float param6,
00225 float param7)
00226 {
00227 mavlink_message_t msg;
00228 const uint8_t tgt_sys_id = (broadcast) ? 0 : uas->get_tgt_system();
00229 const uint8_t tgt_comp_id = (broadcast) ? 0 :
00230 (use_comp_id_system_control) ?
00231 MAV_COMP_ID_SYSTEM_CONTROL : uas->get_tgt_component();
00232 const uint8_t confirmation_fixed = (broadcast) ? 0 : confirmation;
00233
00234 mavlink_msg_command_long_pack_chan(UAS_PACK_CHAN(uas), &msg,
00235 tgt_sys_id,
00236 tgt_comp_id,
00237 command,
00238 confirmation_fixed,
00239 param1, param2,
00240 param3, param4,
00241 param5, param6,
00242 param7);
00243 UAS_FCU(uas)->send_message(&msg);
00244 }
00245
00246 void command_int(bool broadcast,
00247 uint8_t frame, uint16_t command,
00248 uint8_t current, uint8_t autocontinue,
00249 float param1, float param2,
00250 float param3, float param4,
00251 int32_t x, int32_t y,
00252 float z)
00253 {
00254 mavlink_message_t msg;
00255 const uint8_t tgt_sys_id = (broadcast) ? 0 : uas->get_tgt_system();
00256 const uint8_t tgt_comp_id = (broadcast) ? 0 :
00257 (use_comp_id_system_control) ?
00258 MAV_COMP_ID_SYSTEM_CONTROL : uas->get_tgt_component();
00259
00260 mavlink_msg_command_int_pack_chan(UAS_PACK_CHAN(uas), &msg,
00261 tgt_sys_id,
00262 tgt_comp_id,
00263 frame,
00264 command,
00265 current,
00266 autocontinue,
00267 param1, param2,
00268 param3, param4,
00269 x, y, z);
00270 UAS_FCU(uas)->send_message(&msg);
00271 }
00272
00273
00274
00275 bool command_long_cb(mavros_msgs::CommandLong::Request &req,
00276 mavros_msgs::CommandLong::Response &res) {
00277 return send_command_long_and_wait(req.broadcast,
00278 req.command, req.confirmation,
00279 req.param1, req.param2,
00280 req.param3, req.param4,
00281 req.param5, req.param6,
00282 req.param7,
00283 res.success, res.result);
00284 }
00285
00286 bool command_int_cb(mavros_msgs::CommandInt::Request &req,
00287 mavros_msgs::CommandInt::Response &res) {
00288 return send_command_int(req.broadcast,
00289 req.frame, req.command,
00290 req.current, req.autocontinue,
00291 req.param1, req.param2,
00292 req.param3, req.param4,
00293 req.x, req.y, req.z,
00294 res.success);
00295 }
00296
00297 bool arming_cb(mavros_msgs::CommandBool::Request &req,
00298 mavros_msgs::CommandBool::Response &res) {
00299 return send_command_long_and_wait(false,
00300 MAV_CMD_COMPONENT_ARM_DISARM, 1,
00301 (req.value) ? 1.0 : 0.0,
00302 0, 0, 0, 0, 0, 0,
00303 res.success, res.result);
00304 }
00305
00306 bool set_home_cb(mavros_msgs::CommandHome::Request &req,
00307 mavros_msgs::CommandHome::Response &res) {
00308 return send_command_long_and_wait(false,
00309 MAV_CMD_DO_SET_HOME, 1,
00310 (req.current_gps) ? 1.0 : 0.0,
00311 0, 0, 0, req.latitude, req.longitude, req.altitude,
00312 res.success, res.result);
00313 }
00314
00315 bool takeoff_cb(mavros_msgs::CommandTOL::Request &req,
00316 mavros_msgs::CommandTOL::Response &res) {
00317 return send_command_long_and_wait(false,
00318 MAV_CMD_NAV_TAKEOFF, 1,
00319 req.min_pitch,
00320 0, 0,
00321 req.yaw,
00322 req.latitude, req.longitude, req.altitude,
00323 res.success, res.result);
00324 }
00325
00326 bool land_cb(mavros_msgs::CommandTOL::Request &req,
00327 mavros_msgs::CommandTOL::Response &res) {
00328 return send_command_long_and_wait(false,
00329 MAV_CMD_NAV_LAND, 1,
00330 0, 0, 0,
00331 req.yaw,
00332 req.latitude, req.longitude, req.altitude,
00333 res.success, res.result);
00334 }
00335
00336 bool trigger_control_cb(mavros_msgs::CommandTriggerControl::Request &req,
00337 mavros_msgs::CommandTriggerControl::Response &res) {
00338 return send_command_long_and_wait(false,
00339 MAV_CMD_DO_TRIGGER_CONTROL, 1,
00340 (req.trigger_enable)? 1.0 : 0.0,
00341 req.integration_time,
00342 0, 0, 0, 0, 0,
00343 res.success, res.result);
00344 }
00345 };
00346 };
00347
00348 PLUGINLIB_EXPORT_CLASS(mavplugin::CommandPlugin, mavplugin::MavRosPlugin)
00349