You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

92 lines
2.9 KiB

package net.aiterp.git.ykonsole2.infrastructure.drivers
import net.aiterp.git.ykonsole2.application.logging.log
import net.aiterp.git.ykonsole2.domain.models.*
import net.aiterp.git.ykonsole2.domain.runtime.*
import net.aiterp.git.ykonsole2.infrastructure.drivers.abstracts.ReactiveDriver
class ProgramEnforcer(
private val programRepo: ProgramRepository,
private val workoutRepo: WorkoutRepository,
) : ReactiveDriver() {
private var lastTransition: WorkoutState = WorkoutState()
private var lastValues: ValuesReceived? = null
private var lastStep: ProgramStep? = null
private var stepIndex: Int = 0
private var program: Program? = null
private suspend fun runTransition(input: CommandBus, newEvent: ValuesReceived? = null) {
val prog = program ?: return
val event = newEvent ?: lastValues ?: return
if (prog.steps.size > stepIndex + 1) {
stepIndex += 1
log.info("$stepIndex: Jumped!")
// Go to next step and send the change
lastTransition = lastTransition.copy(
time = event.values.find() ?: lastTransition.time,
calories = event.values.find(),
distance = event.values.find(),
level = event.values.find(),
)
lastStep = prog.steps[stepIndex]
lastStep?.values?.forEach { input.emit(SetValueCommand(it)) }
} else {
log.info("$stepIndex: Stopped!")
// The program is done, let's stop it
lastStep = null
program = null
input.emit(StopCommand)
}
}
override suspend fun onEvent(event: Event, input: FlowBus<Command>) {
if (event is Connected && event.initial) {
// Start program on connecting
val programId = workoutRepo.findActive()?.apply {
lastTransition = WorkoutState(workoutId = id)
}?.programId ?: return
program = programRepo.findById(programId)?.takeIf { it.steps.isNotEmpty() }
lastStep = program?.steps?.firstOrNull()
stepIndex = 0
}
if (event is Started) {
// Set up the first step
lastStep?.apply {
values.map { input.emit(SetValueCommand(it)) }
}
}
if (event is Skipped) {
runTransition(input)
}
if (event is ValuesReceived && program != null && lastStep != null) {
// If there's a program, look for next transition
val step = lastStep!!
val transition = when (step.duration) {
is Time -> (event.values.findInt<Time>()) >= lastTransition.time.toInt() + step.duration.toInt()
is Calories -> (event.values.findInt<Calories>()) >= (lastTransition.calories.toInt()) + step.duration.toInt()
is Distance -> (event.values.findInt<Distance>()) >= (lastTransition.distance.toInt()) + step.duration.toInt()
else -> false
}
if (transition) {
runTransition(input, event)
}
lastValues = event
}
if (event is Disconnected) {
// Let's clear everything
lastTransition = WorkoutState()
lastStep = null
program = null
}
}
}