Skip to content

Commit

Permalink
Merge pull request #59 from boschglobal/feature-24
Browse files Browse the repository at this point in the history
feature(TestApp): Remove Field.METADATA from "casual" queries
  • Loading branch information
Chrylo authored Jan 30, 2024
2 parents dc303f5 + cee3141 commit 47004e0
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 80 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,12 @@ import org.eclipse.kuksa.DataBrokerConnection
import org.eclipse.kuksa.DisconnectListener
import org.eclipse.kuksa.PropertyListener
import org.eclipse.kuksa.VssSpecificationListener
import org.eclipse.kuksa.extension.metadata
import org.eclipse.kuksa.extension.entriesMetadata
import org.eclipse.kuksa.extension.valueType
import org.eclipse.kuksa.model.Property
import org.eclipse.kuksa.proto.v1.KuksaValV1
import org.eclipse.kuksa.proto.v1.KuksaValV1.GetResponse
import org.eclipse.kuksa.proto.v1.Types
import org.eclipse.kuksa.proto.v1.Types.DataEntry
import org.eclipse.kuksa.proto.v1.Types.Datapoint
import org.eclipse.kuksa.proto.v1.Types.Field
Expand All @@ -49,6 +50,7 @@ import org.eclipse.kuksa.testapp.databroker.model.ConnectionInfo
import org.eclipse.kuksa.testapp.databroker.view.DataBrokerView
import org.eclipse.kuksa.testapp.databroker.viewmodel.ConnectionViewModel
import org.eclipse.kuksa.testapp.databroker.viewmodel.ConnectionViewModel.ConnectionViewState
import org.eclipse.kuksa.testapp.databroker.viewmodel.OutputEntry
import org.eclipse.kuksa.testapp.databroker.viewmodel.OutputViewModel
import org.eclipse.kuksa.testapp.databroker.viewmodel.TopAppBarViewModel
import org.eclipse.kuksa.testapp.databroker.viewmodel.VSSPropertiesViewModel
Expand All @@ -73,40 +75,40 @@ class KuksaDataBrokerActivity : ComponentActivity() {

private val dataBrokerConnectionCallback = object : CoroutineCallback<DataBrokerConnection>() {
override fun onSuccess(result: DataBrokerConnection?) {
outputViewModel.appendOutput("Connection to DataBroker successful established")
outputViewModel.addOutputEntry("Connection to DataBroker successful established")
connectionViewModel.updateConnectionState(ConnectionViewState.CONNECTED)
}

override fun onError(error: Throwable) {
outputViewModel.appendOutput("Connection to DataBroker failed: ${error.message}")
outputViewModel.addOutputEntry("Connection to DataBroker failed: ${error.message}")
connectionViewModel.updateConnectionState(ConnectionViewState.DISCONNECTED)
}
}

private val onDisconnectListener = DisconnectListener {
connectionViewModel.updateConnectionState(ConnectionViewState.DISCONNECTED)
outputViewModel.clear()
outputViewModel.appendOutput("DataBroker disconnected")
outputViewModel.addOutputEntry("DataBroker disconnected")
}

private val propertyListener = object : PropertyListener {
override fun onPropertyChanged(vssPath: String, field: Field, updatedValue: DataEntry) {
Log.d(TAG, "onPropertyChanged path: vssPath = $vssPath, field = $field, changedValue = $updatedValue")
outputViewModel.appendOutput("Updated value: $updatedValue")
outputViewModel.addOutputEntry("Updated value: $updatedValue")
}

override fun onError(throwable: Throwable) {
outputViewModel.appendOutput("${throwable.message}")
outputViewModel.addOutputEntry("${throwable.message}")
}
}

private val specificationListener = object : VssSpecificationListener<VssSpecification> {
override fun onSpecificationChanged(vssSpecification: VssSpecification) {
outputViewModel.appendOutput("Updated specification: $vssSpecification")
outputViewModel.addOutputEntry("Updated specification: $vssSpecification")
}

override fun onError(throwable: Throwable) {
outputViewModel.appendOutput("Updated specification: ${throwable.message}")
outputViewModel.addOutputEntry("Updated specification: ${throwable.message}")
}
}

Expand Down Expand Up @@ -150,7 +152,7 @@ class KuksaDataBrokerActivity : ComponentActivity() {
kotlinDataBrokerEngine
}
val enabledState = if (isCompatibilityModeEnabled) "enabled" else "disabled"
outputViewModel.appendOutput("Java Compatibility Mode $enabledState")
outputViewModel.addOutputEntry("Java Compatibility Mode $enabledState")
}

connectionViewModel.onConnect = { connectionInfo ->
Expand All @@ -162,6 +164,7 @@ class KuksaDataBrokerActivity : ComponentActivity() {
}

vssPropertiesViewModel.onGetProperty = { property: Property ->
fetchPropertyFieldType(property)
fetchProperty(property)
}

Expand Down Expand Up @@ -199,7 +202,7 @@ class KuksaDataBrokerActivity : ComponentActivity() {
private fun connect(connectionInfo: ConnectionInfo) {
Log.d(TAG, "Connecting to DataBroker: $connectionInfo")

outputViewModel.appendOutput("Connecting to data broker - Please wait")
outputViewModel.addOutputEntry("Connecting to data broker - Please wait")
connectionViewModel.updateConnectionState(ConnectionViewState.CONNECTING)

dataBrokerEngine.registerDisconnectListener(onDisconnectListener)
Expand All @@ -212,32 +215,60 @@ class KuksaDataBrokerActivity : ComponentActivity() {
dataBrokerEngine.unregisterDisconnectListener(onDisconnectListener)
}

private fun fetchPropertyFieldType(property: Property) {
val propertyWithMetaData = property.copy(fields = listOf(Field.FIELD_METADATA))

dataBrokerEngine.fetch(
propertyWithMetaData,
object : CoroutineCallback<GetResponse>() {
override fun onSuccess(result: GetResponse?) {
val entriesMetadata = result?.entriesMetadata ?: emptyList()
val automaticValueType = if (entriesMetadata.size == 1) {
entriesMetadata.first().valueType
} else {
Types.Datapoint.ValueCase.VALUE_NOT_SET
}

Log.d(TAG, "Fetched automatic value type from meta data: $automaticValueType")

val vssProperties = vssPropertiesViewModel.vssProperties
.copy(valueType = automaticValueType)
vssPropertiesViewModel.updateVssProperties(vssProperties)
}

override fun onError(error: Throwable) {
Log.w(TAG, "Could not resolve type of value for $property")
}
},
)
}

private fun fetchProperty(property: Property) {
Log.d(TAG, "Fetch property: $property")

dataBrokerEngine.fetch(
property,
object : CoroutineCallback<GetResponse>() {
override fun onSuccess(result: GetResponse?) {
val automaticValueType = result?.metadata?.valueType ?: Datapoint.ValueCase.VALUE_NOT_SET
Log.d(TAG, "Fetched automatic value type from meta data: $automaticValueType")

val errorsList = result?.errorsList
errorsList?.forEach {
outputViewModel.appendOutput(it.toString())
outputViewModel.addOutputEntry(it.toString())

return
}

val vssProperties = vssPropertiesViewModel.vssProperties
.copy(valueType = automaticValueType)
vssPropertiesViewModel.updateVssProperties(vssProperties)
val outputEntry = OutputEntry()
result?.entriesList?.withIndex()?.forEach {
val dataEntry = it.value
val text = dataEntry.toString().substringAfter("\n")

outputViewModel.appendOutput(result.toString())
outputEntry.addMessage(text)
}
outputViewModel.addOutputEntry(outputEntry)
}

override fun onError(error: Throwable) {
outputViewModel.appendOutput("Connection to data broker failed: ${error.message}")
outputViewModel.addOutputEntry("Connection to data broker failed: ${error.message}")
}
},
)
Expand All @@ -253,15 +284,15 @@ class KuksaDataBrokerActivity : ComponentActivity() {
override fun onSuccess(result: KuksaValV1.SetResponse?) {
val errorsList = result?.errorsList
errorsList?.forEach {
outputViewModel.appendOutput(it.toString())
outputViewModel.addOutputEntry(it.toString())
return
}

outputViewModel.appendOutput(result.toString())
outputViewModel.addOutputEntry(result.toString())
}

override fun onError(error: Throwable) {
outputViewModel.appendOutput("Connection to data broker failed: ${error.message}")
outputViewModel.addOutputEntry("Connection to data broker failed: ${error.message}")
}
},
)
Expand All @@ -273,11 +304,11 @@ class KuksaDataBrokerActivity : ComponentActivity() {
object : CoroutineCallback<VssSpecification>() {
override fun onSuccess(result: VssSpecification?) {
Log.d(TAG, "Fetched specification: $result")
outputViewModel.appendOutput("Fetched specification: $result")
outputViewModel.addOutputEntry("Fetched specification: $result")
}

override fun onError(error: Throwable) {
outputViewModel.appendOutput("Could not fetch specification: ${error.message}")
outputViewModel.addOutputEntry("Could not fetch specification: ${error.message}")
}
},
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.TextLayoutResult
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.style.TextAlign
Expand All @@ -76,7 +77,6 @@ import androidx.compose.ui.unit.sp
import kotlinx.coroutines.launch
import org.eclipse.kuksa.proto.v1.Types.Datapoint.ValueCase
import org.eclipse.kuksa.testapp.databroker.viewmodel.ConnectionViewModel
import org.eclipse.kuksa.testapp.databroker.viewmodel.ConnectionViewModel.*
import org.eclipse.kuksa.testapp.databroker.viewmodel.OutputViewModel
import org.eclipse.kuksa.testapp.databroker.viewmodel.TopAppBarViewModel
import org.eclipse.kuksa.testapp.databroker.viewmodel.TopAppBarViewModel.DataBrokerMode
Expand All @@ -88,6 +88,7 @@ import org.eclipse.kuksa.testapp.extension.compose.OverflowMenu
import org.eclipse.kuksa.testapp.extension.compose.SimpleExposedDropdownMenuBox
import org.eclipse.kuksa.testapp.preferences.ConnectionInfoRepository
import org.eclipse.kuksa.testapp.ui.theme.KuksaAppAndroidTheme
import java.time.format.DateTimeFormatter

val DefaultEdgePadding = 25.dp
val DefaultElementPadding = 10.dp
Expand Down Expand Up @@ -377,35 +378,55 @@ fun DataBrokerOutput(viewModel: OutputViewModel, modifier: Modifier = Modifier)
val shape = RoundedCornerShape(20.dp, 20.dp, 0.dp, 0.dp)
val scrollState = rememberScrollState(0)

val output = viewModel.output
val outputEntries = viewModel.output

Surface(
modifier = modifier.height(500.dp),
color = MaterialTheme.colorScheme.primary,
shape = shape,
) {
Column(modifier = Modifier.verticalScroll(scrollState)) {
val dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss:SSS")
Headline(name = "Output", color = Color.White)
output.forEach { outputElement ->
Text(
modifier = Modifier
.fillMaxHeight()
.fillMaxWidth()
.padding(start = DefaultElementPadding, end = DefaultElementPadding),
text = outputElement,
fontSize = 14.sp,
textAlign = TextAlign.Start,
onTextLayout = {
scope.launch {
scrollState.animateScrollTo(scrollState.maxValue)
}
},
)
outputEntries.forEach { outputEntry ->
val date = outputEntry.localDateTime.format(dateFormatter)
val newLine = System.lineSeparator()

val onTextLayout: ((TextLayoutResult) -> Unit) = {
scope.launch {
scrollState.animateScrollTo(scrollState.maxValue)
}
}

val logTextModifier = Modifier
.fillMaxHeight()
.fillMaxWidth()
.padding(start = DefaultElementPadding, end = DefaultElementPadding)

OutputText(date, logTextModifier, onTextLayout)
outputEntry.messages.forEach {
OutputText(it + newLine, logTextModifier, onTextLayout)
}
}
}
}
}

@Composable
private fun OutputText(
text: String,
modifier: Modifier = Modifier,
onTextLayout: (TextLayoutResult) -> Unit = {},
) {
Text(
modifier = modifier,
text = text,
fontSize = 14.sp,
textAlign = TextAlign.Start,
onTextLayout = onTextLayout,
)
}

@Preview(showBackground = true)
@Composable
fun Preview() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,56 +29,52 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.eclipse.kuksa.testapp.collection.MaxElementSet
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter

private const val MAX_NUMBER_LOG_ENTRIES = 100

class OutputViewModel : ViewModel() {
private val logEntries = MaxElementSet<String>(MAX_NUMBER_LOG_ENTRIES)
private val outputEntries = MaxElementSet<OutputEntry>(MAX_NUMBER_LOG_ENTRIES)

var output: List<String> by mutableStateOf(listOf())
var output: List<OutputEntry> by mutableStateOf(listOf())
private set

fun appendOutput(text: String) {
fun addOutputEntry(message: String) {
val messages = listOf(message)
val outputEntry = OutputEntry(messages = messages)

addOutputEntry(outputEntry)
}

fun addOutputEntry(outputEntry: OutputEntry) {
viewModelScope.launch {
withContext(Dispatchers.Main) {
val sanitizedText = sanitizeString(text)
outputEntries.add(outputEntry)

val emptyLines = if (logEntries.isEmpty()) "\n" else "\n\n"
val dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss:SSS")
val date = LocalDateTime.now().format(dateFormatter)
logEntries += "$emptyLines- $date\n $sanitizedText"

output = logEntries.toList()
output = outputEntries.toList()
}
}
}

// fixes a crash when outputting VssPath(Vehicle). The ScrollBar can't handle input with more than 3971 line breaks
private fun sanitizeString(text: String): String {
var sanitizedText = text
val isTextTooLong = sanitizedText.length >= MAX_LENGTH_LOG_ENTRY
if (isTextTooLong) {
sanitizedText = sanitizedText.substring(0, MAX_LENGTH_LOG_ENTRY) + ""
sanitizedText += System.lineSeparator()
sanitizedText += System.lineSeparator()
sanitizedText += "Text is too long and was truncated"
}

return sanitizedText
}

fun clear() {
viewModelScope.launch {
withContext(Dispatchers.Main) {
logEntries.clear()
outputEntries.clear()

output = logEntries.toList()
output = outputEntries.toList()
}
}
}
}

class OutputEntry(
val localDateTime: LocalDateTime = LocalDateTime.now(),
messages: List<String> = mutableListOf(),
) {
private var _messages: MutableList<String> = messages.toMutableList()
val messages: List<String>
get() = _messages

private companion object {
private const val MAX_LENGTH_LOG_ENTRY = 90_000
fun addMessage(message: String) {
_messages.add(message)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,18 @@ class VSSPropertiesViewModel : ViewModel() {
private set

val valueTypes: List<ValueCase> = ValueCase.values().toList()
val fieldTypes: List<Field> = listOf(Field.FIELD_VALUE, Field.FIELD_ACTUATOR_TARGET)
val fieldTypes: List<Field> = listOf(
Field.FIELD_VALUE,
Field.FIELD_ACTUATOR_TARGET,
Field.FIELD_METADATA,
)

val datapoint: Datapoint
get() = vssProperties.valueType.createDatapoint(vssProperties.value)

// Meta data are always part of the properties
val property: Property
get() = Property(vssProperties.vssPath, listOf(vssProperties.fieldType, Field.FIELD_METADATA))
get() = Property(vssProperties.vssPath, listOf(vssProperties.fieldType))

fun updateVssProperties(vssProperties: VSSProperties = VSSProperties()) {
this.vssProperties = vssProperties
Expand Down
Loading

0 comments on commit 47004e0

Please sign in to comment.