diff --git a/app/build.gradle b/app/build.gradle index 7f7983f5..22efa53b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -215,7 +215,7 @@ dependencies { implementation 'com.fasterxml.jackson.core:jackson-databind:2.11.0' implementation 'com.fasterxml.jackson.module:jackson-module-kotlin:2.11.0' implementation 'io.reactivex:rxjava:1.3.8' - implementation 'io.apisense:rhino-android:1.1.1' + implementation 'io.apisense:rhino-android:1.3.0' implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0' implementation 'io.dropwizard.metrics:metrics-core:4.1.17' implementation 'com.dynatrace.dynahist:dynahist:1.1' diff --git a/app/src/main/java/org/obd/graphs/preferences/PreferencesFragment.kt b/app/src/main/java/org/obd/graphs/preferences/PreferencesFragment.kt index 37d6621c..f0487197 100644 --- a/app/src/main/java/org/obd/graphs/preferences/PreferencesFragment.kt +++ b/app/src/main/java/org/obd/graphs/preferences/PreferencesFragment.kt @@ -228,6 +228,12 @@ class PreferencesFragment : PreferenceFragmentCompat() { p3?.isVisible = true } + "mock" -> { + p1?.isVisible = false + p2?.isVisible = false + p3?.isVisible = false + } + else -> { } } @@ -254,6 +260,12 @@ class PreferencesFragment : PreferenceFragmentCompat() { p3?.isVisible = true } + "mock" -> { + p1?.isVisible = false + p2?.isVisible = false + p3?.isVisible = false + } + else -> { p1?.isVisible = false p2?.isVisible = true diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5baf925a..86a4766b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -101,7 +101,8 @@ OBD2 Adapter Connection This section contains settings related to your adapter type. Here, you can configure the Wi-Fi or Bluetooth parameters of your adapter. - Type of connection + Select OBD adapter connection type + OBD adapter connection type Connection timeout Available bluetooth devices @@ -431,9 +432,10 @@ Theme - wifi bluetooth + wifi usb + mock Settings diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index 0b0d9daa..f0db77fb 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -133,7 +133,7 @@ - - runAsync(wait: Boolean = true, handler: () -> T) : T { - val asyncJob: AsyncTask = object : AsyncTask() { - @Deprecated("Deprecated in Java", ReplaceWith("handler()")) - override fun doInBackground(vararg params: Void?): T { - return handler() - } - } - return if (wait) { - asyncJob.execute().get() - } else { - asyncJob.execute() - null as T - } -} + fun runAsync(wait: Boolean = false, handler: () -> T): T? { + return if (wait) { + runBlocking(Dispatchers.Default) { + handler() + } + } else { + backgroundScope.launch { + handler() + } + null + } + } diff --git a/datalogger/src/main/java/org/obd/graphs/bl/datalogger/AdjustmentsStrategy.kt b/datalogger/src/main/java/org/obd/graphs/bl/datalogger/AdjustmentsStrategy.kt index 93d76bb7..0711ee6e 100644 --- a/datalogger/src/main/java/org/obd/graphs/bl/datalogger/AdjustmentsStrategy.kt +++ b/datalogger/src/main/java/org/obd/graphs/bl/datalogger/AdjustmentsStrategy.kt @@ -28,7 +28,6 @@ import org.obd.metrics.api.model.PidDefinitionCustomization import org.obd.metrics.api.model.ProducerPolicy import org.obd.metrics.api.model.STNxxExtensions import org.obd.metrics.api.model.SniffingPolicy -import org.obd.metrics.codec.GeneratorPolicy import java.io.File internal class AdjustmentsStrategy { @@ -86,12 +85,6 @@ internal class AdjustmentsStrategy { .pidPriority(4, 4) // atm pressure, ambient temp .conditionalSleepEnabled(false) .build(), - ).generatorPolicy( - GeneratorPolicy - .builder() - .enabled(preferences.generatorEnabled) - .increment(0.5) - .build(), ).adaptiveTimeoutPolicy( AdaptiveTimeoutPolicy .builder() @@ -103,13 +96,17 @@ internal class AdjustmentsStrategy { ) if (dataLoggerSettings.instance().adapter.stnExtensionsEnabled) { - val highPriorityOverridePolicy = PidDefinitionCustomization.builder().priority(0).build() + val highPriorityOverridePolicy = + PidDefinitionCustomization.builder().priority(0).build() builder = builder .override(Pid.ATM_PRESSURE_PID_ID.id, highPriorityOverridePolicy) .override(Pid.AMBIENT_TEMP_PID_ID.id, highPriorityOverridePolicy) .override(Pid.DYNAMIC_SELECTOR_PID_ID.id, highPriorityOverridePolicy) - .override(Pid.ENGINE_TORQUE_PID_ID.id, PidDefinitionCustomization.builder().priority(4).build()) + .override( + Pid.ENGINE_TORQUE_PID_ID.id, + PidDefinitionCustomization.builder().priority(4).build(), + ) } return builder.build() @@ -120,9 +117,15 @@ internal class AdjustmentsStrategy { .builder() .sniffing(SniffingPolicy.builder().enabled(false).build()) .debugEnabled(preferences.debugLogging) - .override(Pid.DISTANCE_PID_ID.id, PidDefinitionCustomization.builder().lastInTheQuery(true).build()) - .formulaExternalParams(FormulaExternalParams.builder().param("unit_tank_size", preferences.fuelTankSize).build()) - .errorsPolicy( + .override( + Pid.DISTANCE_PID_ID.id, + PidDefinitionCustomization.builder().lastInTheQuery(true).build(), + ).formulaExternalParams( + FormulaExternalParams + .builder() + .param("unit_tank_size", preferences.fuelTankSize) + .build(), + ).errorsPolicy( ErrorsPolicy .builder() .numberOfRetries(preferences.adapter.maxReconnectNum) @@ -142,7 +145,7 @@ internal class AdjustmentsStrategy { STNxxExtensions .builder() .promoteSlowGroupsEnabled(false) - .stripWhitespaces(true) + .stripWhitespaces(dataLoggerSettings.instance().adapter.connectionType != "mock") .promoteAllGroupsEnabled(preferences.adapter.stnIgnorePIDsPriorities) .enabled(preferences.adapter.stnExtensionsEnabled) .build(), @@ -153,7 +156,12 @@ internal class AdjustmentsStrategy { .cachePolicy( CachePolicy .builder() - .resultCacheFilePath(File(getContext()?.cacheDir, "formula_cache.json").absolutePath) + .resultCacheFilePath( + File( + getContext()?.cacheDir, + "formula_cache.json", + ).absolutePath, + ).storeResultCacheOnDisk(false) .resultCacheEnabled(preferences.adapter.resultsCacheEnabled) .build(), ).producerPolicy( @@ -162,12 +170,6 @@ internal class AdjustmentsStrategy { .conditionalSleepEnabled(preferences.adapter.adaptiveConnectionEnabled) .conditionalSleepSliceSize(10) .build(), - ).generatorPolicy( - GeneratorPolicy - .builder() - .enabled(preferences.generatorEnabled) - .increment(0.5) - .build(), ).adaptiveTimeoutPolicy( AdaptiveTimeoutPolicy .builder() diff --git a/datalogger/src/main/java/org/obd/graphs/bl/datalogger/DataLoggerSettings.kt b/datalogger/src/main/java/org/obd/graphs/bl/datalogger/DataLoggerSettings.kt index 4ccfa1a9..03c70162 100644 --- a/datalogger/src/main/java/org/obd/graphs/bl/datalogger/DataLoggerSettings.kt +++ b/datalogger/src/main/java/org/obd/graphs/bl/datalogger/DataLoggerSettings.kt @@ -117,8 +117,6 @@ data class DataLoggerSettings( var dragRacingCommandFrequency: Long = 10, @XmlPreference("pref.mode", "Generic mode", String::class) var mode: String = "Generic mode", - @XmlPreference("pref.debug.generator.enabled", "false", Boolean::class) - var generatorEnabled: Boolean = false, @XmlPreference("pref.pids.registry.list", "", Set::class) var resources: Set = modules.getDefaultModules().keys, @XmlPreference("pref.debug.trip.save.connector_response", "false", Boolean::class) 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 f820f01e..2b3d03cf 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 @@ -21,6 +21,9 @@ import android.content.Context import android.content.Intent import android.util.Log import androidx.lifecycle.LifecycleOwner +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.SupervisorJob import org.obd.graphs.* import org.obd.graphs.bl.datalogger.connectors.ConnectionManager import org.obd.graphs.bl.query.Query @@ -44,7 +47,7 @@ import org.obd.metrics.pid.Urls import org.obd.metrics.pid.ValueType import java.util.* -private const val JS_ENGINE_NAME = "rhino" +const val JS_ENGINE_NAME = "rhino" /** * That's the wrapper interface on Workflow API. @@ -75,9 +78,12 @@ internal class WorkflowOrchestrator internal constructor() { sendBroadcastEvent(DATA_LOGGER_CONNECTING_EVENT) } - override fun onRoutineCompleted(routineCommand: RoutineCommand, status: RoutineExecutionStatus) { - Log.e(LOG_TAG, "Routine: ${routineCommand.pid.description} execution status: $status") - val event = when (status){ + override fun onRoutineCompleted( + routineCommand: RoutineCommand, + status: RoutineExecutionStatus + ) { + Log.i(LOG_TAG, "Routine: ${routineCommand.pid.description} execution status: $status") + val event = when (status) { RoutineExecutionStatus.ERROR -> ROUTINE_EXECUTION_FAILED_EVENT RoutineExecutionStatus.NO_DATA -> ROUTINE_EXECUTION_NO_DATA_RECEIVED_EVENT RoutineExecutionStatus.SUCCESS -> ROUTINE_EXECUTED_SUCCESSFULLY_EVENT @@ -138,10 +144,10 @@ internal class WorkflowOrchestrator internal constructor() { private val adjustmentsStrategy = AdjustmentsStrategy() fun observe(metricsProcessor: MetricsProcessor) { - if (metricsProcessorsRegistry.contains(metricsProcessor)){ - Log.i(LOG_TAG,"Metrics processor is already registered: $metricsProcessor") - }else { - Log.i(LOG_TAG,"Registering: $metricsProcessor metrics processor") + if (metricsProcessorsRegistry.contains(metricsProcessor)) { + Log.i(LOG_TAG, "Metrics processor is already registered: $metricsProcessor") + } else { + Log.i(LOG_TAG, "Registering: $metricsProcessor metrics processor") metricsProcessorsRegistry.add(metricsProcessor) metricsObserver.observe(metricsProcessor) } @@ -160,9 +166,11 @@ internal class WorkflowOrchestrator internal constructor() { fun diagnostics(): Diagnostics = workflow.diagnostics - fun findHistogramFor(metric: ObdMetric): Histogram = workflow.diagnostics.histogram().findBy(metric.command.pid) + fun findHistogramFor(metric: ObdMetric): Histogram = + workflow.diagnostics.histogram().findBy(metric.command.pid) - fun findRateFor(metric: ObdMetric): Optional = workflow.diagnostics.rate().findBy(RateType.MEAN, metric.command.pid) + fun findRateFor(metric: ObdMetric): Optional = + workflow.diagnostics.rate().findBy(RateType.MEAN, metric.command.pid) fun pidDefinitionRegistry(): PidDefinitionRegistry = workflow.pidRegistry @@ -179,34 +187,54 @@ internal class WorkflowOrchestrator internal constructor() { Log.e(LOG_TAG, "Failed to stop the workflow", e) } } - fun start(query: Query) { - currentQuery = query + runAsync (wait=false) { + android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_BACKGROUND) + currentQuery = query - val dataLoggerQuery = org.obd.metrics.api.model.Query.builder().pids(query.getIDs()).build() - Log.i(LOG_TAG, "Stating collecting process. Strategy: ${query.getStrategy()}. Selected PIDs: ${dataLoggerQuery.pids}") - ConnectionManager.obtain()?.run { - val status = workflow.start( - this, dataLoggerQuery, init(), - adjustmentsStrategy.findAdjustmentFor(query.getStrategy()) + val dataLoggerQuery = + org.obd.metrics.api.model.Query.builder().pids(query.getIDs()).build() + Log.i( + LOG_TAG, + "Stating collecting process. Strategy: ${query.getStrategy()}. Selected PIDs: ${dataLoggerQuery.pids}" ) - Log.i(LOG_TAG, "Collecting process started. Strategy: ${query.getStrategy()}. Status=$status") + val adjustments = adjustmentsStrategy.findAdjustmentFor(query.getStrategy()) + val init = init() + + ConnectionManager.obtain(pidDefinitionRegistry(), dataLoggerQuery, adjustments, init) + ?.run { + val status = workflow.start(this, dataLoggerQuery, init, adjustments) + Log.i( + LOG_TAG, + "Collecting process started. Strategy: ${query.getStrategy()}. Status=$status" + ) + } } } fun executeRoutine(query: Query) { currentQuery = query + val init = init() + val dataLoggerQuery = org.obd.metrics.api.model.Query.builder().pids(query.getIDs()).build() + val adjustments = adjustmentsStrategy.findAdjustmentFor(query.getStrategy()) + ConnectionManager.obtain(pidDefinitionRegistry(), dataLoggerQuery, adjustments, init)?.run { + Log.i( + LOG_TAG, + "Executing routine. Strategy: ${query.getStrategy()}. Selected PIDs: ${dataLoggerQuery.pids}" + ) - ConnectionManager.obtain()?.run { - val dataLoggerQuery = org.obd.metrics.api.model.Query.builder().pids(query.getIDs()).build() - Log.i(LOG_TAG, "Executing routine. Strategy: ${query.getStrategy()}. Selected PIDs: ${dataLoggerQuery.pids}") - - val status = workflow.executeRoutine(dataLoggerQuery.pids.first(), init()) - Log.i(LOG_TAG, "Routines has been completed. Strategy: ${query.getStrategy()}. Status=$status") + val status = workflow.executeRoutine(dataLoggerQuery.pids.first(), init) + Log.i( + LOG_TAG, + "Routines has been completed. Strategy: ${query.getStrategy()}. Status=$status" + ) when (status) { WorkflowExecutionStatus.REJECTED -> sendBroadcastEvent(ROUTINE_REJECTED_EVENT) - WorkflowExecutionStatus.NOT_RUNNING -> sendBroadcastEvent(ROUTINE_WORKFLOW_NOT_RUNNING_EVENT) + WorkflowExecutionStatus.NOT_RUNNING -> sendBroadcastEvent( + ROUTINE_WORKFLOW_NOT_RUNNING_EVENT + ) + else -> sendBroadcastEvent(ROUTINE_UNKNOWN_STATUS_EVENT) } } @@ -215,41 +243,50 @@ internal class WorkflowOrchestrator internal constructor() { private lateinit var currentQuery: Query - fun getCurrentQuery (): Query? = if (::currentQuery.isInitialized) currentQuery else null + fun getCurrentQuery(): Query? = if (::currentQuery.isInitialized) currentQuery else null fun updateQuery(query: Query) { if (isSameQuery(query)) { Log.w(LOG_TAG, "Received same query=${query.getIDs()}. Do not update.") } else { - val dataLoggerQuery = org.obd.metrics.api.model.Query.builder().pids(query.getIDs()).build() + val dataLoggerQuery = + org.obd.metrics.api.model.Query.builder().pids(query.getIDs()).build() val adjustments = adjustmentsStrategy.findAdjustmentFor(query.getStrategy()) + val status = workflow.updateQuery( dataLoggerQuery, init(), adjustments ) - Log.i(LOG_TAG, "Query update finished, strategy: ${query.getStrategy()}. Status=$status") + Log.i( + LOG_TAG, + "Query update finished, strategy: ${query.getStrategy()}. Status=$status" + ) } currentQuery = query } - fun isSameQuery(query: Query) = ::currentQuery.isInitialized && query.getIDs() == currentQuery.getIDs() + fun isSameQuery(query: Query) = + ::currentQuery.isInitialized && query.getIDs() == currentQuery.getIDs() fun isDTCEnabled(): Boolean = workflow.pidRegistry.findBy(PIDsGroup.DTC_READ).isNotEmpty() - private fun init(preferences: DataLoggerSettings = dataLoggerSettings.instance()) = Init.builder() - .delayAfterInit(preferences.adapter.initDelay) - .delayAfterReset(preferences.adapter.delayAfterReset) - .headers(diagnosticRequestIDMapper.getMapping().map { entry -> - Init.Header.builder().mode(entry.key).header(entry.value).build() - }.toMutableList()) - .protocol(Init.Protocol.valueOf(preferences.adapter.initProtocol)) - .sequence(DefaultCommandGroup.INIT).build() + private fun init(preferences: DataLoggerSettings = dataLoggerSettings.instance()) = + Init.builder() + .delayAfterInit(preferences.adapter.initDelay) + .delayAfterReset(preferences.adapter.delayAfterReset) + .headers(diagnosticRequestIDMapper.getMapping().map { entry -> + Init.Header.builder().mode(entry.key).header(entry.value).build() + }.toMutableList()) + .protocol(Init.Protocol.valueOf(preferences.adapter.initProtocol)) + .sequence(DefaultCommandGroup.INIT).build() private fun workflow() = Workflow.instance() - .formulaEvaluatorConfig(FormulaEvaluatorConfig.builder().scriptEngine(JS_ENGINE_NAME).build()) + .formulaEvaluatorConfig( + FormulaEvaluatorConfig.builder().scriptEngine(JS_ENGINE_NAME).build() + ) .pids( pids() ) @@ -271,19 +308,33 @@ internal class WorkflowOrchestrator internal constructor() { } } - registerGPSPids(workflow.pidRegistry) + registerGPSPids(workflow.pidRegistry) } private fun registerGPSPids(pidRegistry: PidDefinitionRegistry) { - Log.d(LOG_TAG,"Registering GPS PIDs") - pidRegistry.register(PidDefinition(Pid.GPS_LOCATION_PID_ID.id, 2, "", "22", "Alt", "m", "GPS", -180, 10000, ValueType.DOUBLE)) + Log.d(LOG_TAG, "Registering GPS PIDs") + pidRegistry.register( + PidDefinition( + Pid.GPS_LOCATION_PID_ID.id, + 2, + "", + "22", + "Alt", + "m", + "GPS", + -180, + 10000, + ValueType.DOUBLE + ) + ) } - private fun pids(): Pids? = Pids.builder().resources(dataLoggerSettings.instance().resources.map { - if (modules.isExternalStorageModule(it)) { - modules.externalModuleToURL(it) - } else { - Urls.resourceToUrl(it) - } - }.toMutableList()).build() + private fun pids(): Pids? = + Pids.builder().resources(dataLoggerSettings.instance().resources.map { + if (modules.isExternalStorageModule(it)) { + modules.externalModuleToURL(it) + } else { + Urls.resourceToUrl(it) + } + }.toMutableList()).build() } diff --git a/datalogger/src/main/java/org/obd/graphs/bl/datalogger/connectors/ConnectionManager.kt b/datalogger/src/main/java/org/obd/graphs/bl/datalogger/connectors/ConnectionManager.kt index 4e41b400..3106bb57 100644 --- a/datalogger/src/main/java/org/obd/graphs/bl/datalogger/connectors/ConnectionManager.kt +++ b/datalogger/src/main/java/org/obd/graphs/bl/datalogger/connectors/ConnectionManager.kt @@ -23,16 +23,24 @@ import org.obd.graphs.bl.datalogger.DATA_LOGGER_ERROR_CONNECT_EVENT import org.obd.graphs.bl.datalogger.DATA_LOGGER_WIFI_INCORRECT import org.obd.graphs.bl.datalogger.DATA_LOGGER_WIFI_NOT_CONNECTED import org.obd.graphs.bl.datalogger.DataLoggerSettings +import org.obd.graphs.bl.datalogger.JS_ENGINE_NAME import org.obd.graphs.bl.datalogger.LOG_TAG import org.obd.graphs.bl.datalogger.dataLoggerSettings import org.obd.graphs.getContext import org.obd.graphs.sendBroadcastEvent +import org.obd.metrics.api.model.Adjustments +import org.obd.metrics.api.model.Init +import org.obd.metrics.api.model.Query +import org.obd.metrics.pid.PidDefinitionRegistry import org.obd.metrics.transport.AdapterConnection +import org.obd.metrics.transport.mock.SmartMockConnectionFactory +import org.obd.metrics.transport.mock.strategy.Strategy -internal object ConnectionManager { + internal object ConnectionManager { - fun obtain(): AdapterConnection? = + fun obtain(registry: PidDefinitionRegistry, query: Query, adjustments: Adjustments, init: Init): AdapterConnection? = when (dataLoggerSettings.instance().adapter.connectionType) { + "mock" -> mockConnection(registry,query,adjustments,init) "wifi" -> wifiConnection() "bluetooth" -> bluetoothConnection() "usb" -> getContext()?.let { UsbConnection.of(context = it) } @@ -40,7 +48,17 @@ internal object ConnectionManager { null } } - + private fun mockConnection(registry: PidDefinitionRegistry, query: Query, adjustments: Adjustments, init: Init): AdapterConnection? = + SmartMockConnectionFactory + .smartBuilder() + .init(init) + .registry(registry) + .query(query) + .jsEngineName(JS_ENGINE_NAME) + .optional(adjustments) + .strategy(Strategy.SmartSawtooth) + .responseCount(500) + .build(); private fun bluetoothConnection(): AdapterConnection? = try { val deviceAddress = dataLoggerSettings.instance().adapter.deviceAddress diff --git a/gradle.properties b/gradle.properties index 4308c5f4..0f73a498 100644 --- a/gradle.properties +++ b/gradle.properties @@ -19,4 +19,4 @@ android.useAndroidX=true android.enableJetifier=true # Kotlin code style for this project: "official" or "obsolete": kotlin.code.style=official -obdMetricVersion=11.12.0-SNAPSHOT +obdMetricVersion=11.14.0-SNAPSHOT