LCOV - code coverage report
Current view: top level - AC_Fence - AC_PolyFence_loader.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 583 792 73.6 %
Date: 2026-09-09 11:02:08 Functions: 36 36 100.0 %

          Line data    Source code
       1             : #include "AC_PolyFence_loader.h"
       2             : 
       3             : #if AP_FENCE_ENABLED
       4             : 
       5             : #include <AP_Vehicle/AP_Vehicle_Type.h>
       6             : #include <AP_BoardConfig/AP_BoardConfig.h>
       7             : 
       8             : #ifndef AC_FENCE_DUMMY_METHODS_ENABLED
       9             : #define AC_FENCE_DUMMY_METHODS_ENABLED  (!(APM_BUILD_TYPE(APM_BUILD_Rover) | APM_BUILD_COPTER_OR_HELI | APM_BUILD_TYPE(APM_BUILD_ArduPlane) | APM_BUILD_TYPE(APM_BUILD_ArduSub) | (AP_FENCE_ENABLED == 1)))
      10             : #endif
      11             : 
      12             : #if !AC_FENCE_DUMMY_METHODS_ENABLED
      13             : 
      14             : #include <AP_AHRS/AP_AHRS.h>
      15             : #include <GCS_MAVLink/GCS.h>
      16             : #include <AP_Logger/AP_Logger.h>
      17             : #include <AC_Fence/AC_Fence.h>
      18             : 
      19             : #include <stdio.h>
      20             : 
      21             : #define POLYFENCE_LOADER_DEBUGGING 0
      22             : 
      23             : #if POLYFENCE_LOADER_DEBUGGING
      24             : #define Debug(fmt, args ...)  do { GCS_SEND_TEXT(MAV_SEVERITY_INFO, fmt, ## args); } while (0)
      25             : #else
      26             : #define Debug(fmt, args ...)
      27             : #endif
      28             : 
      29             : extern const AP_HAL::HAL& hal;
      30             : 
      31             : static StorageAccess fence_storage(StorageManager::StorageFence);
      32             : 
      33             : #if CONFIG_HAL_BOARD == HAL_BOARD_CHIBIOS
      34             : #define AC_FENCE_SDCARD_FILENAME "APM/fence.stg"
      35             : #else
      36             : #define AC_FENCE_SDCARD_FILENAME "fence.stg"
      37             : #endif
      38             : 
      39        2054 : void AC_PolyFence_loader::init()
      40             : {
      41             : #if AP_SDCARD_STORAGE_ENABLED
      42             :     // check for extra storage on microsd
      43        2054 :     const auto *bc = AP::boardConfig();
      44        2054 :     if (bc != nullptr) {
      45        2054 :         const auto size_kb = bc->get_sdcard_fence_kb();
      46        2054 :         if (size_kb > 0) {
      47           1 :             _failed_sdcard_storage = !fence_storage.attach_file(AC_FENCE_SDCARD_FILENAME, size_kb);
      48           1 :             if (_failed_sdcard_storage) {
      49             :                 // wipe fence if storage not available, but don't
      50             :                 // save. This allows sdcard error to be fixed and
      51             :                 // reboot
      52           0 :                 _total.set(0);
      53             :             }
      54             :         }
      55             :     }
      56             : #endif
      57        2054 :     if (!check_indexed()) {
      58             :         // tell the user, perhaps?
      59             :     }
      60        2054 :     _old_total = _total;
      61        2054 : }
      62             : 
      63        2786 : bool AC_PolyFence_loader::find_index_for_seq(const uint16_t seq, const FenceIndex *&entry, uint16_t &i) const
      64             : {
      65        2786 :     if (_index == nullptr) {
      66           0 :         return false;
      67             :     }
      68             : 
      69        2786 :     if (seq > _eeprom_item_count) {
      70           0 :         return false;
      71             :     }
      72             : 
      73        2786 :     i = 0;
      74        9212 :     for (uint16_t j=0; j<_num_fences; j++) {
      75        9212 :         entry = &_index[j];
      76        9212 :         if (seq < i + entry->count) {
      77        2786 :             return true;
      78             :         }
      79        6426 :         i += entry->count;
      80             :     }
      81           0 :     return false;
      82             : }
      83             : 
      84        2786 : bool AC_PolyFence_loader::find_storage_offset_for_seq(const uint16_t seq, uint16_t &offset, AC_PolyFenceType &type, uint16_t &vertex_count_offset) const
      85             : {
      86        2786 :     if (_index == nullptr) {
      87           0 :         return false;
      88             :     }
      89             : 
      90        2786 :     uint16_t i = 0;
      91        2786 :     const FenceIndex *entry = nullptr;
      92        2786 :     if (!find_index_for_seq(seq, entry, i)) {
      93           0 :         return false;
      94             :     }
      95             : 
      96        2786 :     if (entry == nullptr) {
      97           0 :         INTERNAL_ERROR(AP_InternalError::error_t::flow_of_control);
      98           0 :         return false;
      99             :     }
     100             : 
     101        2786 :     const uint16_t delta = seq - i;
     102             : 
     103        2786 :     offset = entry->storage_offset;
     104        2786 :     type = entry->type;
     105        2786 :     offset++; // skip over type
     106        2786 :     switch (type) {
     107             : #if AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED
     108          53 :     case AC_PolyFenceType::CIRCLE_INCLUSION_INT:
     109             :     case AC_PolyFenceType::CIRCLE_EXCLUSION_INT:
     110             : #endif  // AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED
     111             :     case AC_PolyFenceType::CIRCLE_INCLUSION:
     112             :     case AC_PolyFenceType::CIRCLE_EXCLUSION:
     113             :     case AC_PolyFenceType::HOME_CIRCLE_INCLUSION:
     114          53 :         if (delta != 0) {
     115           0 :             INTERNAL_ERROR(AP_InternalError::error_t::flow_of_control);
     116           0 :             return false;
     117             :         }
     118          53 :         break;
     119        2732 :     case AC_PolyFenceType::POLYGON_INCLUSION:
     120             :     case AC_PolyFenceType::POLYGON_EXCLUSION:
     121        2732 :         vertex_count_offset = offset;
     122        2732 :         offset += 1; // the count of points in the fence
     123        2732 :         offset += (delta * 8);
     124        2732 :         break;
     125           1 :     case AC_PolyFenceType::RETURN_POINT:
     126           1 :         if (delta != 0) {
     127           0 :             INTERNAL_ERROR(AP_InternalError::error_t::flow_of_control);
     128           0 :             return false;
     129             :         }
     130           1 :         break;
     131           0 :     case AC_PolyFenceType::END_OF_STORAGE:
     132           0 :         INTERNAL_ERROR(AP_InternalError::error_t::flow_of_control);
     133           0 :         return false;
     134             :     }
     135        2786 :     return true;
     136             : }
     137             : 
     138        2786 : bool AC_PolyFence_loader::get_item(const uint16_t seq, AC_PolyFenceItem &item)
     139             : {
     140        2786 :     if (!check_indexed()) {
     141           0 :         return false;
     142             :     }
     143             : 
     144        2786 :     uint16_t vertex_count_offset = 0; // initialised to make compiler happy
     145             :     uint16_t offset;
     146             :     AC_PolyFenceType type;
     147        2786 :     if (!find_storage_offset_for_seq(seq, offset, type, vertex_count_offset)) {
     148           0 :         return false;
     149             :     }
     150             : 
     151        2786 :     item.type = type;
     152             : 
     153        2786 :     switch (type) {
     154          35 :     case AC_PolyFenceType::CIRCLE_INCLUSION:
     155             :     case AC_PolyFenceType::CIRCLE_EXCLUSION:
     156          35 :         if (!read_latlon_from_storage(offset, item.loc)) {
     157           0 :             return false;
     158             :         }
     159          35 :         item.radius = fence_storage.read_float(offset);
     160          35 :         break;
     161          18 :     case AC_PolyFenceType::HOME_CIRCLE_INCLUSION: {
     162          18 :         item.radius = fence_storage.read_float(offset);
     163          18 :         break;
     164             :     }
     165             : #if AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED
     166           0 :     case AC_PolyFenceType::CIRCLE_INCLUSION_INT:
     167             :     case AC_PolyFenceType::CIRCLE_EXCLUSION_INT:
     168           0 :         if (!read_latlon_from_storage(offset, item.loc)) {
     169           0 :             return false;
     170             :         }
     171           0 :         item.radius = fence_storage.read_uint32(offset);
     172             :         // magically change int item into a float item:
     173           0 :         if (type == AC_PolyFenceType::CIRCLE_INCLUSION_INT) {
     174           0 :             item.type = AC_PolyFenceType::CIRCLE_INCLUSION;
     175             :         } else {
     176           0 :             item.type = AC_PolyFenceType::CIRCLE_EXCLUSION;
     177             :         }
     178           0 :         break;
     179             : #endif  // AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED
     180        2732 :     case AC_PolyFenceType::POLYGON_INCLUSION:
     181             :     case AC_PolyFenceType::POLYGON_EXCLUSION:
     182        2732 :         if (!read_latlon_from_storage(offset, item.loc)) {
     183           0 :             return false;
     184             :         }
     185        2732 :         item.vertex_count = fence_storage.read_uint8(vertex_count_offset);
     186        2732 :         break;
     187           1 :     case AC_PolyFenceType::RETURN_POINT:
     188           1 :         if (!read_latlon_from_storage(offset, item.loc)) {
     189           0 :             return false;
     190             :         }
     191           1 :         break;
     192           0 :     case AC_PolyFenceType::END_OF_STORAGE:
     193             :         // read end-of-storage when I should never do so
     194           0 :         INTERNAL_ERROR(AP_InternalError::error_t::flow_of_control);
     195           0 :         return false;
     196             :     }
     197        2786 :     return true;
     198             : }
     199             : 
     200        2367 : bool AC_PolyFence_loader::write_type_to_storage(uint16_t &offset, const AC_PolyFenceType type)
     201             : {
     202        2367 :     fence_storage.write_uint8(offset, (uint8_t)type);
     203        2367 :     offset++;
     204        2367 :     return true;
     205             : }
     206             : 
     207         950 : bool AC_PolyFence_loader::write_latlon_to_storage(uint16_t &offset, const Vector2l &latlon)
     208             : {
     209         950 :     fence_storage.write_uint32(offset, latlon.x);
     210         950 :     offset += 4;
     211         950 :     fence_storage.write_uint32(offset, latlon.y);
     212         950 :     offset += 4;
     213         950 :     return true;
     214             : }
     215             : 
     216        4465 : bool AC_PolyFence_loader::read_latlon_from_storage(uint16_t &read_offset, Vector2l &ret) const
     217             : {
     218        4465 :     ret.x = fence_storage.read_uint32(read_offset);
     219        4465 :     read_offset += 4;
     220        4465 :     ret.y = fence_storage.read_uint32(read_offset);
     221        4465 :     read_offset += 4;
     222        4465 :     return true;
     223             : }
     224             : 
     225       43919 : bool AC_PolyFence_loader::breached() const
     226             : {
     227       43919 :     Location loc;
     228       43919 :     if (!AP::ahrs().get_location(loc)) {
     229        2899 :         return false;
     230             :     }
     231             : 
     232       41020 :     return breached(loc);
     233             : }
     234             : 
     235             : // check if a position (expressed as lat/lng) is within the boundary
     236             : //   returns true if location is outside the boundary
     237     3465310 : bool AC_PolyFence_loader::breached(const Location& loc, float& distance_outside_fence, Vector2f& fence_direction) const
     238             : {
     239     3465310 :     if (!loaded() || total_fence_count() == 0) {
     240        2730 :         return false;
     241             :     }
     242             : 
     243     3462580 :     Vector2f scaled_pos;
     244     3462580 :     Vector2l pos { loc.lat, loc.lng };
     245     3462580 :     if (!scale_latlon_from_origin(loaded_origin, pos, scaled_pos)) {
     246           0 :         return false;
     247             :     }
     248             : 
     249     3462580 :     const uint16_t num_inclusion = _num_loaded_circle_inclusion_boundaries + _num_loaded_inclusion_boundaries;
     250     3462580 :     uint16_t num_inclusion_outside = 0;
     251     3462580 :     distance_outside_fence = -FLT_MAX;
     252             : 
     253             :     // check we are inside each inclusion zone:
     254     6911541 :     for (uint8_t i=0; i<_num_loaded_inclusion_boundaries; i++) {
     255     3448961 :         const InclusionBoundary &boundary = _loaded_inclusion_boundary[i];
     256     3448961 :         bool valid_distance = Polygon_closest_distance_point(boundary.points, boundary.count, scaled_pos, fence_direction);
     257     3448961 :         float distance = fence_direction.length() * 0.01f; // convert back to meters
     258     3448961 :         if (Polygon_outside(pos, boundary.points_lla, boundary.count)) {
     259     2336974 :             num_inclusion_outside++;
     260     2336974 :             if (valid_distance) {
     261     2336974 :                 if (is_positive(distance_outside_fence)) {
     262           0 :                     distance_outside_fence = MIN(distance_outside_fence, distance);
     263             :                 } else {
     264     2336974 :                     distance_outside_fence = distance;
     265             :                 }
     266             :             }
     267     1111987 :         } else if (valid_distance) {
     268     1111987 :             distance_outside_fence = MAX(distance_outside_fence, -distance);
     269             :         }
     270             :     }
     271             : 
     272             :     // check we are outside each exclusion zone:
     273     3467727 :     for (uint8_t i=0; i<_num_loaded_exclusion_boundaries; i++) {
     274        5735 :         const ExclusionBoundary &boundary = _loaded_exclusion_boundary[i];
     275        5735 :         bool valid_distance = Polygon_closest_distance_point(boundary.points, boundary.count, scaled_pos, fence_direction);
     276        5735 :         float distance = fence_direction.length() * 0.01f; // convert back to meters
     277        5735 :         if (!Polygon_outside(pos, boundary.points_lla, boundary.count)) {
     278         588 :             if (valid_distance) {
     279         588 :                 distance_outside_fence = distance;
     280             :             } else {
     281           0 :                 distance_outside_fence = 0.0f;
     282             :             }
     283         588 :             return true;
     284        5147 :         } else if (valid_distance) {
     285        5147 :             distance_outside_fence = MAX(distance_outside_fence, -distance);
     286             :         }
     287             :     }
     288             : 
     289     3472105 :     for (uint8_t i=0; i<_num_loaded_circle_exclusion_boundaries; i++) {
     290       10494 :         const ExclusionCircle &circle = _loaded_circle_exclusion_boundary[i];
     291       10494 :         Location circle_center;
     292       10494 :         circle_center.lat = circle.point.x;
     293       10494 :         circle_center.lng = circle.point.y;
     294       10494 :         const float diff_cm = loc.get_distance(circle_center)*100.0f;
     295       10494 :         distance_outside_fence = MAX(distance_outside_fence, circle.radius - diff_cm*0.01f);
     296       10494 :         if (diff_cm < circle.radius * 100.0f) {
     297         381 :             return true;
     298             :         }
     299             :     }
     300             : 
     301     3477623 :     for (uint8_t i=0; i<_num_loaded_circle_inclusion_boundaries; i++) {
     302       16012 :         const InclusionCircle &circle = _loaded_circle_inclusion_boundary[i];
     303       16012 :         Location circle_center;
     304       16012 :         if (circle.is_home_centered()) {
     305       13864 :             circle_center = AP::ahrs().get_home();
     306             :         } else {
     307        2148 :             circle_center.lat = circle.point.x;
     308        2148 :             circle_center.lng = circle.point.y;
     309             :         }
     310             : 
     311       16012 :         const float diff_cm = loc.get_distance(circle_center)*100.0f;
     312       16012 :         distance_outside_fence = MAX(distance_outside_fence, diff_cm*0.01f - circle.radius);
     313       16012 :         if (diff_cm > circle.radius * 100.0f) {
     314        1643 :             num_inclusion_outside++;
     315             :         }
     316             :     }
     317             : 
     318     3461611 :     if (AC_Fence::option_enabled(AC_Fence::OPTIONS::INCLUSION_UNION, _options)) {
     319             :         // using union of inclusion areas, we are outside the fence if
     320             :         // there is at least one inclusion areas and we are outside
     321             :         // all of them
     322           0 :         if (num_inclusion > 0 && num_inclusion == num_inclusion_outside) {
     323           0 :             return true;
     324             :         }
     325             :     } else {
     326             :         // using intersection of inclusion areas. We are outside if we
     327             :         // are outside any of them
     328     3461611 :         if (num_inclusion_outside > 0) {
     329     2338522 :             return true;
     330             :         }
     331             :     }
     332             : 
     333             :     // if no value was found, reset to 0
     334     1123089 :     if (is_equal(distance_outside_fence, -FLT_MAX)) {
     335           0 :         distance_outside_fence = 0.0f;
     336             :     }
     337             : 
     338             :     // no fence breached
     339     1123089 :     return false;
     340             : }
     341             : 
     342        8236 : bool AC_PolyFence_loader::formatted() const
     343             : {
     344       16181 :     return (fence_storage.read_uint8(0) == new_fence_storage_magic &&
     345       15890 :             fence_storage.read_uint8(1) == 0 &&
     346       24126 :             fence_storage.read_uint8(2) == 0 &&
     347       16181 :             fence_storage.read_uint8(3) == 0);
     348             : }
     349             : 
     350        1008 : uint16_t AC_PolyFence_loader::max_items() const
     351             : {
     352             :     // this is 84 items on PixHawk
     353        1008 :     return fence_storage.size() / sizeof(Vector2l);
     354             : }
     355             : 
     356        1292 : bool AC_PolyFence_loader::format()
     357             : {
     358        1292 :     uint16_t offset = 0;
     359        1292 :     fence_storage.write_uint32(offset, 0);
     360        1292 :     fence_storage.write_uint8(offset, new_fence_storage_magic);
     361        1292 :     offset += 4;
     362        1292 :     void_index();
     363        1292 :     _eeprom_fence_count = 0;
     364        1292 :     _eeprom_item_count = 0;
     365        1292 :     return write_eos_to_storage(offset);
     366             : }
     367             : 
     368     3464266 : bool AC_PolyFence_loader::scale_latlon_from_origin(const Location &origin, const Vector2l &point, Vector2f &pos_cm) const
     369             : {
     370     3464266 :     Location tmp_loc;
     371     3464266 :     tmp_loc.lat = point.x;
     372     3464266 :     tmp_loc.lng = point.y;
     373     3464266 :     pos_cm = origin.get_distance_NE(tmp_loc) * 100.0f;
     374     3464266 :     return true;
     375             : }
     376             : 
     377          63 : bool AC_PolyFence_loader::read_polygon_from_storage(const Location &origin, uint16_t &read_offset, const uint8_t vertex_count, Vector2f *&next_storage_point, Vector2l *&next_storage_point_lla)
     378             : {
     379        1732 :     for (uint8_t i=0; i<vertex_count; i++) {
     380             :         // read from storage to lat/lon
     381        1669 :         if (!read_latlon_from_storage(read_offset, *next_storage_point_lla)) {
     382           0 :             return false;
     383             :         }
     384             :         // convert lat/lon to position in cm from origin
     385        1669 :         if (!scale_latlon_from_origin(origin, *next_storage_point_lla, *next_storage_point)) {
     386           0 :             return false;
     387             :         }
     388             :         
     389        1669 :         next_storage_point_lla++;
     390        1669 :         next_storage_point++;
     391             :     }
     392          63 :     return true;
     393             : }
     394             : 
     395        4180 : bool AC_PolyFence_loader::scan_eeprom(scan_fn_t scan_fn)
     396             : {
     397        4180 :     uint16_t read_offset = 0; // skipping reserved first 4 bytes
     398        4180 :     if (!formatted()) {
     399           0 :         return false;
     400             :     }
     401        4180 :     read_offset += 4;
     402        4180 :     bool all_done = false;
     403        8752 :     while (!all_done) {
     404        4572 :         if (read_offset > fence_storage.size()) {
     405             : #if CONFIG_HAL_BOARD == HAL_BOARD_SITL
     406           0 :             AP_HAL::panic("did not find end-of-storage-marker before running out of space");
     407             : #endif
     408             :             return false;
     409             :         }
     410        4572 :         const AC_PolyFenceType type = (AC_PolyFenceType)fence_storage.read_uint8(read_offset);
     411             :         // validate what we've just pulled back from storage:
     412        4572 :         switch (type) {
     413        4572 :         case AC_PolyFenceType::END_OF_STORAGE:
     414             :         case AC_PolyFenceType::POLYGON_INCLUSION:
     415             :         case AC_PolyFenceType::POLYGON_EXCLUSION:
     416             :         case AC_PolyFenceType::CIRCLE_INCLUSION:
     417             :         case AC_PolyFenceType::CIRCLE_EXCLUSION:
     418             :         case AC_PolyFenceType::HOME_CIRCLE_INCLUSION:
     419             : #if AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED
     420             :         case AC_PolyFenceType::CIRCLE_INCLUSION_INT:
     421             :         case AC_PolyFenceType::CIRCLE_EXCLUSION_INT:
     422             : #endif  // AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED
     423             :         case AC_PolyFenceType::RETURN_POINT:
     424        4572 :             break;
     425           0 :         default:
     426             : #if CONFIG_HAL_BOARD == HAL_BOARD_SITL
     427           0 :             AP_HAL::panic("Fence corrupt (offset=%u)", read_offset);
     428             : #endif
     429             :             GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "Fence corrupt");
     430             :             return false;
     431             :         }
     432             : 
     433        4572 :         scan_fn(type, read_offset);
     434        4572 :         read_offset++;
     435        4572 :         switch (type) {
     436        4180 :         case AC_PolyFenceType::END_OF_STORAGE:
     437        4180 :             _eos_offset = read_offset-1;
     438        4180 :             all_done = true;
     439        4180 :             break;
     440         272 :         case AC_PolyFenceType::POLYGON_INCLUSION:
     441             :         case AC_PolyFenceType::POLYGON_EXCLUSION: {
     442         272 :             const uint8_t vertex_count = fence_storage.read_uint8(read_offset);
     443         272 :             read_offset += 1; // for the count we just read
     444         272 :             read_offset += vertex_count*8;
     445         272 :             break;
     446             :         }
     447             : #if AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED
     448          80 :         case AC_PolyFenceType::CIRCLE_INCLUSION_INT:
     449             :         case AC_PolyFenceType::CIRCLE_EXCLUSION_INT:
     450             : #endif  // AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED
     451             :         case AC_PolyFenceType::CIRCLE_INCLUSION:
     452             :         case AC_PolyFenceType::CIRCLE_EXCLUSION: {
     453          80 :             read_offset += 8; // for latlon
     454          80 :             read_offset += 4; // for radius
     455          80 :             break;
     456             :         }
     457          36 :         case AC_PolyFenceType::HOME_CIRCLE_INCLUSION:
     458             :             // Home circle only has a radius
     459          36 :             read_offset += 4; // for radius
     460          36 :             break;
     461           4 :         case AC_PolyFenceType::RETURN_POINT:
     462           4 :             read_offset += 8; // for latlon
     463           4 :             break;
     464             :         }
     465             :     }
     466        4180 :     return true;
     467             : }
     468             : 
     469             : // note read_offset here isn't const and ALSO is not a reference
     470        4252 : void AC_PolyFence_loader::scan_eeprom_count_fences(const AC_PolyFenceType type, uint16_t read_offset)
     471             : {
     472        4252 :     if (type == AC_PolyFenceType::END_OF_STORAGE) {
     473        4056 :         return;
     474             :     }
     475         196 :     _eeprom_fence_count++;
     476         196 :     switch (type) {
     477           0 :     case AC_PolyFenceType::END_OF_STORAGE:
     478           0 :         INTERNAL_ERROR(AP_InternalError::error_t::flow_of_control);
     479           0 :         break;
     480         136 :     case AC_PolyFenceType::POLYGON_EXCLUSION:
     481             :     case AC_PolyFenceType::POLYGON_INCLUSION: {
     482         136 :         const uint8_t vertex_count = fence_storage.read_uint8(read_offset+1); // skip type
     483         136 :         _eeprom_item_count += vertex_count;
     484         136 :         break;
     485             :     }
     486          60 :     case AC_PolyFenceType::CIRCLE_INCLUSION:
     487             :     case AC_PolyFenceType::CIRCLE_EXCLUSION:
     488             :     case AC_PolyFenceType::HOME_CIRCLE_INCLUSION:
     489             : #if AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED
     490             :     case AC_PolyFenceType::CIRCLE_INCLUSION_INT:
     491             :     case AC_PolyFenceType::CIRCLE_EXCLUSION_INT:
     492             : #endif  // AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED
     493             :     case AC_PolyFenceType::RETURN_POINT:
     494          60 :         _eeprom_item_count++;
     495          60 :         break;
     496             :     }
     497             : }
     498             : 
     499        4056 : bool AC_PolyFence_loader::count_eeprom_fences()
     500             : {
     501        4056 :     _eeprom_fence_count = 0;
     502        4056 :     _eeprom_item_count = 0;
     503        4056 :     const bool ret = scan_eeprom(FUNCTOR_BIND_MEMBER(&AC_PolyFence_loader::scan_eeprom_count_fences, void, const AC_PolyFenceType, uint16_t));
     504        4056 :     return ret;
     505             : }
     506             : 
     507         320 : void AC_PolyFence_loader::scan_eeprom_index_fences(const AC_PolyFenceType type, uint16_t read_offset)
     508             : {
     509         320 :     if (_index == nullptr) {
     510           0 :         INTERNAL_ERROR(AP_InternalError::error_t::flow_of_control);
     511           0 :         return;
     512             :     }
     513         320 :     if (type == AC_PolyFenceType::END_OF_STORAGE) {
     514         124 :         return;
     515             :     }
     516         196 :     FenceIndex &index = _index[_num_fences++];
     517         196 :     index.type = type;
     518         196 :     index.storage_offset = read_offset;
     519         196 :     switch (type) {
     520           0 :     case AC_PolyFenceType::END_OF_STORAGE:
     521           0 :         INTERNAL_ERROR(AP_InternalError::error_t::flow_of_control);
     522           0 :         break;
     523         136 :     case AC_PolyFenceType::POLYGON_EXCLUSION:
     524             :     case AC_PolyFenceType::POLYGON_INCLUSION: {
     525         136 :         const uint8_t vertex_count = fence_storage.read_uint8(read_offset+1);
     526         136 :         index.count = vertex_count;
     527         136 :         break;
     528             :     }
     529             : #if AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED
     530          60 :     case AC_PolyFenceType::CIRCLE_INCLUSION_INT:
     531             :     case AC_PolyFenceType::CIRCLE_EXCLUSION_INT:
     532             : #endif  // AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED
     533             :     case AC_PolyFenceType::CIRCLE_INCLUSION:
     534             :     case AC_PolyFenceType::CIRCLE_EXCLUSION:
     535             :     case AC_PolyFenceType::HOME_CIRCLE_INCLUSION:
     536             :     case AC_PolyFenceType::RETURN_POINT:
     537          60 :         index.count = 1;
     538          60 :         break;
     539             :     }
     540             : }
     541             : 
     542        4056 : bool AC_PolyFence_loader::index_eeprom()
     543             : {
     544        4056 :     if (!formatted()) {
     545         291 :         if (!format()) {
     546           0 :             return false;
     547             :         }
     548             :     }
     549             : 
     550        4056 :     if (!count_eeprom_fences()) {
     551           0 :         return false;
     552             :     }
     553             : 
     554        4056 :     void_index();
     555             : 
     556        4056 :     if (_eeprom_fence_count == 0) {
     557        3932 :         _num_fences = 0;
     558        3932 :         _load_attempted = false;
     559        3932 :         return true;
     560             :     }
     561             : 
     562             :     Debug("Fence: Allocating %u bytes for index",
     563             :           (unsigned)(_eeprom_fence_count*sizeof(FenceIndex)));
     564         124 :     _index = NEW_NOTHROW FenceIndex[_eeprom_fence_count];
     565         124 :     if (_index == nullptr) {
     566           0 :         return false;
     567             :     }
     568             : 
     569         124 :     _num_fences = 0;
     570         124 :     if (!scan_eeprom(FUNCTOR_BIND_MEMBER(&AC_PolyFence_loader::scan_eeprom_index_fences, void, const AC_PolyFenceType, uint16_t))) {
     571           0 :         void_index();
     572           0 :         return false;
     573             :     }
     574             : 
     575             : #if CONFIG_HAL_BOARD == HAL_BOARD_SITL
     576         124 :     if (_num_fences != _eeprom_fence_count) {
     577           0 :         AP_HAL::panic("indexed fences not equal to eeprom fences");
     578             :     }
     579             : #endif
     580             : 
     581         124 :     _load_attempted = false;
     582             : 
     583         124 :     return true;
     584             : }
     585             : 
     586     1491641 : bool AC_PolyFence_loader::check_indexed()
     587             : {
     588     1491641 :     if (!_index_attempted) {
     589        3055 :         _indexed = index_eeprom();
     590        3055 :         _index_attempted = true;
     591             :     }
     592     1491641 :     return _indexed;
     593             : }
     594             : 
     595        1358 : void AC_PolyFence_loader::unload()
     596             : {
     597        1358 :     delete[] _loaded_offsets_from_origin;
     598        1358 :     _loaded_offsets_from_origin = nullptr;
     599             : 
     600        1358 :     delete[] _loaded_points_lla;
     601        1358 :     _loaded_points_lla = nullptr;
     602             : 
     603        1358 :     delete[] _loaded_inclusion_boundary;
     604        1358 :     _loaded_inclusion_boundary = nullptr;
     605        1358 :     _num_loaded_inclusion_boundaries = 0;
     606             : 
     607        1358 :     delete[] _loaded_exclusion_boundary;
     608        1358 :     _loaded_exclusion_boundary = nullptr;
     609        1358 :     _num_loaded_exclusion_boundaries = 0;
     610             : 
     611        1358 :     delete[] _loaded_circle_inclusion_boundary;
     612        1358 :     _loaded_circle_inclusion_boundary = nullptr;
     613        1358 :     _num_loaded_circle_inclusion_boundaries = 0;
     614             : 
     615        1358 :     delete[] _loaded_circle_exclusion_boundary;
     616        1358 :     _loaded_circle_exclusion_boundary = nullptr;
     617        1358 :     _num_loaded_circle_exclusion_boundaries = 0;
     618             : 
     619        1358 :     _loaded_return_point = nullptr;
     620        1358 :     _loaded_return_point_lla = nullptr;
     621        1358 :     _load_time_ms = 0;
     622        1358 : }
     623             : 
     624             : // return the number of fences of type type in the index:
     625         371 : uint16_t AC_PolyFence_loader::index_fence_count(const AC_PolyFenceType type)
     626             : {
     627         371 :     uint16_t ret = 0;
     628         980 :     for (uint8_t i=0; i<_eeprom_fence_count; i++) {
     629         609 :         const FenceIndex &index = _index[i];
     630         609 :         if (index.type == type) {
     631          86 :             ret++;
     632             :         }
     633             :     }
     634         371 :     return ret;
     635             : }
     636             : 
     637          53 : uint16_t AC_PolyFence_loader::sum_of_polygon_point_counts_and_returnpoint()
     638             : {
     639          53 :     uint16_t ret = 0;
     640         140 :     for (uint8_t i=0; i<_eeprom_fence_count; i++) {
     641          87 :         const FenceIndex &index = _index[i];
     642          87 :         switch (index.type) {
     643             : #if AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED
     644          23 :         case AC_PolyFenceType::CIRCLE_INCLUSION_INT:
     645             :         case AC_PolyFenceType::CIRCLE_EXCLUSION_INT:
     646             : #endif  // AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED
     647             :         case AC_PolyFenceType::CIRCLE_INCLUSION:
     648             :         case AC_PolyFenceType::CIRCLE_EXCLUSION:
     649             :         case AC_PolyFenceType::HOME_CIRCLE_INCLUSION:
     650          23 :             break;
     651           1 :         case AC_PolyFenceType::RETURN_POINT:
     652           1 :             ret += 1;
     653           1 :             break;
     654          63 :         case AC_PolyFenceType::POLYGON_INCLUSION:
     655             :         case AC_PolyFenceType::POLYGON_EXCLUSION:
     656          63 :             ret += index.count;
     657          63 :             break;
     658           0 :         case AC_PolyFenceType::END_OF_STORAGE:
     659           0 :             INTERNAL_ERROR(AP_InternalError::error_t::flow_of_control);
     660           0 :             break;
     661             :         }
     662             :     }
     663          53 :     return ret;
     664             : }
     665             : 
     666     1486795 : bool AC_PolyFence_loader::load_from_storage()
     667             : {
     668     1486795 :     if (!check_indexed()) {
     669           0 :         return false;
     670             :     }
     671             : 
     672     1486795 :     if (_load_attempted) {
     673     1299247 :         return _load_time_ms != 0;
     674             :     }
     675             : 
     676      187548 :     if (!AP::ahrs().get_origin(loaded_origin)) {
     677             : //        Debug("fence load requires origin");
     678      186190 :         return false;
     679             :     }
     680             : 
     681             :     // find indexes of each fence:
     682        1358 :     if (!get_loaded_fence_semaphore().take_nonblocking()) {
     683           0 :         return false;
     684             :     }
     685             : 
     686        1358 :     _load_attempted = true;
     687             : 
     688        1358 :     unload();
     689             : 
     690        1358 :     if (_eeprom_item_count == 0) {
     691        1305 :         get_loaded_fence_semaphore().give();
     692        1305 :         _load_time_ms = AP_HAL::millis();
     693        1305 :         return true;
     694             :     }
     695             : 
     696             :     { // allocate array to hold offsets-from-origin
     697          53 :         const uint16_t count = sum_of_polygon_point_counts_and_returnpoint();
     698             :         Debug("Fence: Allocating %u bytes for points",
     699             :               (unsigned)(count * sizeof(Vector2f)));
     700        1723 :         _loaded_offsets_from_origin = NEW_NOTHROW Vector2f[count];
     701        1723 :         _loaded_points_lla = NEW_NOTHROW Vector2l[count];
     702          53 :         if (_loaded_offsets_from_origin == nullptr || _loaded_points_lla == nullptr) {
     703           0 :             unload();
     704           0 :             get_loaded_fence_semaphore().give();
     705           0 :             return false;
     706             :         }
     707             :     }
     708             : 
     709             :     // FIXME: find some way of factoring out all of these allocation routines.
     710             : 
     711             :     { // allocate storage for inclusion polyfences:
     712          53 :         const auto count = index_fence_count(AC_PolyFenceType::POLYGON_INCLUSION);
     713             :         Debug("Fence: Allocating %u bytes for inc. fences",
     714             :               (unsigned)(count * sizeof(InclusionBoundary)));
     715          53 :         _loaded_inclusion_boundary = NEW_NOTHROW InclusionBoundary[count];
     716          53 :         if (_loaded_inclusion_boundary == nullptr) {
     717           0 :             unload();
     718           0 :             get_loaded_fence_semaphore().give();
     719           0 :             return false;
     720             :         }
     721             :     }
     722             : 
     723             :     { // allocate storage for exclusion polyfences:
     724          53 :         const auto count = index_fence_count(AC_PolyFenceType::POLYGON_EXCLUSION);
     725             :         Debug("Fence: Allocating %u bytes for exc. fences",
     726             :               (unsigned)(count * sizeof(ExclusionBoundary)));
     727          53 :         _loaded_exclusion_boundary = NEW_NOTHROW ExclusionBoundary[count];
     728          53 :         if (_loaded_exclusion_boundary == nullptr) {
     729           0 :             unload();
     730           0 :             get_loaded_fence_semaphore().give();
     731           0 :             return false;
     732             :         }
     733             :     }
     734             : 
     735             :     { // allocate storage for circular inclusion fences:
     736          53 :         uint32_t count = index_fence_count(AC_PolyFenceType::CIRCLE_INCLUSION);
     737          53 :         count += index_fence_count(AC_PolyFenceType::HOME_CIRCLE_INCLUSION);
     738             : #if AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED
     739          53 :         count += index_fence_count(AC_PolyFenceType::CIRCLE_INCLUSION_INT);
     740             : #endif  // AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED
     741             :         Debug("Fence: Allocating %u bytes for circ. inc. fences",
     742             :               (unsigned)(count * sizeof(InclusionCircle)));
     743          66 :         _loaded_circle_inclusion_boundary = NEW_NOTHROW InclusionCircle[count];
     744          53 :         if (_loaded_circle_inclusion_boundary == nullptr) {
     745           0 :             unload();
     746           0 :             get_loaded_fence_semaphore().give();
     747           0 :             return false;
     748             :         }
     749             :     }
     750             : 
     751             :     { // allocate storage for circular exclusion fences:
     752          53 :         uint32_t count = index_fence_count(AC_PolyFenceType::CIRCLE_EXCLUSION);
     753             : #if AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED
     754          53 :         count += index_fence_count(AC_PolyFenceType::CIRCLE_EXCLUSION_INT);
     755             : #endif  // AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED
     756             :         Debug("Fence: Allocating %u bytes for circ. exc. fences",
     757             :               (unsigned)(count * sizeof(ExclusionCircle)));
     758          63 :         _loaded_circle_exclusion_boundary = NEW_NOTHROW ExclusionCircle[count];
     759          53 :         if (_loaded_circle_exclusion_boundary == nullptr) {
     760           0 :             unload();
     761           0 :             get_loaded_fence_semaphore().give();
     762           0 :             return false;
     763             :         }
     764             :     }
     765             : 
     766          53 :     Vector2f *next_storage_point = _loaded_offsets_from_origin;
     767          53 :     Vector2l *next_storage_point_lla = _loaded_points_lla;
     768             : 
     769             :     // use index to load fences from eeprom
     770          53 :     bool storage_valid = true;
     771         140 :     for (uint8_t i=0; i<_eeprom_fence_count; i++) {
     772          87 :         if (!storage_valid) {
     773           0 :             break;
     774             :         }
     775          87 :         const FenceIndex &index = _index[i];
     776          87 :         uint16_t storage_offset = index.storage_offset;
     777          87 :         storage_offset += 1; // skip type
     778          87 :         switch (index.type) {
     779           0 :         case AC_PolyFenceType::END_OF_STORAGE:
     780             : #if CONFIG_HAL_BOARD == HAL_BOARD_SITL
     781           0 :             AP_HAL::panic("indexed end of storage found");
     782             : #endif
     783             :             storage_valid = false;
     784             :             break;
     785          36 :         case AC_PolyFenceType::POLYGON_INCLUSION: {
     786             :             // FIXME: consider factoring this with the EXCLUSION case
     787          36 :             InclusionBoundary &boundary = _loaded_inclusion_boundary[_num_loaded_inclusion_boundaries];
     788          36 :             boundary.points = next_storage_point;
     789          36 :             boundary.points_lla = next_storage_point_lla;
     790          36 :             boundary.count = index.count;
     791          36 :             if (index.count < 3) {
     792           0 :                 GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "AC_Fence: invalid polygon vertex count %u", index.count);
     793           0 :                 storage_valid = false;
     794           0 :                 break;
     795             :             }
     796          36 :             storage_offset += 1; // skip vertex count
     797          36 :             if (!read_polygon_from_storage(loaded_origin, storage_offset, index.count, next_storage_point, next_storage_point_lla)) {
     798           0 :                 GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "AC_Fence: polygon read failed");
     799           0 :                 storage_valid = false;
     800           0 :                 break;
     801             :             }
     802          36 :             _num_loaded_inclusion_boundaries++;
     803          36 :             break;
     804             :         }
     805          27 :         case AC_PolyFenceType::POLYGON_EXCLUSION: {
     806          27 :             ExclusionBoundary &boundary = _loaded_exclusion_boundary[_num_loaded_exclusion_boundaries];
     807          27 :             boundary.points = next_storage_point;
     808          27 :             boundary.points_lla = next_storage_point_lla;
     809          27 :             boundary.count = index.count;
     810          27 :             if (index.count < 3) {
     811           0 :                 GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "AC_Fence: invalid polygon vertex count %u", index.count);
     812           0 :                 storage_valid = false;
     813           0 :                 break;
     814             :             }
     815          27 :             storage_offset += 1; // skip vertex count
     816          27 :             if (!read_polygon_from_storage(loaded_origin, storage_offset, index.count, next_storage_point, next_storage_point_lla)) {
     817           0 :                 GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "AC_Fence: polygon read failed");
     818           0 :                 storage_valid = false;
     819           0 :                 break;
     820             :             }
     821          27 :             _num_loaded_exclusion_boundaries++;
     822          27 :             break;
     823             :         }
     824             : #if AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED
     825          10 :         case AC_PolyFenceType::CIRCLE_EXCLUSION_INT:
     826             : #endif  // AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED
     827             :         case AC_PolyFenceType::CIRCLE_EXCLUSION: {
     828          10 :             ExclusionCircle &circle = _loaded_circle_exclusion_boundary[_num_loaded_circle_exclusion_boundaries];
     829          10 :             if (!read_latlon_from_storage(storage_offset, circle.point)) {
     830           0 :                 GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "AC_Fence: latlon read failed");
     831           0 :                 storage_valid = false;
     832           0 :                 break;
     833             :             }
     834          10 :             if (!scale_latlon_from_origin(loaded_origin, circle.point, circle.pos_cm)) {
     835           0 :                 GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "AC_Fence: latlon read failed");
     836           0 :                 storage_valid = false;
     837           0 :                 break;
     838             :             }
     839             :             // now read the radius
     840             : #if AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED
     841          10 :             if (index.type == AC_PolyFenceType::CIRCLE_EXCLUSION_INT) {
     842           0 :                 circle.radius = fence_storage.read_uint32(storage_offset);
     843             :             } else
     844             : #endif  // AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED
     845             :             {
     846          10 :                 circle.radius = fence_storage.read_float(storage_offset);
     847             :             }
     848          10 :             if (!is_positive(circle.radius)) {
     849           0 :                 GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "AC_Fence: non-positive circle radius");
     850           0 :                 storage_valid = false;
     851           0 :                 break;
     852             :             }
     853          10 :             _num_loaded_circle_exclusion_boundaries++;
     854          10 :             break;
     855             :         }
     856             : #if AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED
     857           6 :         case AC_PolyFenceType::CIRCLE_INCLUSION_INT:
     858             : #endif  // AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED
     859             :         case AC_PolyFenceType::CIRCLE_INCLUSION: {
     860           6 :             InclusionCircle &circle = _loaded_circle_inclusion_boundary[_num_loaded_circle_inclusion_boundaries];
     861           6 :             if (!read_latlon_from_storage(storage_offset, circle.point)) {
     862           0 :                 GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "AC_Fence: latlon read failed");
     863           0 :                 storage_valid = false;
     864           0 :                 break;
     865             :             }
     866           6 :             if (!scale_latlon_from_origin(loaded_origin, circle.point, circle.pos_cm)){
     867           0 :                 GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "AC_Fence: latlon read failed");
     868           0 :                 storage_valid = false;
     869           0 :                 break;
     870             :             }
     871             :             // now read the radius
     872             : #if AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED
     873           6 :             if (index.type == AC_PolyFenceType::CIRCLE_INCLUSION_INT) {
     874           0 :                 circle.radius = fence_storage.read_uint32(storage_offset);
     875             :             } else
     876             : #endif  // AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED
     877             :             {
     878           6 :                 circle.radius = fence_storage.read_float(storage_offset);
     879             :             }
     880           6 :             if (!is_positive(circle.radius)) {
     881           0 :                 GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "AC_Fence: non-positive circle radius");
     882           0 :                 storage_valid = false;
     883           0 :                 break;
     884             :             }
     885           6 :             _num_loaded_circle_inclusion_boundaries++;
     886           6 :             break;
     887             :         }
     888           7 :         case AC_PolyFenceType::HOME_CIRCLE_INCLUSION: {
     889           7 :             if (!AP::ahrs().home_is_set()) {
     890             :                 // technically we could look at this when we go to use
     891             :                 // the fence, but that's a lot of places to get
     892             :                 // correct!
     893           0 :                 return false;
     894             :             }
     895           7 :             InclusionCircle &circle = _loaded_circle_inclusion_boundary[_num_loaded_circle_inclusion_boundaries];
     896           7 :             circle.set_home_centered();
     897             :             // now read the radius
     898           7 :             circle.radius = fence_storage.read_float(storage_offset);
     899           7 :             if (!is_positive(circle.radius)) {
     900           0 :                 GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "AC_Fence: non-positive circle radius");
     901           0 :                 storage_valid = false;
     902           0 :                 break;
     903             :             }
     904           7 :             _num_loaded_circle_inclusion_boundaries++;
     905           7 :             break;
     906             :         }
     907           1 :         case AC_PolyFenceType::RETURN_POINT:
     908           1 :             if (_loaded_return_point != nullptr) {
     909           0 :                 GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "PolyFence: Multiple return points found");
     910           0 :                 storage_valid = false;
     911           0 :                 break;
     912             :             }
     913           1 :             _loaded_return_point = next_storage_point;
     914           1 :             if (_loaded_return_point_lla != nullptr) {
     915           0 :                 GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "PolyFence: Multiple return points found");
     916           0 :                 storage_valid = false;
     917           0 :                 break;
     918             :             }
     919           1 :             _loaded_return_point_lla = next_storage_point_lla;
     920             :             // Read the point from storage
     921           1 :             if (!read_latlon_from_storage(storage_offset, *next_storage_point_lla)) {
     922           0 :                 storage_valid = false;
     923           0 :                 GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "PolyFence: latlon read failed");
     924           0 :                 break;
     925             :             }
     926           1 :             if (!scale_latlon_from_origin(loaded_origin, *next_storage_point_lla, *next_storage_point)) {
     927           0 :                 storage_valid = false;
     928           0 :                 GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "PolyFence: latlon read failed");
     929           0 :                 break;
     930             :             }
     931           1 :             next_storage_point++;
     932           1 :             next_storage_point_lla++;
     933           1 :             break;
     934             :         }
     935             :     }
     936             : 
     937          53 :     if (!storage_valid) {
     938           0 :         unload();
     939           0 :         get_loaded_fence_semaphore().give();
     940           0 :         return false;
     941             :     }
     942             : 
     943          53 :     _load_time_ms = AP_HAL::millis();
     944             : 
     945          53 :     get_loaded_fence_semaphore().give();
     946          53 :     return true;
     947             : }
     948             : 
     949             : /// returns pointer to array of exclusion polygon points and num_points is filled in with the number of points in the polygon
     950             : /// points are offsets in cm from EKF origin in NE frame
     951       14495 : Vector2f* AC_PolyFence_loader::get_exclusion_polygon(uint16_t index, uint16_t &num_points) const
     952             : {
     953       14495 :     if (index >= _num_loaded_exclusion_boundaries) {
     954           0 :         num_points = 0;
     955           0 :         return nullptr;
     956             :     }
     957       14495 :     const ExclusionBoundary &boundary = _loaded_exclusion_boundary[index];
     958       14495 :     num_points = boundary.count;
     959             : 
     960       14495 :     return boundary.points;
     961             : }
     962             : 
     963             : /// returns pointer to array of inclusion polygon points and num_points is filled in with the number of points in the polygon
     964             : /// points are offsets in cm from EKF origin in NE frame
     965      146027 : Vector2f* AC_PolyFence_loader::get_inclusion_polygon(uint16_t index, uint16_t &num_points) const
     966             : {
     967      146027 :     if (index >= _num_loaded_inclusion_boundaries) {
     968           0 :         num_points = 0;
     969           0 :         return nullptr;
     970             :     }
     971      146027 :     const InclusionBoundary &boundary = _loaded_inclusion_boundary[index];
     972      146027 :     num_points = boundary.count;
     973             : 
     974      146027 :     return boundary.points;
     975             : }
     976             : 
     977             : /// returns the specified exclusion circle
     978             : /// circle center offsets in cm from EKF origin in NE frame, radius is in meters
     979       56620 : bool AC_PolyFence_loader::get_exclusion_circle(uint8_t index, Vector2f &center_pos_cm, float &radius) const
     980             : {
     981       56620 :     if (index >= _num_loaded_circle_exclusion_boundaries) {
     982           0 :         return false;
     983             :     }
     984       56620 :     center_pos_cm = _loaded_circle_exclusion_boundary[index].pos_cm;
     985       56620 :     radius =  _loaded_circle_exclusion_boundary[index].radius;
     986       56620 :     return true;
     987             : }
     988             : 
     989             : /// returns the specified inclusion circle
     990             : /// circle centre offsets in cm from EKF origin in NE frame, radius is in meters
     991       50722 : bool AC_PolyFence_loader::get_inclusion_circle(uint8_t index, Vector2f &center_pos_cm, float &radius) const
     992             : {
     993       50722 :     if (index >= _num_loaded_circle_inclusion_boundaries) {
     994           0 :         return false;
     995             :     }
     996       50722 :     const auto &circle = _loaded_circle_inclusion_boundary[index];
     997       50722 :     if (circle.is_home_centered()) {
     998             :         // Express home as an offset from EKF origin in the NE frame, matching
     999             :         // the coordinate space callers (AC_Avoid, OA) expect.  Queried live so
    1000             :         // that avoidance tracks home if it is relocated during flight.
    1001       50275 :         Location origin;
    1002       50275 :         if (!AP::ahrs().get_origin(origin)) {
    1003           0 :             return false;
    1004             :         }
    1005       50275 :         center_pos_cm = origin.get_distance_NE(AP::ahrs().get_home()) * 100.0f;
    1006             :     } else {
    1007         447 :         center_pos_cm = circle.pos_cm;
    1008             :     }
    1009       50722 :     radius =  circle.radius;
    1010       50722 :     return true;
    1011             : }
    1012             : 
    1013         874 : bool AC_PolyFence_loader::check_inclusion_circle_margin(float margin) const
    1014             : {
    1015             :     // check circular includes
    1016        1094 :     for (uint8_t i=0; i<_num_loaded_circle_inclusion_boundaries; i++) {
    1017         220 :         const InclusionCircle &circle = _loaded_circle_inclusion_boundary[i];
    1018         220 :         if (circle.radius < margin) {
    1019             :             // circle radius should never be less than margin
    1020           0 :             return false;
    1021             :         } 
    1022             :     }
    1023         874 :     return true;
    1024             : }
    1025             : 
    1026        1001 : bool AC_PolyFence_loader::validate_fence(const AC_PolyFenceItem *new_items, uint16_t count) const
    1027             : {
    1028             :     // validate the fence items...
    1029        1001 :     AC_PolyFenceType expecting_type = AC_PolyFenceType::END_OF_STORAGE;
    1030        1001 :     uint16_t expected_type_count = 0;
    1031        1001 :     uint16_t orig_expected_type_count = 0;
    1032        1001 :     bool seen_return_point = false;
    1033             : 
    1034        1958 :     for (uint16_t i=0; i<count; i++) {
    1035         957 :         bool validate_latlon = false;
    1036             : 
    1037         957 :         switch (new_items[i].type) {
    1038           0 :         case AC_PolyFenceType::END_OF_STORAGE:
    1039             : #if CONFIG_HAL_BOARD == HAL_BOARD_SITL
    1040           0 :             AP_HAL::panic("passed in an END_OF_STORAGE");
    1041             : #endif
    1042             :             return false;
    1043             : 
    1044         934 :         case AC_PolyFenceType::POLYGON_INCLUSION:
    1045             :         case AC_PolyFenceType::POLYGON_EXCLUSION:
    1046         934 :             if (new_items[i].vertex_count < 3) {
    1047           0 :                 GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "Invalid vertex count (%u)", new_items[i].vertex_count);
    1048           0 :                 return false;
    1049             :             }
    1050         934 :             if (expected_type_count == 0) {
    1051          51 :                 expected_type_count = new_items[i].vertex_count;
    1052          51 :                 orig_expected_type_count = expected_type_count;
    1053          51 :                 expecting_type = new_items[i].type;
    1054             :             } else {
    1055         883 :                 if (new_items[i].type != expecting_type) {
    1056           0 :                     GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "Received incorrect vertex type (want=%u got=%u)", (unsigned)expecting_type, (unsigned)new_items[i].type);
    1057           0 :                     return false;
    1058         883 :                 } else if (new_items[i].vertex_count != orig_expected_type_count) {
    1059           0 :                     GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "Unexpected vertex count want=%u got=%u", orig_expected_type_count, new_items[i].vertex_count);
    1060           0 :                     return false;
    1061             :                 }
    1062             :             }
    1063         934 :             expected_type_count--;
    1064         934 :             validate_latlon = true;
    1065         934 :             break;
    1066             : 
    1067             : #if AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED
    1068           0 :         case AC_PolyFenceType::CIRCLE_INCLUSION_INT:
    1069             :         case AC_PolyFenceType::CIRCLE_EXCLUSION_INT:
    1070             :             // should never have AC_PolyFenceItems of these types
    1071           0 :             INTERNAL_ERROR(AP_InternalError::error_t::flow_of_control);
    1072           0 :             return false;
    1073             : #endif  // AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED
    1074          15 :         case AC_PolyFenceType::CIRCLE_INCLUSION:
    1075             :         case AC_PolyFenceType::CIRCLE_EXCLUSION:
    1076          15 :             if (expected_type_count) {
    1077           0 :                 GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "Received incorrect type (want=%u got=%u)", (unsigned)expecting_type, (unsigned)new_items[i].type);
    1078           0 :                 return false;
    1079             :             }
    1080          15 :             if (!is_positive(new_items[i].radius)) {
    1081           0 :                 GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "Non-positive circle radius");
    1082           0 :                 return false;
    1083             :             }
    1084          15 :             validate_latlon = true;
    1085          15 :             break;
    1086           7 :         case AC_PolyFenceType::HOME_CIRCLE_INCLUSION:
    1087           7 :             if (expected_type_count) {
    1088           0 :                GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "Received incorrect type (want=%u got=%u)", (unsigned)expecting_type, (unsigned)new_items[i].type);
    1089           0 :                return false;
    1090             :             }
    1091           7 :             if (!is_positive(new_items[i].radius)) {
    1092           0 :                 GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "Non-positive circle radius");
    1093           0 :                 return false;
    1094             :             }
    1095             :             // Rely on the AHRS home checking.
    1096           7 :             break;
    1097           1 :         case AC_PolyFenceType::RETURN_POINT:
    1098           1 :             if (expected_type_count) {
    1099           0 :                 GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "Received incorrect type (want=%u got=%u)", (unsigned)expecting_type, (unsigned)new_items[i].type);
    1100           0 :                 return false;
    1101             :             }
    1102             : 
    1103             :             // spec says only one return point allowed
    1104           1 :             if (seen_return_point) {
    1105           0 :                 GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "Multiple return points");
    1106           0 :                 return false;
    1107             :             }
    1108           1 :             seen_return_point = true;
    1109           1 :             validate_latlon = true;
    1110             :             // TODO: ensure return point is within all fences and
    1111             :             // outside all exclusion zones
    1112           1 :             break;
    1113             :         }
    1114             : 
    1115         957 :         if (validate_latlon) {
    1116         950 :             if (!check_latlng(new_items[i].loc[0], new_items[i].loc[1])) {
    1117           0 :                 GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "Bad lat or lon");
    1118           0 :                 return false;
    1119             :             }
    1120             :         }
    1121             :     }
    1122             : 
    1123        1001 :     if (expected_type_count) {
    1124           0 :         GCS_SEND_TEXT(MAV_SEVERITY_INFO, "Incorrect item count");
    1125           0 :         return false;
    1126             :     }
    1127             : 
    1128        1001 :     return true;
    1129             : }
    1130             : 
    1131        1001 : uint16_t AC_PolyFence_loader::fence_storage_space_required(const AC_PolyFenceItem *new_items, uint16_t count)
    1132             : {
    1133        1001 :     uint16_t ret = 4; // for the format header
    1134        1001 :     uint16_t i = 0;
    1135        1075 :     while (i < count) {
    1136          74 :         ret += 1; // one byte for type
    1137          74 :         switch (new_items[i].type) {
    1138          51 :         case AC_PolyFenceType::POLYGON_INCLUSION:
    1139             :         case AC_PolyFenceType::POLYGON_EXCLUSION:
    1140          51 :             ret += 1 + 8 * new_items[i].vertex_count; // 1 count, 4 lat, 4 lon for each point
    1141          51 :             i += new_items[i].vertex_count - 1; // i is incremented down below
    1142          51 :             break;
    1143           0 :         case AC_PolyFenceType::END_OF_STORAGE:
    1144           0 :             INTERNAL_ERROR(AP_InternalError::error_t::flow_of_control);
    1145           0 :             break;
    1146             : #if AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED
    1147           0 :         case AC_PolyFenceType::CIRCLE_INCLUSION_INT:
    1148             :         case AC_PolyFenceType::CIRCLE_EXCLUSION_INT:
    1149             :             // should never have AC_PolyFenceItems of these types
    1150           0 :             INTERNAL_ERROR(AP_InternalError::error_t::flow_of_control);
    1151           0 :             break;
    1152             : #endif  // AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED
    1153          15 :         case AC_PolyFenceType::CIRCLE_INCLUSION:
    1154             :         case AC_PolyFenceType::CIRCLE_EXCLUSION:
    1155          15 :             ret += 12; // 4 radius, 4 lat, 4 lon
    1156          15 :             break;
    1157           7 :         case AC_PolyFenceType::HOME_CIRCLE_INCLUSION:
    1158           7 :             ret += 4; // radius
    1159           7 :             break;
    1160           1 :         case AC_PolyFenceType::RETURN_POINT:
    1161           1 :             ret += 8; // 4 lat, 4 lon
    1162           1 :             break;
    1163             :         }
    1164          74 :         i++;
    1165             :     }
    1166        1001 :     return ret;
    1167             : }
    1168             : 
    1169        1001 : bool AC_PolyFence_loader::write_fence(const AC_PolyFenceItem *new_items, uint16_t count)
    1170             : {
    1171        1001 :     if (!validate_fence(new_items, count)) {
    1172           0 :         GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "Fence validation failed");
    1173           0 :         return false;
    1174             :     }
    1175             : 
    1176        1001 :     if (fence_storage_space_required(new_items, count) > fence_storage.size()) {
    1177           0 :         GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "Fence exceeds storage size");
    1178           0 :         return false;
    1179             :     }
    1180             : 
    1181        1001 :     if (!format()) {
    1182           0 :         return false;
    1183             :     }
    1184             : 
    1185        1001 :     uint16_t total_vertex_count = 0;
    1186        1001 :     uint16_t offset = 4; // skipping magic
    1187        1001 :     uint8_t vertex_count = 0;
    1188        1958 :     for (uint16_t i=0; i<count; i++) {
    1189         957 :         const AC_PolyFenceItem new_item = new_items[i];
    1190         957 :         switch (new_item.type) {
    1191         934 :         case AC_PolyFenceType::POLYGON_INCLUSION:
    1192             :         case AC_PolyFenceType::POLYGON_EXCLUSION:
    1193         934 :             if (vertex_count == 0) {
    1194             :                 // write out new polygon count
    1195          51 :                 vertex_count = new_item.vertex_count;
    1196          51 :                 total_vertex_count += vertex_count;
    1197          51 :                 if (!write_type_to_storage(offset, new_item.type)) {
    1198           0 :                     return false;
    1199             :                 }
    1200          51 :                 fence_storage.write_uint8(offset, vertex_count);
    1201          51 :                 offset++;
    1202             :             }
    1203         934 :             vertex_count--;
    1204         934 :             if (!write_latlon_to_storage(offset, new_item.loc)) {
    1205           0 :                 return false;
    1206             :             }
    1207         934 :             break;
    1208           0 :         case AC_PolyFenceType::END_OF_STORAGE:
    1209             : #if CONFIG_HAL_BOARD == HAL_BOARD_SITL
    1210           0 :             AP_HAL::panic("asked to store end-of-storage marker");
    1211             : #endif
    1212             :             return false;
    1213             : #if AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED
    1214           0 :         case AC_PolyFenceType::CIRCLE_INCLUSION_INT:
    1215             :         case AC_PolyFenceType::CIRCLE_EXCLUSION_INT:
    1216             :             // should never have AC_PolyFenceItems of these types
    1217           0 :             INTERNAL_ERROR(AP_InternalError::error_t::flow_of_control);
    1218           0 :             return false;
    1219             : #endif  // AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED
    1220          15 :         case AC_PolyFenceType::CIRCLE_INCLUSION:
    1221             :         case AC_PolyFenceType::CIRCLE_EXCLUSION: {
    1222          15 :             total_vertex_count++; // useful to make number of lines in QGC file match FENCE_TOTAL
    1223          15 :             if (!write_type_to_storage(offset, new_item.type)) {
    1224           0 :                 return false;
    1225             :             }
    1226          15 :             if (!write_latlon_to_storage(offset, new_item.loc)) {
    1227           0 :                 return false;
    1228             :             }
    1229          15 :             fence_storage.write_float(offset, new_item.radius);
    1230          15 :             offset += 4; // radius is 4 bytes.
    1231          15 :             break;
    1232             :         }
    1233           7 :         case AC_PolyFenceType::HOME_CIRCLE_INCLUSION: {
    1234           7 :             total_vertex_count++; // useful to make number of lines in QGC file match FENCE_TOTAL
    1235           7 :             if (!write_type_to_storage(offset, new_item.type)) {
    1236           0 :                 return false;
    1237             :             }
    1238           7 :             fence_storage.write_float(offset, new_item.radius);
    1239           7 :             offset += 4; // radius is 4 bytes.
    1240           7 :             break;
    1241             :         }
    1242           1 :         case AC_PolyFenceType::RETURN_POINT:
    1243           1 :             if (!write_type_to_storage(offset, new_item.type)) {
    1244           0 :                 return false;
    1245             :             }
    1246           1 :             if (!write_latlon_to_storage(offset, new_item.loc)) {
    1247           0 :                 return false;
    1248             :             }
    1249           1 :             break;
    1250             :         }
    1251             :     }
    1252        1001 :     if (!write_eos_to_storage(offset)) {
    1253           0 :         return false;
    1254             :     }
    1255             : 
    1256             : #if CONFIG_HAL_BOARD == HAL_BOARD_SITL
    1257             :     // sanity-check the EEPROM in SITL to make sure we can read what
    1258             :     // we've just written.
    1259        1001 :     if (!index_eeprom()) {
    1260           0 :         AP_HAL::panic("Failed to index eeprom");
    1261             :     }
    1262        1001 :     GCS_SEND_TEXT(MAV_SEVERITY_DEBUG, "Fence Indexed OK");
    1263             : #endif
    1264             : 
    1265             : #if HAL_LOGGING_ENABLED
    1266             :     // start logger logging new fence
    1267        1001 :     AP::logger().Write_Fence();
    1268             : #endif
    1269             : 
    1270        1001 :     void_index();
    1271             : 
    1272             :     // this may be completely bogus total.  If we are storing an
    1273             :     // advanced fence then the old protocol which relies on this value
    1274             :     // will error off if the GCS tries to fetch points.  This number
    1275             :     // should be correct for a "compatible" fence, however.
    1276        1001 :     uint16_t new_total = 0;
    1277        1001 :     if (total_vertex_count >= 3) {
    1278          37 :         new_total = total_vertex_count+2;
    1279             :     }
    1280        1001 :     _total.set_and_save(new_total);
    1281             : 
    1282        1001 :     return true;
    1283             : }
    1284             : 
    1285             : 
    1286           6 : bool AC_PolyFence_loader::get_return_point(Vector2l &ret)
    1287             : {
    1288           6 :     if (!check_indexed()) {
    1289           0 :         return false;
    1290             :     }
    1291             : 
    1292           6 :     const FenceIndex *rp = find_first_fence(AC_PolyFenceType::RETURN_POINT);
    1293           6 :     if (rp != nullptr) {
    1294           1 :         uint16_t read_offset = rp->storage_offset + 1;
    1295           1 :         return read_latlon_from_storage(read_offset, ret);
    1296             :     }
    1297             : 
    1298           5 :     const FenceIndex *inc = find_first_fence(AC_PolyFenceType::POLYGON_INCLUSION);
    1299           5 :     if (inc == nullptr) {
    1300           3 :         return false;
    1301             :     }
    1302             : 
    1303             :     // we found an inclusion fence but not a return point.  Calculate
    1304             :     // and return the centroid.  Note that this may not actually be
    1305             :     // inside all inclusion fences...
    1306           2 :     uint16_t offset = inc->storage_offset;
    1307           2 :     if ((AC_PolyFenceType)fence_storage.read_uint8(offset) != AC_PolyFenceType::POLYGON_INCLUSION) {
    1308             : #if CONFIG_HAL_BOARD == HAL_BOARD_SITL
    1309           0 :         AP_HAL::panic("wrong type at offset");
    1310             : #endif
    1311             :         return false;
    1312             :     }
    1313           2 :     offset++;
    1314           2 :     const uint8_t count = fence_storage.read_uint8(offset);
    1315           2 :     if (count < 3) {
    1316             : #if CONFIG_HAL_BOARD == HAL_BOARD_SITL
    1317           0 :         AP_HAL::panic("invalid count found");
    1318             : #endif
    1319             :         return false;
    1320             :     }
    1321           2 :     offset++;
    1322           2 :     Vector2l min_loc;
    1323           2 :     if (!read_latlon_from_storage(offset, min_loc)) {
    1324           0 :         return false;
    1325             :     }
    1326           2 :     if (min_loc.is_zero()) {
    1327           0 :         return false;
    1328             :     }
    1329           2 :     Vector2l max_loc = min_loc;
    1330          10 :     for (uint8_t i=1; i<count; i++) {
    1331           8 :         Vector2l new_loc;
    1332           8 :         if (!read_latlon_from_storage(offset, new_loc)) {
    1333           0 :             return false;
    1334             :         }
    1335           8 :         if (new_loc.is_zero()) {
    1336           0 :             return false;
    1337             :         }
    1338           8 :         if (new_loc.x < min_loc.x) {
    1339           1 :             min_loc.x = new_loc.x;
    1340             :         }
    1341           8 :         if (new_loc.y < min_loc.y) {
    1342           1 :             min_loc.y = new_loc.y;
    1343             :         }
    1344           8 :         if (new_loc.x > max_loc.x) {
    1345           2 :             max_loc.x = new_loc.x;
    1346             :         }
    1347           8 :         if (new_loc.y > max_loc.y) {
    1348           3 :             max_loc.y = new_loc.y;
    1349             :         }
    1350             :     }
    1351             : 
    1352             :     // Potential for int32_t overflow when longitudes are beyond [-107, 107].
    1353             :     // As a result, the calculated return point's longitude is calculated using overflowed figure.
    1354             :     // Dividing first before adding avoids the potential overflow.
    1355           2 :     ret.x = (min_loc.x / 2) + (max_loc.x / 2);
    1356           2 :     ret.y = (min_loc.y / 2) + (max_loc.y / 2);
    1357             : 
    1358           2 :     return true;
    1359             : }
    1360             : 
    1361          11 : AC_PolyFence_loader::FenceIndex *AC_PolyFence_loader::find_first_fence(const AC_PolyFenceType type) const
    1362             : {
    1363          11 :     if (_index == nullptr) {
    1364           6 :         return nullptr;
    1365             :     }
    1366           7 :     for (uint8_t i=0; i<_num_fences; i++) {
    1367           5 :         if (_index[i].type == type) {
    1368           3 :             return &_index[i];
    1369             :         }
    1370             :     }
    1371           2 :     return nullptr;
    1372             : }
    1373             : 
    1374        2293 : bool AC_PolyFence_loader::write_eos_to_storage(uint16_t &offset)
    1375             : {
    1376        2293 :     if (!write_type_to_storage(offset, AC_PolyFenceType::END_OF_STORAGE)) {
    1377           0 :         return false;
    1378             :     }
    1379        2293 :     _eos_offset = offset - 1; // should point to the marker
    1380        2293 :     return true;
    1381             : }
    1382             : 
    1383     1486795 : void AC_PolyFence_loader::update()
    1384             : {
    1385     1486795 :     if (!load_from_storage()) {
    1386      186190 :         return;
    1387             :     }
    1388             : }
    1389             : 
    1390             : #else  // build type is not appropriate; provide a dummy implementation:
    1391             : 
    1392          32 : void AC_PolyFence_loader::init() {};
    1393             : 
    1394           0 : bool AC_PolyFence_loader::get_item(const uint16_t seq, AC_PolyFenceItem &item) { return false; }
    1395             : 
    1396           0 : Vector2f* AC_PolyFence_loader::get_exclusion_polygon(uint16_t index, uint16_t &num_points) const { return nullptr; }
    1397           0 : Vector2f* AC_PolyFence_loader::get_inclusion_polygon(uint16_t index, uint16_t &num_points) const { return nullptr; }
    1398             : 
    1399           0 : bool AC_PolyFence_loader::get_exclusion_circle(uint8_t index, Vector2f &center_pos_cm, float &radius) const { return false; }
    1400           0 : bool AC_PolyFence_loader::get_inclusion_circle(uint8_t index, Vector2f &center_pos_cm, float &radius) const { return false; }
    1401             : 
    1402           0 : bool AC_PolyFence_loader::breached() const { return false; }
    1403           0 : bool AC_PolyFence_loader::breached(const Location& loc, float& distance_outside_fence, Vector2f& fence_direction) const { return false; }
    1404             : 
    1405           0 : uint16_t AC_PolyFence_loader::max_items() const { return 0; }
    1406             : 
    1407           0 : bool AC_PolyFence_loader::write_fence(const AC_PolyFenceItem *new_items, uint16_t count) { return false; }
    1408             : 
    1409           0 : void AC_PolyFence_loader::update() {};
    1410             : 
    1411             : #endif // #if AC_FENCE_DUMMY_METHODS_ENABLED
    1412             : #endif // AP_FENCE_ENABLED

Generated by: LCOV version 1.14