Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import com.buzbuz.smartautoclicker.core.base.extensions.safeRecreate
import com.buzbuz.smartautoclicker.core.smart.debugging.data.mapping.toDomain
import com.buzbuz.smartautoclicker.core.smart.debugging.data.mapping.toCountersInitialValues
import com.buzbuz.smartautoclicker.core.smart.debugging.data.mapping.toProtobuf
import com.buzbuz.smartautoclicker.core.smart.debugging.domain.model.report.ConditionProfile
import com.buzbuz.smartautoclicker.core.smart.debugging.domain.model.report.DebugReportCounterInitialValue
import com.buzbuz.smartautoclicker.core.smart.debugging.domain.model.report.DebugReportEventOccurrence
import com.buzbuz.smartautoclicker.core.smart.debugging.domain.model.report.DebugReportOverview
Expand Down Expand Up @@ -201,6 +202,22 @@ internal class DebugReportLocalDataSource @Inject constructor(
}
}

/** Read aggregate condition timings from the last completed report, if the report contains them. */
suspend fun readConditionProfile(): List<ConditionProfile> =
filesMutex.withLock {
if (isWritingReport) return@withLock emptyList()

messagesFile.safeInputStream()?.use { inputStream ->
while (true) {
val protoMessage = inputStream.safeParseDebugReportMessage() ?: break
if (protoMessage.hasConditionProfileMessage()) {
return@withLock protoMessage.conditionProfileMessage.toDomain()
}
}
}
emptyList()
}

/** Delete the current report files, if any. */
suspend fun deleteReport() {
filesMutex.withLock {
Expand Down Expand Up @@ -249,4 +266,4 @@ internal class DebugReportLocalDataSource @Inject constructor(

private const val DEBUG_REPORT_MESSAGES_FILE_NAME = "DebugReportMessages.pb"
private const val DEBUG_REPORT_OVERVIEW_FILE_NAME = "DebugReportOverview.pb"
private const val LOG_TAG = "DebugReportFileAccess"
private const val LOG_TAG = "DebugReportFileAccess"
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/* Copyright (C) 2026 Kevin Buzeau */
package com.buzbuz.smartautoclicker.core.smart.debugging.data.mapping

import com.buzbuz.smartautoclicker.core.smart.debugging.ConditionProfileMessageKt.conditionProfileEntry
import com.buzbuz.smartautoclicker.core.smart.debugging.conditionProfileMessage
import com.buzbuz.smartautoclicker.core.smart.debugging.debugReportMessage
import com.buzbuz.smartautoclicker.core.smart.debugging.domain.model.report.ConditionProfile
import com.buzbuz.smartautoclicker.core.smart.debugging.DebugReportMessage as ProtoDebugReportMessage
import com.buzbuz.smartautoclicker.core.smart.debugging.ConditionProfileMessage as ProtoConditionProfileMessage

internal fun List<ConditionProfile>.toProtobuf(): ProtoDebugReportMessage =
debugReportMessage {
conditionProfileMessage = conditionProfileMessage {
entries.addAll(this@toProtobuf.map { profile ->
conditionProfileEntry {
conditionId = profile.conditionId
checkCount = profile.checkCount
fulfilledCount = profile.fulfilledCount
totalDurationNs = profile.totalDurationNs
minDurationNs = profile.minDurationNs
maxDurationNs = profile.maxDurationNs
}
})
}
}

internal fun ProtoConditionProfileMessage.toDomain(): List<ConditionProfile> =
entriesList.map { entry ->
ConditionProfile(
conditionId = entry.conditionId,
checkCount = entry.checkCount,
fulfilledCount = entry.fulfilledCount,
totalDurationNs = entry.totalDurationNs,
minDurationNs = entry.minDurationNs,
maxDurationNs = entry.maxDurationNs,
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ internal fun ProtoDebugReportMessage.toDomain(): DebugReportEventOccurrence? =
ProtoDebugReportMessage.MessageTypeCase.TRIGGEREVENTMESSAGE ->
triggerEventMessage.toDomain(relativeTimestampMs)
ProtoDebugReportMessage.MessageTypeCase.COUNTERSINITMESSAGE -> null
ProtoDebugReportMessage.MessageTypeCase.CONDITIONPROFILEMESSAGE -> null
ProtoDebugReportMessage.MessageTypeCase.MESSAGETYPE_NOT_SET -> {
Log.e(LOG_TAG, "Can't read DebugReportEventOccurrence from protobuf")
null
Expand All @@ -123,4 +124,4 @@ private fun ProtoTriggerEventMessage.toDomain(relativeTimestamp: Long): DebugRep
conditionsResults = resultsList.map { conditionResult -> conditionResult.toDomain() },
)

private const val LOG_TAG = "DebugReportEventOccurrenceMapping"
private const val LOG_TAG = "DebugReportEventOccurrenceMapping"
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package com.buzbuz.smartautoclicker.core.smart.debugging.data.mapping
import com.buzbuz.smartautoclicker.core.smart.debugging.debugReportOverview
import com.buzbuz.smartautoclicker.core.smart.debugging.domain.model.report.DebugReportOverview
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.Duration.Companion.nanoseconds

import com.buzbuz.smartautoclicker.core.smart.debugging.DebugReportOverview as ProtoDebugReportOverview

Expand All @@ -32,6 +33,8 @@ internal fun DebugReportOverview.toProtobuf(): ProtoDebugReportOverview =
imageEventFulfilledCount = this@toProtobuf.imageEventFulfilledCount
triggerEventFulfilledCount = this@toProtobuf.triggerEventFulfilledCount
countersName.addAll(this@toProtobuf.counterNames)
activeDetectionDurationNs = this@toProtobuf.activeDetectionDuration.inWholeNanoseconds
executionLimiterWaitDurationNs = this@toProtobuf.executionLimiterWaitDuration.inWholeNanoseconds
}

internal fun ProtoDebugReportOverview.toDomain(): DebugReportOverview =
Expand All @@ -43,4 +46,6 @@ internal fun ProtoDebugReportOverview.toDomain(): DebugReportOverview =
imageEventFulfilledCount = imageEventFulfilledCount,
triggerEventFulfilledCount = triggerEventFulfilledCount,
counterNames = countersNameList.toSet(),
)
activeDetectionDuration = activeDetectionDurationNs.nanoseconds,
executionLimiterWaitDuration = executionLimiterWaitDurationNs.nanoseconds,
)
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package com.buzbuz.smartautoclicker.core.smart.debugging.di

import com.buzbuz.smartautoclicker.core.processing.domain.SmartProcessingListener
import com.buzbuz.smartautoclicker.core.processing.domain.DebugReportTimingListener
import com.buzbuz.smartautoclicker.core.smart.debugging.domain.DebuggingRepository
import com.buzbuz.smartautoclicker.core.smart.debugging.domain.DebuggingRepositoryImpl
import com.buzbuz.smartautoclicker.core.smart.debugging.engine.DebugEngine
Expand All @@ -40,4 +41,9 @@ object SmartDebuggingModule {
@Singleton
internal fun providesDebuggingListener(debugEngine: DebugEngine): SmartProcessingListener =
debugEngine

@Provides
@Singleton
internal fun providesDebugReportTimingListener(debugEngine: DebugEngine): DebugReportTimingListener =
debugEngine
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import com.buzbuz.smartautoclicker.core.smart.debugging.domain.model.live.DebugL
import com.buzbuz.smartautoclicker.core.smart.debugging.domain.model.report.DebugReportCounterInitialValue
import com.buzbuz.smartautoclicker.core.smart.debugging.domain.model.report.DebugReportEventOccurrence
import com.buzbuz.smartautoclicker.core.smart.debugging.domain.model.report.DebugReportOverview
import com.buzbuz.smartautoclicker.core.smart.debugging.domain.model.report.ConditionProfile
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow

Expand Down Expand Up @@ -52,4 +53,7 @@ interface DebuggingRepository {

/** Read the last detection session events occurrences. List will be empty no rapport is available. */
fun getLastReportEventsOccurrences(): Flow<List<DebugReportEventOccurrence>?>
}

/** Read aggregate condition timings from the last detection session report. */
fun getLastReportConditionProfiles(): Flow<List<ConditionProfile>?>
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import com.buzbuz.smartautoclicker.core.smart.debugging.domain.model.live.DebugL
import com.buzbuz.smartautoclicker.core.smart.debugging.domain.model.report.DebugReportCounterInitialValue
import com.buzbuz.smartautoclicker.core.smart.debugging.domain.model.report.DebugReportEventOccurrence
import com.buzbuz.smartautoclicker.core.smart.debugging.domain.model.report.DebugReportOverview
import com.buzbuz.smartautoclicker.core.smart.debugging.domain.model.report.ConditionProfile
import com.buzbuz.smartautoclicker.core.smart.debugging.engine.DebugEngine

import kotlinx.coroutines.CoroutineDispatcher
Expand Down Expand Up @@ -95,4 +96,9 @@ internal class DebuggingRepositoryImpl @Inject constructor(
flow {
emit(debugReportDataSource.readMessages())
}.flowOn(ioDispatcher)
}

override fun getLastReportConditionProfiles(): Flow<List<ConditionProfile>?> =
flow {
emit(debugReportDataSource.readConditionProfile())
}.flowOn(ioDispatcher)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*
* Copyright (C) 2026 Kevin Buzeau
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*/
package com.buzbuz.smartautoclicker.core.smart.debugging.domain.model.report

/** Aggregate timing statistics for one condition during a detection session. */
data class ConditionProfile(
val conditionId: Long,
val checkCount: Long,
val fulfilledCount: Long,
val totalDurationNs: Long,
val minDurationNs: Long,
val maxDurationNs: Long,
)
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ import kotlin.time.Duration
* @param imageEventFulfilledCount The number of image events that have been triggered during the session.
* @param triggerEventFulfilledCount The number of image events that have been triggered during the session.
* @param counterNames The names of all counters available in the scenario that was ran to made this report.
* @param activeDetectionDuration Time spent actively processing scenario loops.
* @param executionLimiterWaitDuration Time spent suspended by the user-configured Execution Limiter.
*/
data class DebugReportOverview(
val scenarioId: Long,
Expand All @@ -36,4 +38,6 @@ data class DebugReportOverview(
val imageEventFulfilledCount: Int,
val triggerEventFulfilledCount: Int,
val counterNames: Set<String>,
)
val activeDetectionDuration: Duration = Duration.ZERO,
val executionLimiterWaitDuration: Duration = Duration.ZERO,
)
Loading
Loading