feat(connector): add database logging for simulator test

This commit is contained in:
Anthony Berg 2024-05-02 18:01:35 +01:00
parent c615149624
commit 9482ba3d97
5 changed files with 195 additions and 54 deletions

View File

@ -5,6 +5,7 @@ class InterfaceState : KoinComponent {
var simConnection = mutableStateOf(false)
var projectId: Int? = null
var procedureId: Int? = null
var testId: Int? = null
val projectSelected: Boolean
get() = projectId != null

View File

@ -4,11 +4,13 @@ import InterfaceState
import io.anthonyberg.connector.shared.ActionTransaction
import io.anthonyberg.connector.shared.ProcedureTransaction
import io.anthonyberg.connector.shared.ProjectTransaction
import io.anthonyberg.connector.shared.TestTransaction
import io.anthonyberg.connector.shared.database.DriverFactory
import org.koin.dsl.module
import tab.procedure.ActionsScreenModel
import tab.procedure.ProcedureScreenModel
import tab.project.ProjectsScreenModel
import tab.test.TestScreenModel
fun commonModule() = module {
single<DriverFactory> {
@ -26,6 +28,10 @@ fun commonModule() = module {
single<ActionTransaction> {
ActionTransaction(driverFactory = get<DriverFactory>())
}
single<TestTransaction> {
TestTransaction(driverFactory = get<DriverFactory>())
}
}
fun viewModelModule() = module {
@ -44,4 +50,8 @@ fun viewModelModule() = module {
single<ActionsScreenModel> {
ActionsScreenModel(db = get(), interfaceState = get())
}
single<TestScreenModel> {
TestScreenModel(db = get(), interfaceState = get())
}
}

View File

@ -16,36 +16,60 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import cafe.adriel.voyager.core.screen.Screen
import cafe.adriel.voyager.koin.getScreenModel
import io.anthonyberg.connector.shared.entity.Action
import io.anthonyberg.connector.shared.xpc.XPC
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
class TestRun (
private val actions: List<Action>
) : Screen {
private val xpc = XPC()
private val xpcConnected = xpc.connected()
private var tested = mutableStateListOf<Boolean>()
private val initState = getInitState()
@Composable
override fun Content() {
val lazyState = rememberLazyListState(0)
var running by remember { mutableStateOf(false) }
val scope = rememberCoroutineScope()
var step by remember { mutableIntStateOf(0) }
val screenModel = getScreenModel<TestScreenModel>()
val state by screenModel.state.collectAsState()
if (!running and (tested.size == 0)) {
running = true
screenModel.init()
}
when (val s = state) {
is TestState.Init -> println("Loading Simulator Tests")
is TestState.NoSimulator -> {
running = false
Text("Could not connect to the simulator!")
return
}
is TestState.Ready -> {
println("Loaded Simulator Tests")
screenModel.runAction(actions[step])
}
is TestState.Running -> println("Running Action: ${s.step}")
is TestState.Complete -> {
tested.add(s.pass)
step += 1
if (step == actions.size) {
screenModel.end()
} else {
screenModel.runAction(actions[step])
}
}
is TestState.Idle -> running = false
is TestState.Error -> return Text("An error occurred!")
}
Column(
modifier = Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
if (!xpcConnected) {
Text("Could not connect to the simulator!")
return
}
// Progress Indicator
if (running) {
LinearProgressIndicator(
@ -59,7 +83,7 @@ class TestRun (
) {
LazyColumn {
items(actions) { action ->
ActionItem(action, initState[action.step])
ActionItem(action)
}
}
@ -73,20 +97,11 @@ class TestRun (
)
}
}
if (!running and (tested.size == 0)) {
scope.launch {
running = true
runSteps()
running = false
}
}
}
@Composable
private fun ActionItem(action: Action, initState: FloatArray) {
private fun ActionItem(action: Action) {
ListItem(
overlineContent = { Text("Initial State: ${initState[0]}") },
headlineContent = { Text(action.type) },
supportingContent = { Text("Goal: ${action.goal}") },
leadingContent = {
@ -111,31 +126,4 @@ class TestRun (
HorizontalDivider()
}
private suspend fun runSteps() {
for (action in actions) {
delay(1000L)
// TODO add try catch
val result = xpc.runChecklist(action.type, action.goal.toInt())
// TODO add more detailed results
tested.add(result)
}
}
private fun getInitState(): Array<FloatArray> {
if (!xpc.connected()) {
return Array(actions.size) { FloatArray(0) }
}
var initDrefs = arrayOf<String>()
for (action in actions) {
initDrefs += action.type
}
val result = xpc.getStates(initDrefs)
return result
}
}

View File

@ -0,0 +1,130 @@
package tab.test
import InterfaceState
import cafe.adriel.voyager.core.model.StateScreenModel
import cafe.adriel.voyager.core.model.screenModelScope
import io.anthonyberg.connector.shared.TestTransaction
import io.anthonyberg.connector.shared.entity.Action
import io.anthonyberg.connector.shared.xpc.XPC
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
class TestScreenModel (
private val db: TestTransaction,
private val interfaceState: InterfaceState
) : StateScreenModel<TestState>(TestState.Init) {
private val xpc = XPC()
/**
* Sets up necessary connections
*/
fun init() {
screenModelScope.launch {
mutableState.value = TestState.Init
val procedureId = interfaceState.procedureId
// Checks if procedureId is set
if (procedureId == null) {
mutableState.value = TestState.Error
return@launch
}
// Checks if the simulator is running
val simConnection = xpc.connected()
interfaceState.simConnection.value = simConnection
if (!simConnection) {
mutableState.value = TestState.NoSimulator
return@launch
}
// Starts the test in the database
interfaceState.testId = db.startTest(procedureId)
mutableState.value = TestState.Ready
}
}
/**
* Run an action in the simulator
*
* @param action Action to be run in the simulator
*/
fun runAction(action: Action) {
screenModelScope.launch {
mutableState.value = TestState.Running(step = action.step)
val testId = interfaceState.testId
if (testId == null) {
mutableState.value = TestState.Error
return@launch
}
// Checks if the simulator is still running
val simConnection = xpc.connected()
interfaceState.simConnection.value = simConnection
if (!simConnection) {
mutableState.value = TestState.Error
return@launch
}
// Prerequisite before testing the action
val initDref = xpc.getState(action.type)[0]
val actionTestId = db.startAction(
testId = testId,
actionId = action.id,
initState = initDref.toString()
)
delay(1500L)
// Running the action in the simulator
// TODO deal with action.goal being a String in the database
val goal = action.goal.toInt()
val result = xpc.runChecklist(action.type, goal)[0]
// Saving result to the database
db.finishAction(
id = actionTestId,
endState = result.toString()
)
mutableState.value = TestState.Complete(
step = action.step,
pass = goal.toFloat() == result
)
}
}
/**
* To be run after running all the actions in the simulator
*/
fun end() {
screenModelScope.launch {
val testId = interfaceState.testId
if (testId == null) {
mutableState.value = TestState.Error
return@launch
}
// Completes the test on the database
db.endTest(testId)
mutableState.value = TestState.Idle
}
}
}
sealed class TestState {
data object Init : TestState()
data object NoSimulator : TestState()
data object Error : TestState()
data object Ready : TestState()
data class Running(val step: Int) : TestState()
data class Complete(val step: Int, val pass: Boolean) : TestState()
data object Idle : TestState()
}

View File

@ -45,20 +45,32 @@ class XPC {
return result
}
/**
* Gets the state of a specific Dataref
*
* @param dref Dataref name to get the value for
* @return Value of the Dataref
*/
fun getState(dref: String): FloatArray {
val result = xpc.getDREF(dref)
return result
}
/**
* Sets a dataref in X-Plane to the set goal
*
* @param dref Dataref name in X-Plane to change the value for
* @param goal The value that should be set for the dataref in X-Plane
*
* @return `true` if successfully set, `false` otherwise
* @return The state of the Dataref in the simulator
*/
@Throws(SocketException::class, IOException::class)
fun runChecklist(dref: String, goal: Int) : Boolean {
fun runChecklist(dref: String, goal: Int) : FloatArray {
xpc.sendDREF(dref, goal.toFloat())
val result = xpc.getDREF(dref)
return goal.toFloat() == result[0]
return result
}
}