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