00001
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027 #include <mavros/mavros_plugin.h>
00028 #include <pluginlib/class_list_macros.h>
00029
00030 #include <mavros/State.h>
00031 #include <mavros/BatteryStatus.h>
00032 #include <mavros/StreamRate.h>
00033 #include <mavros/SetMode.h>
00034
00035 namespace mavplugin {
00036
00042 class HeartbeatStatus : public diagnostic_updater::DiagnosticTask
00043 {
00044 public:
00045 HeartbeatStatus(const std::string name, size_t win_size) :
00046 diagnostic_updater::DiagnosticTask(name),
00047 window_size_(win_size),
00048 min_freq_(0.2),
00049 max_freq_(100),
00050 tolerance_(0.1),
00051 times_(win_size),
00052 seq_nums_(win_size),
00053 last_hb{}
00054 {
00055 clear();
00056 }
00057
00058 void clear() {
00059 lock_guard lock(mutex);
00060 ros::Time curtime = ros::Time::now();
00061 count_ = 0;
00062
00063 for (int i = 0; i < window_size_; i++)
00064 {
00065 times_[i] = curtime;
00066 seq_nums_[i] = count_;
00067 }
00068
00069 hist_indx_ = 0;
00070 }
00071
00072 void tick(mavlink_heartbeat_t &hb_struct) {
00073 lock_guard lock(mutex);
00074 count_++;
00075 last_hb = hb_struct;
00076 }
00077
00078 void run(diagnostic_updater::DiagnosticStatusWrapper &stat) {
00079 lock_guard lock(mutex);
00080 ros::Time curtime = ros::Time::now();
00081 int curseq = count_;
00082 int events = curseq - seq_nums_[hist_indx_];
00083 double window = (curtime - times_[hist_indx_]).toSec();
00084 double freq = events / window;
00085 seq_nums_[hist_indx_] = curseq;
00086 times_[hist_indx_] = curtime;
00087 hist_indx_ = (hist_indx_ + 1) % window_size_;
00088
00089 if (events == 0) {
00090 stat.summary(2, "No events recorded.");
00091 }
00092 else if (freq < min_freq_ * (1 - tolerance_)) {
00093 stat.summary(1, "Frequency too low.");
00094 }
00095 else if (freq > max_freq_ * (1 + tolerance_)) {
00096 stat.summary(1, "Frequency too high.");
00097 }
00098 else {
00099 stat.summary(0, "Normal");
00100 }
00101
00102 stat.addf("Events in window", "%d", events);
00103 stat.addf("Events since startup", "%d", count_);
00104 stat.addf("Duration of window (s)", "%f", window);
00105 stat.addf("Actual frequency (Hz)", "%f", freq);
00106 stat.addf("MAV Type", "%u", last_hb.type);
00107 stat.addf("Autopilot type", "%u", last_hb.autopilot);
00108 stat.addf("Autopilot base mode", "0x%02X", last_hb.base_mode);
00109 stat.addf("Autopilot custom mode", "0x%08X", last_hb.custom_mode);
00110 stat.addf("Autopilot system status", "%u", last_hb.system_status);
00111 }
00112
00113 private:
00114 int count_;
00115 std::vector<ros::Time> times_;
00116 std::vector<int> seq_nums_;
00117 int hist_indx_;
00118 std::recursive_mutex mutex;
00119 const size_t window_size_;
00120 const double min_freq_;
00121 const double max_freq_;
00122 const double tolerance_;
00123 mavlink_heartbeat_t last_hb;
00124 };
00125
00126
00127 class SystemStatusDiag : public diagnostic_updater::DiagnosticTask
00128 {
00129 public:
00130 SystemStatusDiag(const std::string name) :
00131 diagnostic_updater::DiagnosticTask(name),
00132 last_st{}
00133 { };
00134
00135 void set(mavlink_sys_status_t &st) {
00136 lock_guard lock(mutex);
00137 last_st = st;
00138 }
00139
00140 void run(diagnostic_updater::DiagnosticStatusWrapper &stat) {
00141 lock_guard lock(mutex);
00142
00143 if ((last_st.onboard_control_sensors_health & last_st.onboard_control_sensors_enabled)
00144 != last_st.onboard_control_sensors_enabled)
00145 stat.summary(2, "Sensor helth");
00146 else
00147 stat.summary(0, "Normal");
00148
00149 stat.addf("Sensor present", "0x%08X", last_st.onboard_control_sensors_present);
00150 stat.addf("Sensor enabled", "0x%08X", last_st.onboard_control_sensors_enabled);
00151 stat.addf("Sensor helth", "0x%08X", last_st.onboard_control_sensors_health);
00152
00153
00154 #define STAT_ADD_SENSOR(msg, sensor_mask) \
00155 if (last_st.onboard_control_sensors_enabled & sensor_mask) \
00156 stat.add(msg, (last_st.onboard_control_sensors_health & sensor_mask)? "Ok" : "Fail")
00157
00158 STAT_ADD_SENSOR("Sensor 3D Gyro", MAV_SYS_STATUS_SENSOR_3D_GYRO);
00159 STAT_ADD_SENSOR("Sensor 3D Accel", MAV_SYS_STATUS_SENSOR_3D_ACCEL);
00160 STAT_ADD_SENSOR("Sensor 3D Mag", MAV_SYS_STATUS_SENSOR_3D_MAG);
00161 STAT_ADD_SENSOR("Sensor Abs Pressure", MAV_SYS_STATUS_SENSOR_ABSOLUTE_PRESSURE);
00162 STAT_ADD_SENSOR("Sensor Diff Pressure", MAV_SYS_STATUS_SENSOR_DIFFERENTIAL_PRESSURE);
00163 STAT_ADD_SENSOR("Sensor GPS", MAV_SYS_STATUS_SENSOR_GPS);
00164 STAT_ADD_SENSOR("Sensor Optical Flow", MAV_SYS_STATUS_SENSOR_OPTICAL_FLOW);
00165 STAT_ADD_SENSOR("Sensor Vision Position", MAV_SYS_STATUS_SENSOR_VISION_POSITION);
00166 STAT_ADD_SENSOR("Sensor Laser Position", MAV_SYS_STATUS_SENSOR_LASER_POSITION);
00167 STAT_ADD_SENSOR("Sensor Ext Grount Truth", MAV_SYS_STATUS_SENSOR_EXTERNAL_GROUND_TRUTH);
00168 STAT_ADD_SENSOR("Sensor Ang Rate Control", MAV_SYS_STATUS_SENSOR_ANGULAR_RATE_CONTROL);
00169 STAT_ADD_SENSOR("Sensor Attitude Stab", MAV_SYS_STATUS_SENSOR_ATTITUDE_STABILIZATION);
00170 STAT_ADD_SENSOR("Sensor Yaw Position", MAV_SYS_STATUS_SENSOR_YAW_POSITION);
00171 STAT_ADD_SENSOR("Sensor Z/Alt Control", MAV_SYS_STATUS_SENSOR_Z_ALTITUDE_CONTROL);
00172 STAT_ADD_SENSOR("Sensor X/Y Pos Control", MAV_SYS_STATUS_SENSOR_XY_POSITION_CONTROL);
00173 STAT_ADD_SENSOR("Sensor Motor Outputs", MAV_SYS_STATUS_SENSOR_MOTOR_OUTPUTS);
00174 STAT_ADD_SENSOR("Sensor RC Receiver", MAV_SYS_STATUS_SENSOR_RC_RECEIVER);
00175 STAT_ADD_SENSOR("Sensor 3D Gyro 2", MAV_SYS_STATUS_SENSOR_3D_GYRO2);
00176 STAT_ADD_SENSOR("Sensor 3D Accel 2", MAV_SYS_STATUS_SENSOR_3D_ACCEL2);
00177 STAT_ADD_SENSOR("Sensor 3D Mag 2", MAV_SYS_STATUS_SENSOR_3D_MAG2);
00178 STAT_ADD_SENSOR("Geofence health", MAV_SYS_STATUS_GEOFENCE);
00179 STAT_ADD_SENSOR("AHRS health", MAV_SYS_STATUS_AHRS);
00180
00181
00182 #undef STAT_ADD_SENSOR
00183
00184 stat.addf("CPU Load (%)", "%.1f", last_st.load / 10.0);
00185 stat.addf("Drop rate (%)", "%.1f", last_st.drop_rate_comm / 10.0);
00186 stat.addf("Errors comm", "%d", last_st.errors_comm);
00187 stat.addf("Errors count #1", "%d", last_st.errors_count1);
00188 stat.addf("Errors count #2", "%d", last_st.errors_count2);
00189 stat.addf("Errors count #3", "%d", last_st.errors_count3);
00190 stat.addf("Errors count #4", "%d", last_st.errors_count4);
00191 }
00192
00193 private:
00194 std::recursive_mutex mutex;
00195 mavlink_sys_status_t last_st;
00196 };
00197
00198
00199 class BatteryStatusDiag : public diagnostic_updater::DiagnosticTask
00200 {
00201 public:
00202 BatteryStatusDiag(const std::string name) :
00203 diagnostic_updater::DiagnosticTask(name),
00204 voltage(-1.0),
00205 current(0.0),
00206 remaining(0.0),
00207 min_voltage(6)
00208 {};
00209
00210 void set_min_voltage(float volt) {
00211 lock_guard lock(mutex);
00212 min_voltage = volt;
00213 }
00214
00215 void set(float volt, float curr, float rem) {
00216 lock_guard lock(mutex);
00217 voltage = volt;
00218 current = curr;
00219 remaining = rem;
00220 }
00221
00222 void run(diagnostic_updater::DiagnosticStatusWrapper &stat) {
00223 lock_guard lock(mutex);
00224
00225 if (voltage < 0)
00226 stat.summary(2, "No data");
00227 else if (voltage < min_voltage)
00228 stat.summary(1, "Low voltage");
00229 else
00230 stat.summary(0, "Normal");
00231
00232 stat.addf("Voltage", "%.2f", voltage);
00233 stat.addf("Current", "%.1f", current);
00234 stat.addf("Remaining", "%.1f", remaining * 100);
00235 }
00236
00237 private:
00238 std::recursive_mutex mutex;
00239 float voltage;
00240 float current;
00241 float remaining;
00242 float min_voltage;
00243 };
00244
00245
00246 class MemInfo : public diagnostic_updater::DiagnosticTask
00247 {
00248 public:
00249 MemInfo(const std::string name) :
00250 diagnostic_updater::DiagnosticTask(name),
00251 freemem(-1),
00252 brkval(0)
00253 {};
00254
00255 void set(uint16_t f, uint16_t b) {
00256 lock_guard lock(mutex);
00257 freemem = f;
00258 brkval = b;
00259 }
00260
00261 void run(diagnostic_updater::DiagnosticStatusWrapper &stat) {
00262 lock_guard lock(mutex);
00263
00264 if (freemem < 0)
00265 stat.summary(2, "No data");
00266 else if (freemem < 200)
00267 stat.summary(1, "Low mem");
00268 else
00269 stat.summary(0, "Normal");
00270
00271 stat.addf("Free memory (B)", "%zd", freemem);
00272 stat.addf("Heap top", "0x%04X", brkval);
00273 }
00274
00275 private:
00276 std::recursive_mutex mutex;
00277 ssize_t freemem;
00278 uint16_t brkval;
00279 };
00280
00281
00282 class HwStatus : public diagnostic_updater::DiagnosticTask
00283 {
00284 public:
00285 HwStatus(const std::string name) :
00286 diagnostic_updater::DiagnosticTask(name),
00287 vcc(-1.0),
00288 i2cerr(0),
00289 i2cerr_last(0)
00290 {};
00291
00292 void set(uint16_t v, uint8_t e) {
00293 lock_guard lock(mutex);
00294 vcc = v / 1000.0;
00295 i2cerr = e;
00296 }
00297
00298 void run(diagnostic_updater::DiagnosticStatusWrapper &stat) {
00299 lock_guard lock(mutex);
00300
00301 if (vcc < 0)
00302 stat.summary(2, "No data");
00303 else if (vcc < 4.5)
00304 stat.summary(1, "Low voltage");
00305 else if (i2cerr != i2cerr_last) {
00306 i2cerr_last = i2cerr;
00307 stat.summary(1, "New I2C error");
00308 }
00309 else
00310 stat.summary(0, "Normal");
00311
00312 stat.addf("Core voltage", "%f", vcc);
00313 stat.addf("I2C errors", "%zu", i2cerr);
00314 }
00315
00316 private:
00317 std::recursive_mutex mutex;
00318 float vcc;
00319 size_t i2cerr;
00320 size_t i2cerr_last;
00321 };
00322
00323
00328 class SystemStatusPlugin : public MavRosPlugin
00329 {
00330 public:
00331 SystemStatusPlugin() :
00332 uas(nullptr),
00333 hb_diag("Heartbeat", 10),
00334 mem_diag("APM Memory"),
00335 hwst_diag("APM Hardware"),
00336 sys_diag("System"),
00337 batt_diag("Battery")
00338 {};
00339
00340 void initialize(UAS &uas_,
00341 ros::NodeHandle &nh,
00342 diagnostic_updater::Updater &diag_updater)
00343 {
00344 uas = &uas_;
00345
00346 double conn_timeout_d;
00347 double conn_heartbeat_d;
00348 double min_voltage;
00349 bool disable_diag;
00350
00351 nh.param("conn_timeout", conn_timeout_d, 30.0);
00352 nh.param("conn_heartbeat", conn_heartbeat_d, 0.0);
00353 nh.param("sys/min_voltage", min_voltage, 6.0);
00354 nh.param("sys/disable_diag", disable_diag, false);
00355
00356
00357 diag_updater.add(hb_diag);
00358 if (!disable_diag) {
00359 diag_updater.add(sys_diag);
00360 diag_updater.add(batt_diag);
00361 #ifdef MAVLINK_MSG_ID_MEMINFO
00362 diag_updater.add(mem_diag);
00363 #endif
00364 #ifdef MAVLINK_MSG_ID_HWSTATUS
00365 diag_updater.add(hwst_diag);
00366 #endif
00367
00368 batt_diag.set_min_voltage(min_voltage);
00369 }
00370
00371
00372
00373 timeout_timer = nh.createTimer(ros::Duration(conn_timeout_d),
00374 &SystemStatusPlugin::timeout_cb, this, true);
00375 timeout_timer.start();
00376
00377 if (conn_heartbeat_d > 0.0) {
00378 heartbeat_timer = nh.createTimer(ros::Duration(conn_heartbeat_d),
00379 &SystemStatusPlugin::heartbeat_cb, this);
00380 heartbeat_timer.start();
00381 }
00382
00383 state_pub = nh.advertise<mavros::State>("state", 10);
00384 batt_pub = nh.advertise<mavros::BatteryStatus>("battery", 10);
00385 rate_srv = nh.advertiseService("set_stream_rate", &SystemStatusPlugin::set_rate_cb, this);
00386 mode_srv = nh.advertiseService("set_mode", &SystemStatusPlugin::set_mode_cb, this);
00387 }
00388
00389 const std::string get_name() const {
00390 return "SystemStatus";
00391 }
00392
00393 const message_map get_rx_handlers() {
00394 return {
00395 MESSAGE_HANDLER(MAVLINK_MSG_ID_HEARTBEAT, &SystemStatusPlugin::handle_heartbeat),
00396 MESSAGE_HANDLER(MAVLINK_MSG_ID_SYS_STATUS, &SystemStatusPlugin::handle_sys_status),
00397 MESSAGE_HANDLER(MAVLINK_MSG_ID_STATUSTEXT, &SystemStatusPlugin::handle_statustext),
00398 #ifdef MAVLINK_MSG_ID_MEMINFO
00399 MESSAGE_HANDLER(MAVLINK_MSG_ID_MEMINFO, &SystemStatusPlugin::handle_meminfo),
00400 #endif
00401 #ifdef MAVLINK_MSG_ID_HWSTATUS
00402 MESSAGE_HANDLER(MAVLINK_MSG_ID_HWSTATUS, &SystemStatusPlugin::handle_hwstatus),
00403 #endif
00404 };
00405 }
00406
00407 private:
00408 UAS *uas;
00409 HeartbeatStatus hb_diag;
00410 MemInfo mem_diag;
00411 HwStatus hwst_diag;
00412 SystemStatusDiag sys_diag;
00413 BatteryStatusDiag batt_diag;
00414 ros::Timer timeout_timer;
00415 ros::Timer heartbeat_timer;
00416
00417 ros::Publisher state_pub;
00418 ros::Publisher batt_pub;
00419 ros::ServiceServer rate_srv;
00420 ros::ServiceServer mode_srv;
00421
00422
00423
00429 void process_statustext_normal(uint8_t severity, std::string &text) {
00430 switch (severity) {
00431 case MAV_SEVERITY_EMERGENCY:
00432 case MAV_SEVERITY_ALERT:
00433 case MAV_SEVERITY_CRITICAL:
00434 case MAV_SEVERITY_ERROR:
00435 ROS_ERROR_STREAM_NAMED("fcu", "FCU: " << text);
00436 break;
00437
00438 case MAV_SEVERITY_WARNING:
00439 case MAV_SEVERITY_NOTICE:
00440 ROS_WARN_STREAM_NAMED("fcu", "FCU: " << text);
00441 break;
00442
00443 case MAV_SEVERITY_INFO:
00444 ROS_INFO_STREAM_NAMED("fcu", "FCU: " << text);
00445 break;
00446
00447 case MAV_SEVERITY_DEBUG:
00448 default:
00449 ROS_DEBUG_STREAM_NAMED("fcu", "FCU: " << text);
00450 break;
00451 };
00452
00453 }
00454
00460 void process_statustext_apm_quirk(uint8_t severity, std::string &text) {
00461 switch (severity) {
00462 case 1:
00463 ROS_INFO_STREAM_NAMED("fcu", "FCU: " << text);
00464 break;
00465
00466 case 2:
00467 ROS_WARN_STREAM_NAMED("fcu", "FCU: " << text);
00468 break;
00469
00470 case 3:
00471 case 4:
00472 case 5:
00473 ROS_ERROR_STREAM_NAMED("fcu", "FCU: " << text);
00474 break;
00475
00476 default:
00477 ROS_DEBUG_STREAM_NAMED("fcu", "FCU: UNK(" <<
00478 (int)severity << "): " << text);
00479 break;
00480 };
00481
00482 }
00483
00484
00485
00486 void handle_heartbeat(const mavlink_message_t *msg, uint8_t sysid, uint8_t compid) {
00487 mavlink_heartbeat_t hb;
00488 mavlink_msg_heartbeat_decode(msg, &hb);
00489 hb_diag.tick(hb);
00490
00491
00492 uas->update_heartbeat(hb.type, hb.autopilot);
00493 uas->update_connection_status(true);
00494 timeout_timer.stop();
00495 timeout_timer.start();
00496
00497 mavros::StatePtr state_msg = boost::make_shared<mavros::State>();
00498 state_msg->header.stamp = ros::Time::now();
00499 state_msg->armed = hb.base_mode & MAV_MODE_FLAG_SAFETY_ARMED;
00500 state_msg->guided = hb.base_mode & MAV_MODE_FLAG_GUIDED_ENABLED;
00501 state_msg->mode = uas->str_mode_v10(hb.base_mode, hb.custom_mode);
00502
00503 state_pub.publish(state_msg);
00504 }
00505
00506 void handle_sys_status(const mavlink_message_t *msg, uint8_t sysid, uint8_t compid) {
00507 mavlink_sys_status_t stat;
00508 mavlink_msg_sys_status_decode(msg, &stat);
00509
00510 float volt = stat.voltage_battery / 1000.0;
00511 float curr = stat.current_battery / 100.0;
00512 float rem = stat.battery_remaining / 100.0;
00513
00514 mavros::BatteryStatusPtr batt_msg = boost::make_shared<mavros::BatteryStatus>();
00515 batt_msg->header.stamp = ros::Time::now();
00516 batt_msg->voltage = volt;
00517 batt_msg->current = curr;
00518 batt_msg->remaining = rem;
00519
00520 sys_diag.set(stat);
00521 batt_diag.set(volt, curr, rem);
00522 batt_pub.publish(batt_msg);
00523 }
00524
00525 void handle_statustext(const mavlink_message_t *msg, uint8_t sysid, uint8_t compid) {
00526 mavlink_statustext_t textm;
00527 mavlink_msg_statustext_decode(msg, &textm);
00528
00529 std::string text(textm.text,
00530 strnlen(textm.text, sizeof(textm.text)));
00531
00532 if (uas->is_ardupilotmega())
00533 process_statustext_apm_quirk(textm.severity, text);
00534 else
00535 process_statustext_normal(textm.severity, text);
00536 }
00537
00538 #ifdef MAVLINK_MSG_ID_MEMINFO
00539 void handle_meminfo(const mavlink_message_t *msg, uint8_t sysid, uint8_t compid) {
00540 mavlink_meminfo_t mem;
00541 mavlink_msg_meminfo_decode(msg, &mem);
00542 mem_diag.set(mem.freemem, mem.brkval);
00543 }
00544 #endif
00545
00546 #ifdef MAVLINK_MSG_ID_HWSTATUS
00547 void handle_hwstatus(const mavlink_message_t *msg, uint8_t sysid, uint8_t compid) {
00548 mavlink_hwstatus_t hwst;
00549 mavlink_msg_hwstatus_decode(msg, &hwst);
00550 hwst_diag.set(hwst.Vcc, hwst.I2Cerr);
00551 }
00552 #endif
00553
00554
00555
00556 void timeout_cb(const ros::TimerEvent &event) {
00557 uas->update_connection_status(false);
00558 }
00559
00560 void heartbeat_cb(const ros::TimerEvent &event) {
00561 mavlink_message_t msg;
00562 mavlink_msg_heartbeat_pack_chan(UAS_PACK_CHAN(uas), &msg,
00563 MAV_TYPE_ONBOARD_CONTROLLER,
00564 MAV_AUTOPILOT_INVALID,
00565 MAV_MODE_MANUAL_ARMED,
00566 0,
00567 MAV_STATE_ACTIVE
00568 );
00569
00570 UAS_FCU(uas)->send_message(&msg);
00571 }
00572
00573
00574
00575 bool set_rate_cb(mavros::StreamRate::Request &req,
00576 mavros::StreamRate::Response &res) {
00577
00578 mavlink_message_t msg;
00579 mavlink_msg_request_data_stream_pack_chan(UAS_PACK_CHAN(uas), &msg,
00580 UAS_PACK_TGT(uas),
00581 req.stream_id,
00582 req.message_rate,
00583 (req.on_off)? 1 : 0
00584 );
00585
00586 UAS_FCU(uas)->send_message(&msg);
00587 return true;
00588 }
00589
00590 bool set_mode_cb(mavros::SetMode::Request &req,
00591 mavros::SetMode::Response &res) {
00592 mavlink_message_t msg;
00593 uint8_t base_mode = req.base_mode;
00594 uint32_t custom_mode = 0;
00595
00596 if (req.custom_mode != "") {
00597 if (!uas->cmode_from_str(req.custom_mode, custom_mode)) {
00598 res.success = false;
00599 return true;
00600 }
00601
00602 base_mode |= MAV_MODE_FLAG_CUSTOM_MODE_ENABLED;
00603 }
00604
00605 mavlink_msg_set_mode_pack_chan(UAS_PACK_CHAN(uas), &msg,
00606 uas->get_tgt_system(),
00607 base_mode,
00608 custom_mode);
00609 UAS_FCU(uas)->send_message(&msg);
00610 res.success = true;
00611 return true;
00612 }
00613 };
00614
00615 };
00616
00617 PLUGINLIB_EXPORT_CLASS(mavplugin::SystemStatusPlugin, mavplugin::MavRosPlugin)
00618