18 #include <condition_variable> 21 #include <mavros_msgs/ParamSet.h> 22 #include <mavros_msgs/ParamGet.h> 23 #include <mavros_msgs/ParamPull.h> 24 #include <mavros_msgs/ParamPush.h> 25 #include <mavros_msgs/Param.h> 28 namespace std_plugins {
51 using MT = mavlink::common::MAV_PARAM_TYPE;
60 void set_value(mavlink::common::msg::PARAM_VALUE &pmsg)
62 mavlink::mavlink_param_union_t uv;
63 uv.param_float = pmsg.param_value;
69 switch (pmsg.param_type) {
87 int_tmp = uv.param_int8;
88 param_value = int_tmp;
91 int_tmp = uv.param_uint8;
92 param_value = int_tmp;
95 int_tmp = uv.param_int16;
96 param_value = int_tmp;
99 int_tmp = uv.param_uint16;
100 param_value = int_tmp;
103 int_tmp = uv.param_int32;
104 param_value = int_tmp;
107 int_tmp = uv.param_uint32;
108 param_value = int_tmp;
111 float_tmp = uv.param_float;
112 param_value = float_tmp;
117 ROS_WARN_NAMED(
"param",
"PM: Unsupported param %.16s (%u/%u) type: %u",
118 pmsg.param_id.data(), pmsg.param_index, pmsg.param_count, pmsg.param_type);
131 switch (pmsg.param_type) {
141 int_tmp = pmsg.param_value;
142 param_value = int_tmp;
145 int_tmp = pmsg.param_value;
146 param_value = int_tmp;
149 int_tmp = pmsg.param_value;
150 param_value = int_tmp;
153 int_tmp = pmsg.param_value;
154 param_value = int_tmp;
157 int_tmp = pmsg.param_value;
158 param_value = int_tmp;
161 int_tmp = pmsg.param_value;
162 param_value = int_tmp;
165 float_tmp = pmsg.param_value;
166 param_value = float_tmp;
171 ROS_WARN_NAMED(
"param",
"PM: Unsupported param %.16s (%u/%u) type: %u",
172 pmsg.param_id.data(), pmsg.param_index, pmsg.param_count, pmsg.param_type);
183 mavlink::mavlink_param_union_t uv;
188 switch (param_value.
getType()) {
208 uv.param_int32 =
static_cast<int32_t
>(
param_value);
221 ret.param_value = uv.param_float;
232 switch (param_value.
getType()) {
241 ret.param_value =
static_cast<bool &
>(
param_value);
245 ret.param_value =
static_cast<int32_t &
>(
param_value);
249 ret.param_value =
static_cast<double &
>(
param_value);
266 switch (param_value.
getType()) {
285 return static_cast<double>(param_value);
298 mavros_msgs::Param
msg;
317 return param_id ==
"SYSID_SW_MREV" ||
318 param_id ==
"SYS_NUM_RESETS" ||
319 param_id ==
"ARSPD_OFFSET" ||
320 param_id ==
"GND_ABS_PRESS" ||
321 param_id ==
"GND_ABS_PRESS2" ||
322 param_id ==
"GND_ABS_PRESS3" ||
323 param_id ==
"STAT_BOOTCNT" ||
324 param_id ==
"STAT_FLTTIME" ||
325 param_id ==
"STAT_RESET" ||
326 param_id ==
"STAT_RUNTIME" ||
327 param_id ==
"GND_TEMP" ||
328 param_id ==
"CMD_TOTAL" ||
329 param_id ==
"CMD_INDEX" ||
330 param_id ==
"LOG_LASTFILE" ||
331 param_id ==
"FENCE_TOTAL" ||
332 param_id ==
"FORMAT_VERSION";
344 retries_remaining(_rem),
352 std::condition_variable
ack;
364 param_state(
PR::IDLE),
366 RETRIES_COUNT(_RETRIES_COUNT),
367 param_rx_retries(RETRIES_COUNT),
368 BOOTUP_TIME_DT(BOOTUP_TIME_MS / 1000.0),
369 LIST_TIMEOUT_DT(LIST_TIMEOUT_MS / 1000.0),
370 PARAM_TIMEOUT_DT(PARAM_TIMEOUT_MS / 1000.0)
375 PluginBase::initialize(uas_);
382 param_value_pub = param_nh.advertise<mavros_msgs::Param>(
"param_value", 100);
385 shedule_timer.stop();
387 timeout_timer.stop();
388 enable_connection_cb();
415 static constexpr
int BOOTUP_TIME_MS = 10000;
416 static constexpr
int PARAM_TIMEOUT_MS = 1000;
417 static constexpr
int LIST_TIMEOUT_MS = 30000;
418 static constexpr
int _RETRIES_COUNT = 3;
445 void handle_param_value(
const mavlink::mavlink_message_t *msg, mavlink::common::msg::PARAM_VALUE &pmsg)
452 auto param_it = parameters.find(
param_id);
453 if (param_it != parameters.end()) {
455 auto &p = param_it->second;
457 if (m_uas->is_ardupilotmega())
458 p.set_value_apm_quirk(pmsg);
463 auto set_it = set_parameters.find(
param_id);
464 if (set_it != set_parameters.end()) {
465 set_it->second->ack.notify_all();
468 param_value_pub.
publish(p.to_msg());
471 ((p.param_index != pmsg.param_index &&
472 pmsg.param_index != UINT16_MAX) ||
473 p.param_count != pmsg.param_count),
475 "PR: Param " << p.to_string() <<
" different index: " << pmsg.param_index <<
"/" << pmsg.param_count);
482 p.param_index = pmsg.param_index;
483 p.param_count = pmsg.param_count;
485 if (m_uas->is_ardupilotmega())
486 p.set_value_apm_quirk(pmsg);
492 param_value_pub.
publish(p.to_msg());
497 if (param_state == PR::RXLIST || param_state == PR::RXPARAM || param_state == PR::RXPARAM_TIMEDOUT) {
500 if (param_state == PR::RXLIST) {
501 param_count = pmsg.param_count;
502 param_state = PR::RXPARAM;
504 parameters_missing_idx.clear();
505 if (param_count != UINT16_MAX) {
509 parameters_missing_idx.push_back(idx);
512 ROS_WARN_NAMED(
"param",
"PR: FCU does not know index for first element! " 513 "Param list may be truncated.");
518 bool it_is_first_requested = parameters_missing_idx.front() == pmsg.param_index;
521 parameters_missing_idx.remove(pmsg.param_index);
524 if (it_is_first_requested) {
525 ROS_DEBUG_NAMED(
"param",
"PR: got a value of a requested param idx=%u, " 526 "resetting retries count", pmsg.param_index);
527 param_rx_retries = RETRIES_COUNT;
528 }
else if (param_state == PR::RXPARAM_TIMEDOUT) {
529 ROS_INFO_NAMED(
"param",
"PR: got an unsolicited param value idx=%u, " 530 "not resetting retries count %zu", pmsg.param_index, param_rx_retries);
533 restart_timeout_timer();
536 if (parameters_missing_idx.empty()) {
537 ssize_t missed = param_count - parameters.size();
540 "PR: parameters list received, but %zd parametars are missed",
543 list_receiving.notify_all();
544 }
else if (param_state == PR::RXPARAM_TIMEDOUT) {
545 uint16_t first_miss_idx = parameters_missing_idx.front();
546 ROS_DEBUG_NAMED(
"param",
"PR: requesting next timed out parameter idx=%u", first_miss_idx);
547 param_request_read(
"", first_miss_idx);
558 mavlink::common::msg::PARAM_REQUEST_LIST rql{};
559 m_uas->msg_set_target(rql);
561 UAS_FCU(m_uas)->send_message_ignore_drop(rql);
568 ROS_DEBUG_NAMED(
"param",
"PR:m: request '%s', idx %d",
id.c_str(), index);
570 mavlink::common::msg::PARAM_REQUEST_READ rqr{};
571 m_uas->msg_set_target(rqr);
572 rqr.param_index = index;
578 UAS_FCU(m_uas)->send_message_ignore_drop(rqr);
586 auto ps = ([
this, ¶m]() -> mavlink::common::msg::PARAM_SET {
587 if (m_uas->is_ardupilotmega())
593 m_uas->msg_set_target(ps);
595 UAS_FCU(m_uas)->send_message_ignore_drop(ps);
604 shedule_pull(BOOTUP_TIME_DT);
607 shedule_timer.
stop();
613 shedule_timer.
stop();
615 shedule_timer.
start();
621 if (param_state != PR::IDLE) {
624 shedule_pull(BOOTUP_TIME_DT);
628 param_state = PR::RXLIST;
629 param_rx_retries = RETRIES_COUNT;
632 restart_timeout_timer();
633 param_request_list();
639 if (param_state == PR::RXLIST && param_rx_retries > 0) {
641 ROS_WARN_NAMED(
"param",
"PR: request list timeout, retries left %zu", param_rx_retries);
643 restart_timeout_timer();
644 param_request_list();
646 else if (param_state == PR::RXPARAM || param_state == PR::RXPARAM_TIMEDOUT) {
647 if (parameters_missing_idx.empty()) {
648 ROS_WARN_NAMED(
"param",
"PR: missing list is clear, but we in RXPARAM state, " 649 "maybe last rerequest fails. Params missed: %zd",
650 param_count - parameters.size());
652 list_receiving.notify_all();
656 param_state = PR::RXPARAM_TIMEDOUT;
657 uint16_t first_miss_idx = parameters_missing_idx.front();
658 if (param_rx_retries > 0) {
660 ROS_WARN_NAMED(
"param",
"PR: request param #%u timeout, retries left %zu, and %zu params still missing",
661 first_miss_idx, param_rx_retries, parameters_missing_idx.size());
662 restart_timeout_timer();
663 param_request_read(
"", first_miss_idx);
666 ROS_ERROR_NAMED(
"param",
"PR: request param #%u completely missing.", first_miss_idx);
667 parameters_missing_idx.pop_front();
668 restart_timeout_timer();
669 if (!parameters_missing_idx.empty()) {
670 param_rx_retries = RETRIES_COUNT;
671 first_miss_idx = parameters_missing_idx.front();
673 ROS_WARN_NAMED(
"param",
"PR: %zu params still missing, trying to request next: #%u",
674 parameters_missing_idx.size(), first_miss_idx);
675 param_request_read(
"", first_miss_idx);
679 else if (param_state == PR::TXPARAM) {
680 auto it = set_parameters.begin();
681 if (it == set_parameters.end()) {
687 if (it->second->retries_remaining > 0) {
688 it->second->retries_remaining--;
689 ROS_WARN_NAMED(
"param",
"PR: Resend param set for %s, retries left %zu",
690 it->second->param.param_id.c_str(),
691 it->second->retries_remaining);
692 restart_timeout_timer();
697 it->second->param.param_id.c_str());
698 it->second->is_timedout =
true;
699 it->second->ack.notify_all();
710 timeout_timer.
stop();
711 timeout_timer.
start();
716 param_state = PR::IDLE;
717 timeout_timer.
stop();
722 std::unique_lock<std::mutex> lock(list_cond_mutex);
724 return list_receiving.wait_for(lock, std::chrono::nanoseconds(LIST_TIMEOUT_DT.
toNSec()))
725 == std::cv_status::no_timeout
731 std::unique_lock<std::mutex> lock(opt->cond_mutex);
733 return opt->ack.wait_for(lock, std::chrono::nanoseconds(PARAM_TIMEOUT_DT.
toNSec()) * (RETRIES_COUNT + 2))
734 == std::cv_status::no_timeout
735 && !opt->is_timedout;
743 auto opt = std::make_shared<ParamSetOpt>(param, RETRIES_COUNT);
744 set_parameters[param.param_id] = opt;
746 param_state = PR::TXPARAM;
747 restart_timeout_timer();
751 bool is_not_timeout = wait_param_set_ack_for(opt);
755 set_parameters.erase(param.param_id);
758 return is_not_timeout;
764 if (m_uas->is_px4() && p.
param_id ==
"_HASH_CHECK") {
766 ROS_INFO_NAMED(
"param",
"PR: PX4 parameter _HASH_CHECK ignored: 0x%8x", static_cast<int32_t>(v));
780 bool pull_cb(mavros_msgs::ParamPull::Request &req,
781 mavros_msgs::ParamPull::Response &res)
785 if ((param_state == PR::IDLE && parameters.empty())
792 param_state = PR::RXLIST;
793 param_rx_retries = RETRIES_COUNT;
796 shedule_timer.
stop();
797 restart_timeout_timer();
798 param_request_list();
801 res.success = wait_fetch_all();
803 else if (param_state == PR::RXLIST || param_state == PR::RXPARAM || param_state == PR::RXPARAM_TIMEDOUT) {
805 res.success = wait_fetch_all();
813 res.param_received = parameters.size();
815 for (
auto &p : parameters) {
817 rosparam_set_allowed(p.second);
828 bool push_cb(mavros_msgs::ParamPush::Request &req,
829 mavros_msgs::ParamPush::Response &res)
832 if (!param_nh.
getParam(
"", param_dict))
838 for (
auto &
param : param_dict) {
845 auto param_it = parameters.find(
param.first);
846 if (param_it != parameters.end()) {
848 auto to_send = param_it->second;
851 to_send.param_value =
param.second;
854 bool set_res = send_param_set_and_wait(to_send);
866 res.param_transfered = tx_count;
875 bool set_cb(mavros_msgs::ParamSet::Request &req,
876 mavros_msgs::ParamSet::Response &res)
880 if (param_state == PR::RXLIST || param_state == PR::RXPARAM || param_state == PR::RXPARAM_TIMEDOUT) {
885 auto param_it = parameters.find(req.param_id);
886 if (param_it != parameters.end()) {
887 auto to_send = param_it->second;
890 if (req.value.integer != 0)
891 to_send.param_value =
static_cast<int>(req.value.integer);
892 else if (req.value.real != 0.0)
893 to_send.param_value = req.value.real;
894 else if (param_it->second.param_value.getType() == XmlRpc::XmlRpcValue::Type::TypeDouble)
895 to_send.param_value = req.value.real;
897 to_send.param_value = 0;
900 res.success = send_param_set_and_wait(to_send);
903 res.value.integer = param_it->second.to_integer();
904 res.value.real = param_it->second.to_real();
907 rosparam_set_allowed(param_it->second);
921 bool get_cb(mavros_msgs::ParamGet::Request &req,
922 mavros_msgs::ParamGet::Response &res)
926 auto param_it = parameters.find(req.param_id);
927 if (param_it != parameters.end()) {
930 res.value.integer = param_it->second.to_integer();
931 res.value.real = param_it->second.to_real();
MAVROS Plugin base class.
#define ROS_WARN_COND_NAMED(cond, name,...)
std::unique_lock< std::recursive_mutex > unique_lock
bool param(const std::string ¶m_name, T ¶m_val, const T &default_val)
#define ROS_WARN_STREAM_COND_NAMED(cond, name, args)
#define ROS_INFO_NAMED(name,...)
#define ROS_INFO_COND_NAMED(cond, name,...)
#define ROS_DEBUG_STREAM_NAMED(name, args)
std::string format(const std::string &fmt, Args...args)
void publish(const boost::shared_ptr< M > &message) const
#define ROS_ERROR_STREAM_NAMED(name, args)
void timeout_cb(const ros::TimerEvent &event)
#define ROS_WARN_NAMED(name,...)
std::condition_variable ack
def param_set(param_id, value)
std::string to_string() const
std::condition_variable list_receiving
void set_value_apm_quirk(mavlink::common::msg::PARAM_VALUE &pmsg)
ros::Publisher param_value_pub
void setPeriod(const Duration &period, bool reset=true)
const ros::Duration PARAM_TIMEOUT_DT
mavlink::common::msg::PARAM_SET PARAM_SET
bool wait_param_set_ack_for(std::shared_ptr< ParamSetOpt > opt)
PARAM_SET to_param_set()
Make PARAM_SET message. Set target ids manually!
void shedule_cb(const ros::TimerEvent &event)
static bool check_exclude_param_id(std::string param_id)
void set_value(mavlink::common::msg::PARAM_VALUE &pmsg)
mavros_msgs::Param to_msg()
Type const & getType() const
void restart_timeout_timer()
std::mutex list_cond_mutex
bool set_cb(mavros_msgs::ParamSet::Request &req, mavros_msgs::ParamSet::Response &res)
sets parameter value ~param/set
#define ROS_DEBUG_NAMED(name,...)
PARAM_SET to_param_set_apm_qurk()
Make PARAM_SET message. Set target ids manually!
Parameter manipulation plugin.
std::string toXml() const
void param_request_read(std::string id, int16_t index=-1)
Subscriptions get_subscriptions() override
Return vector of MAVLink message subscriptions (handlers)
mavlink::common::MAV_PARAM_TYPE MT
ros::ServiceServer push_srv
bool send_param_set_and_wait(Parameter ¶m)
#define UAS_FCU(uasobjptr)
helper accessor to FCU link interface
ros::Timer shedule_timer
for startup shedule fetch
ParamSetOpt(Parameter &_p, size_t _rem)
void param_set(Parameter ¶m)
const ros::Duration BOOTUP_TIME_DT
bool get_cb(mavros_msgs::ParamGet::Request &req, mavros_msgs::ParamGet::Response &res)
get parameter ~param/get
bool pull_cb(mavros_msgs::ParamPull::Request &req, mavros_msgs::ParamPull::Response &res)
fetches all parameters from device ~param/pull
void param_request_list()
void shedule_pull(const ros::Duration &dt)
ros::Timer timeout_timer
for timeout resend
const ros::Duration LIST_TIMEOUT_DT
ros::ServiceServer pull_srv
std::vector< HandlerInfo > Subscriptions
Subscriptions vector.
Parameter set transaction data.
bool getParam(const std::string &key, std::string &s) const
void set_string(std::array< char, _N > &a, const std::string &s)
std::unordered_map< std::string, std::shared_ptr< ParamSetOpt > > set_parameters
bool push_cb(mavros_msgs::ParamPush::Request &req, mavros_msgs::ParamPush::Response &res)
push all parameter value to device ~param/push
std::unordered_map< std::string, Parameter > parameters
#define ROS_ERROR_NAMED(name,...)
void handle_param_value(const mavlink::mavlink_message_t *msg, mavlink::common::msg::PARAM_VALUE &pmsg)
ros::ServiceServer set_srv
std::lock_guard< std::recursive_mutex > lock_guard
ros::ServiceServer get_srv
#define PLUGINLIB_EXPORT_CLASS(class_type, base_class_type)
void connection_cb(bool connected) override
void setParam(const std::string &key, const XmlRpc::XmlRpcValue &v) const
std::string to_string(const std::array< char, _N > &a)
void initialize(UAS &uas_) override
Plugin initializer.
std::list< uint16_t > parameters_missing_idx
std::recursive_mutex mutex
constexpr std::underlying_type< _T >::type enum_value(_T e)
std::recursive_mutex mutex
#define ROS_WARN_STREAM_NAMED(name, args)
bool rosparam_set_allowed(const Parameter &p)
Set ROS param only if name is good.