LCOV - code coverage report
Current view: top level - AP_Filesystem - AP_Filesystem_Mission.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 91 273 33.3 %
Date: 2026-09-09 11:02:08 Functions: 8 14 57.1 %

          Line data    Source code
       1             : /*
       2             :    This program is free software: you can redistribute it and/or modify
       3             :    it under the terms of the GNU General Public License as published by
       4             :    the Free Software Foundation, either version 3 of the License, or
       5             :    (at your option) any later version.
       6             : 
       7             :    This program is distributed in the hope that it will be useful,
       8             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
       9             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      10             :    GNU General Public License for more details.
      11             : 
      12             :    You should have received a copy of the GNU General Public License
      13             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      14             :  */
      15             : /*
      16             :   ArduPilot filesystem interface for mission/fence/rally
      17             :  */
      18             : 
      19             : #include "AP_Filesystem_config.h"
      20             : 
      21             : #if AP_FILESYSTEM_MISSION_ENABLED
      22             : 
      23             : #include "AP_Filesystem.h"
      24             : #include "AP_Filesystem_Mission.h"
      25             : #include <AP_Mission/AP_Mission.h>
      26             : #include <AC_Fence/AC_Fence.h>
      27             : #include <AP_Rally/AP_Rally.h>
      28             : #include <GCS_MAVLink/MissionItemProtocol_Rally.h>
      29             : #include <GCS_MAVLink/MissionItemProtocol_Fence.h>
      30             : #include <GCS_MAVLink/GCS.h>
      31             : 
      32             : extern const AP_HAL::HAL& hal;
      33             : 
      34             : // QURT HAL already has a declaration of errno in errno.h
      35             : #if CONFIG_HAL_BOARD != HAL_BOARD_QURT
      36             : extern int errno;
      37             : #endif
      38             : 
      39             : #define IDLE_TIMEOUT_MS 30000
      40             : 
      41           3 : int AP_Filesystem_Mission::open(const char *fname, int flags, bool allow_absolute_paths)
      42             : {
      43             :     enum MAV_MISSION_TYPE mtype;
      44             : 
      45           3 :     if (!check_file_name(fname, mtype)) {
      46           0 :         errno = ENOENT;
      47           0 :         return -1;
      48             :     }
      49             :     uint8_t idx;
      50           3 :     bool readonly = ((flags & O_ACCMODE) == O_RDONLY);
      51           3 :     uint32_t now = AP_HAL::millis();
      52           3 :     for (idx=0; idx<max_open_file; idx++) {
      53           3 :         if (now - file[idx].last_op_ms > IDLE_TIMEOUT_MS) {
      54           1 :             file[idx].open = false;
      55           1 :             delete file[idx].writebuf;
      56           1 :             file[idx].writebuf = nullptr;
      57             :         }
      58           3 :         if (!readonly && file[idx].writebuf != nullptr) {
      59             :             // only one upload at a time
      60           0 :             return -1;
      61             :         }
      62           3 :         if (!file[idx].open) {
      63           3 :             break;
      64             :         }
      65             :     }
      66           3 :     if (idx == max_open_file) {
      67           0 :         errno = ENFILE;
      68           0 :         return -1;
      69             :     }
      70           3 :     struct rfile &r = file[idx];
      71           3 :     r.file_ofs = 0;
      72           3 :     r.open = true;
      73           3 :     r.mtype = mtype;
      74           3 :     r.num_items = get_num_items(r.mtype);
      75           3 :     if (!readonly) {
      76             :         // setup for upload
      77           0 :         r.writebuf = NEW_NOTHROW ExpandingString();
      78             :     } else {
      79           3 :         r.writebuf = nullptr;
      80             :     }
      81           3 :     r.last_op_ms = now;
      82             : 
      83           3 :     return idx;
      84             : }
      85             : 
      86           3 : int AP_Filesystem_Mission::close(int fd)
      87             : {
      88           3 :     if (fd < 0 || fd >= max_open_file || !file[fd].open) {
      89           0 :         errno = EBADF;
      90           0 :         return -1;
      91             :     }
      92           3 :     struct rfile &r = file[fd];
      93           3 :     r.open = false;
      94           3 :     if (r.writebuf != nullptr) {
      95           0 :         bool ok = finish_upload(r);
      96           0 :         delete r.writebuf;
      97           0 :         r.writebuf = nullptr;
      98           0 :         if (!ok) {
      99           0 :             errno = EINVAL;
     100           0 :             return -1;
     101             :         }
     102             :     }
     103           3 :     return 0;
     104             : }
     105             : 
     106             : /*
     107             :   packed format:
     108             :     file header:
     109             :       uint16_t magic = 0x671b
     110             :       uint16_t data_type MAV_MISSION_TYPE_*
     111             :       uint32_t total_items
     112             : 
     113             :     per-entry is mavlink packed item
     114             :  */
     115             : 
     116             : /*
     117             :   read from file handle
     118             :  */
     119         100 : int32_t AP_Filesystem_Mission::read(int fd, void *buf, uint32_t count)
     120             : {
     121         100 :     if (fd < 0 || fd >= max_open_file || !file[fd].open) {
     122           0 :         errno = EBADF;
     123           0 :         return -1;
     124             :     }
     125             : 
     126         100 :     struct rfile &r = file[fd];
     127             : 
     128         100 :     if (r.writebuf != nullptr) {
     129           0 :         errno = EBADF;
     130           0 :         return -1;
     131             :     }
     132             : 
     133         100 :     r.last_op_ms = AP_HAL::millis();
     134             : 
     135         100 :     size_t header_total = 0;
     136         100 :     uint8_t *ubuf = (uint8_t *)buf;
     137             : 
     138         100 :     if (r.file_ofs < sizeof(struct header)) {
     139           3 :         struct header hdr {};
     140           3 :         hdr.data_type = uint16_t(r.mtype);
     141           3 :         hdr.num_items = r.num_items;
     142           3 :         uint8_t n = MIN(sizeof(hdr) - r.file_ofs, count);
     143           3 :         const uint8_t *b = (const uint8_t *)&hdr;
     144           3 :         memcpy(ubuf, &b[r.file_ofs], n);
     145           3 :         count -= n;
     146           3 :         header_total += n;
     147           3 :         r.file_ofs += n;
     148           3 :         ubuf += n;
     149           3 :         if (count == 0) {
     150           0 :             return header_total;
     151             :         }
     152             :     }
     153             : 
     154         100 :     uint32_t data_ofs = r.file_ofs - sizeof(struct header);
     155         100 :     mavlink_mission_item_int_t item {};
     156         100 :     const uint8_t item_size = MAVLINK_MSG_ID_MISSION_ITEM_INT_LEN;
     157         100 :     uint32_t item_ofs = data_ofs % item_size;
     158         100 :     uint32_t total = 0;
     159             : 
     160         796 :     while (count > 0) {
     161         701 :         uint32_t item_idx = data_ofs / item_size;
     162             : 
     163         701 :         const uint8_t *ibuf = (const uint8_t *)&item;
     164         701 :         if (!get_item(item_idx, r.mtype, item)) {
     165           5 :             break;
     166             :         }
     167         696 :         const uint8_t n = MIN(item_size - item_ofs, count);
     168         696 :         memcpy(ubuf, &ibuf[item_ofs], n);
     169         696 :         ubuf += n;
     170         696 :         count -= n;
     171         696 :         total += n;
     172         696 :         item_ofs = 0;
     173         696 :         data_ofs += n;
     174             :     }
     175             : 
     176         100 :     r.file_ofs += total;
     177         100 :     return total + header_total;
     178             : }
     179             : 
     180           3 : int32_t AP_Filesystem_Mission::lseek(int fd, int32_t offset, int seek_from)
     181             : {
     182           3 :     if (fd < 0 || fd >= max_open_file || !file[fd].open) {
     183           0 :         errno = EBADF;
     184           0 :         return -1;
     185             :     }
     186           3 :     struct rfile &r = file[fd];
     187           3 :     switch (seek_from) {
     188           3 :     case SEEK_SET:
     189           3 :         r.file_ofs = offset;
     190           3 :         break;
     191           0 :     case SEEK_CUR:
     192           0 :         r.file_ofs += offset;
     193           0 :         break;
     194           0 :     case SEEK_END:
     195           0 :         errno = EINVAL;
     196           0 :         return -1;
     197             :     }
     198           3 :     return r.file_ofs;
     199             : }
     200             : 
     201           3 : int AP_Filesystem_Mission::stat(const char *name, struct stat *stbuf)
     202             : {
     203             :     enum MAV_MISSION_TYPE mtype;
     204           3 :     if (!check_file_name(name, mtype)) {
     205           0 :         errno = ENOENT;
     206           0 :         return -1;
     207             :     }
     208           3 :     memset(stbuf, 0, sizeof(*stbuf));
     209           3 :     stbuf->st_size = sizeof(struct header) + get_num_items(mtype) * MAVLINK_MSG_ID_MISSION_ITEM_INT_LEN;
     210           3 :     return 0;
     211             : }
     212             : 
     213             : /*
     214             :   check for the right file name
     215             :  */
     216           6 : bool AP_Filesystem_Mission::check_file_name(const char *name, enum MAV_MISSION_TYPE &mtype)
     217             : {
     218             : #if AP_MISSION_ENABLED
     219           6 :     if (strcmp(name, "mission.dat") == 0) {
     220           6 :         mtype = MAV_MISSION_TYPE_MISSION;
     221           6 :         return true;
     222             :     }
     223             : #endif
     224             : #if AP_FENCE_ENABLED
     225           0 :     if (strcmp(name, "fence.dat") == 0) {
     226           0 :         mtype = MAV_MISSION_TYPE_FENCE;
     227           0 :         return true;
     228             :     }
     229             : #endif
     230             : #if HAL_RALLY_ENABLED
     231           0 :     if (strcmp(name, "rally.dat") == 0) {
     232           0 :         mtype = MAV_MISSION_TYPE_RALLY;
     233           0 :         return true;
     234             :     }
     235             : #endif
     236           0 :     return false;
     237             : }
     238             : 
     239             : /*
     240             :   get one item
     241             :  */
     242         701 : bool AP_Filesystem_Mission::get_item(uint32_t idx, enum MAV_MISSION_TYPE mtype, mavlink_mission_item_int_t &item) const
     243             : {
     244         701 :     switch (mtype) {
     245         701 :     case MAV_MISSION_TYPE_MISSION: {
     246         701 :         return AP::mission().get_item(idx, item);
     247             :     }
     248             : #if AP_FENCE_ENABLED
     249           0 :     case MAV_MISSION_TYPE_FENCE:
     250           0 :         return MissionItemProtocol_Fence::get_item_as_mission_item(idx, item);
     251             : #endif
     252             : #if HAL_RALLY_ENABLED
     253           0 :     case MAV_MISSION_TYPE_RALLY:
     254           0 :         return MissionItemProtocol_Rally::get_item_as_mission_item(idx, item);
     255             : #endif
     256           0 :     default:
     257           0 :         break;
     258             :     }
     259           0 :     return false;
     260             : }
     261             : 
     262             : // get number of items
     263           6 : uint32_t AP_Filesystem_Mission::get_num_items(enum MAV_MISSION_TYPE mtype) const
     264             : {
     265           6 :     switch (mtype) {
     266             : #if AP_MISSION_ENABLED
     267           6 :     case MAV_MISSION_TYPE_MISSION: {
     268           6 :         return AP::mission().num_commands();
     269             :     }
     270             : #endif
     271             : 
     272             : #if AP_FENCE_ENABLED
     273           0 :     case MAV_MISSION_TYPE_FENCE: {
     274           0 :         auto *fence = AP::fence();
     275           0 :         if (fence == nullptr) {
     276           0 :             return 0;
     277             :         }
     278           0 :         return fence->polyfence().num_stored_items();
     279             :     }
     280             : #endif
     281             : 
     282             : #if HAL_RALLY_ENABLED
     283           0 :     case MAV_MISSION_TYPE_RALLY: {
     284           0 :         auto *rally = AP::rally();
     285           0 :         if (rally == nullptr) {
     286           0 :             return 0;
     287             :         }
     288           0 :         return rally->get_rally_total();
     289             :     }
     290             : #endif
     291             : 
     292           0 :     default:
     293           0 :         break;
     294             :     }
     295           0 :     return 0;
     296             : }
     297             : 
     298             : /*
     299             :   support mission upload
     300             :  */
     301           0 : int32_t AP_Filesystem_Mission::write(int fd, const void *buf, uint32_t count)
     302             : {
     303           0 :     if (fd < 0 || fd >= max_open_file || !file[fd].open) {
     304           0 :         errno = EBADF;
     305           0 :         return -1;
     306             :     }
     307           0 :     struct rfile &r = file[fd];
     308           0 :     if (r.writebuf == nullptr) {
     309           0 :         errno = EBADF;
     310           0 :         return -1;
     311             :     }
     312           0 :     r.last_op_ms = AP_HAL::millis();
     313           0 :     struct header hdr;
     314           0 :     if (r.file_ofs == 0 && count >= sizeof(hdr)) {
     315             :         // pre-expand the buffer to the full size when we get the header
     316           0 :         memcpy(&hdr, buf, sizeof(hdr));
     317           0 :         if (hdr.num_items < 0xFFFF) {
     318           0 :             const uint32_t flen = sizeof(hdr) + hdr.num_items * MAVLINK_MSG_ID_MISSION_ITEM_INT_LEN;
     319           0 :             if (flen > r.writebuf->get_length()) {
     320           0 :                 if (!r.writebuf->append(nullptr, flen - r.writebuf->get_length())) {
     321             :                     // not enough memory
     322           0 :                     errno = ENOSPC;
     323           0 :                     return -1;
     324             :                 }
     325             :             }
     326             :         }
     327             :     }
     328           0 :     if (r.file_ofs + count > r.writebuf->get_length()) {
     329           0 :         if (!r.writebuf->append(nullptr, r.file_ofs + count - r.writebuf->get_length())) {
     330           0 :             errno = ENOSPC;
     331           0 :             return -1;
     332             :         }
     333             :     }
     334           0 :     uint8_t *b = (uint8_t *)r.writebuf->get_writeable_string();
     335           0 :     memcpy(&b[r.file_ofs], buf, count);
     336           0 :     r.file_ofs += count;
     337           0 :     return count;
     338             : }
     339             : 
     340             : // see if a block of memory is all zero
     341           0 : bool AP_Filesystem_Mission::all_zero(const uint8_t *b, uint8_t len) const
     342             : {
     343           0 :     while (len--) {
     344           0 :         if (*b++ != 0) {
     345           0 :             return false;
     346             :         }
     347             :     }
     348           0 :     return true;
     349             : }
     350             : 
     351             : /*
     352             :   finish mission upload
     353             :  */
     354           0 : bool AP_Filesystem_Mission::finish_upload(const rfile &r)
     355             : {
     356           0 :     const uint32_t flen = r.writebuf->get_length();
     357           0 :     const uint8_t *b = (const uint8_t *)r.writebuf->get_string();
     358           0 :     struct header hdr;
     359           0 :     if (flen < sizeof(hdr)) {
     360           0 :         return false;
     361             :     }
     362           0 :     const uint8_t item_size = MAVLINK_MSG_ID_MISSION_ITEM_INT_LEN;
     363           0 :     const uint32_t nitems = (flen - sizeof(hdr)) / item_size;
     364             : 
     365           0 :     memcpy(&hdr, b, sizeof(hdr));
     366           0 :     if (hdr.magic != mission_magic) {
     367           0 :         return false;
     368             :     }
     369           0 :     if (nitems != hdr.num_items) {
     370           0 :         return false;
     371             :     }
     372             : 
     373             :     // if any item is all zeros then reject, it means client didn't
     374             :     // fill in the whole file
     375           0 :     for (uint32_t i=0; i<nitems; i++) {
     376           0 :         const uint8_t *b2 = b + sizeof(hdr) + i*item_size;
     377           0 :         if (all_zero(b2, item_size)) {
     378           0 :             return false;
     379             :         }
     380             :     }
     381             : 
     382           0 :     switch (r.mtype) {
     383             : #if AP_MISSION_ENABLED
     384           0 :     case MAV_MISSION_TYPE_MISSION:
     385           0 :         return finish_upload_mission(hdr, r, b);
     386             : #endif
     387             : #if HAL_RALLY_ENABLED
     388           0 :     case MAV_MISSION_TYPE_RALLY:
     389           0 :         return finish_upload_rally(hdr, r, b);
     390             : #endif
     391             : #if AP_FENCE_ENABLED
     392           0 :     case MAV_MISSION_TYPE_FENCE:
     393           0 :         return finish_upload_fence(hdr, r, b);
     394             : #endif
     395           0 :     default:
     396             :         // really should not get here....
     397           0 :         break;
     398             :     }
     399             : 
     400           0 :     return false;
     401             : }
     402             : 
     403             : #if AP_MISSION_ENABLED
     404           0 : bool AP_Filesystem_Mission::finish_upload_mission(const struct header &hdr, const rfile &r, const uint8_t *b)
     405             : {
     406           0 :     auto &mission = AP::mission();
     407           0 :     WITH_SEMAPHORE(mission.get_semaphore());
     408           0 :     if ((hdr.options & unsigned(Options::NO_CLEAR)) == 0) {
     409           0 :         mission.clear();
     410             :     }
     411           0 :     for (uint32_t i=0; i<hdr.num_items; i++) {
     412           0 :         mavlink_mission_item_int_t m {};
     413           0 :         AP_Mission::Mission_Command cmd;
     414           0 :         const uint8_t item_size = MAVLINK_MSG_ID_MISSION_ITEM_INT_LEN;
     415           0 :         memcpy(&m, &b[sizeof(hdr)+i*item_size], item_size);
     416           0 :         const MAV_MISSION_RESULT res = AP_Mission::mavlink_int_to_mission_cmd(m, cmd);
     417           0 :         if (res != MAV_MISSION_ACCEPTED) {
     418           0 :             return false;
     419             :         }
     420           0 :         if (cmd.id == MAV_CMD_DO_JUMP &&
     421           0 :             (cmd.content.jump.target >= hdr.num_items || cmd.content.jump.target == 0)) {
     422           0 :             return false;
     423             :         }
     424           0 :         uint16_t idx = i + hdr.start;
     425           0 :         if (idx == mission.num_commands()) {
     426           0 :             if (!mission.add_cmd(cmd)) {
     427           0 :                 return false;
     428             :             }
     429             :         } else {
     430           0 :             if (!mission.replace_cmd(idx, cmd)) {
     431           0 :                 return false;
     432             :             }
     433             :         }
     434             :     }
     435           0 :     return true;
     436           0 : }
     437             : #endif  // AP_MISSION_ENABLED
     438             : 
     439             : #if AP_FENCE_ENABLED
     440           0 : bool AP_Filesystem_Mission::finish_upload_fence(const struct header &hdr, const rfile &r, const uint8_t *b)
     441             : {
     442           0 :     bool success = false;
     443             : 
     444           0 :     AC_PolyFenceItem *new_items = nullptr;
     445             : 
     446           0 :     auto *fence = AP::fence();
     447           0 :     if (fence == nullptr) {
     448           0 :         goto OUT;
     449             :     }
     450             : 
     451           0 :     if ((hdr.options & unsigned(Options::NO_CLEAR)) != 0) {
     452             :         // Only complete fences can be uploaded for now.
     453           0 :         goto OUT;
     454             :     }
     455             : 
     456             :     // passing nullptr and 0 items through to Polyfence loader is
     457             :     // absolutely OK:
     458           0 :     if (hdr.num_items != 0) {
     459           0 :         new_items = NEW_NOTHROW AC_PolyFenceItem[hdr.num_items];
     460           0 :         if (new_items == nullptr) {
     461           0 :             GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "Out of memory for upload");
     462           0 :             goto OUT;
     463             :         }
     464             :     }
     465             : 
     466             :     // convert from MISSION_ITEM_INT to AC_PolyFenceItem:
     467           0 :     for (uint32_t i=0; i<hdr.num_items; i++) {
     468           0 :         mavlink_mission_item_int_t m {};
     469           0 :         const uint8_t item_size = MAVLINK_MSG_ID_MISSION_ITEM_INT_LEN;
     470           0 :         memcpy(&m, &b[sizeof(hdr)+i*item_size], item_size);
     471           0 :         const MAV_MISSION_RESULT res = MissionItemProtocol_Fence::convert_MISSION_ITEM_INT_to_AC_PolyFenceItem(m, new_items[i]);
     472           0 :         if (res != MAV_MISSION_ACCEPTED) {
     473           0 :             goto OUT;
     474             :         }
     475             :     }
     476             : 
     477           0 :     success = fence->polyfence().write_fence(new_items, hdr.num_items);
     478             : 
     479           0 : OUT:
     480             : 
     481           0 :     delete[] new_items;
     482             : 
     483           0 :     return success;
     484             : }
     485             : #endif  // AP_FENCE_ENABLED
     486             : 
     487             : #if HAL_RALLY_ENABLED
     488           0 : bool AP_Filesystem_Mission::finish_upload_rally(const struct header &hdr, const rfile &r, const uint8_t *b)
     489             : {
     490           0 :     bool success = false;
     491             : 
     492           0 :     auto *rally = AP::rally();
     493           0 :     if (rally == nullptr) {
     494           0 :         goto OUT;
     495             :     }
     496             : 
     497           0 :     if ((hdr.options & unsigned(Options::NO_CLEAR)) != 0) {
     498             :         //only complete sets of rally points can be added ATM
     499           0 :         goto OUT;
     500             :     }
     501             : 
     502           0 :     rally->truncate(0);
     503             : 
     504           0 :     for (uint32_t i=0; i<hdr.num_items; i++) {
     505           0 :         mavlink_mission_item_int_t m {};
     506             :         RallyLocation cmd;
     507           0 :         const uint8_t item_size = MAVLINK_MSG_ID_MISSION_ITEM_INT_LEN;
     508           0 :         memcpy(&m, &b[sizeof(hdr)+i*item_size], item_size);
     509           0 :         const MAV_MISSION_RESULT res = MissionItemProtocol_Rally::convert_MISSION_ITEM_INT_to_RallyLocation(m, cmd);
     510           0 :         if (res != MAV_MISSION_ACCEPTED) {
     511           0 :             goto OUT;
     512             :         }
     513             : 
     514           0 :         if (!rally->append(cmd)) {
     515           0 :             goto OUT;
     516             :         }
     517             :     }
     518           0 :     success = true;
     519             : 
     520           0 : OUT:
     521           0 :     return success;
     522             : }
     523             : #endif  // HAL_RALLY_ENABLED
     524             : 
     525             : #endif  // AP_FILESYSTEM_MISSION_ENABLED

Generated by: LCOV version 1.14