mirror of
				https://github.com/smyalygames/checklist-tester.git
				synced 2025-11-04 04:49:49 +01: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