From 29618e5a2318255091bf7b8aef0c6e5653a00b7a Mon Sep 17 00:00:00 2001 From: Tomek Zebrowski Date: Mon, 6 Apr 2026 09:40:34 +0200 Subject: [PATCH 1/2] refactor: get rid of async methods from TripManager interface --- .../preferences/trips/TripViewAdapter.kt | 2 +- .../org/obd/graphs/ui/graph/GraphFragment.kt | 2 - .../bl/datalogger/WorkflowOrchestrator.kt | 2 +- .../obd/graphs/bl/trip/DefaultTripManager.kt | 172 +++++++++++------- .../org/obd/graphs/bl/trip/TripManager.kt | 37 +--- .../java/org/obd/graphs/bl/trip/TripModel.kt | 3 +- 6 files changed, 106 insertions(+), 112 deletions(-) diff --git a/app/src/main/java/org/obd/graphs/preferences/trips/TripViewAdapter.kt b/app/src/main/java/org/obd/graphs/preferences/trips/TripViewAdapter.kt index 9af0b547..c17b535c 100644 --- a/app/src/main/java/org/obd/graphs/preferences/trips/TripViewAdapter.kt +++ b/app/src/main/java/org/obd/graphs/preferences/trips/TripViewAdapter.kt @@ -119,7 +119,7 @@ class TripViewAdapter internal constructor( init { if (showDeleteButton) { loadTrip.setOnClickListener { - tripManager.loadTripAsync(data.elementAt(adapterPosition).source.fileName) + tripManager.loadTrip(data.elementAt(adapterPosition).source.fileName) } deleteTrip.setOnClickListener { diff --git a/app/src/main/java/org/obd/graphs/ui/graph/GraphFragment.kt b/app/src/main/java/org/obd/graphs/ui/graph/GraphFragment.kt index 58270e95..acdbab68 100644 --- a/app/src/main/java/org/obd/graphs/ui/graph/GraphFragment.kt +++ b/app/src/main/java/org/obd/graphs/ui/graph/GraphFragment.kt @@ -73,7 +73,6 @@ import org.obd.metrics.pid.PidDefinitionRegistry import java.text.SimpleDateFormat import java.util.Date import java.util.Locale -import java.util.concurrent.ConcurrentLinkedDeque private const val LOG_TAG = "Graph" @@ -261,7 +260,6 @@ class GraphFragment : Fragment() { val sensorData = SensorData( id = it.command.pid.id, - metrics = ConcurrentLinkedDeque(), min = hist.min, max = hist.max, mean = hist.mean diff --git a/datalogger/src/main/java/org/obd/graphs/bl/datalogger/WorkflowOrchestrator.kt b/datalogger/src/main/java/org/obd/graphs/bl/datalogger/WorkflowOrchestrator.kt index 36831a0f..fd66ed17 100644 --- a/datalogger/src/main/java/org/obd/graphs/bl/datalogger/WorkflowOrchestrator.kt +++ b/datalogger/src/main/java/org/obd/graphs/bl/datalogger/WorkflowOrchestrator.kt @@ -141,7 +141,7 @@ internal class WorkflowOrchestrator internal constructor() { "Collecting process is completed." ) sendBroadcastEvent(DATA_LOGGER_STOPPED_EVENT) - tripManager.saveCurrentTripAsync() + tripManager.saveCurrentTrip() } } diff --git a/datalogger/src/main/java/org/obd/graphs/bl/trip/DefaultTripManager.kt b/datalogger/src/main/java/org/obd/graphs/bl/trip/DefaultTripManager.kt index 4bed473b..2efccb65 100644 --- a/datalogger/src/main/java/org/obd/graphs/bl/trip/DefaultTripManager.kt +++ b/datalogger/src/main/java/org/obd/graphs/bl/trip/DefaultTripManager.kt @@ -18,13 +18,19 @@ package org.obd.graphs.bl.trip import android.content.Context import android.util.Log -import org.obd.graphs.bl.datalogger.MetricsProcessor +import org.obd.graphs.SCREEN_LOCK_PROGRESS_EVENT +import org.obd.graphs.SCREEN_UNLOCK_PROGRESS_EVENT +import org.obd.graphs.ScreenLock +import org.obd.graphs.bl.datalogger.DataLoggerRepository import org.obd.graphs.bl.datalogger.scaleToRange +import org.obd.graphs.commons.R import org.obd.graphs.getContext import org.obd.graphs.isNumber import org.obd.graphs.preferences.Prefs import org.obd.graphs.preferences.isEnabled import org.obd.graphs.profile.profile +import org.obd.graphs.runAsync +import org.obd.graphs.sendBroadcastEvent import org.obd.metrics.api.model.ObdMetric import java.text.SimpleDateFormat import java.util.Date @@ -32,15 +38,13 @@ import java.util.Locale val tripManager: TripManager by lazy { DefaultTripManager() } -private const val LOGGER_TAG = "TripManager" +private const val LOG_TAG = "TripManager" private const val MIN_TRIP_LENGTH = 5 private const val TRIP_FILE_PREFIX = "trip" private const val MAX_CACHED_METRICS_PER_SENSOR = 18000 -internal class DefaultTripManager : - TripManager, - MetricsProcessor { +internal class DefaultTripManager : TripManager { private val dateFormat: SimpleDateFormat = SimpleDateFormat("MM.dd HH:mm:ss", Locale.getDefault()) private val tripCache = TripCache() @@ -56,8 +60,8 @@ internal class DefaultTripManager : try { tripCache.getTrip { trip -> val ts = (System.currentTimeMillis() - trip.startTs).toFloat() - val key = obdMetric.command.pid.id - val newRecord = if (obdMetric.isNumber()) Entry(ts, obdMetric.scaleToRange(), key) else Entry(ts, obdMetric.value, key) + val id = obdMetric.command.pid.id + val newRecord = if (obdMetric.isNumber()) Entry(ts, obdMetric.scaleToRange(), id) else Entry(ts, obdMetric.value, id) val metric = Metric( entry = newRecord, @@ -67,18 +71,20 @@ internal class DefaultTripManager : repository.saveMetric(metric) - val tripEntry = trip.entries.getOrPut(key) { - SensorData(id = key) + val tripEntry = trip.entries.getOrPut(id) { + SensorData(id = id) } - tripEntry.metrics.add(metric) + synchronized(tripEntry.metrics) { + tripEntry.metrics.add(metric) - while (tripEntry.metrics.size > MAX_CACHED_METRICS_PER_SENSOR) { - tripEntry.metrics.removeFirst() + while (tripEntry.metrics.size > MAX_CACHED_METRICS_PER_SENSOR) { + tripEntry.metrics.removeFirst() + } } } } catch (e: Throwable) { - Log.e(LOGGER_TAG, "Failed to process metric for ${obdMetric.command.pid.pid}", e) + Log.e(LOG_TAG, "Failed to process metric for ${obdMetric.command.pid.pid}", e) } } @@ -89,36 +95,101 @@ internal class DefaultTripManager : tripCache.getTrip() ?: Trip(startTs = newTs) } - Log.i(LOGGER_TAG, "Get current trip ts: '${formatTimestamp(trip.startTs)}'") + Log.i(LOG_TAG, "Get current trip ts: '${formatTimestamp(trip.startTs)}'") return trip } override fun startNewTrip(newTs: Long) { - Log.i(LOGGER_TAG, "Starting new trip, timestamp: '${formatTimestamp(newTs)}'") + Log.i(LOG_TAG, "Starting new trip, timestamp: '${formatTimestamp(newTs)}'") updateCache(newTs) activeTripId = "$TRIP_FILE_PREFIX-${profile.getCurrentProfile()}-$newTs.jsonl" repository.initStorage(activeTripId!!) } - override fun saveCurrentTrip() { - tripCache.getTrip { trip -> - val recordShortTrip = Prefs.isEnabled("pref.trips.recordings.save.short.trip") - val tripLength = getTripLength(trip) - val currentTripId = activeTripId ?: return@getTrip - - Log.i(LOGGER_TAG, "Stopping trip, length: ${tripLength}s") - - repository.releaseStorage(currentTripId) - - if (recordShortTrip || tripLength > MIN_TRIP_LENGTH) { - repository.updateTripMetadata(currentTripId, trip.startTs, tripLength, profile.getCurrentProfile()) - } else { - Log.w(LOGGER_TAG, "Trip time is less than ${MIN_TRIP_LENGTH}s. Discarding.") - repository.deleteTrip(currentTripId) + override fun loadTrip(tripName: String) { + if (!DataLoggerRepository.isRunning()) { + runAsync(wait = false) { + try { + Log.i(LOG_TAG, "Loading trip: '$tripName' ...................") + + if (tripName.isEmpty()) { + updateCache(System.currentTimeMillis()) + } else { + sendBroadcastEvent( + SCREEN_LOCK_PROGRESS_EVENT, + ScreenLock(message = R.string.dialog_screen_lock_trip_load_message) + ) + + try { + val parts = tripDescParser.decodeTripName(tripName) + val startTs = parts.getOrNull(2)?.toLongOrNull() ?: System.currentTimeMillis() + val trip = Trip(startTs = startTs) + + repository.loadTrip(tripName) { metric -> + val key = metric.entry.data + if (!trip.entries.containsKey(key)) { + trip.entries[key] = SensorData(id = key) + } + trip.entries[key]!!.metrics.add(metric) + } + + trip.entries.values.forEach { sensorData -> + val values = sensorData.metrics.mapNotNull { it.entry.y.toString().toFloatOrNull() } + if (values.isNotEmpty()) { + sensorData.min = values.minOrNull() ?: 0f + sensorData.max = values.maxOrNull() ?: 0f + sensorData.mean = values.average() + } + } + + Log.i(LOG_TAG, "Trip loaded successfully. PIDs: ${trip.entries.keys}") + + tripCache.updateTrip(trip) + tripVirtualScreenManager.updateReservedVirtualScreen(trip.entries.keys.map { it.toString() }) + + Log.i(LOG_TAG, "Trip: '$tripName' is loaded") + } catch (e: Throwable) { + Log.e(LOG_TAG, "Failed to load trip '$tripName'.", e) + updateCache(System.currentTimeMillis()) + } + } + } finally { + sendBroadcastEvent(SCREEN_UNLOCK_PROGRESS_EVENT) + } } + } + } - activeTripId = null + override fun saveCurrentTrip() { + sendBroadcastEvent( + SCREEN_LOCK_PROGRESS_EVENT, + ScreenLock(message = R.string.dialog_screen_lock_trip_save_message) + ) + + runAsync(wait = false) { + try { + tripCache.getTrip { trip -> + val recordShortTrip = Prefs.isEnabled("pref.trips.recordings.save.short.trip") + val tripLength = getTripLength(trip) + val currentTripId = activeTripId ?: return@getTrip + + Log.i(LOG_TAG, "Stopping trip, length: ${tripLength}s") + + repository.releaseStorage(currentTripId) + + if (recordShortTrip || tripLength > MIN_TRIP_LENGTH) { + repository.updateTripMetadata(currentTripId, trip.startTs, tripLength, profile.getCurrentProfile()) + } else { + Log.w(LOG_TAG, "Trip time is less than ${MIN_TRIP_LENGTH}s. Discarding.") + repository.deleteTrip(currentTripId) + } + + activeTripId = null + } + } finally { + sendBroadcastEvent(SCREEN_UNLOCK_PROGRESS_EVENT) + } } } @@ -130,45 +201,6 @@ internal class DefaultTripManager : repository.deleteTrip(trip.fileName) } - override fun loadTrip(tripId: String) { - Log.i(LOGGER_TAG, "Loading trip ID: '$tripId'") - - if (tripId.isEmpty()) { - updateCache(System.currentTimeMillis()) - return - } - - try { - val parts = tripDescParser.decodeTripName(tripId) - val startTs = parts.getOrNull(2)?.toLongOrNull() ?: System.currentTimeMillis() - val trip = Trip(startTs = startTs) - - repository.loadTrip(tripId) { metric -> - val key = metric.entry.data - if (!trip.entries.containsKey(key)) { - trip.entries[key] = SensorData(id = key) - } - trip.entries[key]!!.metrics.add(metric) - } - - trip.entries.values.forEach { sensorData -> - val values = sensorData.metrics.mapNotNull { it.entry.y.toString().toFloatOrNull() } - if (values.isNotEmpty()) { - sensorData.min = values.minOrNull() ?: 0f - sensorData.max = values.maxOrNull() ?: 0f - sensorData.mean = values.average() - } - } - - Log.i(LOGGER_TAG, "Trip loaded successfully. PIDs: ${trip.entries.keys}") - tripCache.updateTrip(trip) - tripVirtualScreenManager.updateReservedVirtualScreen(trip.entries.keys.map { it.toString() }) - } catch (e: Throwable) { - Log.e(LOGGER_TAG, "Failed to load trip '$tripId'.", e) - updateCache(System.currentTimeMillis()) - } - } - private fun updateCache(newTs: Long) { val trip = Trip(startTs = newTs) tripCache.updateTrip(trip) diff --git a/datalogger/src/main/java/org/obd/graphs/bl/trip/TripManager.kt b/datalogger/src/main/java/org/obd/graphs/bl/trip/TripManager.kt index 093e9b74..5609d91d 100644 --- a/datalogger/src/main/java/org/obd/graphs/bl/trip/TripManager.kt +++ b/datalogger/src/main/java/org/obd/graphs/bl/trip/TripManager.kt @@ -17,51 +17,16 @@ package org.obd.graphs.bl.trip import android.content.Context -import android.util.Log -import org.obd.graphs.* -import org.obd.graphs.bl.datalogger.DataLoggerRepository import org.obd.graphs.bl.datalogger.MetricsProcessor -import org.obd.graphs.commons.R - -private const val LOG_TAG = "TripManager" interface TripManager : MetricsProcessor { fun getCurrentTrip(): Trip fun startNewTrip(newTs: Long) - fun saveCurrentTrip() fun getTripsDirectory(context: Context): String - fun saveCurrentTripAsync(){ - sendBroadcastEvent(SCREEN_LOCK_PROGRESS_EVENT, - ScreenLock(message = R.string.dialog_screen_lock_trip_save_message)) - - runAsync (wait = false) { - try { - tripManager.saveCurrentTrip() - } finally { - sendBroadcastEvent(SCREEN_UNLOCK_PROGRESS_EVENT) - } - } - } + fun saveCurrentTrip() fun findAllTripsBy(filter: String = ""): MutableCollection fun deleteTrip(trip: TripFileDesc) fun loadTrip(tripName: String) - fun loadTripAsync(tripName: String){ - if (!DataLoggerRepository.isRunning()) { - sendBroadcastEvent(SCREEN_LOCK_PROGRESS_EVENT, - ScreenLock(message = R.string.dialog_screen_lock_trip_load_message)) - - - runAsync (wait = false) { - try { - Log.i(LOG_TAG, "Loading trip: '$tripName' ...................") - tripManager.loadTrip(tripName) - Log.i(LOG_TAG, "Trip: '$tripName' is loaded") - } finally { - sendBroadcastEvent(SCREEN_UNLOCK_PROGRESS_EVENT) - } - } - } - } } diff --git a/datalogger/src/main/java/org/obd/graphs/bl/trip/TripModel.kt b/datalogger/src/main/java/org/obd/graphs/bl/trip/TripModel.kt index 6cb77e6f..c12b5a04 100644 --- a/datalogger/src/main/java/org/obd/graphs/bl/trip/TripModel.kt +++ b/datalogger/src/main/java/org/obd/graphs/bl/trip/TripModel.kt @@ -19,7 +19,6 @@ package org.obd.graphs.bl.trip import com.fasterxml.jackson.annotation.JsonIgnoreProperties import org.obd.metrics.transport.message.ConnectorResponse import java.util.concurrent.ConcurrentHashMap -import java.util.concurrent.ConcurrentLinkedDeque data class TripFileDesc( val fileName: String, @@ -46,7 +45,7 @@ data class Metric( @JsonIgnoreProperties(ignoreUnknown = true) data class SensorData( val id: Long, - val metrics: ConcurrentLinkedDeque = ConcurrentLinkedDeque(), + val metrics: ArrayDeque = ArrayDeque(), var min: Number = 0, var max: Number = 0, var mean: Number = 0 From dab6db96d431124a183f07297a0b66b977758bb3 Mon Sep 17 00:00:00 2001 From: Tomek Zebrowski Date: Mon, 6 Apr 2026 10:37:25 +0200 Subject: [PATCH 2/2] refactor: naming refactoring --- .../obd/graphs/bl/trip/DefaultTripManager.kt | 5 ++++- .../org/obd/graphs/bl/trip/TripRepository.kt | 20 +++++++++---------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/datalogger/src/main/java/org/obd/graphs/bl/trip/DefaultTripManager.kt b/datalogger/src/main/java/org/obd/graphs/bl/trip/DefaultTripManager.kt index 2efccb65..dcd1257f 100644 --- a/datalogger/src/main/java/org/obd/graphs/bl/trip/DefaultTripManager.kt +++ b/datalogger/src/main/java/org/obd/graphs/bl/trip/DefaultTripManager.kt @@ -170,11 +170,12 @@ internal class DefaultTripManager : TripManager { runAsync(wait = false) { try { tripCache.getTrip { trip -> + var ts = System.currentTimeMillis() val recordShortTrip = Prefs.isEnabled("pref.trips.recordings.save.short.trip") val tripLength = getTripLength(trip) val currentTripId = activeTripId ?: return@getTrip - Log.i(LOG_TAG, "Stopping trip, length: ${tripLength}s") + Log.i(LOG_TAG, "Stopping current trip: $currentTripId, length: ${tripLength}s") repository.releaseStorage(currentTripId) @@ -186,6 +187,8 @@ internal class DefaultTripManager : TripManager { } activeTripId = null + ts = System.currentTimeMillis() - ts + Log.i(LOG_TAG, "Trip: $currentTripId is saved. It took $ts ms") } } finally { sendBroadcastEvent(SCREEN_UNLOCK_PROGRESS_EVENT) diff --git a/datalogger/src/main/java/org/obd/graphs/bl/trip/TripRepository.kt b/datalogger/src/main/java/org/obd/graphs/bl/trip/TripRepository.kt index 22cc236e..a08d140f 100644 --- a/datalogger/src/main/java/org/obd/graphs/bl/trip/TripRepository.kt +++ b/datalogger/src/main/java/org/obd/graphs/bl/trip/TripRepository.kt @@ -26,7 +26,7 @@ import java.io.FileOutputStream import java.util.concurrent.Executors import java.util.concurrent.atomic.AtomicLong -private const val LOGGER_TAG = "TripRepository" +private const val LOG_TAG = "TripRepository" private const val TRIP_DIRECTORY = "trips" private const val TRIP_FILE_PREFIX = "trip" @@ -66,9 +66,9 @@ internal class FileTripRepository( try { val file = getTripFile(activeTripId!!) activeFileOutputStream = FileOutputStream(file, true) - Log.i(LOGGER_TAG, "Started saving trip to: $activeTripId") + Log.i(LOG_TAG, "Started saving trip to: $activeTripId") } catch (e: Exception) { - Log.e(LOGGER_TAG, "Failed to initialize storage for trip", e) + Log.e(LOG_TAG, "Failed to initialize storage for trip", e) } } } @@ -80,7 +80,7 @@ internal class FileTripRepository( activeFileOutputStream?.close() activeFileOutputStream = null } catch (e: Exception) { - Log.e(LOGGER_TAG, "Failed to release storage", e) + Log.e(LOG_TAG, "Failed to release storage", e) } } } @@ -94,7 +94,7 @@ internal class FileTripRepository( totalMetricsSaved.incrementAndGet() } } catch (e: Exception) { - Log.e(LOGGER_TAG, "Failed to save metric", e) + Log.e(LOG_TAG, "Failed to save metric", e) } } } @@ -112,13 +112,13 @@ internal class FileTripRepository( if (isRenamed) { val totalItems = totalMetricsSaved.get() val fileSizeMb = finalFile.length() / (1024.0 * 1024.0) - Log.i(LOGGER_TAG, "Trip finished. ID: '$finalName' | Saved: $totalItems metrics | Size: ${String.format("%.2f", fileSizeMb)} MB") + Log.i(LOG_TAG, "Trip finished. ID: '$finalName' | Saved: $totalItems metrics | Size: ${String.format("%.2f", fileSizeMb)} MB") } else { - Log.e(LOGGER_TAG, "Failed to rename temporary trip file '$tripId' to '$finalName'.") + Log.e(LOG_TAG, "Failed to rename temporary trip file '$tripId' to '$finalName'.") } } } catch (e: Exception) { - Log.e(LOGGER_TAG, "Failed to update trip metadata", e) + Log.e(LOG_TAG, "Failed to update trip metadata", e) } finally { activeTripId = null } @@ -129,9 +129,9 @@ internal class FileTripRepository( ioScope.launch { try { getTripFile(tripId).delete() - Log.i(LOGGER_TAG, "Deleted trip data: $tripId") + Log.i(LOG_TAG, "Deleted trip data: $tripId") } catch (e: Exception) { - Log.e(LOGGER_TAG, "Failed to delete trip data", e) + Log.e(LOG_TAG, "Failed to delete trip data", e) } finally { if (activeTripId == tripId) activeTripId = null }