mirror of
https://github.com/smyalygames/checklist-tester.git
synced 2025-05-18 14:34:12 +02:00
feat(connector): add database logging for simulator test
This commit is contained in:
parent
c615149624
commit
9482ba3d97
@ -5,6 +5,7 @@ class InterfaceState : KoinComponent {
|
|||||||
var simConnection = mutableStateOf(false)
|
var simConnection = mutableStateOf(false)
|
||||||
var projectId: Int? = null
|
var projectId: Int? = null
|
||||||
var procedureId: Int? = null
|
var procedureId: Int? = null
|
||||||
|
var testId: Int? = null
|
||||||
|
|
||||||
val projectSelected: Boolean
|
val projectSelected: Boolean
|
||||||
get() = projectId != null
|
get() = projectId != null
|
||||||
|
@ -4,11 +4,13 @@ import InterfaceState
|
|||||||
import io.anthonyberg.connector.shared.ActionTransaction
|
import io.anthonyberg.connector.shared.ActionTransaction
|
||||||
import io.anthonyberg.connector.shared.ProcedureTransaction
|
import io.anthonyberg.connector.shared.ProcedureTransaction
|
||||||
import io.anthonyberg.connector.shared.ProjectTransaction
|
import io.anthonyberg.connector.shared.ProjectTransaction
|
||||||
|
import io.anthonyberg.connector.shared.TestTransaction
|
||||||
import io.anthonyberg.connector.shared.database.DriverFactory
|
import io.anthonyberg.connector.shared.database.DriverFactory
|
||||||
import org.koin.dsl.module
|
import org.koin.dsl.module
|
||||||
import tab.procedure.ActionsScreenModel
|
import tab.procedure.ActionsScreenModel
|
||||||
import tab.procedure.ProcedureScreenModel
|
import tab.procedure.ProcedureScreenModel
|
||||||
import tab.project.ProjectsScreenModel
|
import tab.project.ProjectsScreenModel
|
||||||
|
import tab.test.TestScreenModel
|
||||||
|
|
||||||
fun commonModule() = module {
|
fun commonModule() = module {
|
||||||
single<DriverFactory> {
|
single<DriverFactory> {
|
||||||
@ -26,6 +28,10 @@ fun commonModule() = module {
|
|||||||
single<ActionTransaction> {
|
single<ActionTransaction> {
|
||||||
ActionTransaction(driverFactory = get<DriverFactory>())
|
ActionTransaction(driverFactory = get<DriverFactory>())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
single<TestTransaction> {
|
||||||
|
TestTransaction(driverFactory = get<DriverFactory>())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun viewModelModule() = module {
|
fun viewModelModule() = module {
|
||||||
@ -44,4 +50,8 @@ fun viewModelModule() = module {
|
|||||||
single<ActionsScreenModel> {
|
single<ActionsScreenModel> {
|
||||||
ActionsScreenModel(db = get(), interfaceState = get())
|
ActionsScreenModel(db = get(), interfaceState = get())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
single<TestScreenModel> {
|
||||||
|
TestScreenModel(db = get(), interfaceState = get())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,36 +16,60 @@ import androidx.compose.ui.Alignment
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import cafe.adriel.voyager.core.screen.Screen
|
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.entity.Action
|
||||||
import io.anthonyberg.connector.shared.xpc.XPC
|
|
||||||
import kotlinx.coroutines.delay
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
|
|
||||||
class TestRun (
|
class TestRun (
|
||||||
private val actions: List<Action>
|
private val actions: List<Action>
|
||||||
) : Screen {
|
) : Screen {
|
||||||
|
|
||||||
private val xpc = XPC()
|
|
||||||
private val xpcConnected = xpc.connected()
|
|
||||||
|
|
||||||
private var tested = mutableStateListOf<Boolean>()
|
private var tested = mutableStateListOf<Boolean>()
|
||||||
private val initState = getInitState()
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
override fun Content() {
|
override fun Content() {
|
||||||
val lazyState = rememberLazyListState(0)
|
val lazyState = rememberLazyListState(0)
|
||||||
var running by remember { mutableStateOf(false) }
|
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(
|
Column(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
) {
|
) {
|
||||||
if (!xpcConnected) {
|
|
||||||
Text("Could not connect to the simulator!")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Progress Indicator
|
// Progress Indicator
|
||||||
if (running) {
|
if (running) {
|
||||||
LinearProgressIndicator(
|
LinearProgressIndicator(
|
||||||
@ -59,7 +83,7 @@ class TestRun (
|
|||||||
) {
|
) {
|
||||||
LazyColumn {
|
LazyColumn {
|
||||||
items(actions) { action ->
|
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
|
@Composable
|
||||||
private fun ActionItem(action: Action, initState: FloatArray) {
|
private fun ActionItem(action: Action) {
|
||||||
ListItem(
|
ListItem(
|
||||||
overlineContent = { Text("Initial State: ${initState[0]}") },
|
|
||||||
headlineContent = { Text(action.type) },
|
headlineContent = { Text(action.type) },
|
||||||
supportingContent = { Text("Goal: ${action.goal}") },
|
supportingContent = { Text("Goal: ${action.goal}") },
|
||||||
leadingContent = {
|
leadingContent = {
|
||||||
@ -111,31 +126,4 @@ class TestRun (
|
|||||||
|
|
||||||
HorizontalDivider()
|
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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -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()
|
||||||
|
}
|
@ -45,20 +45,32 @@ class XPC {
|
|||||||
return result
|
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
|
* Sets a dataref in X-Plane to the set goal
|
||||||
*
|
*
|
||||||
* @param dref Dataref name in X-Plane to change the value for
|
* @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
|
* @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)
|
@Throws(SocketException::class, IOException::class)
|
||||||
fun runChecklist(dref: String, goal: Int) : Boolean {
|
fun runChecklist(dref: String, goal: Int) : FloatArray {
|
||||||
xpc.sendDREF(dref, goal.toFloat())
|
xpc.sendDREF(dref, goal.toFloat())
|
||||||
|
|
||||||
val result = xpc.getDREF(dref)
|
val result = xpc.getDREF(dref)
|
||||||
|
|
||||||
return goal.toFloat() == result[0]
|
return result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user