LCOV - code coverage report
Current view: top level - GCS_MAVLink - MissionItemProtocol.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 155 217 71.4 %
Date: 2026-09-09 11:02:08 Functions: 14 16 87.5 %

          Line data    Source code
       1             : #include "GCS_config.h"
       2             : 
       3             : #if HAL_GCS_ENABLED
       4             : 
       5             : #include "MissionItemProtocol.h"
       6             : 
       7             : #include "GCS.h"
       8             : 
       9         394 : void MissionItemProtocol::init_send_requests(GCS_MAVLINK &_link,
      10             :                                              const mavlink_message_t &msg,
      11             :                                              const int16_t _request_first,
      12             :                                              const int16_t _request_last)
      13             : {
      14             :     // set variables to help handle the expected receiving of commands from the GCS
      15         394 :     timelast_receive_ms = AP_HAL::millis();    // set time we last received commands to now
      16         394 :     receiving = true;              // record that we expect to receive commands
      17         394 :     request_i = _request_first;                 // reset the next expected command number to zero
      18         394 :     request_last = _request_last;         // record how many commands we expect to receive
      19             : 
      20         394 :     dest_sysid = msg.sysid;       // record system id of GCS who wants to upload the mission
      21         394 :     dest_compid = msg.compid;     // record component id of GCS who wants to upload the mission
      22             : 
      23         394 :     link = &_link;
      24             : 
      25         394 :     timelast_request_ms = AP_HAL::millis();
      26         394 :     link->send_message(next_item_ap_message_id());
      27             : 
      28         394 :     mission_item_warning_sent = false;
      29         394 :     mission_request_warning_sent = false;
      30         394 : }
      31             : 
      32           4 : void MissionItemProtocol::handle_mission_clear_all(const GCS_MAVLINK &_link,
      33             :                                                    const mavlink_message_t &msg)
      34             : {
      35           4 :     bool success = true;
      36           4 :     success = success && cancel_upload(_link, msg);
      37           4 :     success = success && clear_all_items();
      38           4 :     send_mission_ack(_link, msg, success ? MAV_MISSION_ACCEPTED : MAV_MISSION_ERROR);
      39           4 : }
      40             : 
      41        9231 : bool MissionItemProtocol::mavlink2_requirement_met(const GCS_MAVLINK &_link, const mavlink_message_t &msg) const
      42             : {
      43             :     // need mavlink2 to do mission types other than mission:
      44        9231 :     if (mission_type() == MAV_MISSION_TYPE_MISSION) {
      45        6337 :         return true;
      46             :     }
      47        2894 :     if (!_link.sending_mavlink1()) {
      48        2894 :         return true;
      49             :     }
      50           0 :     GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "Need mavlink2 for item transfer");
      51           0 :     send_mission_ack(_link, msg, MAV_MISSION_UNSUPPORTED);
      52           0 :     return false;
      53             : }
      54             : 
      55             : // returns true if we are either not receiving, or we successfully
      56             : // cancelled an existing upload:
      57        3181 : bool MissionItemProtocol::cancel_upload(const GCS_MAVLINK &_link, const mavlink_message_t &msg)
      58             : {
      59        3181 :     if (receiving) {
      60             :         // someone is already uploading a mission.  If we are
      61             :         // receiving from someone then we will allow them to restart -
      62             :         // otherwise we deny.
      63           3 :         if (msg.sysid != dest_sysid || msg.compid != dest_compid) {
      64             :             // reject another upload until
      65           1 :             send_mission_ack(_link, msg, MAV_MISSION_DENIED);
      66           1 :             return false;
      67             :         }
      68             :         // the upload count may have changed; free resources and
      69             :         // allocate them again:
      70           2 :         free_upload_resources();
      71           2 :         receiving = false;
      72           2 :         link = nullptr;
      73             :     }
      74             : 
      75        3180 :     return true;
      76             : }
      77             : 
      78        3177 : void MissionItemProtocol::handle_mission_count(
      79             :     GCS_MAVLINK &_link,
      80             :     const mavlink_mission_count_t &packet,
      81             :     const mavlink_message_t &msg)
      82             : {
      83        3177 :     if (!mavlink2_requirement_met(_link, msg)) {
      84           0 :         return;
      85             :     }
      86             : 
      87        3177 :     if (!cancel_upload(_link, msg)) {
      88           0 :         return;
      89             :     }
      90             : 
      91        3177 :     if (packet.count > max_items()) {
      92             :         // FIXME: different items take up different storage space!
      93           1 :         send_mission_ack(_link, msg, MAV_MISSION_NO_SPACE);
      94           1 :         GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "Only %u items are supported", (unsigned)max_items());
      95           1 :         return;
      96             :     }
      97             : 
      98        3176 :     MAV_MISSION_RESULT ret_alloc = allocate_receive_resources(packet.count);
      99        3176 :     if (ret_alloc != MAV_MISSION_ACCEPTED) {
     100           0 :         send_mission_ack(_link, msg, ret_alloc);
     101           0 :         return;
     102             :     }
     103             : 
     104        3176 :     truncate(packet);
     105             : 
     106        3176 :     if (packet.count == 0) {
     107             :         // no requests to send...
     108        2786 :         transfer_is_complete(_link, msg);
     109        2786 :         return;
     110             :     }
     111             : 
     112             :     // start waypoint receiving
     113         390 :     init_send_requests(_link, msg, 0, packet.count-1);
     114             : }
     115             : 
     116         467 : void MissionItemProtocol::handle_mission_request_list(
     117             :     const GCS_MAVLINK &_link,
     118             :     const mavlink_mission_request_list_t &packet,
     119             :     const mavlink_message_t &msg)
     120             : {
     121         467 :     if (!mavlink2_requirement_met(_link, msg)) {
     122           0 :         return;
     123             :     }
     124             : 
     125         467 :     if (receiving) {
     126             :         // someone is uploading a mission; reject fetching of points
     127             :         // until done or timeout
     128           0 :         send_mission_ack(_link, msg, MAV_MISSION_DENIED);
     129           0 :         return;
     130             :     }
     131             : 
     132             :     // reply with number of commands in the mission.  The GCS will
     133             :     // then request each command separately
     134         467 :     CHECK_PAYLOAD_SIZE2_VOID(_link.get_chan(), MISSION_COUNT);
     135         467 :     mavlink_msg_mission_count_send(_link.get_chan(),
     136         467 :                                    msg.sysid,
     137         467 :                                    msg.compid,
     138         467 :                                    item_count(),
     139         467 :                                    mission_type());
     140             : }
     141             : 
     142        5582 : void MissionItemProtocol::handle_mission_request_int(GCS_MAVLINK &_link,
     143             :                                                      const mavlink_mission_request_int_t &packet,
     144             :                                                      const mavlink_message_t &msg)
     145             : {
     146        5582 :     if (!mavlink2_requirement_met(_link, msg)) {
     147           1 :         return;
     148             :     }
     149             : 
     150        5582 :     if (receiving) {
     151             :         // someone is uploading a mission; reject fetching of points
     152             :         // until done or timeout
     153           0 :         send_mission_ack(_link, msg, MAV_MISSION_DENIED);
     154           0 :         return;
     155             :     }
     156             : 
     157             :     mavlink_mission_item_int_t ret_packet;
     158        5582 :     const MAV_MISSION_RESULT result_code = get_item(packet.seq, ret_packet);
     159        5582 :     if (result_code != MAV_MISSION_ACCEPTED) {
     160           1 :         if (result_code == MAV_MISSION_INVALID_SEQUENCE) {
     161             :             // try to educate the GCS on the actual size of the mission:
     162           1 :             const mavlink_channel_t chan = _link.get_chan();
     163           1 :             if (HAVE_PAYLOAD_SPACE(chan, MISSION_COUNT)) {
     164           2 :                 mavlink_msg_mission_count_send(chan,
     165           1 :                                                msg.sysid,
     166           1 :                                                msg.compid,
     167           1 :                                                item_count(),
     168           1 :                                                mission_type());
     169             :             }
     170             :         }
     171             :         // send failure message
     172           1 :         send_mission_ack(_link, msg, result_code);
     173           1 :         return;
     174             :     }
     175             : 
     176        5581 :     ret_packet.target_system = msg.sysid;
     177        5581 :     ret_packet.target_component = msg.compid;
     178             : 
     179        5581 :     _link.send_message(MAVLINK_MSG_ID_MISSION_ITEM_INT, (const char*)&ret_packet);
     180             : }
     181             : 
     182             : #if AP_MAVLINK_MSG_MISSION_REQUEST_ENABLED
     183           0 : void MissionItemProtocol::handle_mission_request(GCS_MAVLINK &_link,
     184             :                                                  const mavlink_mission_request_t &packet,
     185             :                                                  const mavlink_message_t &msg
     186             : )
     187             : {
     188           0 :     if (!mavlink2_requirement_met(_link, msg)) {
     189           0 :         return;
     190             :     }
     191             : 
     192             :     mavlink_mission_item_int_t item_int;
     193           0 :     MAV_MISSION_RESULT ret = get_item(packet.seq, item_int);
     194           0 :     if (ret != MAV_MISSION_ACCEPTED) {
     195           0 :         send_mission_ack(_link, msg, ret);
     196           0 :         return;
     197             :     }
     198             : 
     199           0 :     item_int.target_system = msg.sysid;
     200           0 :     item_int.target_component = msg.compid;
     201             : 
     202           0 :     mavlink_mission_item_t ret_packet{};
     203           0 :     ret = AP_Mission::convert_MISSION_ITEM_INT_to_MISSION_ITEM(item_int, ret_packet);
     204           0 :     if (ret != MAV_MISSION_ACCEPTED) {
     205           0 :         send_mission_ack(_link, msg, ret);
     206           0 :         return;
     207             :     }
     208             : 
     209           0 :     if (!mission_request_warning_sent) {
     210           0 :         mission_request_warning_sent = true;
     211           0 :         GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "got MISSION_REQUEST; use MISSION_REQUEST_INT!");
     212             :     }
     213             : 
     214             :     // buffer space is checked by send_message
     215           0 :     _link.send_message(MAVLINK_MSG_ID_MISSION_ITEM, (const char*)&ret_packet);
     216             : }
     217             : #endif  // AP_MAVLINK_MSG_MISSION_REQUEST_ENABLED
     218             : 
     219           4 : void MissionItemProtocol::send_mission_item_warning()
     220             : {
     221           4 :     if (mission_item_warning_sent) {
     222           3 :         return;
     223             :     }
     224           1 :     mission_item_warning_sent = true;
     225           1 :     GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "got MISSION_ITEM; GCS should send MISSION_ITEM_INT");
     226             : }
     227             : 
     228           5 : void MissionItemProtocol::handle_mission_write_partial_list(GCS_MAVLINK &_link,
     229             :                                                             const mavlink_message_t &msg,
     230             :                                                             const mavlink_mission_write_partial_list_t &packet)
     231             : {
     232           5 :     if (!mavlink2_requirement_met(_link, msg)) {
     233           0 :         return;
     234             :     }
     235             : 
     236           5 :     if (receiving) {
     237             :         // someone is already uploading a mission.  Deny ability to
     238             :         // write a partial list here as they might be trying to
     239             :         // overwrite a subset of the waypoints which the current
     240             :         // transfer is uploading, and that may lead to storing a whole
     241             :         // bunch of empty items.
     242           1 :         send_mission_ack(_link, msg, MAV_MISSION_DENIED);
     243           1 :         return;
     244             :     }
     245             : 
     246             :     // start waypoint receiving
     247           4 :     if ((unsigned)packet.start_index > item_count() ||
     248           8 :         (unsigned)packet.end_index > item_count() ||
     249           4 :         packet.end_index < packet.start_index) {
     250           0 :         GCS_SEND_TEXT(MAV_SEVERITY_WARNING,"Flight plan update rejected"); // FIXME: Remove this anytime after 2020-01-22
     251           0 :         send_mission_ack(_link, msg, MAV_MISSION_ERROR);
     252           0 :         return;
     253             :     }
     254             : 
     255           4 :     MAV_MISSION_RESULT ret_alloc = allocate_update_resources();
     256           4 :     if (ret_alloc != MAV_MISSION_ACCEPTED) {
     257           0 :         send_mission_ack(_link, msg, ret_alloc);
     258           0 :         return;
     259             :     }
     260             : 
     261           4 :     init_send_requests(_link, msg, packet.start_index, packet.end_index);
     262             : }
     263             : 
     264        5239 : void MissionItemProtocol::handle_mission_item(const mavlink_message_t &msg, const mavlink_mission_item_int_t &cmd)
     265             : {
     266        5239 :     if (link == nullptr) {
     267           0 :         INTERNAL_ERROR(AP_InternalError::error_t::gcs_bad_missionprotocol_link);
     268           0 :         return;
     269             :     }
     270             : 
     271             :     // check if this is the requested waypoint
     272        5239 :     if (cmd.seq != request_i) {
     273           0 :         send_mission_ack(msg, MAV_MISSION_INVALID_SEQUENCE);
     274           0 :         return;
     275             :     }
     276             :     // make sure the item is coming from the system that initiated the upload
     277        5239 :     if (msg.sysid != dest_sysid) {
     278           0 :         send_mission_ack(msg, MAV_MISSION_DENIED);
     279           0 :         return;
     280             :     }
     281        5239 :     if (msg.compid != dest_compid) {
     282           0 :         send_mission_ack(msg, MAV_MISSION_DENIED);
     283           0 :         return;
     284             :     }
     285             : 
     286        5239 :     const uint16_t _item_count = item_count();
     287             : 
     288             :     MAV_MISSION_RESULT result;
     289        5239 :     if (cmd.seq < _item_count) {
     290             :         // command index is within the existing list, replace the command
     291        2458 :         result = replace_item(cmd);
     292        2781 :     } else if (cmd.seq == _item_count) {
     293             :         // command is at the end of command list, add the command
     294        2781 :         result = append_item(cmd);
     295             :     } else {
     296             :         // beyond the end of the command list, return an error
     297           0 :         result = MAV_MISSION_ERROR;
     298             :     }
     299        5239 :     if (result != MAV_MISSION_ACCEPTED) {
     300           0 :         send_mission_ack(msg, result);
     301           0 :         receiving = false;
     302           0 :         link = nullptr;
     303           0 :         free_upload_resources();
     304           0 :         return;
     305             :     }
     306             : 
     307             :     // update waypoint receiving state machine
     308        5239 :     timelast_receive_ms = AP_HAL::millis();
     309        5239 :     request_i++;
     310             : 
     311        5239 :     if (request_i > request_last) {
     312         388 :         transfer_is_complete(*link, msg);
     313         388 :         return;
     314             :     }
     315             :     // if we have enough space, then send the next WP request immediately
     316        4851 :     if (HAVE_PAYLOAD_SPACE(link->get_chan(), MISSION_REQUEST)) {
     317        4851 :         queued_request_send();
     318             :     } else {
     319           0 :         link->send_message(next_item_ap_message_id());
     320             :     }
     321             : }
     322             : 
     323        3174 : void MissionItemProtocol::transfer_is_complete(const GCS_MAVLINK &_link, const mavlink_message_t &msg)
     324             : {
     325        3174 :     const MAV_MISSION_RESULT result = complete(_link);
     326        3174 :     send_mission_ack(_link, msg, result);
     327        3174 :     free_upload_resources();
     328        3174 :     receiving = false;
     329        3174 :     link = nullptr;
     330        3174 : }
     331             : 
     332           0 : void MissionItemProtocol::send_mission_ack(const mavlink_message_t &msg,
     333             :                                            MAV_MISSION_RESULT result) const
     334             : {
     335           0 :     if (link == nullptr) {
     336           0 :         INTERNAL_ERROR(AP_InternalError::error_t::gcs_bad_missionprotocol_link);
     337           0 :         return;
     338             :     }
     339           0 :     send_mission_ack(*link, msg, result);
     340             : }
     341        3182 : void MissionItemProtocol::send_mission_ack(const GCS_MAVLINK &_link,
     342             :                                            const mavlink_message_t &msg,
     343             :                                            MAV_MISSION_RESULT result) const
     344             : {
     345        3182 :     CHECK_PAYLOAD_SIZE2_VOID(_link.get_chan(), MISSION_ACK);
     346        3182 :     mavlink_msg_mission_ack_send(_link.get_chan(),
     347        3182 :                                  msg.sysid,
     348        3182 :                                  msg.compid,
     349             :                                  result,
     350        3182 :                                  mission_type());
     351             : }
     352             : 
     353             : /**
     354             :  * @brief Send the next pending waypoint, called from deferred message
     355             :  * handling code
     356             :  */
     357        5273 : void MissionItemProtocol::queued_request_send()
     358             : {
     359        5273 :     if (!receiving) {
     360           0 :         return;
     361             :     }
     362        5273 :     if (request_i > request_last) {
     363           0 :         return;
     364             :     }
     365        5273 :     if (link == nullptr) {
     366           0 :         INTERNAL_ERROR(AP_InternalError::error_t::gcs_bad_missionprotocol_link);
     367           0 :         return;
     368             :     }
     369        5273 :     CHECK_PAYLOAD_SIZE2_VOID(link->get_chan(), MISSION_REQUEST);
     370        5273 :     mavlink_msg_mission_request_send(
     371        5273 :         link->get_chan(),
     372        5273 :         dest_sysid,
     373        5273 :         dest_compid,
     374        5273 :         request_i,
     375        5273 :         mission_type());
     376        5273 :     timelast_request_ms = AP_HAL::millis();
     377             : }
     378             : 
     379   125596513 : void MissionItemProtocol::update()
     380             : {
     381   125596513 :     if (!receiving) {
     382             :         // we don't need to do anything unless we're sending requests
     383   125587184 :         return;
     384             :     }
     385        9329 :     if (link == nullptr) {
     386           0 :         INTERNAL_ERROR(AP_InternalError::error_t::gcs_bad_missionprotocol_link);
     387           0 :         return;
     388             :     }
     389             :     // stop waypoint receiving if timeout
     390        9329 :     const uint32_t tnow = AP_HAL::millis();
     391        9329 :     if (tnow - timelast_receive_ms > upload_timeout_ms) {
     392           4 :         receiving = false;
     393           4 :         timeout();
     394           4 :         const mavlink_channel_t chan = link->get_chan();
     395           4 :         if (HAVE_PAYLOAD_SPACE(chan, MISSION_ACK)) {
     396           4 :             mavlink_msg_mission_ack_send(chan,
     397           4 :                                          dest_sysid,
     398           4 :                                          dest_compid,
     399             :                                          MAV_MISSION_OPERATION_CANCELLED,
     400           4 :                                          mission_type());
     401             :         }
     402           4 :         link = nullptr;
     403           4 :         free_upload_resources();
     404           4 :         return;
     405             :     }
     406             :     // resend request if we haven't gotten one:
     407        9325 :     const uint32_t wp_recv_timeout_ms = 1000U + link->get_stream_slowdown_ms();
     408        9325 :     if (tnow - timelast_request_ms > wp_recv_timeout_ms) {
     409          28 :         timelast_request_ms = tnow;
     410          28 :         link->send_message(next_item_ap_message_id());
     411             :     }
     412             : }
     413             : 
     414             : #endif  // HAL_GCS_ENABLED

Generated by: LCOV version 1.14