mirror of
https://github.com/liquidraver/ZephCore.git
synced 2026-09-01 20:38:19 +00:00
timesync cosmetics and uptime back to UI
This commit is contained in:
@@ -206,7 +206,7 @@ All `set uplink.*` changes are saved immediately and only applied after reboot.
|
||||
| `get radio.rxgain` | RX gain boost: `0` or `1` |
|
||||
| `get rxduty` | RX duty cycle mode: `0` or `1` |
|
||||
| `get gps duty` | Now-effective GPS duty interval in seconds (`always on (0)` when continuous) |
|
||||
| `get meshtimesync` | Mesh time-sync state + live dry-run: on/off, eligible voter count, votes for/against, consensus skew and radius, would-be verdict (`ok`/`in-band`/`step±N`/`abstain (reason)`/`hold (reason)`; a recent clock set — manual or GPS — shows as `hold (suppressed)`, and a backward step a forward-only role would refuse is annotated `(skipped: forward-only)`), step counters, suppression countdown, and a per-sender evidence table (`prefix hops count skew E`, `E` = tenure-eligible). Sensing runs even while off, so this works as a dry-run before enabling. Over remote admin the reply is truncated to the packet size (summary always fits); the full table needs the USB CLI. |
|
||||
| `get meshtimesync` | Mesh time-sync state + live dry-run: on/off, eligible voter count, votes for/against, consensus skew and radius, would-be verdict (`ok`/`in-band`/`step±N`/`abstain (reason)`/`hold (reason)`; a recent clock set — manual or GPS — shows as `hold (suppressed)`, and a backward step a forward-only role would refuse is annotated `(skipped: forward-only)`), step counters, suppression countdown, and a per-sender evidence table (`prefix hops count skew E`, `E` = counted toward the verdict above). Entries that count print first, so a size-capped reply never hides the ones that explain the summary; if the table doesn't fully fit, a trailing `+N more` shows how many were left out. Sensing runs even while off, so this works as a dry-run before enabling. Over remote admin the reply is truncated to the packet size (summary always fits); the full table needs the USB CLI. |
|
||||
| `get dc.restarts` | Duty-cycle preamble false-positive re-arm counter (RxTimeout re-arms + parked-RX watchdog recoveries). High values mean the preamble detector is tripping on noise/interference without real packets arriving — inflates RX-on time and drains battery; packets are never lost to it. Reset by `clear stats`. |
|
||||
| `get adc.multiplier` | Battery voltage ADC calibration multiplier |
|
||||
| `get bootloader.ver` | Bootloader version string |
|
||||
|
||||
@@ -55,10 +55,10 @@ bool MeshTimeSync::slotTenured(const Slot &s, uint32_t uptime_secs) const
|
||||
s.count >= TENURE_MIN_ADVERTS;
|
||||
}
|
||||
|
||||
bool MeshTimeSync::slotEligible(const Slot &s, uint32_t uptime_secs) const
|
||||
bool MeshTimeSync::slotEligible(const Slot &s, uint32_t uptime_secs, bool bootstrap) const
|
||||
{
|
||||
return s.used && slotTenured(s, uptime_secs) &&
|
||||
(uptime_secs - s.arrival_uptime) <= MAX_SAMPLE_AGE_SECS;
|
||||
return s.used && (uptime_secs - s.arrival_uptime) <= MAX_SAMPLE_AGE_SECS &&
|
||||
(bootstrap || slotTenured(s, uptime_secs));
|
||||
}
|
||||
|
||||
int64_t MeshTimeSync::slotSkew(const Slot &s, uint32_t local_time, uint32_t uptime_secs) const
|
||||
@@ -159,9 +159,7 @@ MeshTimeSync::Consensus MeshTimeSync::computeConsensus(uint32_t local_time,
|
||||
int n = 0, m = 0;
|
||||
for (int i = 0; i < MESHTIMESYNC_TABLE_SIZE; i++) {
|
||||
const Slot &s = _slots[i];
|
||||
if (!s.used) continue;
|
||||
if ((uptime_secs - s.arrival_uptime) > MAX_SAMPLE_AGE_SECS) continue;
|
||||
if (!bootstrap && !slotTenured(s, uptime_secs)) continue;
|
||||
if (!slotEligible(s, uptime_secs, bootstrap)) continue;
|
||||
int64_t skew = slotSkew(s, local_time, uptime_secs);
|
||||
int32_t r = RADIUS_BASE_SECS + RADIUS_PER_HOP_SECS * s.hops;
|
||||
val[m] = skew - r; typ[m] = 1; m++;
|
||||
@@ -475,22 +473,52 @@ int MeshTimeSync::formatStatus(char *out, size_t cap, uint32_t local_time,
|
||||
(unsigned long)_evals, (unsigned long)_abstains);
|
||||
if (pos < 0 || (size_t)pos >= cap) goto full;
|
||||
|
||||
/* Evidence table: one entry per used slot, as many as fit. */
|
||||
for (int i = 0; i < MESHTIMESYNC_TABLE_SIZE; i++) {
|
||||
const Slot &s = _slots[i];
|
||||
if (!s.used) continue;
|
||||
int w = snprintf(out + pos, cap - pos, "\r\n %02x%02x h%u n%u %+lds%s",
|
||||
s.prefix[0], s.prefix[1], (unsigned)s.hops,
|
||||
(unsigned)(s.count > 99 ? 99 : s.count),
|
||||
clampl(slotSkew(s, local_time, uptime_secs)),
|
||||
slotEligible(s, uptime_secs) ? " E" : "");
|
||||
if (w < 0 || (size_t)(pos + w) >= cap) {
|
||||
out[pos] = 0; /* drop the partial entry */
|
||||
return pos;
|
||||
{
|
||||
/* Evidence table: entries that actually count toward this verdict
|
||||
* print first, then the rest — on a size-capped reply (161 B over
|
||||
* remote admin), the buffer must not run out on still-building-tenure
|
||||
* entries while hiding the ones that explain the verdict.
|
||||
* (Braced so these locals go out of scope before the `full` label
|
||||
* below — a goto may not jump over a variable's initialization.) */
|
||||
int total_used = 0;
|
||||
for (int i = 0; i < MESHTIMESYNC_TABLE_SIZE; i++) {
|
||||
if (_slots[i].used) total_used++;
|
||||
}
|
||||
pos += w;
|
||||
|
||||
int printed = 0;
|
||||
for (int pass = 0; pass < 2 && printed < total_used; pass++) {
|
||||
for (int i = 0; i < MESHTIMESYNC_TABLE_SIZE; i++) {
|
||||
const Slot &s = _slots[i];
|
||||
if (!s.used) continue;
|
||||
bool participates = slotEligible(s, uptime_secs, v.bootstrap);
|
||||
if (participates != (pass == 0)) continue;
|
||||
|
||||
int w = snprintf(out + pos, cap - pos, "\r\n %02x%02x h%u n%u %+lds%s",
|
||||
s.prefix[0], s.prefix[1], (unsigned)s.hops,
|
||||
(unsigned)(s.count > 99 ? 99 : s.count),
|
||||
clampl(slotSkew(s, local_time, uptime_secs)),
|
||||
participates ? " E" : "");
|
||||
if (w < 0 || (size_t)(pos + w) >= cap) {
|
||||
out[pos] = 0; /* drop the partial entry */
|
||||
goto truncated;
|
||||
}
|
||||
pos += w;
|
||||
printed++;
|
||||
}
|
||||
}
|
||||
return pos;
|
||||
|
||||
truncated:
|
||||
if (printed < total_used) {
|
||||
char tail[24];
|
||||
int tw = snprintf(tail, sizeof(tail), "\r\n +%d more", total_used - printed);
|
||||
if (tw > 0 && (size_t)tw < cap - (size_t)pos) {
|
||||
memcpy(out + pos, tail, (size_t)tw + 1);
|
||||
pos += tw;
|
||||
}
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
return pos;
|
||||
|
||||
full:
|
||||
out[cap - 1] = 0;
|
||||
|
||||
@@ -160,7 +160,12 @@ private:
|
||||
|
||||
Slot *findSlot(const uint8_t *prefix);
|
||||
bool slotTenured(const Slot &s, uint32_t uptime_secs) const;
|
||||
bool slotEligible(const Slot &s, uint32_t uptime_secs) const;
|
||||
/* Whether this slot counts toward the current evaluation — bootstrap
|
||||
* relaxes tenure, so this takes the mode explicitly rather than
|
||||
* hardcoding normal-mode rules. Single source of truth shared by
|
||||
* computeConsensus (who votes) and formatStatus (who gets the "E"
|
||||
* marker) — they must never diverge on what "counted" means. */
|
||||
bool slotEligible(const Slot &s, uint32_t uptime_secs, bool bootstrap) const;
|
||||
int64_t slotSkew(const Slot &s, uint32_t local_time, uint32_t uptime_secs) const;
|
||||
|
||||
Consensus computeConsensus(uint32_t local_time, uint32_t uptime_secs,
|
||||
|
||||
@@ -527,6 +527,12 @@ static void render_messages(void)
|
||||
draw_centered_color(y, buf,
|
||||
state.offgrid_enabled ? UI_COLOR_ACTIVE
|
||||
: UI_COLOR_DISABLED);
|
||||
y += LINE_H;
|
||||
|
||||
uint32_t up_s = (uint32_t)(k_uptime_get() / 1000);
|
||||
snprintf(buf, sizeof(buf), "Up: %ud %uh %um",
|
||||
up_s / 86400, (up_s % 86400) / 3600, (up_s % 3600) / 60);
|
||||
draw_centered_color(y, buf, UI_COLOR_LABEL);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -559,21 +565,32 @@ static void render_messages(void)
|
||||
draw_centered_color(y, buf,
|
||||
state.offgrid_enabled ? UI_COLOR_ACTIVE
|
||||
: UI_COLOR_DISABLED);
|
||||
y += LINE_H;
|
||||
|
||||
uint32_t up_s = (uint32_t)(k_uptime_get() / 1000);
|
||||
snprintf(buf, sizeof(buf), "Up: %ud %uh %um",
|
||||
up_s / 86400, (up_s % 86400) / 3600, (up_s % 3600) / 60);
|
||||
draw_centered_color(y, buf, UI_COLOR_LABEL);
|
||||
return;
|
||||
}
|
||||
|
||||
snprintf(buf, sizeof(buf), "MSG: %u", state.msg_count);
|
||||
draw_centered(centered_row(0, 3), buf);
|
||||
draw_centered(centered_row(0, 4), buf);
|
||||
|
||||
if (state.ble_connected) {
|
||||
draw_centered(centered_row(1, 3), "< Connected >");
|
||||
draw_centered(centered_row(1, 4), "< Connected >");
|
||||
} else {
|
||||
draw_centered(centered_row(1, 3), "Waiting for app...");
|
||||
draw_centered(centered_row(1, 4), "Waiting for app...");
|
||||
}
|
||||
|
||||
snprintf(buf, sizeof(buf), "Offgrid: %s",
|
||||
state.offgrid_enabled ? "ON" : "OFF");
|
||||
draw_centered(centered_row(2, 3), buf);
|
||||
draw_centered(centered_row(2, 4), buf);
|
||||
|
||||
uint32_t up_s = (uint32_t)(k_uptime_get() / 1000);
|
||||
snprintf(buf, sizeof(buf), "Up: %ud %uh %um",
|
||||
up_s / 86400, (up_s % 86400) / 3600, (up_s % 3600) / 60);
|
||||
draw_centered(centered_row(3, 4), buf);
|
||||
}
|
||||
|
||||
static void render_recent(void)
|
||||
|
||||
Reference in New Issue
Block a user