updated the sheet
This commit is contained in:
89
lib/data/heartbeat.dart
Normal file
89
lib/data/heartbeat.dart
Normal file
@@ -0,0 +1,89 @@
|
||||
/// ─────────────────────────────────────────────────────────────────────────
|
||||
/// HOW OFTEN THE RIDER LOG BEATS
|
||||
///
|
||||
/// ── Why this constant exists ──
|
||||
///
|
||||
/// The heartbeat's cadence came from one place: `logseconds`, a field the
|
||||
/// **legacy** login returned and the app persisted at sign-in. The v1 contract
|
||||
/// at `api.doormile.com` does not send it — `POST /miler/verify-pin` answers
|
||||
/// with `{success, token, user:{…, profile:{…}}}` and nothing about logging
|
||||
/// cadence — so the adapter in `AuthProvider` had no value to map and wrote the
|
||||
/// `?? 0` fallback into prefs.
|
||||
///
|
||||
/// Zero was then read as an instruction rather than as an absence. Both call
|
||||
/// sites treated it as "do not beat":
|
||||
///
|
||||
/// • `RiderLogController.setOnDuty` started the loop only `if (interval > 0)`.
|
||||
/// • `startAutoCreateLoginLoop` computed `baseInterval = 0` and returned at
|
||||
/// `if (interval <= 0)`.
|
||||
///
|
||||
/// So on every v1 deployment the periodic loop never started. What went with
|
||||
/// it was not just telemetry: `_heartbeat()` in the rider-log provider is the
|
||||
/// one place that writes **both** `POST /miler/logs` (the trail the console's
|
||||
/// Battery, Charging, Connection, GPS Accuracy and Location Service columns are
|
||||
/// read from) and `PUT /miler/location` (the Redis geo-index dispatch searches
|
||||
/// to find a rider at all). A rider on duty was reporting neither, and the
|
||||
/// console drew an em dash against a phone that was measuring all of it
|
||||
/// correctly — see [DeviceTelemetry], which was never the problem.
|
||||
///
|
||||
/// The only reason it was not total silence is that a live pickup forces the
|
||||
/// interval to 30 by a separate path, so a rider mid-collection beat and a
|
||||
/// rider between stops did not.
|
||||
///
|
||||
/// ── Why 30 seconds ──
|
||||
///
|
||||
/// It is the cadence the rest of the app already assumes when nobody has told
|
||||
/// it otherwise: the pickup log's own `_getLogInterval` falls back to 30, and
|
||||
/// both controllers hard-code 30 for the live-pickup case. Matching it means a
|
||||
/// rider's location trail has one shape rather than two, and a hub that later
|
||||
/// starts sending `logseconds` still wins — this is a floor under a missing
|
||||
/// answer, not a replacement for a real one.
|
||||
/// ─────────────────────────────────────────────────────────────────────────
|
||||
library;
|
||||
|
||||
/// Seconds between rider-log heartbeats when the backend has not said.
|
||||
const int kDefaultLogSeconds = 30;
|
||||
|
||||
/// The heartbeat interval to actually use, given whatever the backend said.
|
||||
///
|
||||
/// A positive value is honoured exactly. `null`, a zero, a negative, and a
|
||||
/// string that is none of those all mean *the backend did not answer*, and the
|
||||
/// answer to that is [kDefaultLogSeconds] — never zero, because zero is what
|
||||
/// stopped the heartbeat starting in the first place.
|
||||
///
|
||||
/// Accepts an [Object] rather than an `int?` because the value arrives from two
|
||||
/// different shapes: `prefs.getInt('logseconds')` gives an `int?`, while the
|
||||
/// login envelope's `details['logseconds']` is untyped JSON and has been seen
|
||||
/// as both a number and a string.
|
||||
int resolveLogSeconds(Object? configured) {
|
||||
final int? parsed = switch (configured) {
|
||||
final int n => n,
|
||||
final num n => n.toInt(),
|
||||
final String s => int.tryParse(s.trim()),
|
||||
_ => null,
|
||||
};
|
||||
return (parsed != null && parsed > 0) ? parsed : kDefaultLogSeconds;
|
||||
}
|
||||
|
||||
/// The `status` a heartbeat reports, given whether the rider has live work.
|
||||
///
|
||||
/// ── `active` and `idle` are not words this contract knows ──
|
||||
///
|
||||
/// Four call sites built this string inline as `hasActivePickups ? 'active' :
|
||||
/// 'idle'`, and neither value appears anywhere in the API. `status` on
|
||||
/// `POST /miler/logs` takes an **availability** value — the set
|
||||
/// [MilerApi.availabilityStatuses] lists, and the same set
|
||||
/// `PUT /miler/availability` validates:
|
||||
///
|
||||
/// Offline · Available · Assigned · On_Pickup · At_Customer ·
|
||||
/// Picked_Up · On_Delivery · Break · Blocked
|
||||
///
|
||||
/// This backend rejects an unrecognised enum **silently** — the `Break` /
|
||||
/// `On_Break` note in [MilerApi] records the last time the obvious guess was
|
||||
/// quietly dropped — so a bad `status` risks the whole row, and takes the
|
||||
/// battery, the connection and the location reading down with it.
|
||||
///
|
||||
/// A rider working a counter is `On_Pickup`; a rider between stops is
|
||||
/// `Available`. Both are values the console already renders.
|
||||
String heartbeatStatus({required bool hasActiveWork}) =>
|
||||
hasActiveWork ? 'On_Pickup' : 'Available';
|
||||
Reference in New Issue
Block a user