Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Ardupilot
GitHub Repository: Ardupilot/ardupilot
Path: blob/master/libraries/AC_Fence/AC_PolyFence_loader.cpp
9417 views
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
void AC_PolyFence_loader::init()
40
{
41
#if AP_SDCARD_STORAGE_ENABLED
42
// check for extra storage on microsd
43
const auto *bc = AP::boardConfig();
44
if (bc != nullptr) {
45
const auto size_kb = bc->get_sdcard_fence_kb();
46
if (size_kb > 0) {
47
_failed_sdcard_storage = !fence_storage.attach_file(AC_FENCE_SDCARD_FILENAME, size_kb);
48
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
_total.set(0);
53
}
54
}
55
}
56
#endif
57
if (!check_indexed()) {
58
// tell the user, perhaps?
59
}
60
_old_total = _total;
61
}
62
63
bool AC_PolyFence_loader::find_index_for_seq(const uint16_t seq, const FenceIndex *&entry, uint16_t &i) const
64
{
65
if (_index == nullptr) {
66
return false;
67
}
68
69
if (seq > _eeprom_item_count) {
70
return false;
71
}
72
73
i = 0;
74
for (uint16_t j=0; j<_num_fences; j++) {
75
entry = &_index[j];
76
if (seq < i + entry->count) {
77
return true;
78
}
79
i += entry->count;
80
}
81
return false;
82
}
83
84
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
if (_index == nullptr) {
87
return false;
88
}
89
90
uint16_t i = 0;
91
const FenceIndex *entry = nullptr;
92
if (!find_index_for_seq(seq, entry, i)) {
93
return false;
94
}
95
96
if (entry == nullptr) {
97
INTERNAL_ERROR(AP_InternalError::error_t::flow_of_control);
98
return false;
99
}
100
101
const uint16_t delta = seq - i;
102
103
offset = entry->storage_offset;
104
type = entry->type;
105
offset++; // skip over type
106
switch (type) {
107
#if AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED
108
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
if (delta != 0) {
114
INTERNAL_ERROR(AP_InternalError::error_t::flow_of_control);
115
return false;
116
}
117
break;
118
case AC_PolyFenceType::POLYGON_INCLUSION:
119
case AC_PolyFenceType::POLYGON_EXCLUSION:
120
vertex_count_offset = offset;
121
offset += 1; // the count of points in the fence
122
offset += (delta * 8);
123
break;
124
case AC_PolyFenceType::RETURN_POINT:
125
if (delta != 0) {
126
INTERNAL_ERROR(AP_InternalError::error_t::flow_of_control);
127
return false;
128
}
129
break;
130
case AC_PolyFenceType::END_OF_STORAGE:
131
INTERNAL_ERROR(AP_InternalError::error_t::flow_of_control);
132
return false;
133
}
134
return true;
135
}
136
137
bool AC_PolyFence_loader::get_item(const uint16_t seq, AC_PolyFenceItem &item)
138
{
139
if (!check_indexed()) {
140
return false;
141
}
142
143
uint16_t vertex_count_offset = 0; // initialised to make compiler happy
144
uint16_t offset;
145
AC_PolyFenceType type;
146
if (!find_storage_offset_for_seq(seq, offset, type, vertex_count_offset)) {
147
return false;
148
}
149
150
item.type = type;
151
152
switch (type) {
153
case AC_PolyFenceType::CIRCLE_INCLUSION:
154
case AC_PolyFenceType::CIRCLE_EXCLUSION:
155
if (!read_latlon_from_storage(offset, item.loc)) {
156
return false;
157
}
158
item.radius = fence_storage.read_float(offset);
159
break;
160
#if AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED
161
case AC_PolyFenceType::CIRCLE_INCLUSION_INT:
162
case AC_PolyFenceType::CIRCLE_EXCLUSION_INT:
163
if (!read_latlon_from_storage(offset, item.loc)) {
164
return false;
165
}
166
item.radius = fence_storage.read_uint32(offset);
167
// magically change int item into a float item:
168
if (type == AC_PolyFenceType::CIRCLE_INCLUSION_INT) {
169
item.type = AC_PolyFenceType::CIRCLE_INCLUSION;
170
} else {
171
item.type = AC_PolyFenceType::CIRCLE_EXCLUSION;
172
}
173
break;
174
#endif // AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED
175
case AC_PolyFenceType::POLYGON_INCLUSION:
176
case AC_PolyFenceType::POLYGON_EXCLUSION:
177
if (!read_latlon_from_storage(offset, item.loc)) {
178
return false;
179
}
180
item.vertex_count = fence_storage.read_uint8(vertex_count_offset);
181
break;
182
case AC_PolyFenceType::RETURN_POINT:
183
if (!read_latlon_from_storage(offset, item.loc)) {
184
return false;
185
}
186
break;
187
case AC_PolyFenceType::END_OF_STORAGE:
188
// read end-of-storage when I should never do so
189
INTERNAL_ERROR(AP_InternalError::error_t::flow_of_control);
190
return false;
191
}
192
return true;
193
}
194
195
bool AC_PolyFence_loader::write_type_to_storage(uint16_t &offset, const AC_PolyFenceType type)
196
{
197
fence_storage.write_uint8(offset, (uint8_t)type);
198
offset++;
199
return true;
200
}
201
202
bool AC_PolyFence_loader::write_latlon_to_storage(uint16_t &offset, const Vector2l &latlon)
203
{
204
fence_storage.write_uint32(offset, latlon.x);
205
offset += 4;
206
fence_storage.write_uint32(offset, latlon.y);
207
offset += 4;
208
return true;
209
}
210
211
bool AC_PolyFence_loader::read_latlon_from_storage(uint16_t &read_offset, Vector2l &ret) const
212
{
213
ret.x = fence_storage.read_uint32(read_offset);
214
read_offset += 4;
215
ret.y = fence_storage.read_uint32(read_offset);
216
read_offset += 4;
217
return true;
218
}
219
220
bool AC_PolyFence_loader::breached() const
221
{
222
Location loc;
223
if (!AP::ahrs().get_location(loc)) {
224
return false;
225
}
226
227
return breached(loc);
228
}
229
230
// check if a position (expressed as lat/lng) is within the boundary
231
// returns true if location is outside the boundary
232
bool AC_PolyFence_loader::breached(const Location& loc, float& distance_outside_fence, Vector2f& fence_direction) const
233
{
234
if (!loaded() || total_fence_count() == 0) {
235
return false;
236
}
237
238
Vector2f scaled_pos;
239
Vector2l pos { loc.lat, loc.lng };
240
if (!scale_latlon_from_origin(loaded_origin, pos, scaled_pos)) {
241
return false;
242
}
243
244
const uint16_t num_inclusion = _num_loaded_circle_inclusion_boundaries + _num_loaded_inclusion_boundaries;
245
uint16_t num_inclusion_outside = 0;
246
distance_outside_fence = -FLT_MAX;
247
248
// check we are inside each inclusion zone:
249
for (uint8_t i=0; i<_num_loaded_inclusion_boundaries; i++) {
250
const InclusionBoundary &boundary = _loaded_inclusion_boundary[i];
251
bool valid_distance = Polygon_closest_distance_point(boundary.points, boundary.count, scaled_pos, fence_direction);
252
float distance = fence_direction.length() * 0.01f; // convert back to meters
253
if (Polygon_outside(pos, boundary.points_lla, boundary.count)) {
254
num_inclusion_outside++;
255
if (valid_distance) {
256
if (is_positive(distance_outside_fence)) {
257
distance_outside_fence = MIN(distance_outside_fence, distance);
258
} else {
259
distance_outside_fence = distance;
260
}
261
}
262
} else if (valid_distance) {
263
distance_outside_fence = MAX(distance_outside_fence, -distance);
264
}
265
}
266
267
// check we are outside each exclusion zone:
268
for (uint8_t i=0; i<_num_loaded_exclusion_boundaries; i++) {
269
const ExclusionBoundary &boundary = _loaded_exclusion_boundary[i];
270
bool valid_distance = Polygon_closest_distance_point(boundary.points, boundary.count, scaled_pos, fence_direction);
271
float distance = fence_direction.length() * 0.01f; // convert back to meters
272
if (!Polygon_outside(pos, boundary.points_lla, boundary.count)) {
273
if (valid_distance) {
274
distance_outside_fence = distance;
275
} else {
276
distance_outside_fence = 0.0f;
277
}
278
return true;
279
} else if (valid_distance) {
280
distance_outside_fence = MAX(distance_outside_fence, -distance);
281
}
282
}
283
284
for (uint8_t i=0; i<_num_loaded_circle_exclusion_boundaries; i++) {
285
const ExclusionCircle &circle = _loaded_circle_exclusion_boundary[i];
286
Location circle_center;
287
circle_center.lat = circle.point.x;
288
circle_center.lng = circle.point.y;
289
const float diff_cm = loc.get_distance(circle_center)*100.0f;
290
distance_outside_fence = MAX(distance_outside_fence, circle.radius - diff_cm*0.01f);
291
if (diff_cm < circle.radius * 100.0f) {
292
return true;
293
}
294
}
295
296
for (uint8_t i=0; i<_num_loaded_circle_inclusion_boundaries; i++) {
297
const InclusionCircle &circle = _loaded_circle_inclusion_boundary[i];
298
Location circle_center;
299
circle_center.lat = circle.point.x;
300
circle_center.lng = circle.point.y;
301
const float diff_cm = loc.get_distance(circle_center)*100.0f;
302
distance_outside_fence = MAX(distance_outside_fence, diff_cm*0.01f - circle.radius);
303
if (diff_cm > circle.radius * 100.0f) {
304
num_inclusion_outside++;
305
}
306
}
307
308
if (AC_Fence::option_enabled(AC_Fence::OPTIONS::INCLUSION_UNION, _options)) {
309
// using union of inclusion areas, we are outside the fence if
310
// there is at least one inclusion areas and we are outside
311
// all of them
312
if (num_inclusion > 0 && num_inclusion == num_inclusion_outside) {
313
return true;
314
}
315
} else {
316
// using intersection of inclusion areas. We are outside if we
317
// are outside any of them
318
if (num_inclusion_outside > 0) {
319
return true;
320
}
321
}
322
323
// if no value was found, reset to 0
324
if (is_equal(distance_outside_fence, -FLT_MAX)) {
325
distance_outside_fence = 0.0f;
326
}
327
328
// no fence breached
329
return false;
330
}
331
332
bool AC_PolyFence_loader::formatted() const
333
{
334
return (fence_storage.read_uint8(0) == new_fence_storage_magic &&
335
fence_storage.read_uint8(1) == 0 &&
336
fence_storage.read_uint8(2) == 0 &&
337
fence_storage.read_uint8(3) == 0);
338
}
339
340
uint16_t AC_PolyFence_loader::max_items() const
341
{
342
// this is 84 items on PixHawk
343
return fence_storage.size() / sizeof(Vector2l);
344
}
345
346
bool AC_PolyFence_loader::format()
347
{
348
uint16_t offset = 0;
349
fence_storage.write_uint32(offset, 0);
350
fence_storage.write_uint8(offset, new_fence_storage_magic);
351
offset += 4;
352
void_index();
353
_eeprom_fence_count = 0;
354
_eeprom_item_count = 0;
355
return write_eos_to_storage(offset);
356
}
357
358
bool AC_PolyFence_loader::scale_latlon_from_origin(const Location &origin, const Vector2l &point, Vector2f &pos_cm) const
359
{
360
Location tmp_loc;
361
tmp_loc.lat = point.x;
362
tmp_loc.lng = point.y;
363
pos_cm = origin.get_distance_NE(tmp_loc) * 100.0f;
364
return true;
365
}
366
367
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)
368
{
369
for (uint8_t i=0; i<vertex_count; i++) {
370
// read from storage to lat/lon
371
if (!read_latlon_from_storage(read_offset, *next_storage_point_lla)) {
372
return false;
373
}
374
// convert lat/lon to position in cm from origin
375
if (!scale_latlon_from_origin(origin, *next_storage_point_lla, *next_storage_point)) {
376
return false;
377
}
378
379
next_storage_point_lla++;
380
next_storage_point++;
381
}
382
return true;
383
}
384
385
bool AC_PolyFence_loader::scan_eeprom(scan_fn_t scan_fn)
386
{
387
uint16_t read_offset = 0; // skipping reserved first 4 bytes
388
if (!formatted()) {
389
return false;
390
}
391
read_offset += 4;
392
bool all_done = false;
393
while (!all_done) {
394
if (read_offset > fence_storage.size()) {
395
#if CONFIG_HAL_BOARD == HAL_BOARD_SITL
396
AP_HAL::panic("did not find end-of-storage-marker before running out of space");
397
#endif
398
return false;
399
}
400
const AC_PolyFenceType type = (AC_PolyFenceType)fence_storage.read_uint8(read_offset);
401
// validate what we've just pulled back from storage:
402
switch (type) {
403
case AC_PolyFenceType::END_OF_STORAGE:
404
case AC_PolyFenceType::POLYGON_INCLUSION:
405
case AC_PolyFenceType::POLYGON_EXCLUSION:
406
case AC_PolyFenceType::CIRCLE_INCLUSION:
407
case AC_PolyFenceType::CIRCLE_EXCLUSION:
408
#if AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED
409
case AC_PolyFenceType::CIRCLE_INCLUSION_INT:
410
case AC_PolyFenceType::CIRCLE_EXCLUSION_INT:
411
#endif // AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED
412
case AC_PolyFenceType::RETURN_POINT:
413
break;
414
default:
415
#if CONFIG_HAL_BOARD == HAL_BOARD_SITL
416
AP_HAL::panic("Fence corrupt (offset=%u)", read_offset);
417
#endif
418
GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "Fence corrupt");
419
return false;
420
}
421
422
scan_fn(type, read_offset);
423
read_offset++;
424
switch (type) {
425
case AC_PolyFenceType::END_OF_STORAGE:
426
_eos_offset = read_offset-1;
427
all_done = true;
428
break;
429
case AC_PolyFenceType::POLYGON_INCLUSION:
430
case AC_PolyFenceType::POLYGON_EXCLUSION: {
431
const uint8_t vertex_count = fence_storage.read_uint8(read_offset);
432
read_offset += 1; // for the count we just read
433
read_offset += vertex_count*8;
434
break;
435
}
436
#if AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED
437
case AC_PolyFenceType::CIRCLE_INCLUSION_INT:
438
case AC_PolyFenceType::CIRCLE_EXCLUSION_INT:
439
#endif // AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED
440
case AC_PolyFenceType::CIRCLE_INCLUSION:
441
case AC_PolyFenceType::CIRCLE_EXCLUSION: {
442
read_offset += 8; // for latlon
443
read_offset += 4; // for radius
444
break;
445
}
446
case AC_PolyFenceType::RETURN_POINT:
447
read_offset += 8; // for latlon
448
break;
449
}
450
}
451
return true;
452
}
453
454
// note read_offset here isn't const and ALSO is not a reference
455
void AC_PolyFence_loader::scan_eeprom_count_fences(const AC_PolyFenceType type, uint16_t read_offset)
456
{
457
if (type == AC_PolyFenceType::END_OF_STORAGE) {
458
return;
459
}
460
_eeprom_fence_count++;
461
switch (type) {
462
case AC_PolyFenceType::END_OF_STORAGE:
463
INTERNAL_ERROR(AP_InternalError::error_t::flow_of_control);
464
break;
465
case AC_PolyFenceType::POLYGON_EXCLUSION:
466
case AC_PolyFenceType::POLYGON_INCLUSION: {
467
const uint8_t vertex_count = fence_storage.read_uint8(read_offset+1); // skip type
468
_eeprom_item_count += vertex_count;
469
break;
470
}
471
case AC_PolyFenceType::CIRCLE_INCLUSION:
472
case AC_PolyFenceType::CIRCLE_EXCLUSION:
473
#if AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED
474
case AC_PolyFenceType::CIRCLE_INCLUSION_INT:
475
case AC_PolyFenceType::CIRCLE_EXCLUSION_INT:
476
#endif // AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED
477
case AC_PolyFenceType::RETURN_POINT:
478
_eeprom_item_count++;
479
break;
480
}
481
}
482
483
bool AC_PolyFence_loader::count_eeprom_fences()
484
{
485
_eeprom_fence_count = 0;
486
_eeprom_item_count = 0;
487
const bool ret = scan_eeprom(FUNCTOR_BIND_MEMBER(&AC_PolyFence_loader::scan_eeprom_count_fences, void, const AC_PolyFenceType, uint16_t));
488
return ret;
489
}
490
491
void AC_PolyFence_loader::scan_eeprom_index_fences(const AC_PolyFenceType type, uint16_t read_offset)
492
{
493
if (_index == nullptr) {
494
INTERNAL_ERROR(AP_InternalError::error_t::flow_of_control);
495
return;
496
}
497
if (type == AC_PolyFenceType::END_OF_STORAGE) {
498
return;
499
}
500
FenceIndex &index = _index[_num_fences++];
501
index.type = type;
502
index.storage_offset = read_offset;
503
switch (type) {
504
case AC_PolyFenceType::END_OF_STORAGE:
505
INTERNAL_ERROR(AP_InternalError::error_t::flow_of_control);
506
break;
507
case AC_PolyFenceType::POLYGON_EXCLUSION:
508
case AC_PolyFenceType::POLYGON_INCLUSION: {
509
const uint8_t vertex_count = fence_storage.read_uint8(read_offset+1);
510
index.count = vertex_count;
511
break;
512
}
513
#if AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED
514
case AC_PolyFenceType::CIRCLE_INCLUSION_INT:
515
case AC_PolyFenceType::CIRCLE_EXCLUSION_INT:
516
#endif // AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED
517
case AC_PolyFenceType::CIRCLE_INCLUSION:
518
case AC_PolyFenceType::CIRCLE_EXCLUSION:
519
index.count = 1;
520
break;
521
case AC_PolyFenceType::RETURN_POINT:
522
index.count = 1;
523
break;
524
}
525
}
526
527
bool AC_PolyFence_loader::index_eeprom()
528
{
529
if (!formatted()) {
530
if (!format()) {
531
return false;
532
}
533
}
534
535
if (!count_eeprom_fences()) {
536
return false;
537
}
538
539
void_index();
540
541
if (_eeprom_fence_count == 0) {
542
_num_fences = 0;
543
_load_attempted = false;
544
return true;
545
}
546
547
Debug("Fence: Allocating %u bytes for index",
548
(unsigned)(_eeprom_fence_count*sizeof(FenceIndex)));
549
_index = NEW_NOTHROW FenceIndex[_eeprom_fence_count];
550
if (_index == nullptr) {
551
return false;
552
}
553
554
_num_fences = 0;
555
if (!scan_eeprom(FUNCTOR_BIND_MEMBER(&AC_PolyFence_loader::scan_eeprom_index_fences, void, const AC_PolyFenceType, uint16_t))) {
556
void_index();
557
return false;
558
}
559
560
#if CONFIG_HAL_BOARD == HAL_BOARD_SITL
561
if (_num_fences != _eeprom_fence_count) {
562
AP_HAL::panic("indexed fences not equal to eeprom fences");
563
}
564
#endif
565
566
_load_attempted = false;
567
568
return true;
569
}
570
571
bool AC_PolyFence_loader::check_indexed()
572
{
573
if (!_index_attempted) {
574
_indexed = index_eeprom();
575
_index_attempted = true;
576
}
577
return _indexed;
578
}
579
580
void AC_PolyFence_loader::unload()
581
{
582
delete[] _loaded_offsets_from_origin;
583
_loaded_offsets_from_origin = nullptr;
584
585
delete[] _loaded_points_lla;
586
_loaded_points_lla = nullptr;
587
588
delete[] _loaded_inclusion_boundary;
589
_loaded_inclusion_boundary = nullptr;
590
_num_loaded_inclusion_boundaries = 0;
591
592
delete[] _loaded_exclusion_boundary;
593
_loaded_exclusion_boundary = nullptr;
594
_num_loaded_exclusion_boundaries = 0;
595
596
delete[] _loaded_circle_inclusion_boundary;
597
_loaded_circle_inclusion_boundary = nullptr;
598
_num_loaded_circle_inclusion_boundaries = 0;
599
600
delete[] _loaded_circle_exclusion_boundary;
601
_loaded_circle_exclusion_boundary = nullptr;
602
_num_loaded_circle_exclusion_boundaries = 0;
603
604
_loaded_return_point = nullptr;
605
_loaded_return_point_lla = nullptr;
606
_load_time_ms = 0;
607
}
608
609
// return the number of fences of type type in the index:
610
uint16_t AC_PolyFence_loader::index_fence_count(const AC_PolyFenceType type)
611
{
612
uint16_t ret = 0;
613
for (uint8_t i=0; i<_eeprom_fence_count; i++) {
614
const FenceIndex &index = _index[i];
615
if (index.type == type) {
616
ret++;
617
}
618
}
619
return ret;
620
}
621
622
uint16_t AC_PolyFence_loader::sum_of_polygon_point_counts_and_returnpoint()
623
{
624
uint16_t ret = 0;
625
for (uint8_t i=0; i<_eeprom_fence_count; i++) {
626
const FenceIndex &index = _index[i];
627
switch (index.type) {
628
#if AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED
629
case AC_PolyFenceType::CIRCLE_INCLUSION_INT:
630
case AC_PolyFenceType::CIRCLE_EXCLUSION_INT:
631
#endif // AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED
632
case AC_PolyFenceType::CIRCLE_INCLUSION:
633
case AC_PolyFenceType::CIRCLE_EXCLUSION:
634
break;
635
case AC_PolyFenceType::RETURN_POINT:
636
ret += 1;
637
break;
638
case AC_PolyFenceType::POLYGON_INCLUSION:
639
case AC_PolyFenceType::POLYGON_EXCLUSION:
640
ret += index.count;
641
break;
642
case AC_PolyFenceType::END_OF_STORAGE:
643
INTERNAL_ERROR(AP_InternalError::error_t::flow_of_control);
644
break;
645
}
646
}
647
return ret;
648
}
649
650
bool AC_PolyFence_loader::load_from_storage()
651
{
652
if (!check_indexed()) {
653
return false;
654
}
655
656
if (_load_attempted) {
657
return _load_time_ms != 0;
658
}
659
660
if (!AP::ahrs().get_origin(loaded_origin)) {
661
// Debug("fence load requires origin");
662
return false;
663
}
664
665
// find indexes of each fence:
666
if (!get_loaded_fence_semaphore().take_nonblocking()) {
667
return false;
668
}
669
670
_load_attempted = true;
671
672
unload();
673
674
if (_eeprom_item_count == 0) {
675
get_loaded_fence_semaphore().give();
676
_load_time_ms = AP_HAL::millis();
677
return true;
678
}
679
680
{ // allocate array to hold offsets-from-origin
681
const uint16_t count = sum_of_polygon_point_counts_and_returnpoint();
682
Debug("Fence: Allocating %u bytes for points",
683
(unsigned)(count * sizeof(Vector2f)));
684
_loaded_offsets_from_origin = NEW_NOTHROW Vector2f[count];
685
_loaded_points_lla = NEW_NOTHROW Vector2l[count];
686
if (_loaded_offsets_from_origin == nullptr || _loaded_points_lla == nullptr) {
687
unload();
688
get_loaded_fence_semaphore().give();
689
return false;
690
}
691
}
692
693
// FIXME: find some way of factoring out all of these allocation routines.
694
695
{ // allocate storage for inclusion polyfences:
696
const auto count = index_fence_count(AC_PolyFenceType::POLYGON_INCLUSION);
697
Debug("Fence: Allocating %u bytes for inc. fences",
698
(unsigned)(count * sizeof(InclusionBoundary)));
699
_loaded_inclusion_boundary = NEW_NOTHROW InclusionBoundary[count];
700
if (_loaded_inclusion_boundary == nullptr) {
701
unload();
702
get_loaded_fence_semaphore().give();
703
return false;
704
}
705
}
706
707
{ // allocate storage for exclusion polyfences:
708
const auto count = index_fence_count(AC_PolyFenceType::POLYGON_EXCLUSION);
709
Debug("Fence: Allocating %u bytes for exc. fences",
710
(unsigned)(count * sizeof(ExclusionBoundary)));
711
_loaded_exclusion_boundary = NEW_NOTHROW ExclusionBoundary[count];
712
if (_loaded_exclusion_boundary == nullptr) {
713
unload();
714
get_loaded_fence_semaphore().give();
715
return false;
716
}
717
}
718
719
{ // allocate storage for circular inclusion fences:
720
uint32_t count = index_fence_count(AC_PolyFenceType::CIRCLE_INCLUSION);
721
#if AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED
722
count += index_fence_count(AC_PolyFenceType::CIRCLE_INCLUSION_INT)
723
#endif // AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED
724
Debug("Fence: Allocating %u bytes for circ. inc. fences",
725
(unsigned)(count * sizeof(InclusionCircle)));
726
_loaded_circle_inclusion_boundary = NEW_NOTHROW InclusionCircle[count];
727
if (_loaded_circle_inclusion_boundary == nullptr) {
728
unload();
729
get_loaded_fence_semaphore().give();
730
return false;
731
}
732
}
733
734
{ // allocate storage for circular exclusion fences:
735
uint32_t count = index_fence_count(AC_PolyFenceType::CIRCLE_EXCLUSION);
736
#if AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED
737
count += index_fence_count(AC_PolyFenceType::CIRCLE_EXCLUSION_INT)
738
#endif // AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED
739
Debug("Fence: Allocating %u bytes for circ. exc. fences",
740
(unsigned)(count * sizeof(ExclusionCircle)));
741
_loaded_circle_exclusion_boundary = NEW_NOTHROW ExclusionCircle[count];
742
if (_loaded_circle_exclusion_boundary == nullptr) {
743
unload();
744
get_loaded_fence_semaphore().give();
745
return false;
746
}
747
}
748
749
Vector2f *next_storage_point = _loaded_offsets_from_origin;
750
Vector2l *next_storage_point_lla = _loaded_points_lla;
751
752
// use index to load fences from eeprom
753
bool storage_valid = true;
754
for (uint8_t i=0; i<_eeprom_fence_count; i++) {
755
if (!storage_valid) {
756
break;
757
}
758
const FenceIndex &index = _index[i];
759
uint16_t storage_offset = index.storage_offset;
760
storage_offset += 1; // skip type
761
switch (index.type) {
762
case AC_PolyFenceType::END_OF_STORAGE:
763
#if CONFIG_HAL_BOARD == HAL_BOARD_SITL
764
AP_HAL::panic("indexed end of storage found");
765
#endif
766
storage_valid = false;
767
break;
768
case AC_PolyFenceType::POLYGON_INCLUSION: {
769
// FIXME: consider factoring this with the EXCLUSION case
770
InclusionBoundary &boundary = _loaded_inclusion_boundary[_num_loaded_inclusion_boundaries];
771
boundary.points = next_storage_point;
772
boundary.points_lla = next_storage_point_lla;
773
boundary.count = index.count;
774
if (index.count < 3) {
775
GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "AC_Fence: invalid polygon vertex count %u", index.count);
776
storage_valid = false;
777
break;
778
}
779
storage_offset += 1; // skip vertex count
780
if (!read_polygon_from_storage(loaded_origin, storage_offset, index.count, next_storage_point, next_storage_point_lla)) {
781
GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "AC_Fence: polygon read failed");
782
storage_valid = false;
783
break;
784
}
785
_num_loaded_inclusion_boundaries++;
786
break;
787
}
788
case AC_PolyFenceType::POLYGON_EXCLUSION: {
789
ExclusionBoundary &boundary = _loaded_exclusion_boundary[_num_loaded_exclusion_boundaries];
790
boundary.points = next_storage_point;
791
boundary.points_lla = next_storage_point_lla;
792
boundary.count = index.count;
793
if (index.count < 3) {
794
GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "AC_Fence: invalid polygon vertex count %u", index.count);
795
storage_valid = false;
796
break;
797
}
798
storage_offset += 1; // skip vertex count
799
if (!read_polygon_from_storage(loaded_origin, storage_offset, index.count, next_storage_point, next_storage_point_lla)) {
800
GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "AC_Fence: polygon read failed");
801
storage_valid = false;
802
break;
803
}
804
_num_loaded_exclusion_boundaries++;
805
break;
806
}
807
#if AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED
808
case AC_PolyFenceType::CIRCLE_EXCLUSION_INT:
809
#endif // AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED
810
case AC_PolyFenceType::CIRCLE_EXCLUSION: {
811
ExclusionCircle &circle = _loaded_circle_exclusion_boundary[_num_loaded_circle_exclusion_boundaries];
812
if (!read_latlon_from_storage(storage_offset, circle.point)) {
813
GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "AC_Fence: latlon read failed");
814
storage_valid = false;
815
break;
816
}
817
if (!scale_latlon_from_origin(loaded_origin, circle.point, circle.pos_cm)) {
818
GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "AC_Fence: latlon read failed");
819
storage_valid = false;
820
break;
821
}
822
// now read the radius
823
#if AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED
824
if (index.type == AC_PolyFenceType::CIRCLE_EXCLUSION_INT) {
825
circle.radius = fence_storage.read_uint32(storage_offset);
826
} else
827
#endif // AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED
828
{
829
circle.radius = fence_storage.read_float(storage_offset);
830
}
831
if (!is_positive(circle.radius)) {
832
GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "AC_Fence: non-positive circle radius");
833
storage_valid = false;
834
break;
835
}
836
_num_loaded_circle_exclusion_boundaries++;
837
break;
838
}
839
#if AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED
840
case AC_PolyFenceType::CIRCLE_INCLUSION_INT:
841
#endif // AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED
842
case AC_PolyFenceType::CIRCLE_INCLUSION: {
843
InclusionCircle &circle = _loaded_circle_inclusion_boundary[_num_loaded_circle_inclusion_boundaries];
844
if (!read_latlon_from_storage(storage_offset, circle.point)) {
845
GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "AC_Fence: latlon read failed");
846
storage_valid = false;
847
break;
848
}
849
if (!scale_latlon_from_origin(loaded_origin, circle.point, circle.pos_cm)){
850
GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "AC_Fence: latlon read failed");
851
storage_valid = false;
852
break;
853
}
854
// now read the radius
855
#if AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED
856
if (index.type == AC_PolyFenceType::CIRCLE_INCLUSION_INT) {
857
circle.radius = fence_storage.read_uint32(storage_offset);
858
} else
859
#endif // AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED
860
{
861
circle.radius = fence_storage.read_float(storage_offset);
862
}
863
if (!is_positive(circle.radius)) {
864
GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "AC_Fence: non-positive circle radius");
865
storage_valid = false;
866
break;
867
}
868
_num_loaded_circle_inclusion_boundaries++;
869
break;
870
}
871
case AC_PolyFenceType::RETURN_POINT:
872
if (_loaded_return_point != nullptr) {
873
GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "PolyFence: Multiple return points found");
874
storage_valid = false;
875
break;
876
}
877
_loaded_return_point = next_storage_point;
878
if (_loaded_return_point_lla != nullptr) {
879
GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "PolyFence: Multiple return points found");
880
storage_valid = false;
881
break;
882
}
883
_loaded_return_point_lla = next_storage_point_lla;
884
// Read the point from storage
885
if (!read_latlon_from_storage(storage_offset, *next_storage_point_lla)) {
886
storage_valid = false;
887
GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "PolyFence: latlon read failed");
888
break;
889
}
890
if (!scale_latlon_from_origin(loaded_origin, *next_storage_point_lla, *next_storage_point)) {
891
storage_valid = false;
892
GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "PolyFence: latlon read failed");
893
break;
894
}
895
next_storage_point++;
896
next_storage_point_lla++;
897
break;
898
}
899
}
900
901
if (!storage_valid) {
902
unload();
903
get_loaded_fence_semaphore().give();
904
return false;
905
}
906
907
_load_time_ms = AP_HAL::millis();
908
909
get_loaded_fence_semaphore().give();
910
return true;
911
}
912
913
/// returns pointer to array of exclusion polygon points and num_points is filled in with the number of points in the polygon
914
/// points are offsets in cm from EKF origin in NE frame
915
Vector2f* AC_PolyFence_loader::get_exclusion_polygon(uint16_t index, uint16_t &num_points) const
916
{
917
if (index >= _num_loaded_exclusion_boundaries) {
918
num_points = 0;
919
return nullptr;
920
}
921
const ExclusionBoundary &boundary = _loaded_exclusion_boundary[index];
922
num_points = boundary.count;
923
924
return boundary.points;
925
}
926
927
/// returns pointer to array of inclusion polygon points and num_points is filled in with the number of points in the polygon
928
/// points are offsets in cm from EKF origin in NE frame
929
Vector2f* AC_PolyFence_loader::get_inclusion_polygon(uint16_t index, uint16_t &num_points) const
930
{
931
if (index >= _num_loaded_inclusion_boundaries) {
932
num_points = 0;
933
return nullptr;
934
}
935
const InclusionBoundary &boundary = _loaded_inclusion_boundary[index];
936
num_points = boundary.count;
937
938
return boundary.points;
939
}
940
941
/// returns the specified exclusion circle
942
/// circle center offsets in cm from EKF origin in NE frame, radius is in meters
943
bool AC_PolyFence_loader::get_exclusion_circle(uint8_t index, Vector2f &center_pos_cm, float &radius) const
944
{
945
if (index >= _num_loaded_circle_exclusion_boundaries) {
946
return false;
947
}
948
center_pos_cm = _loaded_circle_exclusion_boundary[index].pos_cm;
949
radius = _loaded_circle_exclusion_boundary[index].radius;
950
return true;
951
}
952
953
/// returns the specified inclusion circle
954
/// circle centre offsets in cm from EKF origin in NE frame, radius is in meters
955
bool AC_PolyFence_loader::get_inclusion_circle(uint8_t index, Vector2f &center_pos_cm, float &radius) const
956
{
957
if (index >= _num_loaded_circle_inclusion_boundaries) {
958
return false;
959
}
960
center_pos_cm = _loaded_circle_inclusion_boundary[index].pos_cm;
961
radius = _loaded_circle_inclusion_boundary[index].radius;
962
return true;
963
}
964
965
bool AC_PolyFence_loader::check_inclusion_circle_margin(float margin) const
966
{
967
// check circular includes
968
for (uint8_t i=0; i<_num_loaded_circle_inclusion_boundaries; i++) {
969
const InclusionCircle &circle = _loaded_circle_inclusion_boundary[i];
970
if (circle.radius < margin) {
971
// circle radius should never be less than margin
972
return false;
973
}
974
}
975
return true;
976
}
977
978
bool AC_PolyFence_loader::validate_fence(const AC_PolyFenceItem *new_items, uint16_t count) const
979
{
980
// validate the fence items...
981
AC_PolyFenceType expecting_type = AC_PolyFenceType::END_OF_STORAGE;
982
uint16_t expected_type_count = 0;
983
uint16_t orig_expected_type_count = 0;
984
bool seen_return_point = false;
985
986
for (uint16_t i=0; i<count; i++) {
987
bool validate_latlon = false;
988
989
switch (new_items[i].type) {
990
case AC_PolyFenceType::END_OF_STORAGE:
991
#if CONFIG_HAL_BOARD == HAL_BOARD_SITL
992
AP_HAL::panic("passed in an END_OF_STORAGE");
993
#endif
994
return false;
995
996
case AC_PolyFenceType::POLYGON_INCLUSION:
997
case AC_PolyFenceType::POLYGON_EXCLUSION:
998
if (new_items[i].vertex_count < 3) {
999
GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "Invalid vertex count (%u)", new_items[i].vertex_count);
1000
return false;
1001
}
1002
if (expected_type_count == 0) {
1003
expected_type_count = new_items[i].vertex_count;
1004
orig_expected_type_count = expected_type_count;
1005
expecting_type = new_items[i].type;
1006
} else {
1007
if (new_items[i].type != expecting_type) {
1008
GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "Received incorrect vertex type (want=%u got=%u)", (unsigned)expecting_type, (unsigned)new_items[i].type);
1009
return false;
1010
} else if (new_items[i].vertex_count != orig_expected_type_count) {
1011
GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "Unexpected vertex count want=%u got=%u", orig_expected_type_count, new_items[i].vertex_count);
1012
return false;
1013
}
1014
}
1015
expected_type_count--;
1016
validate_latlon = true;
1017
break;
1018
1019
#if AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED
1020
case AC_PolyFenceType::CIRCLE_INCLUSION_INT:
1021
case AC_PolyFenceType::CIRCLE_EXCLUSION_INT:
1022
// should never have AC_PolyFenceItems of these types
1023
INTERNAL_ERROR(AP_InternalError::error_t::flow_of_control);
1024
return false;
1025
#endif // AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED
1026
case AC_PolyFenceType::CIRCLE_INCLUSION:
1027
case AC_PolyFenceType::CIRCLE_EXCLUSION:
1028
if (expected_type_count) {
1029
GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "Received incorrect type (want=%u got=%u)", (unsigned)expecting_type, (unsigned)new_items[i].type);
1030
return false;
1031
}
1032
if (!is_positive(new_items[i].radius)) {
1033
GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "Non-positive circle radius");
1034
return false;
1035
}
1036
validate_latlon = true;
1037
break;
1038
1039
case AC_PolyFenceType::RETURN_POINT:
1040
if (expected_type_count) {
1041
GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "Received incorrect type (want=%u got=%u)", (unsigned)expecting_type, (unsigned)new_items[i].type);
1042
return false;
1043
}
1044
1045
// spec says only one return point allowed
1046
if (seen_return_point) {
1047
GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "Multiple return points");
1048
return false;
1049
}
1050
seen_return_point = true;
1051
validate_latlon = true;
1052
// TODO: ensure return point is within all fences and
1053
// outside all exclusion zones
1054
break;
1055
}
1056
1057
if (validate_latlon) {
1058
if (!check_latlng(new_items[i].loc[0], new_items[i].loc[1])) {
1059
GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "Bad lat or lon");
1060
return false;
1061
}
1062
}
1063
}
1064
1065
if (expected_type_count) {
1066
GCS_SEND_TEXT(MAV_SEVERITY_INFO, "Incorrect item count");
1067
return false;
1068
}
1069
1070
return true;
1071
}
1072
1073
uint16_t AC_PolyFence_loader::fence_storage_space_required(const AC_PolyFenceItem *new_items, uint16_t count)
1074
{
1075
uint16_t ret = 4; // for the format header
1076
uint16_t i = 0;
1077
while (i < count) {
1078
ret += 1; // one byte for type
1079
switch (new_items[i].type) {
1080
case AC_PolyFenceType::POLYGON_INCLUSION:
1081
case AC_PolyFenceType::POLYGON_EXCLUSION:
1082
ret += 1 + 8 * new_items[i].vertex_count; // 1 count, 4 lat, 4 lon for each point
1083
i += new_items[i].vertex_count - 1; // i is incremented down below
1084
break;
1085
case AC_PolyFenceType::END_OF_STORAGE:
1086
INTERNAL_ERROR(AP_InternalError::error_t::flow_of_control);
1087
break;
1088
#if AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED
1089
case AC_PolyFenceType::CIRCLE_INCLUSION_INT:
1090
case AC_PolyFenceType::CIRCLE_EXCLUSION_INT:
1091
// should never have AC_PolyFenceItems of these types
1092
INTERNAL_ERROR(AP_InternalError::error_t::flow_of_control);
1093
break;
1094
#endif // AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED
1095
case AC_PolyFenceType::CIRCLE_INCLUSION:
1096
case AC_PolyFenceType::CIRCLE_EXCLUSION:
1097
ret += 12; // 4 radius, 4 lat, 4 lon
1098
break;
1099
case AC_PolyFenceType::RETURN_POINT:
1100
ret += 8; // 4 lat, 4 lon
1101
break;
1102
}
1103
i++;
1104
}
1105
return ret;
1106
}
1107
1108
bool AC_PolyFence_loader::write_fence(const AC_PolyFenceItem *new_items, uint16_t count)
1109
{
1110
if (!validate_fence(new_items, count)) {
1111
GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "Fence validation failed");
1112
return false;
1113
}
1114
1115
if (fence_storage_space_required(new_items, count) > fence_storage.size()) {
1116
GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "Fence exceeds storage size");
1117
return false;
1118
}
1119
1120
if (!format()) {
1121
return false;
1122
}
1123
1124
uint16_t total_vertex_count = 0;
1125
uint16_t offset = 4; // skipping magic
1126
uint8_t vertex_count = 0;
1127
for (uint16_t i=0; i<count; i++) {
1128
const AC_PolyFenceItem new_item = new_items[i];
1129
switch (new_item.type) {
1130
case AC_PolyFenceType::POLYGON_INCLUSION:
1131
case AC_PolyFenceType::POLYGON_EXCLUSION:
1132
if (vertex_count == 0) {
1133
// write out new polygon count
1134
vertex_count = new_item.vertex_count;
1135
total_vertex_count += vertex_count;
1136
if (!write_type_to_storage(offset, new_item.type)) {
1137
return false;
1138
}
1139
fence_storage.write_uint8(offset, vertex_count);
1140
offset++;
1141
}
1142
vertex_count--;
1143
if (!write_latlon_to_storage(offset, new_item.loc)) {
1144
return false;
1145
}
1146
break;
1147
case AC_PolyFenceType::END_OF_STORAGE:
1148
#if CONFIG_HAL_BOARD == HAL_BOARD_SITL
1149
AP_HAL::panic("asked to store end-of-storage marker");
1150
#endif
1151
return false;
1152
#if AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED
1153
case AC_PolyFenceType::CIRCLE_INCLUSION_INT:
1154
case AC_PolyFenceType::CIRCLE_EXCLUSION_INT:
1155
// should never have AC_PolyFenceItems of these types
1156
INTERNAL_ERROR(AP_InternalError::error_t::flow_of_control);
1157
return false;
1158
#endif // AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED
1159
case AC_PolyFenceType::CIRCLE_INCLUSION:
1160
case AC_PolyFenceType::CIRCLE_EXCLUSION: {
1161
total_vertex_count++; // useful to make number of lines in QGC file match FENCE_TOTAL
1162
if (!write_type_to_storage(offset, new_item.type)) {
1163
return false;
1164
}
1165
if (!write_latlon_to_storage(offset, new_item.loc)) {
1166
return false;
1167
}
1168
fence_storage.write_float(offset, new_item.radius);
1169
offset += 4;
1170
break;
1171
}
1172
case AC_PolyFenceType::RETURN_POINT:
1173
if (!write_type_to_storage(offset, new_item.type)) {
1174
return false;
1175
}
1176
if (!write_latlon_to_storage(offset, new_item.loc)) {
1177
return false;
1178
}
1179
break;
1180
}
1181
}
1182
if (!write_eos_to_storage(offset)) {
1183
return false;
1184
}
1185
1186
#if CONFIG_HAL_BOARD == HAL_BOARD_SITL
1187
// sanity-check the EEPROM in SITL to make sure we can read what
1188
// we've just written.
1189
if (!index_eeprom()) {
1190
AP_HAL::panic("Failed to index eeprom");
1191
}
1192
GCS_SEND_TEXT(MAV_SEVERITY_DEBUG, "Fence Indexed OK");
1193
#endif
1194
1195
#if HAL_LOGGING_ENABLED
1196
// start logger logging new fence
1197
AP::logger().Write_Fence();
1198
#endif
1199
1200
void_index();
1201
1202
// this may be completely bogus total. If we are storing an
1203
// advanced fence then the old protocol which relies on this value
1204
// will error off if the GCS tries to fetch points. This number
1205
// should be correct for a "compatible" fence, however.
1206
uint16_t new_total = 0;
1207
if (total_vertex_count >= 3) {
1208
new_total = total_vertex_count+2;
1209
}
1210
_total.set_and_save(new_total);
1211
1212
return true;
1213
}
1214
1215
1216
bool AC_PolyFence_loader::get_return_point(Vector2l &ret)
1217
{
1218
if (!check_indexed()) {
1219
return false;
1220
}
1221
1222
const FenceIndex *rp = find_first_fence(AC_PolyFenceType::RETURN_POINT);
1223
if (rp != nullptr) {
1224
uint16_t read_offset = rp->storage_offset + 1;
1225
return read_latlon_from_storage(read_offset, ret);
1226
}
1227
1228
const FenceIndex *inc = find_first_fence(AC_PolyFenceType::POLYGON_INCLUSION);
1229
if (inc == nullptr) {
1230
return false;
1231
}
1232
1233
// we found an inclusion fence but not a return point. Calculate
1234
// and return the centroid. Note that this may not actually be
1235
// inside all inclusion fences...
1236
uint16_t offset = inc->storage_offset;
1237
if ((AC_PolyFenceType)fence_storage.read_uint8(offset) != AC_PolyFenceType::POLYGON_INCLUSION) {
1238
#if CONFIG_HAL_BOARD == HAL_BOARD_SITL
1239
AP_HAL::panic("wrong type at offset");
1240
#endif
1241
return false;
1242
}
1243
offset++;
1244
const uint8_t count = fence_storage.read_uint8(offset);
1245
if (count < 3) {
1246
#if CONFIG_HAL_BOARD == HAL_BOARD_SITL
1247
AP_HAL::panic("invalid count found");
1248
#endif
1249
return false;
1250
}
1251
offset++;
1252
Vector2l min_loc;
1253
if (!read_latlon_from_storage(offset, min_loc)) {
1254
return false;
1255
}
1256
if (min_loc.is_zero()) {
1257
return false;
1258
}
1259
Vector2l max_loc = min_loc;
1260
for (uint8_t i=1; i<count; i++) {
1261
Vector2l new_loc;
1262
if (!read_latlon_from_storage(offset, new_loc)) {
1263
return false;
1264
}
1265
if (new_loc.is_zero()) {
1266
return false;
1267
}
1268
if (new_loc.x < min_loc.x) {
1269
min_loc.x = new_loc.x;
1270
}
1271
if (new_loc.y < min_loc.y) {
1272
min_loc.y = new_loc.y;
1273
}
1274
if (new_loc.x > max_loc.x) {
1275
max_loc.x = new_loc.x;
1276
}
1277
if (new_loc.y > max_loc.y) {
1278
max_loc.y = new_loc.y;
1279
}
1280
}
1281
1282
// Potential for int32_t overflow when longitudes are beyond [-107, 107].
1283
// As a result, the calculated return point's longitude is calculated using overflowed figure.
1284
// Dividing first before adding avoids the potential overflow.
1285
ret.x = (min_loc.x / 2) + (max_loc.x / 2);
1286
ret.y = (min_loc.y / 2) + (max_loc.y / 2);
1287
1288
return true;
1289
}
1290
1291
AC_PolyFence_loader::FenceIndex *AC_PolyFence_loader::find_first_fence(const AC_PolyFenceType type) const
1292
{
1293
if (_index == nullptr) {
1294
return nullptr;
1295
}
1296
for (uint8_t i=0; i<_num_fences; i++) {
1297
if (_index[i].type == type) {
1298
return &_index[i];
1299
}
1300
}
1301
return nullptr;
1302
}
1303
1304
#if AC_POLYFENCE_FENCE_POINT_PROTOCOL_SUPPORT
1305
void AC_PolyFence_loader::handle_msg_fetch_fence_point(GCS_MAVLINK &link, const mavlink_message_t& msg)
1306
{
1307
if (!check_indexed()) {
1308
return;
1309
}
1310
if (!contains_compatible_fence()) {
1311
link.send_text(MAV_SEVERITY_WARNING, "Vehicle contains advanced fences");
1312
return;
1313
}
1314
1315
if (_total != 0 && _total < 5) {
1316
link.send_text(MAV_SEVERITY_WARNING, "Invalid FENCE_TOTAL");
1317
return;
1318
}
1319
1320
mavlink_fence_fetch_point_t packet;
1321
mavlink_msg_fence_fetch_point_decode(&msg, &packet);
1322
1323
if (packet.idx >= _total) {
1324
link.send_text(MAV_SEVERITY_WARNING, "Invalid fence point, index past total(%u >= %u)", packet.idx, _total.get());
1325
return;
1326
}
1327
1328
mavlink_fence_point_t ret_packet{};
1329
ret_packet.target_system = msg.sysid;
1330
ret_packet.target_component = msg.compid;
1331
ret_packet.idx = packet.idx;
1332
ret_packet.count = _total;
1333
1334
if (packet.idx == 0) {
1335
// return point
1336
Vector2l ret;
1337
if (get_return_point(ret)) {
1338
ret_packet.lat = ret.x * 1.0e-7f;
1339
ret_packet.lng = ret.y * 1.0e-7f;
1340
} else {
1341
link.send_text(MAV_SEVERITY_WARNING, "Failed to get return point");
1342
}
1343
} else {
1344
// find the inclusion fence:
1345
const FenceIndex *inclusion_fence = find_first_fence(AC_PolyFenceType::POLYGON_INCLUSION);
1346
if (inclusion_fence == nullptr) {
1347
// nothing stored yet; just send back zeroes
1348
ret_packet.lat = 0;
1349
ret_packet.lng = 0;
1350
} else {
1351
uint8_t fencepoint_offset; // 1st idx is return point
1352
if (packet.idx == _total-1) {
1353
// the is the loop closure point - send the first point again
1354
fencepoint_offset = 0;
1355
} else {
1356
fencepoint_offset = packet.idx - 1;
1357
}
1358
if (fencepoint_offset >= inclusion_fence->count) {
1359
// we haven't been given a value for this item yet; we will return zeroes
1360
} else {
1361
uint16_t storage_offset = inclusion_fence->storage_offset;
1362
storage_offset++; // skip over type
1363
storage_offset++; // skip over count
1364
storage_offset += 8*fencepoint_offset; // move to point we're interested in
1365
Vector2l bob;
1366
if (!read_latlon_from_storage(storage_offset, bob)) {
1367
link.send_text(MAV_SEVERITY_WARNING, "Fence read failed");
1368
#if CONFIG_HAL_BOARD == HAL_BOARD_SITL
1369
AP_HAL::panic("read failure");
1370
#endif
1371
return;
1372
}
1373
ret_packet.lat = bob[0] * 1.0e-7f;
1374
ret_packet.lng = bob[1] * 1.0e-7f;
1375
}
1376
}
1377
}
1378
1379
link.send_message(MAVLINK_MSG_ID_FENCE_POINT, (const char*)&ret_packet);
1380
}
1381
1382
AC_PolyFence_loader::FenceIndex *AC_PolyFence_loader::get_or_create_return_point()
1383
{
1384
if (!check_indexed()) {
1385
return nullptr;
1386
}
1387
FenceIndex *return_point = find_first_fence(AC_PolyFenceType::RETURN_POINT);
1388
if (return_point != nullptr) {
1389
return return_point;
1390
}
1391
1392
// if the inclusion fence exists we will move it in storage to
1393
// avoid having to continually shift the return point forward as
1394
// we receive fence points
1395
uint16_t offset;
1396
const FenceIndex *inclusion_fence = find_first_fence(AC_PolyFenceType::POLYGON_INCLUSION);
1397
if (inclusion_fence != nullptr) {
1398
offset = inclusion_fence->storage_offset;
1399
// the "9"s below represent the size of a return point in storage
1400
for (uint8_t i=0; i<inclusion_fence->count; i++) {
1401
// we are shifting the last fence point first - so 'i=0'
1402
// means the last point stored.
1403
const uint16_t point_storage_offset = offset + 2 + (inclusion_fence->count-1-i) * 8;
1404
Vector2l latlon;
1405
uint16_t tmp_read_offs = point_storage_offset;
1406
if (!read_latlon_from_storage(tmp_read_offs, latlon)) {
1407
return nullptr;
1408
}
1409
uint16_t write_offset = point_storage_offset + 9;
1410
if (!write_latlon_to_storage(write_offset, latlon)) {
1411
return nullptr;
1412
}
1413
}
1414
// read/write the count:
1415
const uint8_t count = fence_storage.read_uint8(inclusion_fence->storage_offset+1);
1416
fence_storage.write_uint8(inclusion_fence->storage_offset + 1 + 9, count);
1417
// read/write the type:
1418
const uint8_t t = fence_storage.read_uint8(inclusion_fence->storage_offset);
1419
fence_storage.write_uint8(inclusion_fence->storage_offset + 9, t);
1420
1421
uint16_t write_offset = offset + 2 + 8*inclusion_fence->count + 9;
1422
if (!write_eos_to_storage(write_offset)) {
1423
return nullptr;
1424
}
1425
} else {
1426
if (fence_storage.read_uint8(_eos_offset) != (uint8_t)AC_PolyFenceType::END_OF_STORAGE) {
1427
#if CONFIG_HAL_BOARD == HAL_BOARD_SITL
1428
AP_HAL::panic("Expected end-of-storage marker at offset=%u",
1429
_eos_offset);
1430
#endif
1431
return nullptr;
1432
}
1433
offset = _eos_offset;
1434
}
1435
1436
if (!write_type_to_storage(offset, AC_PolyFenceType::RETURN_POINT)) {
1437
return nullptr;
1438
}
1439
if (!write_latlon_to_storage(offset, Vector2l{0, 0})) {
1440
return nullptr;
1441
}
1442
if (inclusion_fence == nullptr) {
1443
if (!write_eos_to_storage(offset)) {
1444
return nullptr;
1445
}
1446
}
1447
1448
if (!index_eeprom()) {
1449
#if CONFIG_HAL_BOARD == HAL_BOARD_SITL
1450
AP_HAL::panic("Failed to index eeprom after moving inclusion fence for return point");
1451
#endif
1452
return nullptr;
1453
}
1454
1455
return_point = find_first_fence(AC_PolyFenceType::RETURN_POINT);
1456
if (return_point == nullptr) {
1457
#if CONFIG_HAL_BOARD == HAL_BOARD_SITL
1458
AP_HAL::panic("Failed to get return point after indexing");
1459
#endif
1460
}
1461
return return_point;
1462
}
1463
1464
AC_PolyFence_loader::FenceIndex *AC_PolyFence_loader::get_or_create_include_fence()
1465
{
1466
if (!check_indexed()) {
1467
return nullptr;
1468
}
1469
FenceIndex *inclusion = find_first_fence(AC_PolyFenceType::POLYGON_INCLUSION);
1470
if (inclusion != nullptr) {
1471
return inclusion;
1472
}
1473
if (_total < 5) {
1474
return nullptr;
1475
}
1476
if (!write_type_to_storage(_eos_offset, AC_PolyFenceType::POLYGON_INCLUSION)) {
1477
return nullptr;
1478
}
1479
fence_storage.write_uint8(_eos_offset, 0);
1480
_eos_offset++;
1481
if (!write_eos_to_storage(_eos_offset)) {
1482
return nullptr;
1483
}
1484
1485
if (!index_eeprom()) {
1486
#if CONFIG_HAL_BOARD == HAL_BOARD_SITL
1487
AP_HAL::panic("Failed to index eeprom after creating fence");
1488
#endif
1489
return nullptr;
1490
}
1491
AC_PolyFence_loader::FenceIndex *ret = find_first_fence(AC_PolyFenceType::POLYGON_INCLUSION);
1492
#if CONFIG_HAL_BOARD == HAL_BOARD_SITL
1493
if (ret == nullptr) {
1494
AP_HAL::panic("Failed to index eeprom after creating fence");
1495
}
1496
#endif
1497
return ret;
1498
}
1499
1500
void AC_PolyFence_loader::handle_msg_fence_point(GCS_MAVLINK &link, const mavlink_message_t& msg)
1501
{
1502
if (!check_indexed()) {
1503
return;
1504
}
1505
1506
mavlink_fence_point_t packet;
1507
mavlink_msg_fence_point_decode(&msg, &packet);
1508
1509
if (_total != 0 && _total < 5) {
1510
link.send_text(MAV_SEVERITY_WARNING, "Invalid FENCE_TOTAL");
1511
return;
1512
}
1513
1514
if (packet.count != _total) {
1515
link.send_text(MAV_SEVERITY_WARNING, "Invalid fence point, bad count (%u vs %u)", packet.count, _total.get());
1516
return;
1517
}
1518
1519
if (packet.idx >= _total) {
1520
// this is a protocol failure
1521
link.send_text(MAV_SEVERITY_WARNING, "Invalid fence point, index past total (%u >= %u)", packet.idx, _total.get());
1522
return;
1523
}
1524
1525
if (!check_latlng(packet.lat, packet.lng)) {
1526
link.send_text(MAV_SEVERITY_WARNING, "Invalid fence point, bad lat or lng");
1527
return;
1528
}
1529
1530
if (!contains_compatible_fence()) {
1531
// the GCS has started to upload using the old protocol;
1532
// ensure we can accept it. We must be able to index the
1533
// fence, so it must be valid (minimum number of points)
1534
if (!format()) {
1535
return;
1536
}
1537
}
1538
1539
const Vector2l point{
1540
(int32_t)(packet.lat*1.0e7f),
1541
(int32_t)(packet.lng*1.0e7f)
1542
};
1543
1544
if (packet.idx == 0) {
1545
// this is the return point; if we have a return point then
1546
// update it, otherwise create a return point fence thingy
1547
const FenceIndex *return_point = get_or_create_return_point();
1548
if (return_point == nullptr) {
1549
#if CONFIG_HAL_BOARD == HAL_BOARD_SITL
1550
AP_HAL::panic("Didn't get return point");
1551
#endif
1552
return;
1553
}
1554
uint16_t offset = return_point->storage_offset;
1555
offset++; // don't overwrite the type!
1556
if (!write_latlon_to_storage(offset, point)) {
1557
link.send_text(MAV_SEVERITY_WARNING, "PolyFence: storage write failed");
1558
return;
1559
}
1560
} else if (packet.idx == _total-1) {
1561
/* this is the fence closing point. We use this to set the vertex
1562
count of the inclusion fence
1563
*/
1564
const FenceIndex *inclusion_fence = get_or_create_include_fence();
1565
if (inclusion_fence == nullptr) {
1566
return;
1567
}
1568
// write type and length
1569
fence_storage.write_uint8(inclusion_fence->storage_offset, uint8_t(AC_PolyFenceType::POLYGON_INCLUSION));
1570
fence_storage.write_uint8(inclusion_fence->storage_offset+1, packet.idx-1);
1571
// and write end of storage marker
1572
fence_storage.write_uint8(inclusion_fence->storage_offset+2+(packet.idx-1)*8, uint8_t(AC_PolyFenceType::END_OF_STORAGE));
1573
void_index();
1574
} else {
1575
const FenceIndex *inclusion_fence = get_or_create_include_fence();
1576
if (inclusion_fence == nullptr) {
1577
#if CONFIG_HAL_BOARD == HAL_BOARD_SITL
1578
AP_HAL::panic("no inclusion fences found");
1579
#endif
1580
return;
1581
}
1582
uint16_t offset = inclusion_fence->storage_offset;
1583
offset++; // skip type
1584
if (packet.idx > inclusion_fence->count) {
1585
// expand the storage space
1586
fence_storage.write_uint8(offset, packet.idx); // remembering that idx[0] is the return point....
1587
}
1588
offset++; // move past number of points
1589
offset += (packet.idx-1)*8;
1590
if (!write_latlon_to_storage(offset, point)) {
1591
link.send_text(MAV_SEVERITY_WARNING, "PolyFence: storage write failed");
1592
return;
1593
}
1594
if (_eos_offset < offset) {
1595
if (!write_eos_to_storage(offset)) {
1596
return;
1597
}
1598
}
1599
void_index();
1600
}
1601
}
1602
1603
bool AC_PolyFence_loader::contains_compatible_fence() const
1604
{
1605
// must contain a single inclusion fence with an optional return point
1606
if (_index == nullptr) {
1607
// this indicates no boundary points present
1608
return true;
1609
}
1610
bool seen_return_point = false;
1611
bool seen_poly_inclusion = false;
1612
for (uint16_t i=0; i<_num_fences; i++) {
1613
switch (_index[i].type) {
1614
case AC_PolyFenceType::END_OF_STORAGE:
1615
#if CONFIG_HAL_BOARD == HAL_BOARD_SITL
1616
AP_HAL::panic("end-of-storage marker found in loaded list");
1617
#endif
1618
return false;
1619
case AC_PolyFenceType::POLYGON_INCLUSION:
1620
if (seen_poly_inclusion) {
1621
return false;
1622
}
1623
seen_poly_inclusion = true;
1624
break;
1625
#if AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED
1626
case AC_PolyFenceType::CIRCLE_INCLUSION_INT:
1627
case AC_PolyFenceType::CIRCLE_EXCLUSION_INT:
1628
#endif // AC_POLYFENCE_CIRCLE_INT_SUPPORT_ENABLED
1629
case AC_PolyFenceType::POLYGON_EXCLUSION:
1630
case AC_PolyFenceType::CIRCLE_INCLUSION:
1631
case AC_PolyFenceType::CIRCLE_EXCLUSION:
1632
return false;
1633
case AC_PolyFenceType::RETURN_POINT:
1634
if (seen_return_point) {
1635
return false;
1636
}
1637
seen_return_point = true;
1638
break;
1639
}
1640
}
1641
return true;
1642
}
1643
1644
#endif // AC_POLYFENCE_FENCE_POINT_PROTOCOL_SUPPORT
1645
1646
bool AC_PolyFence_loader::write_eos_to_storage(uint16_t &offset)
1647
{
1648
if (!write_type_to_storage(offset, AC_PolyFenceType::END_OF_STORAGE)) {
1649
return false;
1650
}
1651
_eos_offset = offset - 1; // should point to the marker
1652
return true;
1653
}
1654
1655
/// handler for polygon fence messages with GCS
1656
void AC_PolyFence_loader::handle_msg(GCS_MAVLINK &link, const mavlink_message_t& msg)
1657
{
1658
switch (msg.msgid) {
1659
#if AC_POLYFENCE_FENCE_POINT_PROTOCOL_SUPPORT
1660
case MAVLINK_MSG_ID_FENCE_POINT:
1661
handle_msg_fence_point(link, msg);
1662
break;
1663
case MAVLINK_MSG_ID_FENCE_FETCH_POINT:
1664
handle_msg_fetch_fence_point(link, msg);
1665
break;
1666
#endif
1667
default:
1668
break;
1669
}
1670
}
1671
1672
void AC_PolyFence_loader::update()
1673
{
1674
#if AC_POLYFENCE_FENCE_POINT_PROTOCOL_SUPPORT
1675
// if an older GCS sets the fence point count to zero then clear the fence
1676
if (_old_total != _total) {
1677
_old_total = _total;
1678
if (_total == 0 && _eeprom_fence_count) {
1679
if (!format()) {
1680
// we are in all sorts of trouble
1681
return;
1682
}
1683
}
1684
}
1685
#endif
1686
if (!load_from_storage()) {
1687
return;
1688
}
1689
}
1690
1691
#else // build type is not appropriate; provide a dummy implementation:
1692
1693
void AC_PolyFence_loader::init() {};
1694
1695
bool AC_PolyFence_loader::get_item(const uint16_t seq, AC_PolyFenceItem &item) { return false; }
1696
1697
Vector2f* AC_PolyFence_loader::get_exclusion_polygon(uint16_t index, uint16_t &num_points) const { return nullptr; }
1698
Vector2f* AC_PolyFence_loader::get_inclusion_polygon(uint16_t index, uint16_t &num_points) const { return nullptr; }
1699
1700
bool AC_PolyFence_loader::get_exclusion_circle(uint8_t index, Vector2f &center_pos_cm, float &radius) const { return false; }
1701
bool AC_PolyFence_loader::get_inclusion_circle(uint8_t index, Vector2f &center_pos_cm, float &radius) const { return false; }
1702
1703
void AC_PolyFence_loader::handle_msg(GCS_MAVLINK &link, const mavlink_message_t& msg) {};
1704
1705
bool AC_PolyFence_loader::breached() const { return false; }
1706
bool AC_PolyFence_loader::breached(const Location& loc, float& distance_outside_fence, Vector2f& fence_direction) const { return false; }
1707
1708
uint16_t AC_PolyFence_loader::max_items() const { return 0; }
1709
1710
bool AC_PolyFence_loader::write_fence(const AC_PolyFenceItem *new_items, uint16_t count) { return false; }
1711
1712
void AC_PolyFence_loader::update() {};
1713
1714
#if AC_POLYFENCE_FENCE_POINT_PROTOCOL_SUPPORT
1715
bool AC_PolyFence_loader::get_return_point(Vector2l &ret) { return false; }
1716
#endif
1717
1718
#endif // #if AC_FENCE_DUMMY_METHODS_ENABLED
1719
#endif // AP_FENCE_ENABLED
1720
1721