Sync today's driving totals every 30 minutes
This commit is contained in:
@@ -1,5 +1,14 @@
|
||||
# Changelog
|
||||
|
||||
## 2.2.39
|
||||
|
||||
### Changed
|
||||
|
||||
- Today's cumulative driving totals are now upserted to MongoDB every 30 minutes instead
|
||||
of waiting until the following day. Re-sending totals cannot double-count a retry.
|
||||
- DC charging sessions remain entirely local while charging and are sent immediately after
|
||||
completion. Failed completed-session uploads remain queued and retry every five minutes.
|
||||
|
||||
## 2.2.38
|
||||
|
||||
### Changed
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
52f58d348874fc62a5eefd470686583677769ff55bfe82c234c4f636769a0a90 FatihsMG4-unstable-2.2.39.apk
|
||||
+2
-2
@@ -34,8 +34,8 @@ android {
|
||||
applicationId "com.evsuite.abrp"
|
||||
minSdk 27
|
||||
targetSdk 36
|
||||
versionCode 55
|
||||
versionName "2.2.38"
|
||||
versionCode 56
|
||||
versionName "2.2.39"
|
||||
|
||||
// MongoDB credentials never ship in the APK. The app only talks to the small
|
||||
// authenticated API in server/, which owns the database connection.
|
||||
|
||||
@@ -15,10 +15,11 @@ import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/** Best-effort MongoDB sync through the API in server/. Never blocks local persistence. */
|
||||
/** Best-effort MongoDB sync through the web API. Never blocks local persistence. */
|
||||
final class ConsumptionCloudClient {
|
||||
private static final String TAG = "FatihsMG4.Mongo";
|
||||
private static final long SYNC_INTERVAL_MS = 5 * 60_000L;
|
||||
private static final long CONSUMPTION_SYNC_INTERVAL_MS = 30 * 60_000L;
|
||||
private static final long CHARGE_RETRY_INTERVAL_MS = 5 * 60_000L;
|
||||
private static final int TIMEOUT_MS = 8_000;
|
||||
private static final String PREFS = "consumption_cloud";
|
||||
private static final String INSTALLATION_ID = "installation_id";
|
||||
@@ -30,7 +31,9 @@ final class ConsumptionCloudClient {
|
||||
private final String token;
|
||||
private final String installationId;
|
||||
private final SharedPreferences prefs;
|
||||
private long lastAttemptMs;
|
||||
private long lastConsumptionAttemptMs;
|
||||
private long lastChargeAttemptMs;
|
||||
private String lastAttemptedChargeId = "";
|
||||
|
||||
ConsumptionCloudClient(Context context) {
|
||||
endpoint = BuildConfig.CONSUMPTION_API_URL.trim();
|
||||
@@ -51,14 +54,15 @@ final class ConsumptionCloudClient {
|
||||
*/
|
||||
void syncIfDue(ConsumptionTracker tracker, ChargeSessionTracker charges, long nowMs) {
|
||||
long elapsed = android.os.SystemClock.elapsedRealtime();
|
||||
if (endpoint.isEmpty() || token.isEmpty()
|
||||
|| (lastAttemptMs != 0L && elapsed - lastAttemptMs < SYNC_INTERVAL_MS)) return;
|
||||
lastAttemptMs = elapsed;
|
||||
if (endpoint.isEmpty() || token.isEmpty()) return;
|
||||
syncPendingChargesIfDue(charges, elapsed);
|
||||
if (lastConsumptionAttemptMs != 0L
|
||||
&& elapsed - lastConsumptionAttemptMs < CONSUMPTION_SYNC_INTERVAL_MS) return;
|
||||
lastConsumptionAttemptMs = elapsed;
|
||||
try {
|
||||
syncDailyConsumption(tracker, nowMs);
|
||||
syncPendingCharges(charges);
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, "Cloud sync unavailable");
|
||||
Log.w(TAG, "Consumption sync unavailable");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,8 +74,22 @@ final class ConsumptionCloudClient {
|
||||
catch (RuntimeException ignored) { }
|
||||
for (int sent = 0; sent < MAX_DAYS_PER_SYNC; sent++) {
|
||||
LocalDate target = tracker.nextStoredDayAfter(lastUploaded);
|
||||
if (target == null) return;
|
||||
if (target == null) break;
|
||||
ConsumptionTracker.CloudSnapshot snapshot = tracker.cloudSnapshot(target);
|
||||
JSONObject body = consumptionBody(snapshot, nowMs);
|
||||
if (!send("/v1/consumption/daily", "PUT", body)) return;
|
||||
prefs.edit().putString(LAST_CONSUMPTION_DATE, target.toString()).commit();
|
||||
lastUploaded = target;
|
||||
}
|
||||
// Today's cumulative counters overwrite the same installation/date document every
|
||||
// 30 minutes. Sending totals rather than increments makes retries idempotent.
|
||||
ConsumptionTracker.CloudSnapshot today = tracker.cloudSnapshot(LocalDate.now());
|
||||
JSONObject current = consumptionBody(today, nowMs);
|
||||
send("/v1/consumption/daily", "PUT", current);
|
||||
}
|
||||
|
||||
private JSONObject consumptionBody(ConsumptionTracker.CloudSnapshot snapshot, long nowMs)
|
||||
throws Exception {
|
||||
JSONObject body = new JSONObject()
|
||||
.put("installationId", installationId)
|
||||
.put("recordedAt", nowMs)
|
||||
@@ -79,14 +97,27 @@ final class ConsumptionCloudClient {
|
||||
.put("day", totals(snapshot.day))
|
||||
.put("lifetime", totals(snapshot.lifetime));
|
||||
if (snapshot.sohPercent != null) body.put("sohPercent", snapshot.sohPercent);
|
||||
if (!send("/v1/consumption/daily", "PUT", body)) return;
|
||||
prefs.edit().putString(LAST_CONSUMPTION_DATE, target.toString()).commit();
|
||||
lastUploaded = target;
|
||||
return body;
|
||||
}
|
||||
|
||||
private void syncPendingChargesIfDue(ChargeSessionTracker charges, long elapsed) {
|
||||
List<JSONObject> pending = charges.pendingUploads();
|
||||
if (pending.isEmpty()) return;
|
||||
String firstId = pending.get(0).optString("sessionId", "");
|
||||
boolean newlyCompleted = !firstId.equals(lastAttemptedChargeId);
|
||||
if (!newlyCompleted && lastChargeAttemptMs != 0L
|
||||
&& elapsed - lastChargeAttemptMs < CHARGE_RETRY_INTERVAL_MS) return;
|
||||
lastAttemptedChargeId = firstId;
|
||||
lastChargeAttemptMs = elapsed;
|
||||
try {
|
||||
syncPendingCharges(charges, pending);
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, "Charging-session sync unavailable");
|
||||
}
|
||||
}
|
||||
|
||||
private void syncPendingCharges(ChargeSessionTracker charges) throws Exception {
|
||||
List<JSONObject> pending = charges.pendingUploads();
|
||||
private void syncPendingCharges(ChargeSessionTracker charges, List<JSONObject> pending)
|
||||
throws Exception {
|
||||
int sent = 0;
|
||||
for (JSONObject session : pending) {
|
||||
JSONObject body = new JSONObject(session.toString()).put("installationId", installationId);
|
||||
|
||||
Reference in New Issue
Block a user