90 lines
4.7 KiB
Dart
90 lines
4.7 KiB
Dart
/// ─────────────────────────────────────────────────────────────────────────
|
|
/// 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';
|