Skip to content

Commit

Permalink
Merge branch 'main' into feature-16
Browse files Browse the repository at this point in the history
  • Loading branch information
wba2hi committed Jan 30, 2024
2 parents 6c4215c + 47004e0 commit cf2e0ab
Show file tree
Hide file tree
Showing 8 changed files with 143 additions and 86 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@

All notable changes to this project will be documented in this file. See [commit-and-tag-version](https://github.com/absolute-version/commit-and-tag-version) for commit guidelines.

## [0.1.3](https://github.com/eclipse-kuksa/kuksa-android-sdk/compare/release/release/v0.1.2...release/v0.1.3) (2024-01-22)


### Bug Fixes

* VSS Specification Generation fails for specific Folder Structure ([9f9f285](https://github.com/eclipse-kuksa/kuksa-android-sdk/commit/9f9f28561dafe2e32fce5fb8be72d5882e04b035))

## [0.1.2](https://github.com/eclipse-kuksa/kuksa-android-sdk/compare/release/release/v0.1.1...release/v0.1.2) (2023-12-04)


Expand Down
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 @@ -74,42 +76,42 @@ 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)

loadVssPathSuggestions()
}

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 @@ -153,7 +155,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 @@ -165,6 +167,7 @@ class KuksaDataBrokerActivity : ComponentActivity() {
}

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

Expand Down Expand Up @@ -202,7 +205,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 @@ -215,32 +218,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 @@ -256,15 +287,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 @@ -276,11 +307,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 All @@ -301,7 +332,7 @@ class KuksaDataBrokerActivity : ComponentActivity() {
}

override fun onError(error: Throwable) {
outputViewModel.appendOutput(error.toString())
outputViewModel.addOutputEntry(error.toString())
}
},
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.TextLayoutResult
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
Expand All @@ -90,6 +91,7 @@ import org.eclipse.kuksa.testapp.preferences.ConnectionInfoRepository
import org.eclipse.kuksa.testapp.ui.theme.KuksaAppAndroidTheme
import org.eclipse.kuksa.vss.VssVehicle
import org.eclipse.kuksa.vsscore.model.VssSpecification
import java.time.format.DateTimeFormatter

val SettingsMenuPadding = 16.dp
val DefaultEdgePadding = 25.dp
Expand Down Expand Up @@ -440,35 +442,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)
}
}
Loading

0 comments on commit cf2e0ab

Please sign in to comment.