mirror of
https://github.com/MarekZegare4/MeshCore-Solo.git
synced 2026-07-26 23:08:11 +00:00
refactor(companion): tidy geo-alert / live-share; dedicated auto-pause gate
Review cleanup of the new location features: - GeoAlertScreen + LiveShareScreen use the shared drawList() helper instead of hand-rolling the scroll/clamp/scrollbar maths, matching every other list screen. - Extract UITask::geoAlertDistance() — one place for the target-distance maths, shared by the crossing evaluator and the proximity beeper. - Geo Alert re-seeds its crossing engine after every edit, so changing the target/radius while armed can't fire a stale arrive/leave before exit. - Trail auto-pause gets its own coarse movement gate (NodePrefs::TRAIL_AUTOPAUSE_MOVE_M = 15 m) instead of reusing the trail min-delta, so GPS jitter while parked doesn't keep the idle timer alive. - Fix stale include comments. Verified: WioTrackerL1_companion_solo_dual builds clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -300,6 +300,11 @@ struct NodePrefs { // persisted to file
|
||||
static const char* L[TRAIL_AUTOPAUSE_COUNT] = { "Off", "1m", "2m", "5m" };
|
||||
return L[idx < TRAIL_AUTOPAUSE_COUNT ? idx : 0];
|
||||
}
|
||||
// Movement under this many metres counts as "stationary" for auto-pause.
|
||||
// Deliberately coarser than the trail min-delta gate (and independent of it)
|
||||
// so GPS jitter while parked doesn't keep resetting the idle timer. Engine
|
||||
// tuning only — not persisted.
|
||||
static const uint16_t TRAIL_AUTOPAUSE_MOVE_M = 15;
|
||||
|
||||
// Tail sentinel written at the end of /new_prefs. Bump the low byte when
|
||||
// adding/removing/reordering fields in DataStore::savePrefs/loadPrefsInt so
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
#include "../NodePrefs.h"
|
||||
#include "../Waypoint.h"
|
||||
#include "icons.h" // scrollIndicatorReserve / drawScrollIndicator
|
||||
#include "icons.h" // drawList (shared scrolling-list helper)
|
||||
|
||||
class GeoAlertScreen : public UIScreen {
|
||||
UITask* _task;
|
||||
@@ -71,31 +71,16 @@ public:
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
display.drawCenteredHeader("GEO ALERT");
|
||||
|
||||
const int y0 = display.listStart();
|
||||
const int step = display.lineStep();
|
||||
|
||||
int vis = (display.height() - y0) / step;
|
||||
if (vis < 1) vis = 1;
|
||||
if (_sel < _scroll) _scroll = _sel;
|
||||
if (_sel >= _scroll + vis) _scroll = _sel - vis + 1;
|
||||
if (_scroll < 0) _scroll = 0;
|
||||
|
||||
const int reserve = scrollIndicatorReserve(display, ROW_COUNT, vis);
|
||||
const int valx = display.width() / 2 + 6;
|
||||
|
||||
for (int i = _scroll; i < ROW_COUNT && i < _scroll + vis; i++) {
|
||||
int y = y0 + (i - _scroll) * step;
|
||||
const int valx = display.width() / 2 + 6;
|
||||
drawList(display, ROW_COUNT, _sel, _scroll, [&](int i, int y, bool sel, int reserve) {
|
||||
Row r = rows(i);
|
||||
bool sel = (i == _sel);
|
||||
display.drawSelectionRow(0, y - 1, display.width() - reserve, step - 1, sel);
|
||||
display.drawSelectionRow(0, y - 1, display.width() - reserve, display.lineStep() - 1, sel);
|
||||
display.setCursor(4, y);
|
||||
display.print(r.label);
|
||||
char val[24];
|
||||
valueLabel(r.kind, val, sizeof(val));
|
||||
if (val[0]) display.drawTextEllipsized(valx, y, display.width() - valx - reserve, val);
|
||||
}
|
||||
|
||||
drawScrollIndicator(display, y0, vis * step, ROW_COUNT, vis, _scroll);
|
||||
});
|
||||
return 500;
|
||||
}
|
||||
|
||||
@@ -128,6 +113,9 @@ public:
|
||||
_dirty = true;
|
||||
break;
|
||||
}
|
||||
// Re-seed the crossing engine after any change so editing the target/radius
|
||||
// while armed can't fire a stale arrive/leave before the screen is closed.
|
||||
_task->resetGeoAlert();
|
||||
}
|
||||
|
||||
// Cycle the target through the saved waypoints, snapshotting the chosen one's
|
||||
@@ -157,7 +145,7 @@ public:
|
||||
|
||||
bool handleInput(char c) override {
|
||||
if (c == KEY_CANCEL || c == KEY_CONTEXT_MENU) {
|
||||
if (_dirty) { the_mesh.savePrefs(); _task->resetGeoAlert(); _dirty = false; }
|
||||
if (_dirty) { the_mesh.savePrefs(); _dirty = false; } // engine re-seeded per edit
|
||||
_task->gotoToolsScreen();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
// Included by UITask.cpp after AutoAdvertScreen.h.
|
||||
|
||||
#include "../NodePrefs.h"
|
||||
#include "icons.h" // scrollIndicatorReserve / drawScrollIndicator
|
||||
#include "icons.h" // drawList (shared scrolling-list helper)
|
||||
|
||||
class LiveShareScreen : public UIScreen {
|
||||
UITask* _task;
|
||||
@@ -89,31 +89,16 @@ public:
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
display.drawCenteredHeader("LIVE SHARE");
|
||||
|
||||
const int y0 = display.listStart();
|
||||
const int step = display.lineStep();
|
||||
|
||||
int vis = (display.height() - y0) / step;
|
||||
if (vis < 1) vis = 1;
|
||||
if (_sel < _scroll) _scroll = _sel;
|
||||
if (_sel >= _scroll + vis) _scroll = _sel - vis + 1;
|
||||
if (_scroll < 0) _scroll = 0;
|
||||
|
||||
const int reserve = scrollIndicatorReserve(display, ROW_COUNT, vis);
|
||||
const int valx = display.width() / 2 + 6;
|
||||
|
||||
for (int i = _scroll; i < ROW_COUNT && i < _scroll + vis; i++) {
|
||||
int y = y0 + (i - _scroll) * step;
|
||||
const int valx = display.width() / 2 + 6;
|
||||
drawList(display, ROW_COUNT, _sel, _scroll, [&](int i, int y, bool sel, int reserve) {
|
||||
Row r = rows(i);
|
||||
bool sel = (i == _sel);
|
||||
display.drawSelectionRow(0, y - 1, display.width() - reserve, step - 1, sel);
|
||||
display.drawSelectionRow(0, y - 1, display.width() - reserve, display.lineStep() - 1, sel);
|
||||
display.setCursor(4, y);
|
||||
display.print(r.label);
|
||||
char val[24];
|
||||
valueLabel(r.kind, val, sizeof(val));
|
||||
if (val[0]) display.drawTextEllipsized(valx, y, display.width() - valx - reserve, val);
|
||||
}
|
||||
|
||||
drawScrollIndicator(display, y0, vis * step, ROW_COUNT, vis, _scroll);
|
||||
});
|
||||
return 500;
|
||||
}
|
||||
|
||||
|
||||
@@ -2032,15 +2032,17 @@ void UITask::loop() {
|
||||
int32_t lo = (int32_t)loc->getLongitude();
|
||||
uint16_t md = TrailStore::minDeltaMeters(_node_prefs->trail_min_delta_idx,
|
||||
_node_prefs->units_imperial);
|
||||
// Auto-pause: freeze the trail once the device has sat within the
|
||||
// min-delta gate for the configured delay; resume on the next real move.
|
||||
// Auto-pause: freeze the trail once the device has stayed within
|
||||
// TRAIL_AUTOPAUSE_MOVE_M of one spot for the configured delay; resume on
|
||||
// the next real move. Its own coarse gate (not the trail min-delta) so
|
||||
// GPS jitter while parked doesn't keep the idle timer alive.
|
||||
uint16_t ap = NodePrefs::trailAutoPauseSecs(_node_prefs->trail_autopause_idx);
|
||||
if (ap > 0) {
|
||||
uint32_t now = millis();
|
||||
float moved = _trail_pause_has_ref
|
||||
? geo::haversineKm(_trail_pause_ref_lat, _trail_pause_ref_lon, la, lo) * 1000.0f
|
||||
: 1e9f;
|
||||
if (!_trail_pause_has_ref || moved >= (float)md) {
|
||||
if (!_trail_pause_has_ref || moved >= (float)NodePrefs::TRAIL_AUTOPAUSE_MOVE_M) {
|
||||
_trail_pause_ref_lat = la; _trail_pause_ref_lon = lo;
|
||||
_trail_pause_has_ref = true;
|
||||
_trail_last_move_ms = now;
|
||||
@@ -2121,17 +2123,27 @@ void UITask::loop() {
|
||||
// fires fireGeoAlert() according to the configured mode; a hysteresis band on
|
||||
// the "leave" edge stops it chattering at the boundary, and the first reading
|
||||
// after arming only seeds the inside/outside state (no spurious alert).
|
||||
// Distance (m) from the current GPS fix to the geo-alert target, plus the
|
||||
// configured radius (m). Returns false when no target is set or there's no fix
|
||||
// — the single place the target-distance maths lives, shared by the crossing
|
||||
// evaluator and the proximity beeper.
|
||||
bool UITask::geoAlertDistance(float& dist_m, float& radius_m) const {
|
||||
if (!_node_prefs || !_node_prefs->geo_alert_has_target) return false;
|
||||
int32_t lat, lon;
|
||||
if (!currentLocation(lat, lon)) return false;
|
||||
dist_m = geo::haversineKm(lat, lon, _node_prefs->geo_alert_lat_1e6,
|
||||
_node_prefs->geo_alert_lon_1e6) * 1000.0f;
|
||||
radius_m = (float)NodePrefs::geoAlertRadiusMeters(_node_prefs->geo_alert_radius_idx);
|
||||
return true;
|
||||
}
|
||||
|
||||
void UITask::evaluateGeoAlert() {
|
||||
if (!_node_prefs || !_node_prefs->geo_alert_enabled || !_node_prefs->geo_alert_has_target) {
|
||||
_geo_alert_known = false;
|
||||
return;
|
||||
}
|
||||
int32_t lat, lon;
|
||||
if (!currentLocation(lat, lon)) return;
|
||||
float dist = geo::haversineKm(lat, lon,
|
||||
_node_prefs->geo_alert_lat_1e6,
|
||||
_node_prefs->geo_alert_lon_1e6) * 1000.0f;
|
||||
float r = (float)NodePrefs::geoAlertRadiusMeters(_node_prefs->geo_alert_radius_idx);
|
||||
float dist, r;
|
||||
if (!geoAlertDistance(dist, r)) return; // armed but no fix yet — keep state
|
||||
bool inside;
|
||||
if (!_geo_alert_known) inside = dist <= r; // seed state
|
||||
else if (_geo_alert_inside) inside = dist <= r * 1.25f; // leave past band
|
||||
@@ -2169,12 +2181,8 @@ void UITask::geoProximityBeeper() {
|
||||
if ((int32_t)(millis() - _geo_beep_check_ms) < 0) return;
|
||||
_geo_beep_check_ms = millis() + 250UL;
|
||||
|
||||
int32_t lat, lon;
|
||||
if (!currentLocation(lat, lon)) return;
|
||||
float dist = geo::haversineKm(lat, lon,
|
||||
_node_prefs->geo_alert_lat_1e6,
|
||||
_node_prefs->geo_alert_lon_1e6) * 1000.0f;
|
||||
float r = (float)NodePrefs::geoAlertRadiusMeters(_node_prefs->geo_alert_radius_idx);
|
||||
float dist, r;
|
||||
if (!geoAlertDistance(dist, r)) return;
|
||||
if (dist > r) { // outside the zone: stay quiet, beep on re-entry
|
||||
_geo_beep_next_ms = millis();
|
||||
return;
|
||||
|
||||
@@ -116,6 +116,7 @@ class UITask : public AbstractUITask {
|
||||
// is when the next tick is due.
|
||||
uint32_t _geo_beep_check_ms = 0;
|
||||
uint32_t _geo_beep_next_ms = 0;
|
||||
bool geoAlertDistance(float& dist_m, float& radius_m) const;
|
||||
void evaluateGeoAlert();
|
||||
void fireGeoAlert(bool arrived);
|
||||
void geoProximityBeeper();
|
||||
|
||||
Reference in New Issue
Block a user