Path: blob/master/libraries/AC_Fence/AC_PolyFence_loader.cpp
9417 views
#include "AC_PolyFence_loader.h"12#if AP_FENCE_ENABLED34#include <AP_Vehicle/AP_Vehicle_Type.h>5#include <AP_BoardConfig/AP_BoardConfig.h>67#ifndef AC_FENCE_DUMMY_METHODS_ENABLED8#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)))9#endif1011#if !AC_FENCE_DUMMY_METHODS_ENABLED1213#include <AP_AHRS/AP_AHRS.h>14#include <GCS_MAVLink/GCS.h>15#include <AP_Logger/AP_Logger.h>16#include <AC_Fence/AC_Fence.h>1718#include <stdio.h>1920#define POLYFENCE_LOADER_DEBUGGING 02122#if POLYFENCE_LOADER_DEBUGGING23#define Debug(fmt, args ...) do { GCS_SEND_TEXT(MAV_SEVERITY_INFO, fmt, ## args); } while (0)24#else25#define Debug(fmt, args ...)26#endif2728extern const AP_HAL::HAL& hal;2930static StorageAccess fence_storage(StorageManager::StorageFence);3132#if CONFIG_HAL_BOARD == HAL_BOARD_CHIBIOS33#define AC_FENCE_SDCARD_FILENAME "APM/fence.stg"34#else35#define AC_FENCE_SDCARD_FILENAME "fence.stg"36#endif3738void AC_PolyFence_loader::init()39{40#if AP_SDCARD_STORAGE_ENABLED41// check for extra storage on microsd42const auto *bc = AP::boardConfig();43if (bc != nullptr) {44const auto size_kb = bc->get_sdcard_fence_kb();45if (size_kb > 0) {46_failed_sdcard_storage = !fence_storage.attach_file(AC_FENCE_SDCARD_FILENAME, size_kb);47if (_failed_sdcard_storage) {48// wipe fence if storage not available, but don't49// save. This allows sdcard error to be fixed and50// reboot51_total.set(0);52}53}54}55#endif56if (!check_indexed()) {57// tell the user, perhaps?58}59_old_total = _total;60}6162bool AC_PolyFence_loader::find_index_for_seq(const uint16_t seq, const FenceIndex *&entry, uint16_t &i) const63{64if (_index == nullptr) {65return false;66}6768if (seq > _eeprom_item_count) {69return false;70}7172i = 0;73for (uint16_t j=0; j<_num_fences; j++) {74entry = &_index[j];75if (seq < i + entry->count) {76return true;77}78i += entry->count;79}80return false;81}8283bool AC_PolyFence_loader::find_storage_offset_for_seq(const uint16_t seq, uint16_t &offset, AC_PolyFenceType &type, uint16_t &vertex_count_offset) const84{85if (_index == nullptr) {86return false;87}8889uint16_t i = 0;90const FenceIndex *entry = nullptr;91if (!find_index_for_seq(seq, entry, i)) {92return false;93}9495if (entry == nullptr) {96INTERNAL_ERROR(AP_InternalError::error_t::flow_of_control);97return false;98}99100const uint16_t delta = seq - i;101102offset = entry->storage_offset;103type = entry->type;104offset++; // skip over type105switch (type) {106#if AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED107case AC_PolyFenceType::CIRCLE_INCLUSION_INT:108case AC_PolyFenceType::CIRCLE_EXCLUSION_INT:109#endif // AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED110case AC_PolyFenceType::CIRCLE_INCLUSION:111case AC_PolyFenceType::CIRCLE_EXCLUSION:112if (delta != 0) {113INTERNAL_ERROR(AP_InternalError::error_t::flow_of_control);114return false;115}116break;117case AC_PolyFenceType::POLYGON_INCLUSION:118case AC_PolyFenceType::POLYGON_EXCLUSION:119vertex_count_offset = offset;120offset += 1; // the count of points in the fence121offset += (delta * 8);122break;123case AC_PolyFenceType::RETURN_POINT:124if (delta != 0) {125INTERNAL_ERROR(AP_InternalError::error_t::flow_of_control);126return false;127}128break;129case AC_PolyFenceType::END_OF_STORAGE:130INTERNAL_ERROR(AP_InternalError::error_t::flow_of_control);131return false;132}133return true;134}135136bool AC_PolyFence_loader::get_item(const uint16_t seq, AC_PolyFenceItem &item)137{138if (!check_indexed()) {139return false;140}141142uint16_t vertex_count_offset = 0; // initialised to make compiler happy143uint16_t offset;144AC_PolyFenceType type;145if (!find_storage_offset_for_seq(seq, offset, type, vertex_count_offset)) {146return false;147}148149item.type = type;150151switch (type) {152case AC_PolyFenceType::CIRCLE_INCLUSION:153case AC_PolyFenceType::CIRCLE_EXCLUSION:154if (!read_latlon_from_storage(offset, item.loc)) {155return false;156}157item.radius = fence_storage.read_float(offset);158break;159#if AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED160case AC_PolyFenceType::CIRCLE_INCLUSION_INT:161case AC_PolyFenceType::CIRCLE_EXCLUSION_INT:162if (!read_latlon_from_storage(offset, item.loc)) {163return false;164}165item.radius = fence_storage.read_uint32(offset);166// magically change int item into a float item:167if (type == AC_PolyFenceType::CIRCLE_INCLUSION_INT) {168item.type = AC_PolyFenceType::CIRCLE_INCLUSION;169} else {170item.type = AC_PolyFenceType::CIRCLE_EXCLUSION;171}172break;173#endif // AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED174case AC_PolyFenceType::POLYGON_INCLUSION:175case AC_PolyFenceType::POLYGON_EXCLUSION:176if (!read_latlon_from_storage(offset, item.loc)) {177return false;178}179item.vertex_count = fence_storage.read_uint8(vertex_count_offset);180break;181case AC_PolyFenceType::RETURN_POINT:182if (!read_latlon_from_storage(offset, item.loc)) {183return false;184}185break;186case AC_PolyFenceType::END_OF_STORAGE:187// read end-of-storage when I should never do so188INTERNAL_ERROR(AP_InternalError::error_t::flow_of_control);189return false;190}191return true;192}193194bool AC_PolyFence_loader::write_type_to_storage(uint16_t &offset, const AC_PolyFenceType type)195{196fence_storage.write_uint8(offset, (uint8_t)type);197offset++;198return true;199}200201bool AC_PolyFence_loader::write_latlon_to_storage(uint16_t &offset, const Vector2l &latlon)202{203fence_storage.write_uint32(offset, latlon.x);204offset += 4;205fence_storage.write_uint32(offset, latlon.y);206offset += 4;207return true;208}209210bool AC_PolyFence_loader::read_latlon_from_storage(uint16_t &read_offset, Vector2l &ret) const211{212ret.x = fence_storage.read_uint32(read_offset);213read_offset += 4;214ret.y = fence_storage.read_uint32(read_offset);215read_offset += 4;216return true;217}218219bool AC_PolyFence_loader::breached() const220{221Location loc;222if (!AP::ahrs().get_location(loc)) {223return false;224}225226return breached(loc);227}228229// check if a position (expressed as lat/lng) is within the boundary230// returns true if location is outside the boundary231bool AC_PolyFence_loader::breached(const Location& loc, float& distance_outside_fence, Vector2f& fence_direction) const232{233if (!loaded() || total_fence_count() == 0) {234return false;235}236237Vector2f scaled_pos;238Vector2l pos { loc.lat, loc.lng };239if (!scale_latlon_from_origin(loaded_origin, pos, scaled_pos)) {240return false;241}242243const uint16_t num_inclusion = _num_loaded_circle_inclusion_boundaries + _num_loaded_inclusion_boundaries;244uint16_t num_inclusion_outside = 0;245distance_outside_fence = -FLT_MAX;246247// check we are inside each inclusion zone:248for (uint8_t i=0; i<_num_loaded_inclusion_boundaries; i++) {249const InclusionBoundary &boundary = _loaded_inclusion_boundary[i];250bool valid_distance = Polygon_closest_distance_point(boundary.points, boundary.count, scaled_pos, fence_direction);251float distance = fence_direction.length() * 0.01f; // convert back to meters252if (Polygon_outside(pos, boundary.points_lla, boundary.count)) {253num_inclusion_outside++;254if (valid_distance) {255if (is_positive(distance_outside_fence)) {256distance_outside_fence = MIN(distance_outside_fence, distance);257} else {258distance_outside_fence = distance;259}260}261} else if (valid_distance) {262distance_outside_fence = MAX(distance_outside_fence, -distance);263}264}265266// check we are outside each exclusion zone:267for (uint8_t i=0; i<_num_loaded_exclusion_boundaries; i++) {268const ExclusionBoundary &boundary = _loaded_exclusion_boundary[i];269bool valid_distance = Polygon_closest_distance_point(boundary.points, boundary.count, scaled_pos, fence_direction);270float distance = fence_direction.length() * 0.01f; // convert back to meters271if (!Polygon_outside(pos, boundary.points_lla, boundary.count)) {272if (valid_distance) {273distance_outside_fence = distance;274} else {275distance_outside_fence = 0.0f;276}277return true;278} else if (valid_distance) {279distance_outside_fence = MAX(distance_outside_fence, -distance);280}281}282283for (uint8_t i=0; i<_num_loaded_circle_exclusion_boundaries; i++) {284const ExclusionCircle &circle = _loaded_circle_exclusion_boundary[i];285Location circle_center;286circle_center.lat = circle.point.x;287circle_center.lng = circle.point.y;288const float diff_cm = loc.get_distance(circle_center)*100.0f;289distance_outside_fence = MAX(distance_outside_fence, circle.radius - diff_cm*0.01f);290if (diff_cm < circle.radius * 100.0f) {291return true;292}293}294295for (uint8_t i=0; i<_num_loaded_circle_inclusion_boundaries; i++) {296const InclusionCircle &circle = _loaded_circle_inclusion_boundary[i];297Location circle_center;298circle_center.lat = circle.point.x;299circle_center.lng = circle.point.y;300const float diff_cm = loc.get_distance(circle_center)*100.0f;301distance_outside_fence = MAX(distance_outside_fence, diff_cm*0.01f - circle.radius);302if (diff_cm > circle.radius * 100.0f) {303num_inclusion_outside++;304}305}306307if (AC_Fence::option_enabled(AC_Fence::OPTIONS::INCLUSION_UNION, _options)) {308// using union of inclusion areas, we are outside the fence if309// there is at least one inclusion areas and we are outside310// all of them311if (num_inclusion > 0 && num_inclusion == num_inclusion_outside) {312return true;313}314} else {315// using intersection of inclusion areas. We are outside if we316// are outside any of them317if (num_inclusion_outside > 0) {318return true;319}320}321322// if no value was found, reset to 0323if (is_equal(distance_outside_fence, -FLT_MAX)) {324distance_outside_fence = 0.0f;325}326327// no fence breached328return false;329}330331bool AC_PolyFence_loader::formatted() const332{333return (fence_storage.read_uint8(0) == new_fence_storage_magic &&334fence_storage.read_uint8(1) == 0 &&335fence_storage.read_uint8(2) == 0 &&336fence_storage.read_uint8(3) == 0);337}338339uint16_t AC_PolyFence_loader::max_items() const340{341// this is 84 items on PixHawk342return fence_storage.size() / sizeof(Vector2l);343}344345bool AC_PolyFence_loader::format()346{347uint16_t offset = 0;348fence_storage.write_uint32(offset, 0);349fence_storage.write_uint8(offset, new_fence_storage_magic);350offset += 4;351void_index();352_eeprom_fence_count = 0;353_eeprom_item_count = 0;354return write_eos_to_storage(offset);355}356357bool AC_PolyFence_loader::scale_latlon_from_origin(const Location &origin, const Vector2l &point, Vector2f &pos_cm) const358{359Location tmp_loc;360tmp_loc.lat = point.x;361tmp_loc.lng = point.y;362pos_cm = origin.get_distance_NE(tmp_loc) * 100.0f;363return true;364}365366bool 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)367{368for (uint8_t i=0; i<vertex_count; i++) {369// read from storage to lat/lon370if (!read_latlon_from_storage(read_offset, *next_storage_point_lla)) {371return false;372}373// convert lat/lon to position in cm from origin374if (!scale_latlon_from_origin(origin, *next_storage_point_lla, *next_storage_point)) {375return false;376}377378next_storage_point_lla++;379next_storage_point++;380}381return true;382}383384bool AC_PolyFence_loader::scan_eeprom(scan_fn_t scan_fn)385{386uint16_t read_offset = 0; // skipping reserved first 4 bytes387if (!formatted()) {388return false;389}390read_offset += 4;391bool all_done = false;392while (!all_done) {393if (read_offset > fence_storage.size()) {394#if CONFIG_HAL_BOARD == HAL_BOARD_SITL395AP_HAL::panic("did not find end-of-storage-marker before running out of space");396#endif397return false;398}399const AC_PolyFenceType type = (AC_PolyFenceType)fence_storage.read_uint8(read_offset);400// validate what we've just pulled back from storage:401switch (type) {402case AC_PolyFenceType::END_OF_STORAGE:403case AC_PolyFenceType::POLYGON_INCLUSION:404case AC_PolyFenceType::POLYGON_EXCLUSION:405case AC_PolyFenceType::CIRCLE_INCLUSION:406case AC_PolyFenceType::CIRCLE_EXCLUSION:407#if AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED408case AC_PolyFenceType::CIRCLE_INCLUSION_INT:409case AC_PolyFenceType::CIRCLE_EXCLUSION_INT:410#endif // AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED411case AC_PolyFenceType::RETURN_POINT:412break;413default:414#if CONFIG_HAL_BOARD == HAL_BOARD_SITL415AP_HAL::panic("Fence corrupt (offset=%u)", read_offset);416#endif417GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "Fence corrupt");418return false;419}420421scan_fn(type, read_offset);422read_offset++;423switch (type) {424case AC_PolyFenceType::END_OF_STORAGE:425_eos_offset = read_offset-1;426all_done = true;427break;428case AC_PolyFenceType::POLYGON_INCLUSION:429case AC_PolyFenceType::POLYGON_EXCLUSION: {430const uint8_t vertex_count = fence_storage.read_uint8(read_offset);431read_offset += 1; // for the count we just read432read_offset += vertex_count*8;433break;434}435#if AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED436case AC_PolyFenceType::CIRCLE_INCLUSION_INT:437case AC_PolyFenceType::CIRCLE_EXCLUSION_INT:438#endif // AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED439case AC_PolyFenceType::CIRCLE_INCLUSION:440case AC_PolyFenceType::CIRCLE_EXCLUSION: {441read_offset += 8; // for latlon442read_offset += 4; // for radius443break;444}445case AC_PolyFenceType::RETURN_POINT:446read_offset += 8; // for latlon447break;448}449}450return true;451}452453// note read_offset here isn't const and ALSO is not a reference454void AC_PolyFence_loader::scan_eeprom_count_fences(const AC_PolyFenceType type, uint16_t read_offset)455{456if (type == AC_PolyFenceType::END_OF_STORAGE) {457return;458}459_eeprom_fence_count++;460switch (type) {461case AC_PolyFenceType::END_OF_STORAGE:462INTERNAL_ERROR(AP_InternalError::error_t::flow_of_control);463break;464case AC_PolyFenceType::POLYGON_EXCLUSION:465case AC_PolyFenceType::POLYGON_INCLUSION: {466const uint8_t vertex_count = fence_storage.read_uint8(read_offset+1); // skip type467_eeprom_item_count += vertex_count;468break;469}470case AC_PolyFenceType::CIRCLE_INCLUSION:471case AC_PolyFenceType::CIRCLE_EXCLUSION:472#if AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED473case AC_PolyFenceType::CIRCLE_INCLUSION_INT:474case AC_PolyFenceType::CIRCLE_EXCLUSION_INT:475#endif // AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED476case AC_PolyFenceType::RETURN_POINT:477_eeprom_item_count++;478break;479}480}481482bool AC_PolyFence_loader::count_eeprom_fences()483{484_eeprom_fence_count = 0;485_eeprom_item_count = 0;486const bool ret = scan_eeprom(FUNCTOR_BIND_MEMBER(&AC_PolyFence_loader::scan_eeprom_count_fences, void, const AC_PolyFenceType, uint16_t));487return ret;488}489490void AC_PolyFence_loader::scan_eeprom_index_fences(const AC_PolyFenceType type, uint16_t read_offset)491{492if (_index == nullptr) {493INTERNAL_ERROR(AP_InternalError::error_t::flow_of_control);494return;495}496if (type == AC_PolyFenceType::END_OF_STORAGE) {497return;498}499FenceIndex &index = _index[_num_fences++];500index.type = type;501index.storage_offset = read_offset;502switch (type) {503case AC_PolyFenceType::END_OF_STORAGE:504INTERNAL_ERROR(AP_InternalError::error_t::flow_of_control);505break;506case AC_PolyFenceType::POLYGON_EXCLUSION:507case AC_PolyFenceType::POLYGON_INCLUSION: {508const uint8_t vertex_count = fence_storage.read_uint8(read_offset+1);509index.count = vertex_count;510break;511}512#if AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED513case AC_PolyFenceType::CIRCLE_INCLUSION_INT:514case AC_PolyFenceType::CIRCLE_EXCLUSION_INT:515#endif // AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED516case AC_PolyFenceType::CIRCLE_INCLUSION:517case AC_PolyFenceType::CIRCLE_EXCLUSION:518index.count = 1;519break;520case AC_PolyFenceType::RETURN_POINT:521index.count = 1;522break;523}524}525526bool AC_PolyFence_loader::index_eeprom()527{528if (!formatted()) {529if (!format()) {530return false;531}532}533534if (!count_eeprom_fences()) {535return false;536}537538void_index();539540if (_eeprom_fence_count == 0) {541_num_fences = 0;542_load_attempted = false;543return true;544}545546Debug("Fence: Allocating %u bytes for index",547(unsigned)(_eeprom_fence_count*sizeof(FenceIndex)));548_index = NEW_NOTHROW FenceIndex[_eeprom_fence_count];549if (_index == nullptr) {550return false;551}552553_num_fences = 0;554if (!scan_eeprom(FUNCTOR_BIND_MEMBER(&AC_PolyFence_loader::scan_eeprom_index_fences, void, const AC_PolyFenceType, uint16_t))) {555void_index();556return false;557}558559#if CONFIG_HAL_BOARD == HAL_BOARD_SITL560if (_num_fences != _eeprom_fence_count) {561AP_HAL::panic("indexed fences not equal to eeprom fences");562}563#endif564565_load_attempted = false;566567return true;568}569570bool AC_PolyFence_loader::check_indexed()571{572if (!_index_attempted) {573_indexed = index_eeprom();574_index_attempted = true;575}576return _indexed;577}578579void AC_PolyFence_loader::unload()580{581delete[] _loaded_offsets_from_origin;582_loaded_offsets_from_origin = nullptr;583584delete[] _loaded_points_lla;585_loaded_points_lla = nullptr;586587delete[] _loaded_inclusion_boundary;588_loaded_inclusion_boundary = nullptr;589_num_loaded_inclusion_boundaries = 0;590591delete[] _loaded_exclusion_boundary;592_loaded_exclusion_boundary = nullptr;593_num_loaded_exclusion_boundaries = 0;594595delete[] _loaded_circle_inclusion_boundary;596_loaded_circle_inclusion_boundary = nullptr;597_num_loaded_circle_inclusion_boundaries = 0;598599delete[] _loaded_circle_exclusion_boundary;600_loaded_circle_exclusion_boundary = nullptr;601_num_loaded_circle_exclusion_boundaries = 0;602603_loaded_return_point = nullptr;604_loaded_return_point_lla = nullptr;605_load_time_ms = 0;606}607608// return the number of fences of type type in the index:609uint16_t AC_PolyFence_loader::index_fence_count(const AC_PolyFenceType type)610{611uint16_t ret = 0;612for (uint8_t i=0; i<_eeprom_fence_count; i++) {613const FenceIndex &index = _index[i];614if (index.type == type) {615ret++;616}617}618return ret;619}620621uint16_t AC_PolyFence_loader::sum_of_polygon_point_counts_and_returnpoint()622{623uint16_t ret = 0;624for (uint8_t i=0; i<_eeprom_fence_count; i++) {625const FenceIndex &index = _index[i];626switch (index.type) {627#if AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED628case AC_PolyFenceType::CIRCLE_INCLUSION_INT:629case AC_PolyFenceType::CIRCLE_EXCLUSION_INT:630#endif // AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED631case AC_PolyFenceType::CIRCLE_INCLUSION:632case AC_PolyFenceType::CIRCLE_EXCLUSION:633break;634case AC_PolyFenceType::RETURN_POINT:635ret += 1;636break;637case AC_PolyFenceType::POLYGON_INCLUSION:638case AC_PolyFenceType::POLYGON_EXCLUSION:639ret += index.count;640break;641case AC_PolyFenceType::END_OF_STORAGE:642INTERNAL_ERROR(AP_InternalError::error_t::flow_of_control);643break;644}645}646return ret;647}648649bool AC_PolyFence_loader::load_from_storage()650{651if (!check_indexed()) {652return false;653}654655if (_load_attempted) {656return _load_time_ms != 0;657}658659if (!AP::ahrs().get_origin(loaded_origin)) {660// Debug("fence load requires origin");661return false;662}663664// find indexes of each fence:665if (!get_loaded_fence_semaphore().take_nonblocking()) {666return false;667}668669_load_attempted = true;670671unload();672673if (_eeprom_item_count == 0) {674get_loaded_fence_semaphore().give();675_load_time_ms = AP_HAL::millis();676return true;677}678679{ // allocate array to hold offsets-from-origin680const uint16_t count = sum_of_polygon_point_counts_and_returnpoint();681Debug("Fence: Allocating %u bytes for points",682(unsigned)(count * sizeof(Vector2f)));683_loaded_offsets_from_origin = NEW_NOTHROW Vector2f[count];684_loaded_points_lla = NEW_NOTHROW Vector2l[count];685if (_loaded_offsets_from_origin == nullptr || _loaded_points_lla == nullptr) {686unload();687get_loaded_fence_semaphore().give();688return false;689}690}691692// FIXME: find some way of factoring out all of these allocation routines.693694{ // allocate storage for inclusion polyfences:695const auto count = index_fence_count(AC_PolyFenceType::POLYGON_INCLUSION);696Debug("Fence: Allocating %u bytes for inc. fences",697(unsigned)(count * sizeof(InclusionBoundary)));698_loaded_inclusion_boundary = NEW_NOTHROW InclusionBoundary[count];699if (_loaded_inclusion_boundary == nullptr) {700unload();701get_loaded_fence_semaphore().give();702return false;703}704}705706{ // allocate storage for exclusion polyfences:707const auto count = index_fence_count(AC_PolyFenceType::POLYGON_EXCLUSION);708Debug("Fence: Allocating %u bytes for exc. fences",709(unsigned)(count * sizeof(ExclusionBoundary)));710_loaded_exclusion_boundary = NEW_NOTHROW ExclusionBoundary[count];711if (_loaded_exclusion_boundary == nullptr) {712unload();713get_loaded_fence_semaphore().give();714return false;715}716}717718{ // allocate storage for circular inclusion fences:719uint32_t count = index_fence_count(AC_PolyFenceType::CIRCLE_INCLUSION);720#if AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED721count += index_fence_count(AC_PolyFenceType::CIRCLE_INCLUSION_INT)722#endif // AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED723Debug("Fence: Allocating %u bytes for circ. inc. fences",724(unsigned)(count * sizeof(InclusionCircle)));725_loaded_circle_inclusion_boundary = NEW_NOTHROW InclusionCircle[count];726if (_loaded_circle_inclusion_boundary == nullptr) {727unload();728get_loaded_fence_semaphore().give();729return false;730}731}732733{ // allocate storage for circular exclusion fences:734uint32_t count = index_fence_count(AC_PolyFenceType::CIRCLE_EXCLUSION);735#if AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED736count += index_fence_count(AC_PolyFenceType::CIRCLE_EXCLUSION_INT)737#endif // AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED738Debug("Fence: Allocating %u bytes for circ. exc. fences",739(unsigned)(count * sizeof(ExclusionCircle)));740_loaded_circle_exclusion_boundary = NEW_NOTHROW ExclusionCircle[count];741if (_loaded_circle_exclusion_boundary == nullptr) {742unload();743get_loaded_fence_semaphore().give();744return false;745}746}747748Vector2f *next_storage_point = _loaded_offsets_from_origin;749Vector2l *next_storage_point_lla = _loaded_points_lla;750751// use index to load fences from eeprom752bool storage_valid = true;753for (uint8_t i=0; i<_eeprom_fence_count; i++) {754if (!storage_valid) {755break;756}757const FenceIndex &index = _index[i];758uint16_t storage_offset = index.storage_offset;759storage_offset += 1; // skip type760switch (index.type) {761case AC_PolyFenceType::END_OF_STORAGE:762#if CONFIG_HAL_BOARD == HAL_BOARD_SITL763AP_HAL::panic("indexed end of storage found");764#endif765storage_valid = false;766break;767case AC_PolyFenceType::POLYGON_INCLUSION: {768// FIXME: consider factoring this with the EXCLUSION case769InclusionBoundary &boundary = _loaded_inclusion_boundary[_num_loaded_inclusion_boundaries];770boundary.points = next_storage_point;771boundary.points_lla = next_storage_point_lla;772boundary.count = index.count;773if (index.count < 3) {774GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "AC_Fence: invalid polygon vertex count %u", index.count);775storage_valid = false;776break;777}778storage_offset += 1; // skip vertex count779if (!read_polygon_from_storage(loaded_origin, storage_offset, index.count, next_storage_point, next_storage_point_lla)) {780GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "AC_Fence: polygon read failed");781storage_valid = false;782break;783}784_num_loaded_inclusion_boundaries++;785break;786}787case AC_PolyFenceType::POLYGON_EXCLUSION: {788ExclusionBoundary &boundary = _loaded_exclusion_boundary[_num_loaded_exclusion_boundaries];789boundary.points = next_storage_point;790boundary.points_lla = next_storage_point_lla;791boundary.count = index.count;792if (index.count < 3) {793GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "AC_Fence: invalid polygon vertex count %u", index.count);794storage_valid = false;795break;796}797storage_offset += 1; // skip vertex count798if (!read_polygon_from_storage(loaded_origin, storage_offset, index.count, next_storage_point, next_storage_point_lla)) {799GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "AC_Fence: polygon read failed");800storage_valid = false;801break;802}803_num_loaded_exclusion_boundaries++;804break;805}806#if AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED807case AC_PolyFenceType::CIRCLE_EXCLUSION_INT:808#endif // AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED809case AC_PolyFenceType::CIRCLE_EXCLUSION: {810ExclusionCircle &circle = _loaded_circle_exclusion_boundary[_num_loaded_circle_exclusion_boundaries];811if (!read_latlon_from_storage(storage_offset, circle.point)) {812GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "AC_Fence: latlon read failed");813storage_valid = false;814break;815}816if (!scale_latlon_from_origin(loaded_origin, circle.point, circle.pos_cm)) {817GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "AC_Fence: latlon read failed");818storage_valid = false;819break;820}821// now read the radius822#if AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED823if (index.type == AC_PolyFenceType::CIRCLE_EXCLUSION_INT) {824circle.radius = fence_storage.read_uint32(storage_offset);825} else826#endif // AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED827{828circle.radius = fence_storage.read_float(storage_offset);829}830if (!is_positive(circle.radius)) {831GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "AC_Fence: non-positive circle radius");832storage_valid = false;833break;834}835_num_loaded_circle_exclusion_boundaries++;836break;837}838#if AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED839case AC_PolyFenceType::CIRCLE_INCLUSION_INT:840#endif // AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED841case AC_PolyFenceType::CIRCLE_INCLUSION: {842InclusionCircle &circle = _loaded_circle_inclusion_boundary[_num_loaded_circle_inclusion_boundaries];843if (!read_latlon_from_storage(storage_offset, circle.point)) {844GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "AC_Fence: latlon read failed");845storage_valid = false;846break;847}848if (!scale_latlon_from_origin(loaded_origin, circle.point, circle.pos_cm)){849GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "AC_Fence: latlon read failed");850storage_valid = false;851break;852}853// now read the radius854#if AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED855if (index.type == AC_PolyFenceType::CIRCLE_INCLUSION_INT) {856circle.radius = fence_storage.read_uint32(storage_offset);857} else858#endif // AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED859{860circle.radius = fence_storage.read_float(storage_offset);861}862if (!is_positive(circle.radius)) {863GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "AC_Fence: non-positive circle radius");864storage_valid = false;865break;866}867_num_loaded_circle_inclusion_boundaries++;868break;869}870case AC_PolyFenceType::RETURN_POINT:871if (_loaded_return_point != nullptr) {872GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "PolyFence: Multiple return points found");873storage_valid = false;874break;875}876_loaded_return_point = next_storage_point;877if (_loaded_return_point_lla != nullptr) {878GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "PolyFence: Multiple return points found");879storage_valid = false;880break;881}882_loaded_return_point_lla = next_storage_point_lla;883// Read the point from storage884if (!read_latlon_from_storage(storage_offset, *next_storage_point_lla)) {885storage_valid = false;886GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "PolyFence: latlon read failed");887break;888}889if (!scale_latlon_from_origin(loaded_origin, *next_storage_point_lla, *next_storage_point)) {890storage_valid = false;891GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "PolyFence: latlon read failed");892break;893}894next_storage_point++;895next_storage_point_lla++;896break;897}898}899900if (!storage_valid) {901unload();902get_loaded_fence_semaphore().give();903return false;904}905906_load_time_ms = AP_HAL::millis();907908get_loaded_fence_semaphore().give();909return true;910}911912/// returns pointer to array of exclusion polygon points and num_points is filled in with the number of points in the polygon913/// points are offsets in cm from EKF origin in NE frame914Vector2f* AC_PolyFence_loader::get_exclusion_polygon(uint16_t index, uint16_t &num_points) const915{916if (index >= _num_loaded_exclusion_boundaries) {917num_points = 0;918return nullptr;919}920const ExclusionBoundary &boundary = _loaded_exclusion_boundary[index];921num_points = boundary.count;922923return boundary.points;924}925926/// returns pointer to array of inclusion polygon points and num_points is filled in with the number of points in the polygon927/// points are offsets in cm from EKF origin in NE frame928Vector2f* AC_PolyFence_loader::get_inclusion_polygon(uint16_t index, uint16_t &num_points) const929{930if (index >= _num_loaded_inclusion_boundaries) {931num_points = 0;932return nullptr;933}934const InclusionBoundary &boundary = _loaded_inclusion_boundary[index];935num_points = boundary.count;936937return boundary.points;938}939940/// returns the specified exclusion circle941/// circle center offsets in cm from EKF origin in NE frame, radius is in meters942bool AC_PolyFence_loader::get_exclusion_circle(uint8_t index, Vector2f ¢er_pos_cm, float &radius) const943{944if (index >= _num_loaded_circle_exclusion_boundaries) {945return false;946}947center_pos_cm = _loaded_circle_exclusion_boundary[index].pos_cm;948radius = _loaded_circle_exclusion_boundary[index].radius;949return true;950}951952/// returns the specified inclusion circle953/// circle centre offsets in cm from EKF origin in NE frame, radius is in meters954bool AC_PolyFence_loader::get_inclusion_circle(uint8_t index, Vector2f ¢er_pos_cm, float &radius) const955{956if (index >= _num_loaded_circle_inclusion_boundaries) {957return false;958}959center_pos_cm = _loaded_circle_inclusion_boundary[index].pos_cm;960radius = _loaded_circle_inclusion_boundary[index].radius;961return true;962}963964bool AC_PolyFence_loader::check_inclusion_circle_margin(float margin) const965{966// check circular includes967for (uint8_t i=0; i<_num_loaded_circle_inclusion_boundaries; i++) {968const InclusionCircle &circle = _loaded_circle_inclusion_boundary[i];969if (circle.radius < margin) {970// circle radius should never be less than margin971return false;972}973}974return true;975}976977bool AC_PolyFence_loader::validate_fence(const AC_PolyFenceItem *new_items, uint16_t count) const978{979// validate the fence items...980AC_PolyFenceType expecting_type = AC_PolyFenceType::END_OF_STORAGE;981uint16_t expected_type_count = 0;982uint16_t orig_expected_type_count = 0;983bool seen_return_point = false;984985for (uint16_t i=0; i<count; i++) {986bool validate_latlon = false;987988switch (new_items[i].type) {989case AC_PolyFenceType::END_OF_STORAGE:990#if CONFIG_HAL_BOARD == HAL_BOARD_SITL991AP_HAL::panic("passed in an END_OF_STORAGE");992#endif993return false;994995case AC_PolyFenceType::POLYGON_INCLUSION:996case AC_PolyFenceType::POLYGON_EXCLUSION:997if (new_items[i].vertex_count < 3) {998GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "Invalid vertex count (%u)", new_items[i].vertex_count);999return false;1000}1001if (expected_type_count == 0) {1002expected_type_count = new_items[i].vertex_count;1003orig_expected_type_count = expected_type_count;1004expecting_type = new_items[i].type;1005} else {1006if (new_items[i].type != expecting_type) {1007GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "Received incorrect vertex type (want=%u got=%u)", (unsigned)expecting_type, (unsigned)new_items[i].type);1008return false;1009} else if (new_items[i].vertex_count != orig_expected_type_count) {1010GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "Unexpected vertex count want=%u got=%u", orig_expected_type_count, new_items[i].vertex_count);1011return false;1012}1013}1014expected_type_count--;1015validate_latlon = true;1016break;10171018#if AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED1019case AC_PolyFenceType::CIRCLE_INCLUSION_INT:1020case AC_PolyFenceType::CIRCLE_EXCLUSION_INT:1021// should never have AC_PolyFenceItems of these types1022INTERNAL_ERROR(AP_InternalError::error_t::flow_of_control);1023return false;1024#endif // AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED1025case AC_PolyFenceType::CIRCLE_INCLUSION:1026case AC_PolyFenceType::CIRCLE_EXCLUSION:1027if (expected_type_count) {1028GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "Received incorrect type (want=%u got=%u)", (unsigned)expecting_type, (unsigned)new_items[i].type);1029return false;1030}1031if (!is_positive(new_items[i].radius)) {1032GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "Non-positive circle radius");1033return false;1034}1035validate_latlon = true;1036break;10371038case AC_PolyFenceType::RETURN_POINT:1039if (expected_type_count) {1040GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "Received incorrect type (want=%u got=%u)", (unsigned)expecting_type, (unsigned)new_items[i].type);1041return false;1042}10431044// spec says only one return point allowed1045if (seen_return_point) {1046GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "Multiple return points");1047return false;1048}1049seen_return_point = true;1050validate_latlon = true;1051// TODO: ensure return point is within all fences and1052// outside all exclusion zones1053break;1054}10551056if (validate_latlon) {1057if (!check_latlng(new_items[i].loc[0], new_items[i].loc[1])) {1058GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "Bad lat or lon");1059return false;1060}1061}1062}10631064if (expected_type_count) {1065GCS_SEND_TEXT(MAV_SEVERITY_INFO, "Incorrect item count");1066return false;1067}10681069return true;1070}10711072uint16_t AC_PolyFence_loader::fence_storage_space_required(const AC_PolyFenceItem *new_items, uint16_t count)1073{1074uint16_t ret = 4; // for the format header1075uint16_t i = 0;1076while (i < count) {1077ret += 1; // one byte for type1078switch (new_items[i].type) {1079case AC_PolyFenceType::POLYGON_INCLUSION:1080case AC_PolyFenceType::POLYGON_EXCLUSION:1081ret += 1 + 8 * new_items[i].vertex_count; // 1 count, 4 lat, 4 lon for each point1082i += new_items[i].vertex_count - 1; // i is incremented down below1083break;1084case AC_PolyFenceType::END_OF_STORAGE:1085INTERNAL_ERROR(AP_InternalError::error_t::flow_of_control);1086break;1087#if AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED1088case AC_PolyFenceType::CIRCLE_INCLUSION_INT:1089case AC_PolyFenceType::CIRCLE_EXCLUSION_INT:1090// should never have AC_PolyFenceItems of these types1091INTERNAL_ERROR(AP_InternalError::error_t::flow_of_control);1092break;1093#endif // AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED1094case AC_PolyFenceType::CIRCLE_INCLUSION:1095case AC_PolyFenceType::CIRCLE_EXCLUSION:1096ret += 12; // 4 radius, 4 lat, 4 lon1097break;1098case AC_PolyFenceType::RETURN_POINT:1099ret += 8; // 4 lat, 4 lon1100break;1101}1102i++;1103}1104return ret;1105}11061107bool AC_PolyFence_loader::write_fence(const AC_PolyFenceItem *new_items, uint16_t count)1108{1109if (!validate_fence(new_items, count)) {1110GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "Fence validation failed");1111return false;1112}11131114if (fence_storage_space_required(new_items, count) > fence_storage.size()) {1115GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "Fence exceeds storage size");1116return false;1117}11181119if (!format()) {1120return false;1121}11221123uint16_t total_vertex_count = 0;1124uint16_t offset = 4; // skipping magic1125uint8_t vertex_count = 0;1126for (uint16_t i=0; i<count; i++) {1127const AC_PolyFenceItem new_item = new_items[i];1128switch (new_item.type) {1129case AC_PolyFenceType::POLYGON_INCLUSION:1130case AC_PolyFenceType::POLYGON_EXCLUSION:1131if (vertex_count == 0) {1132// write out new polygon count1133vertex_count = new_item.vertex_count;1134total_vertex_count += vertex_count;1135if (!write_type_to_storage(offset, new_item.type)) {1136return false;1137}1138fence_storage.write_uint8(offset, vertex_count);1139offset++;1140}1141vertex_count--;1142if (!write_latlon_to_storage(offset, new_item.loc)) {1143return false;1144}1145break;1146case AC_PolyFenceType::END_OF_STORAGE:1147#if CONFIG_HAL_BOARD == HAL_BOARD_SITL1148AP_HAL::panic("asked to store end-of-storage marker");1149#endif1150return false;1151#if AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED1152case AC_PolyFenceType::CIRCLE_INCLUSION_INT:1153case AC_PolyFenceType::CIRCLE_EXCLUSION_INT:1154// should never have AC_PolyFenceItems of these types1155INTERNAL_ERROR(AP_InternalError::error_t::flow_of_control);1156return false;1157#endif // AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED1158case AC_PolyFenceType::CIRCLE_INCLUSION:1159case AC_PolyFenceType::CIRCLE_EXCLUSION: {1160total_vertex_count++; // useful to make number of lines in QGC file match FENCE_TOTAL1161if (!write_type_to_storage(offset, new_item.type)) {1162return false;1163}1164if (!write_latlon_to_storage(offset, new_item.loc)) {1165return false;1166}1167fence_storage.write_float(offset, new_item.radius);1168offset += 4;1169break;1170}1171case AC_PolyFenceType::RETURN_POINT:1172if (!write_type_to_storage(offset, new_item.type)) {1173return false;1174}1175if (!write_latlon_to_storage(offset, new_item.loc)) {1176return false;1177}1178break;1179}1180}1181if (!write_eos_to_storage(offset)) {1182return false;1183}11841185#if CONFIG_HAL_BOARD == HAL_BOARD_SITL1186// sanity-check the EEPROM in SITL to make sure we can read what1187// we've just written.1188if (!index_eeprom()) {1189AP_HAL::panic("Failed to index eeprom");1190}1191GCS_SEND_TEXT(MAV_SEVERITY_DEBUG, "Fence Indexed OK");1192#endif11931194#if HAL_LOGGING_ENABLED1195// start logger logging new fence1196AP::logger().Write_Fence();1197#endif11981199void_index();12001201// this may be completely bogus total. If we are storing an1202// advanced fence then the old protocol which relies on this value1203// will error off if the GCS tries to fetch points. This number1204// should be correct for a "compatible" fence, however.1205uint16_t new_total = 0;1206if (total_vertex_count >= 3) {1207new_total = total_vertex_count+2;1208}1209_total.set_and_save(new_total);12101211return true;1212}121312141215bool AC_PolyFence_loader::get_return_point(Vector2l &ret)1216{1217if (!check_indexed()) {1218return false;1219}12201221const FenceIndex *rp = find_first_fence(AC_PolyFenceType::RETURN_POINT);1222if (rp != nullptr) {1223uint16_t read_offset = rp->storage_offset + 1;1224return read_latlon_from_storage(read_offset, ret);1225}12261227const FenceIndex *inc = find_first_fence(AC_PolyFenceType::POLYGON_INCLUSION);1228if (inc == nullptr) {1229return false;1230}12311232// we found an inclusion fence but not a return point. Calculate1233// and return the centroid. Note that this may not actually be1234// inside all inclusion fences...1235uint16_t offset = inc->storage_offset;1236if ((AC_PolyFenceType)fence_storage.read_uint8(offset) != AC_PolyFenceType::POLYGON_INCLUSION) {1237#if CONFIG_HAL_BOARD == HAL_BOARD_SITL1238AP_HAL::panic("wrong type at offset");1239#endif1240return false;1241}1242offset++;1243const uint8_t count = fence_storage.read_uint8(offset);1244if (count < 3) {1245#if CONFIG_HAL_BOARD == HAL_BOARD_SITL1246AP_HAL::panic("invalid count found");1247#endif1248return false;1249}1250offset++;1251Vector2l min_loc;1252if (!read_latlon_from_storage(offset, min_loc)) {1253return false;1254}1255if (min_loc.is_zero()) {1256return false;1257}1258Vector2l max_loc = min_loc;1259for (uint8_t i=1; i<count; i++) {1260Vector2l new_loc;1261if (!read_latlon_from_storage(offset, new_loc)) {1262return false;1263}1264if (new_loc.is_zero()) {1265return false;1266}1267if (new_loc.x < min_loc.x) {1268min_loc.x = new_loc.x;1269}1270if (new_loc.y < min_loc.y) {1271min_loc.y = new_loc.y;1272}1273if (new_loc.x > max_loc.x) {1274max_loc.x = new_loc.x;1275}1276if (new_loc.y > max_loc.y) {1277max_loc.y = new_loc.y;1278}1279}12801281// Potential for int32_t overflow when longitudes are beyond [-107, 107].1282// As a result, the calculated return point's longitude is calculated using overflowed figure.1283// Dividing first before adding avoids the potential overflow.1284ret.x = (min_loc.x / 2) + (max_loc.x / 2);1285ret.y = (min_loc.y / 2) + (max_loc.y / 2);12861287return true;1288}12891290AC_PolyFence_loader::FenceIndex *AC_PolyFence_loader::find_first_fence(const AC_PolyFenceType type) const1291{1292if (_index == nullptr) {1293return nullptr;1294}1295for (uint8_t i=0; i<_num_fences; i++) {1296if (_index[i].type == type) {1297return &_index[i];1298}1299}1300return nullptr;1301}13021303#if AC_POLYFENCE_FENCE_POINT_PROTOCOL_SUPPORT1304void AC_PolyFence_loader::handle_msg_fetch_fence_point(GCS_MAVLINK &link, const mavlink_message_t& msg)1305{1306if (!check_indexed()) {1307return;1308}1309if (!contains_compatible_fence()) {1310link.send_text(MAV_SEVERITY_WARNING, "Vehicle contains advanced fences");1311return;1312}13131314if (_total != 0 && _total < 5) {1315link.send_text(MAV_SEVERITY_WARNING, "Invalid FENCE_TOTAL");1316return;1317}13181319mavlink_fence_fetch_point_t packet;1320mavlink_msg_fence_fetch_point_decode(&msg, &packet);13211322if (packet.idx >= _total) {1323link.send_text(MAV_SEVERITY_WARNING, "Invalid fence point, index past total(%u >= %u)", packet.idx, _total.get());1324return;1325}13261327mavlink_fence_point_t ret_packet{};1328ret_packet.target_system = msg.sysid;1329ret_packet.target_component = msg.compid;1330ret_packet.idx = packet.idx;1331ret_packet.count = _total;13321333if (packet.idx == 0) {1334// return point1335Vector2l ret;1336if (get_return_point(ret)) {1337ret_packet.lat = ret.x * 1.0e-7f;1338ret_packet.lng = ret.y * 1.0e-7f;1339} else {1340link.send_text(MAV_SEVERITY_WARNING, "Failed to get return point");1341}1342} else {1343// find the inclusion fence:1344const FenceIndex *inclusion_fence = find_first_fence(AC_PolyFenceType::POLYGON_INCLUSION);1345if (inclusion_fence == nullptr) {1346// nothing stored yet; just send back zeroes1347ret_packet.lat = 0;1348ret_packet.lng = 0;1349} else {1350uint8_t fencepoint_offset; // 1st idx is return point1351if (packet.idx == _total-1) {1352// the is the loop closure point - send the first point again1353fencepoint_offset = 0;1354} else {1355fencepoint_offset = packet.idx - 1;1356}1357if (fencepoint_offset >= inclusion_fence->count) {1358// we haven't been given a value for this item yet; we will return zeroes1359} else {1360uint16_t storage_offset = inclusion_fence->storage_offset;1361storage_offset++; // skip over type1362storage_offset++; // skip over count1363storage_offset += 8*fencepoint_offset; // move to point we're interested in1364Vector2l bob;1365if (!read_latlon_from_storage(storage_offset, bob)) {1366link.send_text(MAV_SEVERITY_WARNING, "Fence read failed");1367#if CONFIG_HAL_BOARD == HAL_BOARD_SITL1368AP_HAL::panic("read failure");1369#endif1370return;1371}1372ret_packet.lat = bob[0] * 1.0e-7f;1373ret_packet.lng = bob[1] * 1.0e-7f;1374}1375}1376}13771378link.send_message(MAVLINK_MSG_ID_FENCE_POINT, (const char*)&ret_packet);1379}13801381AC_PolyFence_loader::FenceIndex *AC_PolyFence_loader::get_or_create_return_point()1382{1383if (!check_indexed()) {1384return nullptr;1385}1386FenceIndex *return_point = find_first_fence(AC_PolyFenceType::RETURN_POINT);1387if (return_point != nullptr) {1388return return_point;1389}13901391// if the inclusion fence exists we will move it in storage to1392// avoid having to continually shift the return point forward as1393// we receive fence points1394uint16_t offset;1395const FenceIndex *inclusion_fence = find_first_fence(AC_PolyFenceType::POLYGON_INCLUSION);1396if (inclusion_fence != nullptr) {1397offset = inclusion_fence->storage_offset;1398// the "9"s below represent the size of a return point in storage1399for (uint8_t i=0; i<inclusion_fence->count; i++) {1400// we are shifting the last fence point first - so 'i=0'1401// means the last point stored.1402const uint16_t point_storage_offset = offset + 2 + (inclusion_fence->count-1-i) * 8;1403Vector2l latlon;1404uint16_t tmp_read_offs = point_storage_offset;1405if (!read_latlon_from_storage(tmp_read_offs, latlon)) {1406return nullptr;1407}1408uint16_t write_offset = point_storage_offset + 9;1409if (!write_latlon_to_storage(write_offset, latlon)) {1410return nullptr;1411}1412}1413// read/write the count:1414const uint8_t count = fence_storage.read_uint8(inclusion_fence->storage_offset+1);1415fence_storage.write_uint8(inclusion_fence->storage_offset + 1 + 9, count);1416// read/write the type:1417const uint8_t t = fence_storage.read_uint8(inclusion_fence->storage_offset);1418fence_storage.write_uint8(inclusion_fence->storage_offset + 9, t);14191420uint16_t write_offset = offset + 2 + 8*inclusion_fence->count + 9;1421if (!write_eos_to_storage(write_offset)) {1422return nullptr;1423}1424} else {1425if (fence_storage.read_uint8(_eos_offset) != (uint8_t)AC_PolyFenceType::END_OF_STORAGE) {1426#if CONFIG_HAL_BOARD == HAL_BOARD_SITL1427AP_HAL::panic("Expected end-of-storage marker at offset=%u",1428_eos_offset);1429#endif1430return nullptr;1431}1432offset = _eos_offset;1433}14341435if (!write_type_to_storage(offset, AC_PolyFenceType::RETURN_POINT)) {1436return nullptr;1437}1438if (!write_latlon_to_storage(offset, Vector2l{0, 0})) {1439return nullptr;1440}1441if (inclusion_fence == nullptr) {1442if (!write_eos_to_storage(offset)) {1443return nullptr;1444}1445}14461447if (!index_eeprom()) {1448#if CONFIG_HAL_BOARD == HAL_BOARD_SITL1449AP_HAL::panic("Failed to index eeprom after moving inclusion fence for return point");1450#endif1451return nullptr;1452}14531454return_point = find_first_fence(AC_PolyFenceType::RETURN_POINT);1455if (return_point == nullptr) {1456#if CONFIG_HAL_BOARD == HAL_BOARD_SITL1457AP_HAL::panic("Failed to get return point after indexing");1458#endif1459}1460return return_point;1461}14621463AC_PolyFence_loader::FenceIndex *AC_PolyFence_loader::get_or_create_include_fence()1464{1465if (!check_indexed()) {1466return nullptr;1467}1468FenceIndex *inclusion = find_first_fence(AC_PolyFenceType::POLYGON_INCLUSION);1469if (inclusion != nullptr) {1470return inclusion;1471}1472if (_total < 5) {1473return nullptr;1474}1475if (!write_type_to_storage(_eos_offset, AC_PolyFenceType::POLYGON_INCLUSION)) {1476return nullptr;1477}1478fence_storage.write_uint8(_eos_offset, 0);1479_eos_offset++;1480if (!write_eos_to_storage(_eos_offset)) {1481return nullptr;1482}14831484if (!index_eeprom()) {1485#if CONFIG_HAL_BOARD == HAL_BOARD_SITL1486AP_HAL::panic("Failed to index eeprom after creating fence");1487#endif1488return nullptr;1489}1490AC_PolyFence_loader::FenceIndex *ret = find_first_fence(AC_PolyFenceType::POLYGON_INCLUSION);1491#if CONFIG_HAL_BOARD == HAL_BOARD_SITL1492if (ret == nullptr) {1493AP_HAL::panic("Failed to index eeprom after creating fence");1494}1495#endif1496return ret;1497}14981499void AC_PolyFence_loader::handle_msg_fence_point(GCS_MAVLINK &link, const mavlink_message_t& msg)1500{1501if (!check_indexed()) {1502return;1503}15041505mavlink_fence_point_t packet;1506mavlink_msg_fence_point_decode(&msg, &packet);15071508if (_total != 0 && _total < 5) {1509link.send_text(MAV_SEVERITY_WARNING, "Invalid FENCE_TOTAL");1510return;1511}15121513if (packet.count != _total) {1514link.send_text(MAV_SEVERITY_WARNING, "Invalid fence point, bad count (%u vs %u)", packet.count, _total.get());1515return;1516}15171518if (packet.idx >= _total) {1519// this is a protocol failure1520link.send_text(MAV_SEVERITY_WARNING, "Invalid fence point, index past total (%u >= %u)", packet.idx, _total.get());1521return;1522}15231524if (!check_latlng(packet.lat, packet.lng)) {1525link.send_text(MAV_SEVERITY_WARNING, "Invalid fence point, bad lat or lng");1526return;1527}15281529if (!contains_compatible_fence()) {1530// the GCS has started to upload using the old protocol;1531// ensure we can accept it. We must be able to index the1532// fence, so it must be valid (minimum number of points)1533if (!format()) {1534return;1535}1536}15371538const Vector2l point{1539(int32_t)(packet.lat*1.0e7f),1540(int32_t)(packet.lng*1.0e7f)1541};15421543if (packet.idx == 0) {1544// this is the return point; if we have a return point then1545// update it, otherwise create a return point fence thingy1546const FenceIndex *return_point = get_or_create_return_point();1547if (return_point == nullptr) {1548#if CONFIG_HAL_BOARD == HAL_BOARD_SITL1549AP_HAL::panic("Didn't get return point");1550#endif1551return;1552}1553uint16_t offset = return_point->storage_offset;1554offset++; // don't overwrite the type!1555if (!write_latlon_to_storage(offset, point)) {1556link.send_text(MAV_SEVERITY_WARNING, "PolyFence: storage write failed");1557return;1558}1559} else if (packet.idx == _total-1) {1560/* this is the fence closing point. We use this to set the vertex1561count of the inclusion fence1562*/1563const FenceIndex *inclusion_fence = get_or_create_include_fence();1564if (inclusion_fence == nullptr) {1565return;1566}1567// write type and length1568fence_storage.write_uint8(inclusion_fence->storage_offset, uint8_t(AC_PolyFenceType::POLYGON_INCLUSION));1569fence_storage.write_uint8(inclusion_fence->storage_offset+1, packet.idx-1);1570// and write end of storage marker1571fence_storage.write_uint8(inclusion_fence->storage_offset+2+(packet.idx-1)*8, uint8_t(AC_PolyFenceType::END_OF_STORAGE));1572void_index();1573} else {1574const FenceIndex *inclusion_fence = get_or_create_include_fence();1575if (inclusion_fence == nullptr) {1576#if CONFIG_HAL_BOARD == HAL_BOARD_SITL1577AP_HAL::panic("no inclusion fences found");1578#endif1579return;1580}1581uint16_t offset = inclusion_fence->storage_offset;1582offset++; // skip type1583if (packet.idx > inclusion_fence->count) {1584// expand the storage space1585fence_storage.write_uint8(offset, packet.idx); // remembering that idx[0] is the return point....1586}1587offset++; // move past number of points1588offset += (packet.idx-1)*8;1589if (!write_latlon_to_storage(offset, point)) {1590link.send_text(MAV_SEVERITY_WARNING, "PolyFence: storage write failed");1591return;1592}1593if (_eos_offset < offset) {1594if (!write_eos_to_storage(offset)) {1595return;1596}1597}1598void_index();1599}1600}16011602bool AC_PolyFence_loader::contains_compatible_fence() const1603{1604// must contain a single inclusion fence with an optional return point1605if (_index == nullptr) {1606// this indicates no boundary points present1607return true;1608}1609bool seen_return_point = false;1610bool seen_poly_inclusion = false;1611for (uint16_t i=0; i<_num_fences; i++) {1612switch (_index[i].type) {1613case AC_PolyFenceType::END_OF_STORAGE:1614#if CONFIG_HAL_BOARD == HAL_BOARD_SITL1615AP_HAL::panic("end-of-storage marker found in loaded list");1616#endif1617return false;1618case AC_PolyFenceType::POLYGON_INCLUSION:1619if (seen_poly_inclusion) {1620return false;1621}1622seen_poly_inclusion = true;1623break;1624#if AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED1625case AC_PolyFenceType::CIRCLE_INCLUSION_INT:1626case AC_PolyFenceType::CIRCLE_EXCLUSION_INT:1627#endif // AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED1628case AC_PolyFenceType::POLYGON_EXCLUSION:1629case AC_PolyFenceType::CIRCLE_INCLUSION:1630case AC_PolyFenceType::CIRCLE_EXCLUSION:1631return false;1632case AC_PolyFenceType::RETURN_POINT:1633if (seen_return_point) {1634return false;1635}1636seen_return_point = true;1637break;1638}1639}1640return true;1641}16421643#endif // AC_POLYFENCE_FENCE_POINT_PROTOCOL_SUPPORT16441645bool AC_PolyFence_loader::write_eos_to_storage(uint16_t &offset)1646{1647if (!write_type_to_storage(offset, AC_PolyFenceType::END_OF_STORAGE)) {1648return false;1649}1650_eos_offset = offset - 1; // should point to the marker1651return true;1652}16531654/// handler for polygon fence messages with GCS1655void AC_PolyFence_loader::handle_msg(GCS_MAVLINK &link, const mavlink_message_t& msg)1656{1657switch (msg.msgid) {1658#if AC_POLYFENCE_FENCE_POINT_PROTOCOL_SUPPORT1659case MAVLINK_MSG_ID_FENCE_POINT:1660handle_msg_fence_point(link, msg);1661break;1662case MAVLINK_MSG_ID_FENCE_FETCH_POINT:1663handle_msg_fetch_fence_point(link, msg);1664break;1665#endif1666default:1667break;1668}1669}16701671void AC_PolyFence_loader::update()1672{1673#if AC_POLYFENCE_FENCE_POINT_PROTOCOL_SUPPORT1674// if an older GCS sets the fence point count to zero then clear the fence1675if (_old_total != _total) {1676_old_total = _total;1677if (_total == 0 && _eeprom_fence_count) {1678if (!format()) {1679// we are in all sorts of trouble1680return;1681}1682}1683}1684#endif1685if (!load_from_storage()) {1686return;1687}1688}16891690#else // build type is not appropriate; provide a dummy implementation:16911692void AC_PolyFence_loader::init() {};16931694bool AC_PolyFence_loader::get_item(const uint16_t seq, AC_PolyFenceItem &item) { return false; }16951696Vector2f* AC_PolyFence_loader::get_exclusion_polygon(uint16_t index, uint16_t &num_points) const { return nullptr; }1697Vector2f* AC_PolyFence_loader::get_inclusion_polygon(uint16_t index, uint16_t &num_points) const { return nullptr; }16981699bool AC_PolyFence_loader::get_exclusion_circle(uint8_t index, Vector2f ¢er_pos_cm, float &radius) const { return false; }1700bool AC_PolyFence_loader::get_inclusion_circle(uint8_t index, Vector2f ¢er_pos_cm, float &radius) const { return false; }17011702void AC_PolyFence_loader::handle_msg(GCS_MAVLINK &link, const mavlink_message_t& msg) {};17031704bool AC_PolyFence_loader::breached() const { return false; }1705bool AC_PolyFence_loader::breached(const Location& loc, float& distance_outside_fence, Vector2f& fence_direction) const { return false; }17061707uint16_t AC_PolyFence_loader::max_items() const { return 0; }17081709bool AC_PolyFence_loader::write_fence(const AC_PolyFenceItem *new_items, uint16_t count) { return false; }17101711void AC_PolyFence_loader::update() {};17121713#if AC_POLYFENCE_FENCE_POINT_PROTOCOL_SUPPORT1714bool AC_PolyFence_loader::get_return_point(Vector2l &ret) { return false; }1715#endif17161717#endif // #if AC_FENCE_DUMMY_METHODS_ENABLED1718#endif // AP_FENCE_ENABLED171917201721