Skip to content

Commit d3096de

Browse files
authored
Merge pull request #126 from MrAlders0n/copilot/add-debug-logging-standards-again
Refactor ping/repeat flow: 10s RX window + async API posting
2 parents a0e81b1 + c8a8f7f commit d3096de

3 files changed

Lines changed: 224 additions & 32 deletions

File tree

content/wardrive.js

Lines changed: 74 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ const STATUS_UPDATE_DELAY_MS = 100; // Brief delay to ensure "Ping se
4444
const MAP_REFRESH_DELAY_MS = 1000; // Delay after API post to ensure backend updated
4545
const MIN_PAUSE_THRESHOLD_MS = 1000; // Minimum timer value (1 second) to pause
4646
const MAX_REASONABLE_TIMER_MS = 5 * 60 * 1000; // Maximum reasonable timer value (5 minutes) to handle clock skew
47-
const RX_LOG_LISTEN_WINDOW_MS = 7000; // Listen window for repeater echoes (7 seconds)
47+
const RX_LOG_LISTEN_WINDOW_MS = 10000; // Listen window for repeater echoes (10 seconds)
4848

4949
// Pre-computed channel hash and key for the wardriving channel
5050
// These will be computed once at startup and used for message correlation and decryption
@@ -1322,6 +1322,47 @@ async function postToMeshMapperAPI(lat, lon, heardRepeats) {
13221322
}
13231323
}
13241324

1325+
/**
1326+
* Post to MeshMapper API in background (non-blocking)
1327+
* This function runs asynchronously after the RX listening window completes
1328+
* UI status messages are suppressed for successful posts, errors are shown
1329+
* @param {number} lat - Latitude
1330+
* @param {number} lon - Longitude
1331+
* @param {number} accuracy - GPS accuracy in meters
1332+
* @param {string} heardRepeats - Heard repeats string (e.g., "4e(1.75),b7(-0.75)" or "None")
1333+
*/
1334+
async function postApiInBackground(lat, lon, accuracy, heardRepeats) {
1335+
debugLog(`postApiInBackground called with heard_repeats="${heardRepeats}"`);
1336+
1337+
// Hidden 3-second delay before API POST (no user-facing status message)
1338+
debugLog("Starting 3-second delay before API POST");
1339+
await new Promise(resolve => setTimeout(resolve, 3000));
1340+
1341+
debugLog("3-second delay complete, posting to API");
1342+
try {
1343+
await postToMeshMapperAPI(lat, lon, heardRepeats);
1344+
debugLog("Background API post completed successfully");
1345+
// No success status message - suppress from UI
1346+
} catch (error) {
1347+
debugError("Background API post failed:", error);
1348+
// Errors are propagated to caller for user notification
1349+
throw error;
1350+
}
1351+
1352+
// Update map after API post
1353+
debugLog("Scheduling coverage map refresh");
1354+
setTimeout(() => {
1355+
const shouldRefreshMap = accuracy && accuracy < GPS_ACCURACY_THRESHOLD_M;
1356+
1357+
if (shouldRefreshMap) {
1358+
debugLog(`Refreshing coverage map (accuracy ${accuracy}m within threshold)`);
1359+
scheduleCoverageRefresh(lat, lon);
1360+
} else {
1361+
debugLog(`Skipping map refresh (accuracy ${accuracy}m exceeds threshold)`);
1362+
}
1363+
}, MAP_REFRESH_DELAY_MS);
1364+
}
1365+
13251366
/**
13261367
* Post to MeshMapper API and refresh coverage map after heard repeats are finalized
13271368
* This executes immediately (no delay) because it's called after the RX listening window
@@ -2712,7 +2753,7 @@ async function sendPing(manual = false) {
27122753

27132754
const ch = await ensureChannel();
27142755

2715-
// Capture GPS coordinates at ping time - these will be used for API post after 7s delay
2756+
// Capture GPS coordinates at ping time - these will be used for API post after 10s delay
27162757
state.capturedPingCoords = { lat, lon, accuracy };
27172758
debugLog(`GPS coordinates captured at ping time: lat=${lat.toFixed(5)}, lon=${lon.toFixed(5)}, accuracy=${accuracy}m`);
27182759

@@ -2750,7 +2791,7 @@ async function sendPing(manual = false) {
27502791
startRxListeningCountdown(RX_LOG_LISTEN_WINDOW_MS);
27512792
}
27522793

2753-
// Schedule the sequence: listen for 7s, THEN finalize repeats and post to API
2794+
// Schedule the sequence: listen for 10s, THEN finalize repeats and background the API post
27542795
// This timeout is stored in meshMapperTimer for cleanup purposes
27552796
// Capture coordinates locally to prevent race conditions with concurrent pings
27562797
const capturedCoords = state.capturedPingCoords;
@@ -2771,26 +2812,44 @@ async function sendPing(manual = false) {
27712812
const heardRepeatsStr = formatRepeaterTelemetry(repeaters);
27722813
debugLog(`Formatted heard_repeats for API: "${heardRepeatsStr}"`);
27732814

2815+
// Update status and start next timer IMMEDIATELY (before API post)
2816+
// This is the key change: we don't wait for API to complete
2817+
if (state.connection) {
2818+
if (state.running) {
2819+
// Check if we should resume a paused auto countdown (manual ping during auto mode)
2820+
const resumed = resumeAutoCountdown();
2821+
if (!resumed) {
2822+
// No paused timer to resume, schedule new auto ping (this was an auto ping)
2823+
debugLog("Scheduling next auto ping immediately after RX window");
2824+
scheduleNextAutoPing();
2825+
} else {
2826+
debugLog("Resumed auto countdown after manual ping");
2827+
}
2828+
} else {
2829+
debugLog("Setting dynamic status to Idle (manual mode)");
2830+
setDynamicStatus("Idle");
2831+
}
2832+
}
2833+
2834+
// Unlock ping controls immediately (don't wait for API)
2835+
unlockPingControls("after RX listening window completion");
2836+
2837+
// Background the API posting (runs asynchronously, doesn't block)
27742838
// Use captured coordinates for API post (not current GPS position)
27752839
if (capturedCoords) {
27762840
const { lat: apiLat, lon: apiLon, accuracy: apiAccuracy } = capturedCoords;
2777-
debugLog(`Using captured ping coordinates for API post: lat=${apiLat.toFixed(5)}, lon=${apiLon.toFixed(5)}, accuracy=${apiAccuracy}m`);
2841+
debugLog(`Backgrounding API post for coordinates: lat=${apiLat.toFixed(5)}, lon=${apiLon.toFixed(5)}, accuracy=${apiAccuracy}m`);
27782842

2779-
// Post to API with heard repeats data
2780-
await postApiAndRefreshMap(apiLat, apiLon, apiAccuracy, heardRepeatsStr);
2843+
// Post to API in background (async, fire-and-forget with error handling)
2844+
postApiInBackground(apiLat, apiLon, apiAccuracy, heardRepeatsStr).catch(error => {
2845+
debugError(`Background API post failed: ${error.message}`, error);
2846+
// Show error to user only if API fails
2847+
setDynamicStatus("Error: API post failed", STATUS_COLORS.error);
2848+
});
27812849
} else {
27822850
// This should never happen as coordinates are always captured before ping
27832851
debugError(`CRITICAL: No captured ping coordinates available for API post - this indicates a logic error`);
27842852
debugError(`Skipping API post to avoid posting incorrect coordinates`);
2785-
2786-
// Unlock ping controls since API post is being skipped
2787-
unlockPingControls("after skipping API post due to missing coordinates");
2788-
2789-
// Fix 2: Schedule next auto ping if in auto mode to prevent getting stuck
2790-
if (state.running && !state.autoTimerId) {
2791-
debugLog("Scheduling next auto ping after skipped API post");
2792-
scheduleNextAutoPing();
2793-
}
27942853
}
27952854

27962855
// Clear timer reference

docs/CONNECTION_WORKFLOW.md

Lines changed: 125 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -313,14 +313,15 @@ When a wardriving slot is revoked during an active session (detected during API
313313
**Revocation Sequence:**
314314

315315
1. **Detection**
316-
- During "Posting to API" operation
316+
- During background API POST operation (runs asynchronously after RX window)
317317
- API returns `{"allowed": false, ...}`
318318
- Detected in `postToMeshMapperAPI()` response handler
319319

320320
2. **Initial Status**
321321
- **Dynamic Status**: `"Error: Posting to API (Revoked)"` (red)
322322
- Sets `state.disconnectReason = "slot_revoked"`
323323
- Visible for 1.5 seconds
324+
- **Note**: User may already be seeing "Idle" or "Waiting for next ping" before this error appears (because API runs in background)
324325

325326
3. **Disconnect Initiated**
326327
- Calls `disconnect()` after 1.5s delay
@@ -334,16 +335,23 @@ When a wardriving slot is revoked during an active session (detected during API
334335
- **Dynamic Status**: `"WarDriving slot has been revoked"` (red, terminal - NO "Disconnected:" prefix)
335336
- This is the final terminal status
336337

337-
**Complete Revocation Flow:**
338+
**Complete Revocation Flow (Updated for background API posting):**
338339
```
339340
Connection Status: (unchanged) → "Disconnecting" → "Disconnected"
340-
Dynamic Status: "Posting to API" → "Error: Posting to API (Revoked)" → "—" → "WarDriving slot has been revoked"
341+
Dynamic Status: "Idle"/"Waiting for next ping" → "Error: Posting to API (Revoked)" → "—" → "WarDriving slot has been revoked"
341342
```
343+
**Timeline:**
344+
- T+0s: RX window completes, status shows "Idle" or "Waiting for next ping", next timer starts
345+
- T+0-3s: Background API post running (3s delay, then POST) - silent
346+
- T+3-4s: Revocation detected, "Error: Posting to API (Revoked)" shown (1.5s)
347+
- T+4.5s: Disconnect initiated
348+
- T+5s: Terminal status "WarDriving slot has been revoked"
342349
343350
**Key Differences from Normal Disconnect:**
344351
- Normal disconnect: Dynamic Status shows `"—"` (em dash)
345352
- Revocation: Dynamic Status shows `"WarDriving slot has been revoked"` (red error, no prefix)
346353
- Revocation shows intermediate "Error: Posting to API (Revoked)" state
354+
- With the new ping/repeat flow, revocation may be detected after user already sees "Idle" or "Waiting for next ping" (because API runs in background)
347355
348356
## Workflow Diagrams
349357
@@ -590,6 +598,120 @@ stateDiagram-v2
590598
- Error → Disconnected
591599
- Recovery always possible
592600

601+
## Ping/Repeat Listener Flow
602+
603+
### Overview
604+
605+
The ping/repeat listener flow manages the complete lifecycle of a wardrive ping operation, from sending the ping to listening for repeater echoes to posting data to the MeshMapper API.
606+
607+
**Key Design Change (v1.4.2+):** API posting now runs in the background (asynchronously) to prevent blocking the main ping cycle. This allows the next ping timer to start immediately after the RX listening window completes, without waiting for the API POST to finish.
608+
609+
### New Ping/Repeat Flow (v1.4.2+)
610+
611+
```
612+
┌─────────────────────────────────────────────────────────────────────┐
613+
│ 1. Ping Sent │
614+
│ - Send ping to mesh network │
615+
│ - Start repeater echo tracking │
616+
│ - Show "Ping sent" status │
617+
│ - Lock ping controls │
618+
└──────────────────────────────┬──────────────────────────────────────┘
619+
620+
621+
┌─────────────────────────────────────────────────────────────────────┐
622+
│ 2. RX Listening Window (10 seconds) │
623+
│ - Listen for repeater echoes │
624+
│ - Show "Listening for heard repeats (Xs)" countdown │
625+
│ - Track all repeaters that forward the ping │
626+
│ - Update session log in real-time │
627+
└──────────────────────────────┬──────────────────────────────────────┘
628+
629+
630+
┌─────────────────────────────────────────────────────────────────────┐
631+
│ 3. RX Window Complete - IMMEDIATE ACTIONS │
632+
│ - Stop RX listening countdown │
633+
│ - Finalize heard repeats (stop tracking) │
634+
│ - Update UI log with final repeater data │
635+
│ - **Unlock ping controls** ← NEW: Don't wait for API │
636+
│ - **Start next ping timer** ← NEW: Don't wait for API │
637+
│ - **Set status to "Idle"/"Waiting for next ping"** │
638+
└──────────────────────────────┬──────────────────────────────────────┘
639+
640+
641+
┌─────────────────────────────────────────────────────────────────────┐
642+
│ 4. Background API Posting (Async, Non-Blocking) │
643+
│ - Delay 3 seconds (silent, no status message) │
644+
│ - POST ping data to MeshMapper API │
645+
│ - **Success**: Silent (no UI notification) │
646+
│ - **Error**: Show "Error: API post failed" │
647+
│ - Refresh coverage map after POST completes │
648+
└─────────────────────────────────────────────────────────────────────┘
649+
```
650+
651+
### Key Changes from Previous Flow (v1.4.1 and earlier)
652+
653+
**Old Flow:**
654+
1. Ping sent
655+
2. Listen for repeats (7 seconds)
656+
3. **Wait for API post to complete** (3s delay + POST time)
657+
4. **Then** unlock controls and start next timer
658+
5. Status: "Ping sent" → "Listening (7s)" → "Posting to API" → "Idle"/"Waiting"
659+
660+
**New Flow:**
661+
1. Ping sent
662+
2. Listen for repeats (10 seconds) ← **Increased from 7s to 10s**
663+
3. **Immediately** unlock controls and start next timer ← **Key change**
664+
4. **Background** API post (silent on success, error only if fails)
665+
5. Status: "Ping sent" → "Listening (10s)" → "Idle"/"Waiting" ← **No "Posting to API"**
666+
667+
### Benefits
668+
669+
1. **Faster cycle times**: Next ping can start immediately after 10s RX window, not waiting for API
670+
2. **Better UX**: User sees smooth progression without API delays blocking the UI
671+
3. **More repeater data**: 10-second listen window (vs 7s) captures more mesh echoes
672+
4. **Cleaner UI**: API success messages suppressed, only errors shown
673+
5. **Non-blocking**: API failures don't stall the ping cycle
674+
675+
### Implementation Details
676+
677+
**Functions:**
678+
- `postApiInBackground(lat, lon, accuracy, heardRepeats)` - New async function for background API posting
679+
- `sendPing(manual)` - Refactored to start next timer immediately after RX window
680+
- `RX_LOG_LISTEN_WINDOW_MS` - Increased from 7000ms to 10000ms
681+
682+
**Error Handling:**
683+
- API POST failures are caught with `.catch()` handler
684+
- Error message shown to user: "Error: API post failed"
685+
- Main ping cycle continues unaffected by API failures
686+
- Background failures don't crash or stall the application
687+
688+
**Debug Logging:**
689+
- `[DEBUG] Backgrounding API post for coordinates: ...`
690+
- `[DEBUG] Starting 3-second delay before API POST`
691+
- `[DEBUG] 3-second delay complete, posting to API`
692+
- `[DEBUG] Background API post completed successfully`
693+
- `[DEBUG] Background API post failed: ...`
694+
695+
### Status Message Behavior
696+
697+
**Visible to User:**
698+
- "Ping sent" (500ms minimum)
699+
- "Listening for heard repeats (10s)" (countdown, 10 seconds)
700+
- "Idle" or "Waiting for next ping (Xs)" (immediately after RX window)
701+
- "Error: API post failed" (only if background API fails)
702+
703+
**Not Visible (Suppressed):**
704+
- ~~"Posting to API"~~ - No longer shown for successful API posts
705+
- API success confirmation - Silent operation
706+
707+
### Timing Analysis
708+
709+
**Total time from ping to next ping (Auto mode, 30s interval):**
710+
- Old flow: 10s (ping) + 7s (listen) + 3s (delay) + 0.5s (API) + 30s (interval) = ~50.5s between pings
711+
- New flow: 10s (ping) + 10s (listen) + 30s (interval) = ~50s between pings (API runs in parallel)
712+
713+
**Actual improvement:** Next timer starts ~3.5s earlier (no waiting for API), better responsiveness
714+
593715
## Passive RX Log Listening
594716

595717
### Overview

0 commit comments

Comments
 (0)