diff --git a/README.md b/README.md index 25bd143a..b559da66 100644 --- a/README.md +++ b/README.md @@ -58,8 +58,8 @@ fun connectInsecure(host: String, port: Int) { ```kotlin fun fetch() { lifecycleScope.launch { - val property = Property("Vehicle.Speed", listOf(Field.FIELD_VALUE)) - val response = dataBrokerConnection?.fetch(property) ?: return@launch + val request = FetchRequest("Vehicle.Speed", listOf(Field.FIELD_VALUE)) + val response = dataBrokerConnection?.fetch(request) ?: return@launch val entry = entriesList.first() // Don't forget to handle empty responses val value = entry.value val speed = value.float diff --git a/app/src/main/java/org/eclipse/kuksa/testapp/databroker/JavaDataBrokerEngine.java b/app/src/main/java/org/eclipse/kuksa/testapp/databroker/JavaDataBrokerEngine.java index 6935ac91..920d0098 100644 --- a/app/src/main/java/org/eclipse/kuksa/testapp/databroker/JavaDataBrokerEngine.java +++ b/app/src/main/java/org/eclipse/kuksa/testapp/databroker/JavaDataBrokerEngine.java @@ -23,22 +23,23 @@ import androidx.annotation.NonNull; -import org.eclipse.kuksa.CoroutineCallback; -import org.eclipse.kuksa.DataBrokerConnection; -import org.eclipse.kuksa.DataBrokerConnector; -import org.eclipse.kuksa.DisconnectListener; -import org.eclipse.kuksa.PropertyListener; -import org.eclipse.kuksa.VssSpecificationListener; -import org.eclipse.kuksa.model.Property; +import org.eclipse.kuksa.connectivity.databroker.DataBrokerConnection; +import org.eclipse.kuksa.connectivity.databroker.DataBrokerConnector; +import org.eclipse.kuksa.connectivity.databroker.listener.DisconnectListener; +import org.eclipse.kuksa.connectivity.databroker.listener.VssPathListener; +import org.eclipse.kuksa.connectivity.databroker.listener.VssNodeListener; +import org.eclipse.kuksa.connectivity.databroker.request.FetchRequest; +import org.eclipse.kuksa.connectivity.databroker.request.SubscribeRequest; +import org.eclipse.kuksa.connectivity.databroker.request.UpdateRequest; +import org.eclipse.kuksa.connectivity.databroker.request.VssNodeFetchRequest; +import org.eclipse.kuksa.connectivity.databroker.request.VssNodeSubscribeRequest; +import org.eclipse.kuksa.coroutine.CoroutineCallback; import org.eclipse.kuksa.proto.v1.KuksaValV1.GetResponse; import org.eclipse.kuksa.proto.v1.KuksaValV1.SetResponse; -import org.eclipse.kuksa.proto.v1.Types; -import org.eclipse.kuksa.proto.v1.Types.Datapoint; import org.eclipse.kuksa.testapp.databroker.connection.DataBrokerConnectorFactory; import org.eclipse.kuksa.testapp.databroker.model.ConnectionInfo; -import org.eclipse.kuksa.vsscore.model.VssSpecification; +import org.eclipse.kuksa.vsscore.model.VssNode; -import java.util.ArrayList; import java.util.HashSet; import java.util.Set; @@ -91,82 +92,69 @@ public void onError(@NonNull Throwable error) { } @Override - public void fetch(@NonNull Property property, @NonNull CoroutineCallback callback) { + public void fetch(@NonNull FetchRequest request, @NonNull CoroutineCallback callback) { if (dataBrokerConnection == null) { return; } - dataBrokerConnection.fetch(property, callback); + dataBrokerConnection.fetch(request, callback); } @Override - public void fetch( - @NonNull T specification, + public void fetch( + @NonNull VssNodeFetchRequest request, @NonNull CoroutineCallback callback ) { if (dataBrokerConnection == null) { return; } - dataBrokerConnection.fetch(specification, callback); + dataBrokerConnection.fetch(request, callback); } @Override public void update( - @NonNull Property property, - @NonNull Datapoint datapoint, + @NonNull UpdateRequest request, @NonNull CoroutineCallback callback ) { if (dataBrokerConnection == null) { return; } - dataBrokerConnection.update(property, datapoint, callback); + dataBrokerConnection.update(request, callback); } @Override - public void subscribe(@NonNull Property property, @NonNull PropertyListener propertyListener) { + public void subscribe(@NonNull SubscribeRequest request, @NonNull VssPathListener listener) { if (dataBrokerConnection == null) { return; } - dataBrokerConnection.subscribe(property, propertyListener); + dataBrokerConnection.subscribe(request, listener); } @Override - public void subscribe( - @NonNull T specification, - @NonNull VssSpecificationListener specificationListener + public void subscribe( + @NonNull VssNodeSubscribeRequest request, + @NonNull VssNodeListener vssNodeListener ) { if (dataBrokerConnection == null) { return; } - ArrayList fields = new ArrayList<>() { - { - add(Types.Field.FIELD_VALUE); - } - }; - - dataBrokerConnection.subscribe(specification, fields, specificationListener); + dataBrokerConnection.subscribe(request, vssNodeListener); } @Override - public void unsubscribe( - @NonNull T specification, - @NonNull VssSpecificationListener specificationListener + public void unsubscribe( + @NonNull VssNodeSubscribeRequest request, + @NonNull VssNodeListener vssNodeListener ) { if (dataBrokerConnection == null) { return; } - ArrayList fields = new ArrayList<>() { - { - add(Types.Field.FIELD_VALUE); - } - }; - - dataBrokerConnection.unsubscribe(specification, fields, specificationListener); + dataBrokerConnection.unsubscribe(request, vssNodeListener); } public void disconnect() { @@ -206,9 +194,9 @@ public void unregisterDisconnectListener(@NonNull DisconnectListener listener) { } @Override - public void unsubscribe(@NonNull Property property, @NonNull PropertyListener propertyListener) { + public void unsubscribe(@NonNull SubscribeRequest request, @NonNull VssPathListener listener) { if (dataBrokerConnection != null) { - dataBrokerConnection.unsubscribe(property, propertyListener); + dataBrokerConnection.unsubscribe(request, listener); } } } diff --git a/app/src/main/kotlin/org/eclipse/kuksa/testapp/KuksaDataBrokerActivity.kt b/app/src/main/kotlin/org/eclipse/kuksa/testapp/KuksaDataBrokerActivity.kt index 52b48c0a..83040f81 100644 --- a/app/src/main/kotlin/org/eclipse/kuksa/testapp/KuksaDataBrokerActivity.kt +++ b/app/src/main/kotlin/org/eclipse/kuksa/testapp/KuksaDataBrokerActivity.kt @@ -29,17 +29,20 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.ui.Modifier import androidx.lifecycle.lifecycleScope -import org.eclipse.kuksa.CoroutineCallback -import org.eclipse.kuksa.DataBrokerConnection -import org.eclipse.kuksa.DisconnectListener -import org.eclipse.kuksa.PropertyListener -import org.eclipse.kuksa.VssSpecificationListener +import org.eclipse.kuksa.connectivity.databroker.DataBrokerConnection +import org.eclipse.kuksa.connectivity.databroker.listener.DisconnectListener +import org.eclipse.kuksa.connectivity.databroker.listener.VssNodeListener +import org.eclipse.kuksa.connectivity.databroker.listener.VssPathListener +import org.eclipse.kuksa.connectivity.databroker.request.FetchRequest +import org.eclipse.kuksa.connectivity.databroker.request.SubscribeRequest +import org.eclipse.kuksa.connectivity.databroker.request.UpdateRequest +import org.eclipse.kuksa.connectivity.databroker.request.VssNodeFetchRequest +import org.eclipse.kuksa.connectivity.databroker.request.VssNodeSubscribeRequest +import org.eclipse.kuksa.coroutine.CoroutineCallback 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.Datapoint import org.eclipse.kuksa.proto.v1.Types.Field import org.eclipse.kuksa.testapp.databroker.DataBrokerEngine @@ -49,18 +52,20 @@ 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.DataBrokerProperty 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 -import org.eclipse.kuksa.testapp.databroker.viewmodel.VssSpecificationsViewModel +import org.eclipse.kuksa.testapp.databroker.viewmodel.VSSPathsViewModel +import org.eclipse.kuksa.testapp.databroker.viewmodel.VssNodesViewModel import org.eclipse.kuksa.testapp.extension.TAG import org.eclipse.kuksa.testapp.preferences.ConnectionInfoRepository import org.eclipse.kuksa.testapp.ui.theme.KuksaAppAndroidTheme -import org.eclipse.kuksa.vsscore.annotation.VssDefinition -import org.eclipse.kuksa.vsscore.model.VssSpecification +import org.eclipse.kuksa.vss.VssVehicle +import org.eclipse.kuksa.vsscore.annotation.VssModelGenerator +import org.eclipse.kuksa.vsscore.model.VssNode -@VssDefinition +@VssModelGenerator class KuksaDataBrokerActivity : ComponentActivity() { private lateinit var connectionInfoRepository: ConnectionInfoRepository @@ -68,8 +73,8 @@ class KuksaDataBrokerActivity : ComponentActivity() { private val connectionViewModel: ConnectionViewModel by viewModels { ConnectionViewModel.Factory(connectionInfoRepository) } - private val vssPropertiesViewModel: VSSPropertiesViewModel by viewModels() - private val vssSpecificationsViewModel: VssSpecificationsViewModel by viewModels() + private val vssPathsViewModel: VSSPathsViewModel by viewModels() + private val vssNodesViewModel: VssNodesViewModel by viewModels() private val outputViewModel: OutputViewModel by viewModels() private val dataBrokerConnectionCallback = object : CoroutineCallback() { @@ -92,9 +97,9 @@ class KuksaDataBrokerActivity : ComponentActivity() { outputViewModel.addOutputEntry("DataBroker disconnected") } - private val propertyListener = object : PropertyListener { - override fun onPropertyChanged(entryUpdates: List) { - Log.d(TAG, "onPropertyChanged() called with: updatedValues = $entryUpdates") + private val vssPathListener = object : VssPathListener { + override fun onEntryChanged(entryUpdates: List) { + Log.d(TAG, "onEntryChanged() called with: updatedValues = $entryUpdates") val entries = mutableListOf().apply { add("Updated Entries") @@ -109,13 +114,13 @@ class KuksaDataBrokerActivity : ComponentActivity() { } } - private val specificationListener = object : VssSpecificationListener { - override fun onSpecificationChanged(vssSpecification: VssSpecification) { - outputViewModel.addOutputEntry("Updated specification: $vssSpecification") + private val vssNodeListener = object : VssNodeListener { + override fun onNodeChanged(vssNode: VssNode) { + outputViewModel.addOutputEntry("Updated node: $vssNode") } override fun onError(throwable: Throwable) { - outputViewModel.addOutputEntry("Updated specification: ${throwable.message}") + outputViewModel.addOutputEntry("Updated node: ${throwable.message}") } } @@ -128,7 +133,45 @@ class KuksaDataBrokerActivity : ComponentActivity() { JavaDataBrokerEngine() } + @Suppress("performance:SpreadOperator") // Neglectable: Field types are 1-2 elements mostly override fun onCreate(savedInstanceState: Bundle?) { + fun addVssPathsListeners() { + vssPathsViewModel.onGetProperty = { property: DataBrokerProperty -> + fetchPropertyFieldType(property) + fetchProperty(property) + } + + vssPathsViewModel.onSetProperty = { property: DataBrokerProperty, datapoint: Datapoint -> + updateProperty(property, datapoint) + } + + vssPathsViewModel.onSubscribeProperty = { property: DataBrokerProperty -> + val request = SubscribeRequest(property.vssPath, *property.fieldTypes.toTypedArray()) + dataBrokerEngine.subscribe(request, vssPathListener) + } + + vssPathsViewModel.onUnsubscribeProperty = { property: DataBrokerProperty -> + val request = SubscribeRequest(property.vssPath, *property.fieldTypes.toTypedArray()) + dataBrokerEngine.unsubscribe(request, vssPathListener) + } + } + + fun addVssNodesListeners() { + vssNodesViewModel.onSubscribeNode = { vssNode -> + val request = VssNodeSubscribeRequest(vssNode) + dataBrokerEngine.subscribe(request, vssNodeListener) + } + + vssNodesViewModel.onUnsubscribeNode = { vssNode -> + val request = VssNodeSubscribeRequest(vssNode) + dataBrokerEngine.unsubscribe(request, vssNodeListener) + } + + vssNodesViewModel.onGetNode = { vssNode -> + fetchVssNode(vssNode) + } + } + super.onCreate(savedInstanceState) Log.d(TAG, "onCreate() called with: savedInstanceState = $savedInstanceState") @@ -140,8 +183,8 @@ class KuksaDataBrokerActivity : ComponentActivity() { DataBrokerView( topAppBarViewModel, connectionViewModel, - vssPropertiesViewModel, - vssSpecificationsViewModel, + vssPathsViewModel, + vssNodesViewModel, outputViewModel, ) } @@ -170,34 +213,8 @@ class KuksaDataBrokerActivity : ComponentActivity() { disconnect() } - vssPropertiesViewModel.onGetProperty = { property: Property -> - fetchPropertyFieldType(property) - fetchProperty(property) - } - - vssPropertiesViewModel.onSetProperty = { property: Property, datapoint: Datapoint -> - updateProperty(property, datapoint) - } - - vssPropertiesViewModel.onSubscribeProperty = { property: Property -> - dataBrokerEngine.subscribe(property, propertyListener) - } - - vssPropertiesViewModel.onUnsubscribeProperty = { property: Property -> - dataBrokerEngine.unsubscribe(property, propertyListener) - } - - vssSpecificationsViewModel.onSubscribeSpecification = { specification -> - dataBrokerEngine.subscribe(specification, specificationListener) - } - - vssSpecificationsViewModel.onUnsubscribeSpecification = { specification -> - dataBrokerEngine.unsubscribe(specification, specificationListener) - } - - vssSpecificationsViewModel.onGetSpecification = { specification -> - fetchSpecification(specification) - } + addVssPathsListeners() + addVssNodesListeners() } override fun onDestroy() { @@ -222,25 +239,25 @@ class KuksaDataBrokerActivity : ComponentActivity() { dataBrokerEngine.unregisterDisconnectListener(onDisconnectListener) } - private fun fetchPropertyFieldType(property: Property) { - val propertyWithMetaData = property.copy(fields = listOf(Field.FIELD_METADATA)) + private fun fetchPropertyFieldType(property: DataBrokerProperty) { + val request = FetchRequest(property.vssPath, Field.FIELD_METADATA) dataBrokerEngine.fetch( - propertyWithMetaData, + request, object : CoroutineCallback() { 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 + Datapoint.ValueCase.VALUE_NOT_SET } Log.d(TAG, "Fetched automatic value type from meta data: $automaticValueType") - val vssProperties = vssPropertiesViewModel.vssProperties + val dataBrokerProperty = vssPathsViewModel.dataBrokerProperty .copy(valueType = automaticValueType) - vssPropertiesViewModel.updateVssProperties(vssProperties) + vssPathsViewModel.updateDataBrokerProperty(dataBrokerProperty) } override fun onError(error: Throwable) { @@ -250,11 +267,14 @@ class KuksaDataBrokerActivity : ComponentActivity() { ) } - private fun fetchProperty(property: Property) { + @Suppress("performance:SpreadOperator") // Neglectable: Field types are 1-2 elements mostly + private fun fetchProperty(property: DataBrokerProperty) { Log.d(TAG, "Fetch property: $property") + val request = FetchRequest(property.vssPath, *property.fieldTypes.toTypedArray()) + dataBrokerEngine.fetch( - property, + request, object : CoroutineCallback() { override fun onSuccess(result: GetResponse?) { val errorsList = result?.errorsList @@ -281,12 +301,13 @@ class KuksaDataBrokerActivity : ComponentActivity() { ) } - private fun updateProperty(property: Property, datapoint: Datapoint) { + @Suppress("performance:SpreadOperator") // Neglectable: Field types are 1-2 elements mostly + private fun updateProperty(property: DataBrokerProperty, datapoint: Datapoint) { Log.d(TAG, "Update property: $property dataPoint: $datapoint, type: ${datapoint.valueCase}") + val request = UpdateRequest(property.vssPath, datapoint, *property.fieldTypes.toTypedArray()) dataBrokerEngine.update( - property, - datapoint, + request, object : CoroutineCallback() { override fun onSuccess(result: KuksaValV1.SetResponse?) { val errorsList = result?.errorsList @@ -305,24 +326,25 @@ class KuksaDataBrokerActivity : ComponentActivity() { ) } - private fun fetchSpecification(specification: VssSpecification) { + private fun fetchVssNode(vssNode: VssNode) { + val request = VssNodeFetchRequest(vssNode) dataBrokerEngine.fetch( - specification, - object : CoroutineCallback() { - override fun onSuccess(result: VssSpecification?) { - Log.d(TAG, "Fetched specification: $result") - outputViewModel.addOutputEntry("Fetched specification: $result") + request, + object : CoroutineCallback() { + override fun onSuccess(result: VssNode?) { + Log.d(TAG, "Fetched node: $result") + outputViewModel.addOutputEntry("Fetched node: $result") } override fun onError(error: Throwable) { - outputViewModel.addOutputEntry("Could not fetch specification: ${error.message}") + outputViewModel.addOutputEntry("Could not fetch node: ${error.message}") } }, ) } private fun loadVssPathSuggestions() { - val property = Property("Vehicle", listOf(Field.FIELD_VALUE)) + val property = FetchRequest(VssVehicle().vssPath, Field.FIELD_VALUE) dataBrokerEngine.fetch( property, @@ -331,7 +353,7 @@ class KuksaDataBrokerActivity : ComponentActivity() { val entriesList = result?.entriesList val vssPaths = entriesList?.map { it.path } ?: emptyList() - vssPropertiesViewModel.updateSuggestions(vssPaths) + vssPathsViewModel.updateSuggestions(vssPaths) } override fun onError(error: Throwable) { diff --git a/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/DataBrokerEngine.kt b/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/DataBrokerEngine.kt index 22c21480..7a4547cf 100644 --- a/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/DataBrokerEngine.kt +++ b/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/DataBrokerEngine.kt @@ -20,17 +20,20 @@ package org.eclipse.kuksa.testapp.databroker import android.content.Context -import org.eclipse.kuksa.CoroutineCallback -import org.eclipse.kuksa.DataBrokerConnection -import org.eclipse.kuksa.DisconnectListener -import org.eclipse.kuksa.PropertyListener -import org.eclipse.kuksa.VssSpecificationListener -import org.eclipse.kuksa.model.Property +import org.eclipse.kuksa.connectivity.databroker.DataBrokerConnection +import org.eclipse.kuksa.connectivity.databroker.listener.DisconnectListener +import org.eclipse.kuksa.connectivity.databroker.listener.VssNodeListener +import org.eclipse.kuksa.connectivity.databroker.listener.VssPathListener +import org.eclipse.kuksa.connectivity.databroker.request.FetchRequest +import org.eclipse.kuksa.connectivity.databroker.request.SubscribeRequest +import org.eclipse.kuksa.connectivity.databroker.request.UpdateRequest +import org.eclipse.kuksa.connectivity.databroker.request.VssNodeFetchRequest +import org.eclipse.kuksa.connectivity.databroker.request.VssNodeSubscribeRequest +import org.eclipse.kuksa.coroutine.CoroutineCallback import org.eclipse.kuksa.proto.v1.KuksaValV1.GetResponse import org.eclipse.kuksa.proto.v1.KuksaValV1.SetResponse -import org.eclipse.kuksa.proto.v1.Types.Datapoint import org.eclipse.kuksa.testapp.databroker.model.ConnectionInfo -import org.eclipse.kuksa.vsscore.model.VssSpecification +import org.eclipse.kuksa.vsscore.model.VssNode @Suppress("complexity:TooManyFunctions") // required to test the api interface DataBrokerEngine { @@ -43,30 +46,29 @@ interface DataBrokerEngine { ) fun fetch( - property: Property, + request: FetchRequest, callback: CoroutineCallback, ) - fun fetch(specification: T, callback: CoroutineCallback) + fun fetch(request: VssNodeFetchRequest, callback: CoroutineCallback) fun update( - property: Property, - datapoint: Datapoint, + request: UpdateRequest, callback: CoroutineCallback, ) - fun subscribe(property: Property, propertyListener: PropertyListener) + fun subscribe(request: SubscribeRequest, listener: VssPathListener) - fun unsubscribe(property: Property, propertyListener: PropertyListener) + fun unsubscribe(request: SubscribeRequest, listener: VssPathListener) - fun subscribe( - specification: T, - specificationListener: VssSpecificationListener, + fun subscribe( + request: VssNodeSubscribeRequest, + vssNodeListener: VssNodeListener, ) - fun unsubscribe( - specification: T, - specificationListener: VssSpecificationListener, + fun unsubscribe( + request: VssNodeSubscribeRequest, + vssNodeListener: VssNodeListener, ) fun disconnect() diff --git a/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/KotlinDataBrokerEngine.kt b/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/KotlinDataBrokerEngine.kt index 30731180..6791eef2 100644 --- a/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/KotlinDataBrokerEngine.kt +++ b/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/KotlinDataBrokerEngine.kt @@ -22,20 +22,23 @@ package org.eclipse.kuksa.testapp.databroker import android.content.Context import androidx.lifecycle.LifecycleCoroutineScope import kotlinx.coroutines.launch -import org.eclipse.kuksa.CoroutineCallback -import org.eclipse.kuksa.DataBrokerConnection -import org.eclipse.kuksa.DataBrokerConnector -import org.eclipse.kuksa.DataBrokerException -import org.eclipse.kuksa.DisconnectListener -import org.eclipse.kuksa.PropertyListener -import org.eclipse.kuksa.VssSpecificationListener -import org.eclipse.kuksa.model.Property +import org.eclipse.kuksa.connectivity.databroker.DataBrokerConnection +import org.eclipse.kuksa.connectivity.databroker.DataBrokerConnector +import org.eclipse.kuksa.connectivity.databroker.DataBrokerException +import org.eclipse.kuksa.connectivity.databroker.listener.DisconnectListener +import org.eclipse.kuksa.connectivity.databroker.listener.VssNodeListener +import org.eclipse.kuksa.connectivity.databroker.listener.VssPathListener +import org.eclipse.kuksa.connectivity.databroker.request.FetchRequest +import org.eclipse.kuksa.connectivity.databroker.request.SubscribeRequest +import org.eclipse.kuksa.connectivity.databroker.request.UpdateRequest +import org.eclipse.kuksa.connectivity.databroker.request.VssNodeFetchRequest +import org.eclipse.kuksa.connectivity.databroker.request.VssNodeSubscribeRequest +import org.eclipse.kuksa.coroutine.CoroutineCallback import org.eclipse.kuksa.proto.v1.KuksaValV1.GetResponse import org.eclipse.kuksa.proto.v1.KuksaValV1.SetResponse -import org.eclipse.kuksa.proto.v1.Types.Datapoint import org.eclipse.kuksa.testapp.databroker.connection.DataBrokerConnectorFactory import org.eclipse.kuksa.testapp.databroker.model.ConnectionInfo -import org.eclipse.kuksa.vsscore.model.VssSpecification +import org.eclipse.kuksa.vsscore.model.VssNode @Suppress("complexity:TooManyFunctions") class KotlinDataBrokerEngine( @@ -74,10 +77,10 @@ class KotlinDataBrokerEngine( } } - override fun fetch(property: Property, callback: CoroutineCallback) { + override fun fetch(request: FetchRequest, callback: CoroutineCallback) { lifecycleScope.launch { try { - val response = dataBrokerConnection?.fetch(property) ?: return@launch + val response = dataBrokerConnection?.fetch(request) ?: return@launch callback.onSuccess(response) } catch (e: DataBrokerException) { callback.onError(e) @@ -85,10 +88,10 @@ class KotlinDataBrokerEngine( } } - override fun fetch(specification: T, callback: CoroutineCallback) { + override fun fetch(request: VssNodeFetchRequest, callback: CoroutineCallback) { lifecycleScope.launch { try { - val response = dataBrokerConnection?.fetch(specification) ?: return@launch + val response = dataBrokerConnection?.fetch(request) ?: return@launch callback.onSuccess(response) } catch (e: DataBrokerException) { callback.onError(e) @@ -96,10 +99,10 @@ class KotlinDataBrokerEngine( } } - override fun update(property: Property, datapoint: Datapoint, callback: CoroutineCallback) { + override fun update(request: UpdateRequest, callback: CoroutineCallback) { lifecycleScope.launch { try { - val response = dataBrokerConnection?.update(property, datapoint) ?: return@launch + val response = dataBrokerConnection?.update(request) ?: return@launch callback.onSuccess(response) } catch (e: DataBrokerException) { callback.onError(e) @@ -107,28 +110,28 @@ class KotlinDataBrokerEngine( } } - override fun subscribe(property: Property, propertyListener: PropertyListener) { - dataBrokerConnection?.subscribe(property, propertyListener) + override fun subscribe(request: SubscribeRequest, listener: VssPathListener) { + dataBrokerConnection?.subscribe(request, listener) } - override fun unsubscribe(property: Property, propertyListener: PropertyListener) { - dataBrokerConnection?.unsubscribe(property, propertyListener) + override fun unsubscribe(request: SubscribeRequest, listener: VssPathListener) { + dataBrokerConnection?.unsubscribe(request, listener) } - override fun subscribe( - specification: T, - specificationListener: VssSpecificationListener, + override fun subscribe( + request: VssNodeSubscribeRequest, + vssNodeListener: VssNodeListener, ) { lifecycleScope.launch { - dataBrokerConnection?.subscribe(specification, listener = specificationListener) + dataBrokerConnection?.subscribe(request, listener = vssNodeListener) } } - override fun unsubscribe( - specification: T, - specificationListener: VssSpecificationListener, + override fun unsubscribe( + request: VssNodeSubscribeRequest, + vssNodeListener: VssNodeListener, ) { - dataBrokerConnection?.unsubscribe(specification, listener = specificationListener) + dataBrokerConnection?.unsubscribe(request, listener = vssNodeListener) } override fun disconnect() { diff --git a/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/connection/DataBrokerConnectorFactory.kt b/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/connection/DataBrokerConnectorFactory.kt index 90aef843..e30239bb 100644 --- a/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/connection/DataBrokerConnectorFactory.kt +++ b/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/connection/DataBrokerConnectorFactory.kt @@ -26,9 +26,9 @@ import io.grpc.Grpc import io.grpc.ManagedChannel import io.grpc.ManagedChannelBuilder import io.grpc.TlsChannelCredentials -import org.eclipse.kuksa.DataBrokerConnector -import org.eclipse.kuksa.TimeoutConfig -import org.eclipse.kuksa.authentication.JsonWebToken +import org.eclipse.kuksa.connectivity.authentication.JsonWebToken +import org.eclipse.kuksa.connectivity.databroker.DataBrokerConnector +import org.eclipse.kuksa.model.TimeoutConfig import org.eclipse.kuksa.testapp.databroker.model.ConnectionInfo import org.eclipse.kuksa.testapp.extension.readAsText import java.io.IOException diff --git a/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/view/DataBrokerView.kt b/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/view/DataBrokerView.kt index 7e66978b..11bc75e4 100644 --- a/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/view/DataBrokerView.kt +++ b/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/view/DataBrokerView.kt @@ -23,7 +23,6 @@ import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.ExperimentalLayoutApi import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.consumeWindowInsets @@ -83,15 +82,15 @@ 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 -import org.eclipse.kuksa.testapp.databroker.viewmodel.VSSPropertiesViewModel -import org.eclipse.kuksa.testapp.databroker.viewmodel.VssSpecificationsViewModel +import org.eclipse.kuksa.testapp.databroker.viewmodel.VSSPathsViewModel +import org.eclipse.kuksa.testapp.databroker.viewmodel.VssNodesViewModel import org.eclipse.kuksa.testapp.extension.compose.Headline 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 org.eclipse.kuksa.vss.VssVehicle -import org.eclipse.kuksa.vsscore.model.VssSpecification +import org.eclipse.kuksa.vsscore.model.VssNode import java.time.format.DateTimeFormatter val SettingsMenuPadding = 16.dp @@ -99,13 +98,12 @@ val DefaultEdgePadding = 25.dp val DefaultElementPadding = 10.dp val MinimumButtonWidth = 150.dp -@OptIn(ExperimentalLayoutApi::class) @Composable fun DataBrokerView( topAppBarViewModel: TopAppBarViewModel, connectionViewModel: ConnectionViewModel, - vssPropertiesViewModel: VSSPropertiesViewModel, - vssSpecificationsViewModel: VssSpecificationsViewModel, + vssPathsViewModel: VSSPathsViewModel, + vssNodesViewModel: VssNodesViewModel, outputViewModel: OutputViewModel, ) { Scaffold( @@ -127,8 +125,8 @@ fun DataBrokerView( val dataBrokerMode = topAppBarViewModel.dataBrokerMode if (connectionViewModel.isConnected) { when (dataBrokerMode) { - DataBrokerMode.VSS_PATH -> DataBrokerProperties(vssPropertiesViewModel) - DataBrokerMode.SPECIFICATION -> DataBrokerSpecifications(vssSpecificationsViewModel) + DataBrokerMode.VSS_PATH -> DataBrokerProperties(vssPathsViewModel) + DataBrokerMode.VSS_FILE -> DataBrokerVssNodes(vssNodesViewModel) } } Spacer(modifier = Modifier.padding(top = DefaultElementPadding)) @@ -188,8 +186,8 @@ private fun SettingsMenu( Row( modifier = Modifier .clickable { - val newMode = if (!topAppBarViewModel.isSpecificationModeEnabled) { - DataBrokerMode.SPECIFICATION + val newMode = if (!topAppBarViewModel.isVssFileModeEnabled) { + DataBrokerMode.VSS_FILE } else { DataBrokerMode.VSS_PATH } @@ -198,10 +196,10 @@ private fun SettingsMenu( .padding(horizontal = SettingsMenuPadding), ) { Checkbox( - checked = topAppBarViewModel.isSpecificationModeEnabled, + checked = topAppBarViewModel.isVssFileModeEnabled, onCheckedChange = null, ) - Text(text = "Specification Mode", modifier = Modifier.padding(start = SettingsMenuPadding)) + Text(text = "VSS Mode", modifier = Modifier.padding(start = SettingsMenuPadding)) } } } @@ -235,23 +233,23 @@ private fun ConnectionStatusIcon( } @Composable -fun DataBrokerSpecifications(viewModel: VssSpecificationsViewModel) { +fun DataBrokerVssNodes(viewModel: VssNodesViewModel) { Column { - Headline(name = "Specifications") + Headline(name = "Generated VSS Nodes") - val adapter = object : SuggestionAdapter { - override fun toString(item: VssSpecification): String { + val adapter = object : SuggestionAdapter { + override fun toString(item: VssNode): String { return item.vssPath } } SuggestionTextView( value = "Vehicle", - suggestions = viewModel.specifications, + suggestions = viewModel.vssNodes, adapter = adapter, onItemSelected = { - val specification = it ?: VssVehicle() - viewModel.updateSpecification(specification) + val vssNode = it ?: VssVehicle() + viewModel.updateNode(vssNode) }, modifier = Modifier .fillMaxWidth() @@ -264,7 +262,7 @@ fun DataBrokerSpecifications(viewModel: VssSpecificationsViewModel) { ) { Button( onClick = { - viewModel.onGetSpecification(viewModel.specification) + viewModel.onGetNode(viewModel.node) }, modifier = Modifier.requiredWidth(80.dp), ) { @@ -272,15 +270,15 @@ fun DataBrokerSpecifications(viewModel: VssSpecificationsViewModel) { } if (viewModel.isSubscribed) { Button(onClick = { - viewModel.subscribedSpecifications.remove(viewModel.specification) - viewModel.onUnsubscribeSpecification(viewModel.specification) + viewModel.subscribedNodes.remove(viewModel.node) + viewModel.onUnsubscribeNode(viewModel.node) }) { Text(text = "Unsubscribe") } } else { Button(onClick = { - viewModel.subscribedSpecifications.add(viewModel.specification) - viewModel.onSubscribeSpecification(viewModel.specification) + viewModel.subscribedNodes.add(viewModel.node) + viewModel.onSubscribeNode(viewModel.node) }) { Text(text = "Subscribe") } @@ -290,21 +288,21 @@ fun DataBrokerSpecifications(viewModel: VssSpecificationsViewModel) { } @Composable -fun DataBrokerProperties(viewModel: VSSPropertiesViewModel) { - val vssProperties = viewModel.vssProperties +fun DataBrokerProperties(viewModel: VSSPathsViewModel) { + val dataBrokerProperty = viewModel.dataBrokerProperty var expanded by remember { mutableStateOf(false) } Column { - Headline(name = "Properties") + Headline(name = "VSS Paths") SuggestionTextView( suggestions = viewModel.suggestions, - value = viewModel.vssProperties.vssPath, + value = dataBrokerProperty.vssPath, onValueChanged = { - val newVssProperties = viewModel.vssProperties.copy( + val newVssProperties = dataBrokerProperty.copy( vssPath = it, valueType = ValueCase.VALUE_NOT_SET, ) - viewModel.updateVssProperties(newVssProperties) + viewModel.updateDataBrokerProperty(newVssProperties) }, label = { Text(text = "VSS Path") @@ -317,10 +315,10 @@ fun DataBrokerProperties(viewModel: VSSPropertiesViewModel) { Spacer(modifier = Modifier.padding(top = DefaultElementPadding)) Row { TextField( - value = "${viewModel.vssProperties.valueType}", + value = "${dataBrokerProperty.valueType}", onValueChange = {}, label = { - Text("Field Type") + Text("Value Type") }, readOnly = true, enabled = false, @@ -359,8 +357,8 @@ fun DataBrokerProperties(viewModel: VSSPropertiesViewModel) { onClick = { expanded = false - val newVssProperties = viewModel.vssProperties.copy(valueType = it) - viewModel.updateVssProperties(newVssProperties) + val newVssProperties = dataBrokerProperty.copy(valueType = it) + viewModel.updateDataBrokerProperty(newVssProperties) }, ) } @@ -370,10 +368,10 @@ fun DataBrokerProperties(viewModel: VSSPropertiesViewModel) { Spacer(modifier = Modifier.padding(top = DefaultElementPadding)) Row { TextField( - value = vssProperties.value, + value = dataBrokerProperty.value, onValueChange = { - val newVssProperties = viewModel.vssProperties.copy(value = it) - viewModel.updateVssProperties(newVssProperties) + val newVssProperties = dataBrokerProperty.copy(value = it) + viewModel.updateDataBrokerProperty(newVssProperties) }, modifier = Modifier .padding(start = DefaultEdgePadding) @@ -391,8 +389,8 @@ fun DataBrokerProperties(viewModel: VSSPropertiesViewModel) { label = "Field Type", list = viewModel.fieldTypes, onValueChange = { - val newVssProperties = viewModel.vssProperties.copy(fieldType = it) - viewModel.updateVssProperties(newVssProperties) + val newVssProperties = dataBrokerProperty.copy(fieldTypes = setOf(it)) + viewModel.updateDataBrokerProperty(newVssProperties) }, ) } @@ -403,16 +401,16 @@ fun DataBrokerProperties(viewModel: VSSPropertiesViewModel) { ) { Button( onClick = { - viewModel.onGetProperty(viewModel.property) + viewModel.onGetProperty(dataBrokerProperty) }, modifier = Modifier.requiredWidth(80.dp), ) { Text(text = "Get") } Button( - enabled = viewModel.vssProperties.valueType != ValueCase.VALUE_NOT_SET, + enabled = dataBrokerProperty.valueType != ValueCase.VALUE_NOT_SET, onClick = { - viewModel.onSetProperty(viewModel.property, viewModel.datapoint) + viewModel.onSetProperty(dataBrokerProperty, viewModel.datapoint) }, modifier = Modifier.requiredWidth(80.dp), ) { @@ -420,15 +418,15 @@ fun DataBrokerProperties(viewModel: VSSPropertiesViewModel) { } if (viewModel.isSubscribed) { Button(onClick = { - viewModel.subscribedProperties.remove(viewModel.property) - viewModel.onUnsubscribeProperty(viewModel.property) + viewModel.subscribedProperties.remove(dataBrokerProperty) + viewModel.onUnsubscribeProperty(dataBrokerProperty) }) { Text(text = "Unsubscribe") } } else { Button(onClick = { - viewModel.subscribedProperties.add(viewModel.property) - viewModel.onSubscribeProperty(viewModel.property) + viewModel.subscribedProperties.add(dataBrokerProperty) + viewModel.onSubscribeProperty(dataBrokerProperty) }) { Text(text = "Subscribe") } @@ -500,8 +498,8 @@ fun Preview() { DataBrokerView( TopAppBarViewModel(), ConnectionViewModel(connectionInfoRepository), - VSSPropertiesViewModel(), - VssSpecificationsViewModel(), + VSSPathsViewModel(), + VssNodesViewModel(), OutputViewModel(), ) } diff --git a/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/viewmodel/TopAppBarViewModel.kt b/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/viewmodel/TopAppBarViewModel.kt index 86bb7329..5c56d0c5 100644 --- a/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/viewmodel/TopAppBarViewModel.kt +++ b/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/viewmodel/TopAppBarViewModel.kt @@ -31,11 +31,11 @@ import kotlinx.coroutines.flow.onEach class TopAppBarViewModel : ViewModel() { enum class DataBrokerMode { VSS_PATH, - SPECIFICATION, + VSS_FILE, } var isCompatibilityModeEnabled by mutableStateOf(false) - var isSpecificationModeEnabled by mutableStateOf(false) + var isVssFileModeEnabled by mutableStateOf(false) private set var onCompatibilityModeChanged: ((Boolean) -> Unit)? = null @@ -49,16 +49,16 @@ class TopAppBarViewModel : ViewModel() { .onEach { onCompatibilityModeChanged?.invoke(it) } .launchIn(viewModelScope) - snapshotFlow { isSpecificationModeEnabled } + snapshotFlow { isVssFileModeEnabled } .onEach { - val mode = if (isSpecificationModeEnabled) DataBrokerMode.SPECIFICATION else DataBrokerMode.VSS_PATH + val mode = if (isVssFileModeEnabled) DataBrokerMode.VSS_FILE else DataBrokerMode.VSS_PATH onDataBrokerModeChanged?.invoke(mode) } .launchIn(viewModelScope) snapshotFlow { dataBrokerMode } .onEach { - isSpecificationModeEnabled = it == DataBrokerMode.SPECIFICATION + isVssFileModeEnabled = it == DataBrokerMode.VSS_FILE } .launchIn(viewModelScope) } diff --git a/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/viewmodel/VssSpecificationsViewModel.kt b/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/viewmodel/VssNodesViewModel.kt similarity index 62% rename from app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/viewmodel/VssSpecificationsViewModel.kt rename to app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/viewmodel/VssNodesViewModel.kt index f292f400..1f1cb3da 100644 --- a/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/viewmodel/VssSpecificationsViewModel.kt +++ b/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/viewmodel/VssNodesViewModel.kt @@ -26,27 +26,27 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.lifecycle.ViewModel import org.eclipse.kuksa.vss.VssVehicle -import org.eclipse.kuksa.vsscore.model.VssSpecification +import org.eclipse.kuksa.vsscore.model.VssNode import org.eclipse.kuksa.vsscore.model.heritage -class VssSpecificationsViewModel : ViewModel() { - var onGetSpecification: (specification: VssSpecification) -> Unit = { } - var onSubscribeSpecification: (specification: VssSpecification) -> Unit = { } - var onUnsubscribeSpecification: (specification: VssSpecification) -> Unit = { } +class VssNodesViewModel : ViewModel() { + var onGetNode: (node: VssNode) -> Unit = { } + var onSubscribeNode: (node: VssNode) -> Unit = { } + var onUnsubscribeNode: (node: VssNode) -> Unit = { } - var subscribedSpecifications = mutableStateListOf() + var subscribedNodes = mutableStateListOf() val isSubscribed by derivedStateOf { - subscribedSpecifications.contains(specification) + subscribedNodes.contains(node) } private val vssVehicle = VssVehicle() - val specifications = listOf(vssVehicle) + vssVehicle.heritage + val vssNodes = listOf(vssVehicle) + vssVehicle.heritage - var specification: VssSpecification by mutableStateOf(vssVehicle) + var node: VssNode by mutableStateOf(vssVehicle) private set - fun updateSpecification(specification: VssSpecification) { - this.specification = specification + fun updateNode(node: VssNode) { + this.node = node } } diff --git a/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/viewmodel/VssPropertiesViewModel.kt b/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/viewmodel/VssPropertiesViewModel.kt index 0ba78623..68479304 100644 --- a/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/viewmodel/VssPropertiesViewModel.kt +++ b/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/viewmodel/VssPropertiesViewModel.kt @@ -27,25 +27,28 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.lifecycle.ViewModel import org.eclipse.kuksa.extension.createDatapoint -import org.eclipse.kuksa.model.Property import org.eclipse.kuksa.proto.v1.Types.Datapoint import org.eclipse.kuksa.proto.v1.Types.Datapoint.ValueCase import org.eclipse.kuksa.proto.v1.Types.Field import java.util.TreeSet -class VSSPropertiesViewModel : ViewModel() { - var onGetProperty: (property: Property) -> Unit = { } - var onSetProperty: (property: Property, datapoint: Datapoint) -> Unit = { _: Property, _: Datapoint -> } - var onSubscribeProperty: (property: Property) -> Unit = { } - var onUnsubscribeProperty: (property: Property) -> Unit = { } +class VSSPathsViewModel : ViewModel() { + var onGetProperty: (property: DataBrokerProperty) -> Unit = { } + var onSetProperty: (property: DataBrokerProperty, datapoint: Datapoint) -> Unit = { + _: DataBrokerProperty, + _: Datapoint, + -> + } + var onSubscribeProperty: (property: DataBrokerProperty) -> Unit = { } + var onUnsubscribeProperty: (property: DataBrokerProperty) -> Unit = { } - var subscribedProperties = mutableStateListOf() + var subscribedProperties = mutableStateListOf() val isSubscribed by derivedStateOf { - subscribedProperties.contains(property) + subscribedProperties.contains(dataBrokerProperty) } - var vssProperties: VSSProperties by mutableStateOf(VSSProperties()) + var dataBrokerProperty: DataBrokerProperty by mutableStateOf(DataBrokerProperty()) private set val valueTypes: List = ValueCase.entries @@ -59,13 +62,10 @@ class VSSPropertiesViewModel : ViewModel() { private set val datapoint: Datapoint - get() = vssProperties.valueType.createDatapoint(vssProperties.value) - - val property: Property - get() = Property(vssProperties.vssPath, listOf(vssProperties.fieldType)) + get() = dataBrokerProperty.valueType.createDatapoint(dataBrokerProperty.value) - fun updateVssProperties(vssProperties: VSSProperties = VSSProperties()) { - this.vssProperties = vssProperties + fun updateDataBrokerProperty(property: DataBrokerProperty = DataBrokerProperty()) { + dataBrokerProperty = property } fun updateSuggestions(vssPaths: Collection) { @@ -90,9 +90,9 @@ class VSSPropertiesViewModel : ViewModel() { } @Immutable -data class VSSProperties( +data class DataBrokerProperty( val vssPath: String = "Vehicle", val valueType: ValueCase = ValueCase.VALUE_NOT_SET, val value: String = "", - val fieldType: Field = Field.FIELD_VALUE, + val fieldTypes: Collection = setOf(Field.FIELD_VALUE), ) diff --git a/app/src/main/kotlin/org/eclipse/kuksa/testapp/extension/compose/DropdownMenuView.kt b/app/src/main/kotlin/org/eclipse/kuksa/testapp/extension/compose/DropdownMenuView.kt index 85d61e2b..64df411f 100644 --- a/app/src/main/kotlin/org/eclipse/kuksa/testapp/extension/compose/DropdownMenuView.kt +++ b/app/src/main/kotlin/org/eclipse/kuksa/testapp/extension/compose/DropdownMenuView.kt @@ -30,11 +30,11 @@ import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material3.Divider import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExposedDropdownMenuBox import androidx.compose.material3.ExposedDropdownMenuDefaults +import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.LocalContentColor import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedTextField @@ -205,7 +205,7 @@ fun LazyDropdownMenu( } if (index < items.lastIndex) { - Divider(modifier = Modifier.padding(horizontal = 16.dp)) + HorizontalDivider(modifier = Modifier.padding(horizontal = 16.dp)) } } } diff --git a/docs/QUICKSTART.md b/docs/QUICKSTART.md index 75483f5b..d4ef8bce 100644 --- a/docs/QUICKSTART.md +++ b/docs/QUICKSTART.md @@ -68,8 +68,8 @@ void connectInsecure(String host, int port) { ```kotlin fun fetch() { lifecycleScope.launch { - val property = Property("Vehicle.Speed", listOf(Field.FIELD_VALUE)) - val response = dataBrokerConnection?.fetch(property) ?: return@launch + val request = FetchRequest("Vehicle.Speed", Field.FIELD_VALUE) + val response = dataBrokerConnection?.fetch(request) ?: return@launch val entry = response.entriesList.first() // Don't forget to handle empty responses val value = entry.value val speed = value.float @@ -78,20 +78,20 @@ fun fetch() { fun update() { lifecycleScope.launch { - val property = Property("Vehicle.Speed", listOf(Field.FIELD_VALUE)) + val request = UpdateRequest("Vehicle.Speed", Field.FIELD_VALUE) val datapoint = Datapoint.newBuilder().setFloat(100f).build() - dataBrokerConnection?.update(property, datapoint) + dataBrokerConnection?.update(request, datapoint) } } fun subscribe() { - val property = Property("Vehicle.Speed", listOf(Field.FIELD_VALUE)) - val propertyListener = object : PropertyListener { - override fun onPropertyChanged(entryUpdates: List) { + val request = SubscribeRequest("Vehicle.Speed", Field.FIELD_VALUE) + val listener = object : VssPathListener { + override fun onEntryChanged(entryUpdates: List) { entryUpdates.forEach { entryUpdate -> val updatedValue = entryUpdate.entry - // handle property change + // handle entry change when (updatedValue.path) { "Vehicle.Speed" -> { val speed = updatedValue.value.float @@ -100,14 +100,14 @@ fun subscribe() { } } - dataBrokerConnection?.subscribe(property, propertyListener) + dataBrokerConnection?.subscribe(request, listener) } ``` *Java* ```java void fetch() { - Property property = new Property("Vehicle.Speed", Collections.singleton(Types.Field.FIELD_VALUE)); - dataBrokerConnection.fetch(property, new CoroutineCallback() { + FetchRequest request = new FetchRequest("Vehicle.Speed", Types.Field.FIELD_VALUE); + dataBrokerConnection.fetch(request, new CoroutineCallback() { @Override public void onSuccess(GetResponse result) { result.entriesList.first() // Don't forget to handle empty responses @@ -119,25 +119,25 @@ void fetch() { } void update() { - Property property = new Property("Vehicle.Speed", Collections.singleton(Types.Field.FIELD_VALUE)); Datapoint datapoint = Datapoint.newBuilder().setFloat(100f).build(); - dataBrokerConnection.update(property, datapoint, new CoroutineCallback() { + UpdateRequest request = new UpdateRequest("Vehicle.Speed", datapoint, Types.Field.FIELD_VALUE); + dataBrokerConnection.update(request, new CoroutineCallback() { @Override public void onSuccess(KuksaValV1.SetResponse result) { - // handle result + // handle result } }); } void subscribe() { - Property property = new Property("Vehicle.Speed", Collections.singleton(Types.Field.FIELD_VALUE)); - dataBrokerConnection.subscribe(property, new PropertyListener() { + SubscribeRequest request = new SubscribeRequest("Vehicle.Speed", Types.Field.FIELD_VALUE); + dataBrokerConnection.subscribe(request, new VssPathListener() { @Override - public void onPropertyChanged(@NonNull List entryUpdates) { + public void onEntryChanged(@NonNull List entryUpdates) { for (KuksaValV1.EntryUpdate entryUpdate : entryUpdates) { Types.DataEntry updatedValue = entryUpdate.getEntry(); - // handle property change + // handle entry change switch (updatedValue.getPath()) { case "Vehicle.Speed": float speed = updatedValue.getValue().getFloat(); @@ -152,11 +152,12 @@ void subscribe() { } ``` -## Specifications Symbol Processing +## VSS Model Generation -The generic nature of using the `Property` API will result into an information loss of the type which can be seen in -the `subscribe` example. You may choose to reuse the same listener for multiple properties. Then it requires an additional check -of the `vssPath` after receiving an updated value to correctly cast it back. This is feasible for simple use cases but can get tedious when working with a lot of vehicle signals. +The generic nature of using the `VSS Path` API will result into an information loss of the type which can be seen in +the `subscribe` example. You may choose to reuse the same listener for multiple VSS paths. Then it requires an additional check +of the `vssPath` after receiving an updated value to correctly cast it back. This is feasible for simple use cases but can get tedious +when working with a lot of vehicle signals. For a more convenient usage you can opt in to auto generate Kotlin models via [Symbol Processing](https://kotlinlang.org/docs/ksp-quickstart.html) of the same specification the Databroker uses. For starters you can retrieve an extensive default specification from the @@ -165,7 +166,7 @@ release page of the [COVESA Vehicle Signal Specification GitHub repository](http Currently VSS specification files in .yaml and .json format are supported by the vss-processor. *app/build.gradle.kts* -``` +```kotlin plugins { id("org.eclipse.kuksa.vss-processor-plugin") } @@ -180,26 +181,20 @@ vssProcessor { } ``` -Use the new [`VssDefinition`](https://github.com/eclipse-kuksa/kuksa-android-sdk/blob/main/vss-core/src/main/java/org/eclipse/kuksa/vsscore/annotation/VssDefinition.kt) annotation and provide the path to the specification file (Inside the assets folder). +Use the [`VssModelGenerator`](https://github.com/eclipse-kuksa/kuksa-android-sdk/blob/main/vss-core/src/main/java/org/eclipse/kuksa/vsscore/annotation/VssModelGenerator.kt) annotation. Doing so will generate a complete tree of Kotlin models which can be used in combination with the SDK API. This way you can work with safe types and the SDK takes care of all the model parsing for you. There is also a whole set of -convenience operators and extension methods to work with to manipulate the tree data. See the `VssSpecification` class documentation for this. +convenience operators and extension methods to work with to manipulate the tree data. See the `VssNode` class documentation for this. -*Kotlin* -```kotlin -@VssDefinition -class KotlinActivity -``` -*Java* -```java -@VssDefinition -public class JavaActivity +```kotlin / Java +@VssModelGenerator +class Activity ``` > [!IMPORTANT] -> Keep in mind to always synchronize the specification file between the client and the Databroker. +> Keep in mind to always synchronize a compatible (e.g. subset) VSS file between the client and the Databroker. -*Example .yaml specification file* +*Example .yaml VSS file* ```yaml Vehicle.Speed: datatype: float @@ -214,7 +209,7 @@ Vehicle.Speed: ```kotlin data class VssSpeed @JvmOverloads constructor( override val `value`: Float = 0f, -) : VssProperty { +) : VssNode { override val comment: String get() = "" @@ -230,7 +225,7 @@ data class VssSpeed @JvmOverloads constructor( override val vssPath: String get() = "Vehicle.Speed" - override val children: Set + override val children: Set get() = setOf() override val parentClass: KClass<*> @@ -238,14 +233,15 @@ data class VssSpeed @JvmOverloads constructor( } ``` -### Specification API +### API for generated VSS models *Kotlin* ```kotlin fun fetch() { lifecycleScope.launch { val vssSpeed = VssVehicle.VssSpeed() - val updatedSpeed = dataBrokerConnection?.fetch(vssSpeed) + val request = VssNodeFetchRequest(vssSpeed) + val updatedSpeed = dataBrokerConnection?.fetch(request) val speed = updatedSpeed?.value } } @@ -253,14 +249,16 @@ fun fetch() { fun update() { lifecycleScope.launch { val vssSpeed = VssVehicle.VssSpeed(value = 100f) - dataBrokerConnection?.update(vssSpeed) + val request = VssNodeUpdateRequest(vssSpeed) + dataBrokerConnection?.update(request) } } fun subscribe() { val vssSpeed = VssVehicle.VssSpeed(value = 100f) - dataBrokerConnection?.subscribe(vssSpeed, listener = object : VssSpecificationListener { - override fun onSpecificationChanged(vssSpecification: VssVehicle.VssSpeed) { + val request = VssNodeSubscribeRequest(vssSpeed) + dataBrokerConnection?.subscribe(request, listener = object : VssNodeListener { + override fun onNodeChanged(vssNode: VssVehicle.VssSpeed) { val speed = vssSpeed.value } @@ -274,9 +272,9 @@ fun subscribe() { ```java void fetch() { VssVehicle.VssSpeed vssSpeed = new VssVehicle.VssSpeed(); + VssNodeFetchRequest request = new VssNodeFetchRequest(vssSpeed) dataBrokerConnection.fetch( - vssSpeed, - Collections.singleton(Types.Field.FIELD_VALUE), + request, new CoroutineCallback() { @Override public void onSuccess(@Nullable VssVehicle.VssSpeed result) { @@ -293,9 +291,9 @@ void fetch() { void update() { VssVehicle.VssSpeed vssSpeed = new VssVehicle.VssSpeed(100f); + VssNodeUpdateRequest request = new VssNodeUpdateRequest(vssSpeed) dataBrokerConnection.update( - vssSpeed, - Collections.singleton(Types.Field.FIELD_VALUE), + request, new CoroutineCallback>() { @Override public void onSuccess(@Nullable Collection result) { @@ -312,13 +310,13 @@ void update() { void subscribe() { VssVehicle.VssSpeed vssSpeed = new VssVehicle.VssSpeed(); + VssNodeSubscribeRequest request = new VssNodeSubscribeRequest(vssSpeed) dataBrokerConnection.subscribe( - vssSpeed, - Collections.singleton(Types.Field.FIELD_VALUE), - new VssSpecificationListener() { + request, + new VssNodeListener() { @Override - public void onSpecificationChanged(@NonNull VssVehicle.VssSpeed vssSpecification) { - Float speed = vssSpecification.getValue(); + public void onNodeChanged(@NonNull VssVehicle.VssSpeed vssNode) { + Float speed = vssNode.getValue(); } @Override diff --git a/docs/kuksa-sdk_class-diagram.puml b/docs/kuksa-sdk_class-diagram.puml index 2d7eece8..95303d93 100644 --- a/docs/kuksa-sdk_class-diagram.puml +++ b/docs/kuksa-sdk_class-diagram.puml @@ -26,8 +26,7 @@ package kuksa { DataBrokerConnector -down-> DataBrokerConnection DataBrokerConnection -down-> DataBrokerTransporter DataBrokerConnection -down-> DataBrokerSubscriber - DataBrokerConnection -down-> PropertyListener - DataBrokerConnection -down-> Property + DataBrokerConnection -down-> VssPathListener DataBrokerConnection -left-> DataBrokerException DataBrokerConnection -up-> MultiListener DataBrokerSubscriber -up-> DataBrokerTransporter @@ -53,35 +52,54 @@ package kuksa { } class DataBrokerSubscriber { - + subscribe(vssPath: String, Field, PropertyListener) - + subscribe(T, Field, VssSpecificationListener) - + unsubscribe(vssPath: String, Field, PropertyListener) - + unsubscribe(T, Field, VssSpecificationListener) + + subscribe(vssPath: String, Field, VssPathListener) + + subscribe(T, Field, VssNodeListener) + + unsubscribe(vssPath: String, Field, VssPathListener) + + unsubscribe(T, Field, VssNodeListener) } class DataBrokerConnection { + disconnectListeners: MultiListener + jsonWebToken: JsonWebToken - + subscribe(Property, PropertyListener) - + subscribe(T, Collection, VssSpecificationListener) - + unsubscribe(Property, PropertyListener) - + unsubscribe(T, Collection, VssSpecificationListener) - + fetch(Property): GetResponse - + fetch(T, Collection) - + update(Property, Datapoint): SetResponse - + update(VssSpecification, Collection): SetResponse + + subscribe(SubscribeRequest, VssPathListener) + + subscribe(VssNodeSubscribeRequest, VssNodeListener) + + unsubscribe(SubscribeRequest, VssPathListener) + + unsubscribe(VssNodeSubscribeRequest, VssNodeListener) + + fetch(FetchRequest): GetResponse + + fetch(VssNodeFetchRequest) + + update(UpdateRequest): SetResponse + + update(VssNodeUpdateRequest): SetResponse + disconnect() } - interface PropertyListener { - + onPropertyChanged(List) + interface VssPathListener { + + onEntryChanged(List) + onError(Throwable) } - class Property { - + vssPath: String - + fields: Collection + package request { + interface DataBrokerRequest { + + vssPath: String + + fields: Array + } + + class UpdateRequest { + + dataPoint: Datapoint + } + + class VssNodeDataBrokerRequest { + + vssNode: T + } + + DataBrokerRequest <|-- FetchRequest + DataBrokerRequest <|-- SubscribeRequest + DataBrokerRequest <|-- UpdateRequest + DataBrokerRequest <|-- VssNodeDataBrokerRequest + + VssNodeDataBrokerRequest <|-- VssNodeFetchRequest + VssNodeDataBrokerRequest <|-- VssNodeSubscribeRequest + VssNodeDataBrokerRequest <|-- VssNodeUpdateRequest } class DataBrokerException @@ -103,5 +121,6 @@ package kuksa { DataBrokerConnector -up-> ManagedChannel DataBrokerConnection -right-> proto +DataBrokerConnection -right-> request @enduml diff --git a/docs/kuksa-vss-core_class-diagram.puml b/docs/kuksa-vss-core_class-diagram.puml index 5683f953..52d0a5cd 100644 --- a/docs/kuksa-vss-core_class-diagram.puml +++ b/docs/kuksa-vss-core_class-diagram.puml @@ -3,19 +3,14 @@ !startsub VssCore package VssCore { - VssNode <|- VssSpecification - VssSpecification <|- VssProperty + VssNode <|- VssBranch + VssNode <|-- VssSignal - annotation VssDefinition { - + vssDefinitionPath: String - } + annotation VssModelGenerator interface VssNode { - + children: Set + + children: Set + parentClass: KClass<*> - } - - interface VssSpecification { + uuid: String + vssPath: String + description: String @@ -23,7 +18,9 @@ package VssCore { + comment: String } - interface VssProperty { + interface VssBranch + + interface VssSignal { + value: T } } diff --git a/docs/kuksa-vss-processor_class-diagram.puml b/docs/kuksa-vss-processor_class-diagram.puml index 98845f48..7cd61c5b 100644 --- a/docs/kuksa-vss-processor_class-diagram.puml +++ b/docs/kuksa-vss-processor_class-diagram.puml @@ -4,39 +4,39 @@ !includesub kuksa-vss-core_class-diagram.puml!VssCore package App { - MainActivity --o VssDefinition + MainActivity --o VssModelGenerator class MainActivity } package VssProcessor { - VssDefinitionProcessor -down-> VssDefinitionVisitor - VssDefinitionProcessor -down-> VssDefinitionParser - VssDefinitionProcessor -down-> SpecModel - VssDefinitionParser <|- YamlDefinitionParser - SpecModel <|- VssSpecificationSpecModel - VssSpecification <|- VssSpecificationSpecModel + VssModelGeneratorProcessor -down-> VssModelGeneratorVisitor + VssModelGeneratorProcessor -down-> VssParser + VssModelGeneratorProcessor -down-> SpecModel + VssParser <|- YamlVssParser + SpecModel <|- VssNodeSpecModel + VssNode <|-- VssNodeSpecModel - class VssDefinitionProcessor { - + VssDefinitionProcessor(CodeGenerator, KSPLogger) + class VssModelGeneratorProcessor { + + VssModelGeneratorProcessor(CodeGenerator, KSPLogger) } - class VssDefinitionVisitor { + class VssModelGeneratorVisitor { + visitClassDeclaration(KSClassDeclaration) } - interface VssDefinitionParser { - + parseSpecifications(File, elementDelimiter: String,): Collection + interface VssParser { + + parseNodes(File, elementDelimiter: String,): Collection } - class YamlDefinitionParser + class YamlVssParser interface SpecModel> { - + createClassSpec(packageName: String, relatedSpecifications: Collection, nestedClasses: Collection): TypeSpec + + createClassSpec(packageName: String, relatedNodes: Collection, nestedClasses: Collection): TypeSpec } - class VssSpecificationSpecModel { + class VssNodeSpecModel { } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 05495bc8..eb16ec18 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,6 +1,6 @@ [versions] activityKtx = "1.8.2" -androidGradlePlugin = "8.2.2" # Check with detekt table first: https://detekt.dev/docs/introduction/compatibility/ +androidGradlePlugin = "8.3.0" # Check with detekt table first: https://detekt.dev/docs/introduction/compatibility/ detekt = "1.23.5" datastore = "1.0.0" constraintlayoutCompose = "1.0.1" @@ -10,7 +10,7 @@ gson = "2.10.1" kotlin = "1.9.22" kotlinpoet = "1.16.0" kotlinxSerializationJson = "1.6.1" -runtimeLivedata = "1.6.0" +runtimeLivedata = "1.6.2" symbolProcessingApi = "1.9.22-1.0.17" tomcatAnnotations = "6.0.53" ktlint = "0.0" # Maintained inside ktlint.gradle.kts @@ -23,7 +23,7 @@ mockk = "1.13.7" androidxLifecycle = "2.7.0" kotlinxCoroutines = "1.7.3" kotlinCompilerExtension = "1.5.9" -composeBom = "2024.01.00" +composeBom = "2024.02.01" jvmTarget = "17" [libraries] diff --git a/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/DataBrokerConnection.kt b/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/DataBrokerConnection.kt deleted file mode 100644 index ee7db7f9..00000000 --- a/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/DataBrokerConnection.kt +++ /dev/null @@ -1,245 +0,0 @@ -/* - * Copyright (c) 2023 Contributors to the Eclipse Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - * - */ - -package org.eclipse.kuksa - -import android.util.Log -import io.grpc.ConnectivityState -import io.grpc.ManagedChannel -import kotlinx.coroutines.CoroutineDispatcher -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext -import org.eclipse.kuksa.authentication.JsonWebToken -import org.eclipse.kuksa.extension.TAG -import org.eclipse.kuksa.extension.copy -import org.eclipse.kuksa.extension.datapoint -import org.eclipse.kuksa.model.Property -import org.eclipse.kuksa.pattern.listener.MultiListener -import org.eclipse.kuksa.proto.v1.KuksaValV1.GetResponse -import org.eclipse.kuksa.proto.v1.KuksaValV1.SetResponse -import org.eclipse.kuksa.proto.v1.Types -import org.eclipse.kuksa.proto.v1.Types.Datapoint -import org.eclipse.kuksa.proto.v1.Types.Field -import org.eclipse.kuksa.subscription.DataBrokerSubscriber -import org.eclipse.kuksa.vsscore.model.VssProperty -import org.eclipse.kuksa.vsscore.model.VssSpecification -import org.eclipse.kuksa.vsscore.model.heritage -import org.eclipse.kuksa.vsscore.model.vssProperties -import kotlin.properties.Delegates - -/** - * The DataBrokerConnection holds an active connection to the DataBroker. The Connection can be use to interact with the - * DataBroker. - */ -class DataBrokerConnection internal constructor( - private val managedChannel: ManagedChannel, - private val dispatcher: CoroutineDispatcher = Dispatchers.Default, - private val dataBrokerTransporter: DataBrokerTransporter = DataBrokerTransporter( - managedChannel, - dispatcher, - ), - private val dataBrokerSubscriber: DataBrokerSubscriber = DataBrokerSubscriber(dataBrokerTransporter), -) { - /** - * Used to register and unregister multiple [DisconnectListener]. - */ - val disconnectListeners = MultiListener() - - /** - * A JsonWebToken can be provided to authenticate against the DataBroker. - */ - var jsonWebToken: JsonWebToken? by Delegates.observable(null) { _, _, newValue -> - dataBrokerTransporter.jsonWebToken = newValue - } - - init { - val state = managedChannel.getState(false) - managedChannel.notifyWhenStateChanged(state) { - val newState = managedChannel.getState(false) - Log.d(TAG, "DataBrokerConnection state changed: $newState") - if (newState != ConnectivityState.SHUTDOWN) { - managedChannel.shutdownNow() - } - - disconnectListeners.forEach { listener -> - listener.onDisconnect() - } - } - } - - /** - * Subscribes to the specified [property] and notifies the provided [propertyListener] about updates. - * - * Throws a [DataBrokerException] in case the connection to the DataBroker is no longer active - */ - fun subscribe( - property: Property, - propertyListener: PropertyListener, - ) { - val vssPath = property.vssPath - property.fields.forEach { field -> - dataBrokerSubscriber.subscribe(vssPath, field, propertyListener) - } - } - - /** - * Unsubscribes the [propertyListener] from updates of the specified [property]. - */ - fun unsubscribe( - property: Property, - propertyListener: PropertyListener, - ) { - val vssPath = property.vssPath - property.fields.forEach { field -> - dataBrokerSubscriber.unsubscribe(vssPath, field, propertyListener) - } - } - - /** - * Subscribes to the specified [VssSpecification] with the provided [VssSpecificationListener]. Only a [VssProperty] - * can be subscribed because they have an actual value. When provided with any parent [VssSpecification] then this - * [subscribe] method will find all [VssProperty] children and subscribes them instead. Once subscribed the - * application will be notified about any changes to every subscribed [VssProperty]. The [fields] can be used to - * subscribe to different information of the [specification]. The default for the [fields] parameter is a list with - * a single [Types.Field.FIELD_VALUE] entry. - * - * @throws DataBrokerException in case the connection to the DataBroker is no longer active - */ - @JvmOverloads - fun subscribe( - specification: T, - fields: Collection = listOf(Field.FIELD_VALUE), - listener: VssSpecificationListener, - ) { - fields.forEach { field -> - dataBrokerSubscriber.subscribe(specification, field, listener) - } - } - - /** - * Unsubscribes the [listener] from updates of the specified [fields] and [specification]. - */ - fun unsubscribe( - specification: T, - fields: Collection = listOf(Field.FIELD_VALUE), - listener: VssSpecificationListener, - ) { - fields.forEach { field -> - dataBrokerSubscriber.unsubscribe(specification, field, listener) - } - } - - /** - * Retrieves the underlying property of the specified vssPath and returns it to the corresponding Callback. - * - * @throws DataBrokerException in case the connection to the DataBroker is no longer active - */ - suspend fun fetch(property: Property): GetResponse { - Log.d(TAG, "fetchProperty() called with: property: $property") - return dataBrokerTransporter.fetch(property.vssPath, property.fields) - } - - /** - * Retrieves the [VssSpecification] and returns it. The retrieved [VssSpecification] - * is of the same type as the inputted one. All underlying heirs are changed to reflect the data broker state. - * The [fields] can be used to subscribe to different information of the [specification]. The default for the - * [fields] parameter is a list with a single [Types.Field.FIELD_VALUE] entry. - * - * @throws DataBrokerException in case the connection to the DataBroker is no longer active - */ - @Suppress("exceptions:TooGenericExceptionCaught") // Handling is bundled together - @JvmOverloads - suspend fun fetch( - specification: T, - fields: Collection = listOf(Field.FIELD_VALUE), - ): T { - return withContext(dispatcher) { - try { - val property = Property(specification.vssPath, fields) - val response = fetch(property) - val entries = response.entriesList - - if (entries.isEmpty()) { - Log.w(TAG, "No entries found for fetched specification!") - return@withContext specification - } - - // Update every heir specification - // TODO: Can be optimized to not replace the whole heritage line for every child entry one by one - var updatedSpecification: T = specification - val heritage = updatedSpecification.heritage - entries.forEach { entry -> - updatedSpecification = updatedSpecification.copy(entry.path, entry.value, heritage) - } - - return@withContext updatedSpecification - } catch (e: Exception) { - throw DataBrokerException(e.message, e) - } - } - } - - /** - * Updates the underlying property of the specified vssPath with the updatedProperty. Notifies the callback - * about (un)successful operation. - * - * @throws DataBrokerException in case the connection to the DataBroker is no longer active - */ - suspend fun update( - property: Property, - datapoint: Datapoint, - ): SetResponse { - Log.d(TAG, "updateProperty() called with: updatedProperty = $property") - return dataBrokerTransporter.update(property.vssPath, property.fields, datapoint) - } - - /** - * Only a [VssProperty] can be updated because they have an actual value. When provided with any parent - * [VssSpecification] then this [update] method will find all [VssProperty] children and updates their corresponding - * [fields] instead. - * Compared to [update] with only one [Property] and [Datapoint], here multiple [SetResponse] will be returned - * because a [VssSpecification] may consists of multiple values which may need to be updated. - * - * @throws DataBrokerException in case the connection to the DataBroker is no longer active - * @throws IllegalArgumentException if the [VssProperty] could not be converted to a [Datapoint]. - */ - @JvmOverloads - suspend fun update( - vssSpecification: VssSpecification, - fields: Collection = listOf(Field.FIELD_VALUE), - ): Collection { - val responses = mutableListOf() - - vssSpecification.vssProperties.forEach { vssProperty -> - val property = Property(vssProperty.vssPath, fields) - val response = update(property, vssProperty.datapoint) - responses.add(response) - } - - return responses - } - - /** - * Disconnect from the DataBroker. - */ - fun disconnect() { - Log.d(TAG, "disconnect() called") - managedChannel.shutdownNow() - } -} diff --git a/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/authentication/JsonWebToken.kt b/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/connectivity/authentication/JsonWebToken.kt similarity index 91% rename from kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/authentication/JsonWebToken.kt rename to kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/connectivity/authentication/JsonWebToken.kt index a4c37603..5360b8b8 100644 --- a/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/authentication/JsonWebToken.kt +++ b/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/connectivity/authentication/JsonWebToken.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Contributors to the Eclipse Foundation + * Copyright (c) 2023 Contributors to the Eclipse Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,10 +14,9 @@ * limitations under the License. * * SPDX-License-Identifier: Apache-2.0 - * */ -package org.eclipse.kuksa.authentication +package org.eclipse.kuksa.connectivity.authentication /** * A JsonWebToken can be used to authenticate against the DataBroker. For authentication to work the DataBroker must be diff --git a/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/authentication/VALStubExtension.kt b/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/connectivity/authentication/VALStubExtension.kt similarity index 93% rename from kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/authentication/VALStubExtension.kt rename to kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/connectivity/authentication/VALStubExtension.kt index 7150f76b..be14d9bd 100644 --- a/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/authentication/VALStubExtension.kt +++ b/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/connectivity/authentication/VALStubExtension.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Contributors to the Eclipse Foundation + * Copyright (c) 2023 Contributors to the Eclipse Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,10 +14,9 @@ * limitations under the License. * * SPDX-License-Identifier: Apache-2.0 - * */ -package org.eclipse.kuksa.authentication +package org.eclipse.kuksa.connectivity.authentication import com.google.common.net.HttpHeaders import io.grpc.ClientInterceptor diff --git a/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/connectivity/databroker/DataBrokerConnection.kt b/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/connectivity/databroker/DataBrokerConnection.kt new file mode 100644 index 00000000..3134f3ee --- /dev/null +++ b/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/connectivity/databroker/DataBrokerConnection.kt @@ -0,0 +1,248 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.eclipse.kuksa.connectivity.databroker + +import android.util.Log +import io.grpc.ConnectivityState +import io.grpc.ManagedChannel +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import org.eclipse.kuksa.connectivity.authentication.JsonWebToken +import org.eclipse.kuksa.connectivity.databroker.listener.DisconnectListener +import org.eclipse.kuksa.connectivity.databroker.listener.VssNodeListener +import org.eclipse.kuksa.connectivity.databroker.listener.VssPathListener +import org.eclipse.kuksa.connectivity.databroker.request.FetchRequest +import org.eclipse.kuksa.connectivity.databroker.request.SubscribeRequest +import org.eclipse.kuksa.connectivity.databroker.request.UpdateRequest +import org.eclipse.kuksa.connectivity.databroker.request.VssNodeFetchRequest +import org.eclipse.kuksa.connectivity.databroker.request.VssNodeSubscribeRequest +import org.eclipse.kuksa.connectivity.databroker.request.VssNodeUpdateRequest +import org.eclipse.kuksa.connectivity.databroker.subscription.DataBrokerSubscriber +import org.eclipse.kuksa.extension.TAG +import org.eclipse.kuksa.extension.datapoint +import org.eclipse.kuksa.extension.vss.copy +import org.eclipse.kuksa.pattern.listener.MultiListener +import org.eclipse.kuksa.proto.v1.KuksaValV1.GetResponse +import org.eclipse.kuksa.proto.v1.KuksaValV1.SetResponse +import org.eclipse.kuksa.proto.v1.Types +import org.eclipse.kuksa.proto.v1.Types.Datapoint +import org.eclipse.kuksa.vsscore.model.VssNode +import org.eclipse.kuksa.vsscore.model.VssSignal +import org.eclipse.kuksa.vsscore.model.heritage +import org.eclipse.kuksa.vsscore.model.vssSignals +import kotlin.properties.Delegates + +/** + * The DataBrokerConnection holds an active connection to the DataBroker. The Connection can be use to interact with the + * DataBroker. + */ +class DataBrokerConnection internal constructor( + private val managedChannel: ManagedChannel, + private val dispatcher: CoroutineDispatcher = Dispatchers.Default, + private val dataBrokerTransporter: DataBrokerTransporter = DataBrokerTransporter( + managedChannel, + dispatcher, + ), + private val dataBrokerSubscriber: DataBrokerSubscriber = DataBrokerSubscriber(dataBrokerTransporter), +) { + /** + * Used to register and unregister multiple [DisconnectListener]. + */ + val disconnectListeners = MultiListener() + + /** + * A JsonWebToken can be provided to authenticate against the DataBroker. + */ + var jsonWebToken: JsonWebToken? by Delegates.observable(null) { _, _, newValue -> + dataBrokerTransporter.jsonWebToken = newValue + } + + init { + val state = managedChannel.getState(false) + managedChannel.notifyWhenStateChanged(state) { + val newState = managedChannel.getState(false) + Log.d(TAG, "DataBrokerConnection state changed: $newState") + if (newState != ConnectivityState.SHUTDOWN) { + managedChannel.shutdownNow() + } + + disconnectListeners.forEach { listener -> + listener.onDisconnect() + } + } + } + + /** + * Subscribes to the specified [request] and notifies the provided [listener] about updates. + * + * Throws a [DataBrokerException] in case the connection to the DataBroker is no longer active + */ + fun subscribe( + request: SubscribeRequest, + listener: VssPathListener, + ) { + val vssPath = request.vssPath + request.fields.forEach { field -> + dataBrokerSubscriber.subscribe(vssPath, field, listener) + } + } + + /** + * Unsubscribes the [listener] from updates of the specified [request]. + */ + fun unsubscribe( + request: SubscribeRequest, + listener: VssPathListener, + ) { + val vssPath = request.vssPath + request.fields.forEach { field -> + dataBrokerSubscriber.unsubscribe(vssPath, field, listener) + } + } + + /** + * Subscribes to the specified [VssNode] with the provided [VssNodeListener]. Only a [VssSignal] + * can be subscribed because they have an actual value. When provided with any parent [VssNode] then this + * [subscribe] method will find all [VssSignal] children and subscribes them instead. Once subscribed the + * application will be notified about any changes to every subscribed [VssSignal]. + * The [VssNodeSubscribeRequest.fields] can be used to subscribe to different information of the [VssNode]. + * The default for the [Types.Field] parameter is a list with a single [Types.Field.FIELD_VALUE] entry. + * + * @throws DataBrokerException in case the connection to the DataBroker is no longer active + */ + fun subscribe( + request: VssNodeSubscribeRequest, + listener: VssNodeListener, + ) { + val fields = request.fields + val vssNode = request.vssNode + fields.forEach { field -> + dataBrokerSubscriber.subscribe(vssNode, field, listener) + } + } + + /** + * Unsubscribes the [listener] from updates of the specified [VssNodeSubscribeRequest.fields] and + * [VssNodeSubscribeRequest.vssNode]. + */ + fun unsubscribe( + request: VssNodeSubscribeRequest, + listener: VssNodeListener, + ) { + val fields = request.fields + val vssNode = request.vssNode + fields.forEach { field -> + dataBrokerSubscriber.unsubscribe(vssNode, field, listener) + } + } + + /** + * Retrieves the underlying data broker information of the specified vssPath and returns it to the corresponding + * callback. + * + * @throws DataBrokerException in case the connection to the DataBroker is no longer active + */ + suspend fun fetch(request: FetchRequest): GetResponse { + Log.d(TAG, "Fetching via request: $request") + return dataBrokerTransporter.fetch(request.vssPath, request.fields.toSet()) + } + + /** + * Retrieves the [VssNode] and returns it. The retrieved [VssNode] + * is of the same type as the inputted one. All underlying heirs are changed to reflect the data broker state. + * + * @throws DataBrokerException in case the connection to the DataBroker is no longer active + */ + + // SpreadOperator: Neglectable - Field types are 1-2 elements mostly + // TooGenericExceptionCaught: Handling is bundled together + @Suppress("exceptions:TooGenericExceptionCaught", "performance:SpreadOperator") + suspend fun fetch(request: VssNodeFetchRequest): T { + return withContext(dispatcher) { + try { + val vssNode = request.vssNode + val simpleFetchRequest = FetchRequest(request.vssPath, *request.fields) + val response = fetch(simpleFetchRequest) + val entries = response.entriesList + + if (entries.isEmpty()) { + Log.w(TAG, "No entries found for fetched VssNode!") + return@withContext vssNode + } + + // Update every heir node + // TODO: Can be optimized to not replace the whole heritage line for every child entry one by one + var updatedVssNode: T = vssNode + val heritage = updatedVssNode.heritage + entries.forEach { entry -> + updatedVssNode = updatedVssNode.copy(entry.path, entry.value, heritage) + } + + return@withContext updatedVssNode + } catch (e: Exception) { + throw DataBrokerException(e.message, e) + } + } + } + + /** + * Updates the underlying data broker property of the specified [UpdateRequest.vssPath] with the + * [UpdateRequest.dataPoint]. Notifies the callback about (un)successful operation. + * + * @throws DataBrokerException in case the connection to the DataBroker is no longer active + */ + suspend fun update(request: UpdateRequest): SetResponse { + Log.d(TAG, "Update with request: $request") + return dataBrokerTransporter.update(request.vssPath, request.dataPoint, request.fields.toSet()) + } + + /** + * Only a [VssSignal] can be updated because they have an actual value. When provided with any parent + * [VssNode] then this [update] method will find all [VssSignal] children and updates their corresponding + * [Types.Field] instead. + * Compared to [update] with only one [UpdateRequest], here multiple [SetResponse] will be returned + * because a [VssNode] may consists of multiple values which may need to be updated. + * + * @throws DataBrokerException in case the connection to the DataBroker is no longer active + * @throws IllegalArgumentException if the [VssSignal] could not be converted to a [Datapoint]. + */ + @Suppress("performance:SpreadOperator") // Neglectable: Field types are 1-2 elements mostly + suspend fun update(request: VssNodeUpdateRequest): Collection { + val responses = mutableListOf() + val vssNode = request.vssNode + + vssNode.vssSignals.forEach { signal -> + val simpleUpdateRequest = UpdateRequest(signal.vssPath, signal.datapoint, *request.fields) + val response = update(simpleUpdateRequest) + + responses.add(response) + } + + return responses + } + + /** + * Disconnect from the DataBroker. + */ + fun disconnect() { + Log.d(TAG, "disconnect() called") + managedChannel.shutdownNow() + } +} diff --git a/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/DataBrokerConnector.kt b/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/connectivity/databroker/DataBrokerConnector.kt similarity index 95% rename from kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/DataBrokerConnector.kt rename to kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/connectivity/databroker/DataBrokerConnector.kt index 9d885395..1e09d11e 100644 --- a/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/DataBrokerConnector.kt +++ b/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/connectivity/databroker/DataBrokerConnector.kt @@ -14,10 +14,9 @@ * limitations under the License. * * SPDX-License-Identifier: Apache-2.0 - * */ -package org.eclipse.kuksa +package org.eclipse.kuksa.connectivity.databroker import android.util.Log import io.grpc.ConnectivityState @@ -26,8 +25,9 @@ import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.withContext -import org.eclipse.kuksa.authentication.JsonWebToken +import org.eclipse.kuksa.connectivity.authentication.JsonWebToken import org.eclipse.kuksa.extension.TAG +import org.eclipse.kuksa.model.TimeoutConfig /** * The DataBrokerConnector is used to establish a successful connection to the DataBroker. The communication takes @@ -38,7 +38,6 @@ class DataBrokerConnector @JvmOverloads constructor( private val jsonWebToken: JsonWebToken? = null, private val defaultDispatcher: CoroutineDispatcher = Dispatchers.Default, ) { - /** * Configuration to be used during connection. */ diff --git a/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/DataBrokerException.kt b/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/connectivity/databroker/DataBrokerException.kt similarity index 96% rename from kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/DataBrokerException.kt rename to kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/connectivity/databroker/DataBrokerException.kt index 49edda33..e31cc12b 100644 --- a/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/DataBrokerException.kt +++ b/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/connectivity/databroker/DataBrokerException.kt @@ -14,10 +14,9 @@ * limitations under the License. * * SPDX-License-Identifier: Apache-2.0 - * */ -package org.eclipse.kuksa +package org.eclipse.kuksa.connectivity.databroker /** * An Exception which will be thrown when there are problems with the connection to the DataBroker. diff --git a/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/DataBrokerTransporter.kt b/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/connectivity/databroker/DataBrokerTransporter.kt similarity index 90% rename from kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/DataBrokerTransporter.kt rename to kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/connectivity/databroker/DataBrokerTransporter.kt index f6d3482b..5df67882 100644 --- a/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/DataBrokerTransporter.kt +++ b/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/connectivity/databroker/DataBrokerTransporter.kt @@ -14,10 +14,9 @@ * limitations under the License. * * SPDX-License-Identifier: Apache-2.0 - * */ -package org.eclipse.kuksa +package org.eclipse.kuksa.connectivity.databroker import android.util.Log import io.grpc.ConnectivityState @@ -28,8 +27,9 @@ import io.grpc.stub.StreamObserver import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext -import org.eclipse.kuksa.authentication.JsonWebToken -import org.eclipse.kuksa.authentication.withAuthenticationInterceptor +import org.eclipse.kuksa.connectivity.authentication.JsonWebToken +import org.eclipse.kuksa.connectivity.authentication.withAuthenticationInterceptor +import org.eclipse.kuksa.connectivity.databroker.subscription.DataBrokerSubscription import org.eclipse.kuksa.extension.TAG import org.eclipse.kuksa.extension.applyDatapoint import org.eclipse.kuksa.proto.v1.KuksaValV1 @@ -37,12 +37,11 @@ import org.eclipse.kuksa.proto.v1.KuksaValV1.SubscribeResponse import org.eclipse.kuksa.proto.v1.Types import org.eclipse.kuksa.proto.v1.Types.Field import org.eclipse.kuksa.proto.v1.VALGrpc -import org.eclipse.kuksa.subscription.Subscription /** * Encapsulates the Protobuf-specific interactions with the DataBroker send over gRPC. Provides fetch, update and * subscribe methods to retrieve and update data, as well as registering to be notified about external data updates - * using a [Subscription]. + * using a [DataBrokerSubscription]. * The DataBrokerTransporter requires a [managedChannel] which is already connected to the corresponding DataBroker. * * @throws IllegalStateException in case the state of the [managedChannel] is not [ConnectivityState.READY] @@ -77,7 +76,7 @@ internal class DataBrokerTransporter( val blockingStub = VALGrpc.newBlockingStub(managedChannel) val entryRequest = KuksaValV1.EntryRequest.newBuilder() .setPath(vssPath) - .addAllFields(fields) + .addAllFields(fields.toSet()) .build() val request = KuksaValV1.GetRequest.newBuilder() .addEntries(entryRequest) @@ -101,8 +100,8 @@ internal class DataBrokerTransporter( */ suspend fun update( vssPath: String, - fields: Collection, updatedDatapoint: Types.Datapoint, + fields: Collection, ): KuksaValV1.SetResponse { return withContext(defaultDispatcher) { val blockingStub = VALGrpc.newBlockingStub(managedChannel) @@ -135,15 +134,15 @@ internal class DataBrokerTransporter( /** * Sends a request to the DataBroker to subscribe to updates of the specified [vssPath] and [field]. - * Returns a [Subscription] which can be used to register or unregister additional listeners or cancel / closing - * the subscription. + * Returns a [DataBrokerSubscription] which can be used to register or unregister additional listeners or + * cancel / closing the subscription. * * @throws DataBrokerException in case the connection to the DataBroker is no longer active */ fun subscribe( vssPath: String, field: Field, - ): Subscription { + ): DataBrokerSubscription { val asyncStub = VALGrpc.newStub(managedChannel) val subscribeEntry = KuksaValV1.SubscribeEntry.newBuilder() @@ -158,11 +157,11 @@ internal class DataBrokerTransporter( val currentContext = Context.current() val cancellableContext = currentContext.withCancellation() - val subscription = Subscription(vssPath, field, cancellableContext) + val subscription = DataBrokerSubscription(vssPath, field, cancellableContext) val streamObserver = object : StreamObserver { override fun onNext(value: SubscribeResponse) { subscription.listeners.forEach { observer -> - observer.onPropertyChanged(value.updatesList) + observer.onEntryChanged(value.updatesList) } subscription.lastSubscribeResponse = value diff --git a/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/DisconnectListener.kt b/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/connectivity/databroker/listener/DisconnectListener.kt similarity index 83% rename from kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/DisconnectListener.kt rename to kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/connectivity/databroker/listener/DisconnectListener.kt index c61a5706..ae8e4fa5 100644 --- a/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/DisconnectListener.kt +++ b/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/connectivity/databroker/listener/DisconnectListener.kt @@ -14,15 +14,15 @@ * limitations under the License. * * SPDX-License-Identifier: Apache-2.0 - * */ -package org.eclipse.kuksa +package org.eclipse.kuksa.connectivity.databroker.listener import org.eclipse.kuksa.pattern.listener.Listener /** - * The [DisconnectListener] can be registered to [DataBrokerConnection.disconnectListeners] + * The [DisconnectListener] can be registered to + * [org.eclipse.kuksa.connectivity.databroker.DataBrokerConnection.disconnectListeners] * When registered it will notify about manual or unexpected connection disconnects from the DataBroker. */ fun interface DisconnectListener : Listener { diff --git a/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/PropertyListener.kt b/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/connectivity/databroker/listener/VssPathListener.kt similarity index 58% rename from kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/PropertyListener.kt rename to kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/connectivity/databroker/listener/VssPathListener.kt index 50f0af6a..90401c0b 100644 --- a/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/PropertyListener.kt +++ b/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/connectivity/databroker/listener/VssPathListener.kt @@ -14,25 +14,24 @@ * limitations under the License. * * SPDX-License-Identifier: Apache-2.0 - * */ -package org.eclipse.kuksa +package org.eclipse.kuksa.connectivity.databroker.listener import org.eclipse.kuksa.pattern.listener.Listener import org.eclipse.kuksa.proto.v1.KuksaValV1 -import org.eclipse.kuksa.vsscore.model.VssSpecification +import org.eclipse.kuksa.vsscore.model.VssNode /** - * The Listener is used to notify about changes to subscribed properties. When registering the listener to - * Vehicle.ADAS.ABS this listener will also be notified about changes of sub-properties e.g. Vehicle.ADAS.ABS.IsEnabled - * or Vehicle.ADAS.ABS.IsEngaged. + * The Listener is used to notify about changes to subscribed VSS paths. When registering the + * listener to e.g. Vehicle.ADAS.ABS this listener will also be notified about changes of the children + * Vehicle.ADAS.ABS.IsEnabled or Vehicle.ADAS.ABS.IsEngaged. */ -interface PropertyListener : Listener { +interface VssPathListener : Listener { /** * Will be triggered with a list of [entryUpdates] of the corresponding field. */ - fun onPropertyChanged(entryUpdates: List) + fun onEntryChanged(entryUpdates: List) /** * Will be triggered when an error happens during subscription and forwards the [throwable]. @@ -41,15 +40,15 @@ interface PropertyListener : Listener { } /** - * The Listener is used to notify about subscribed [VssSpecification]. If a [VssSpecification] has children - * then [onSpecificationChanged] will be called on every value change for every children. + * The Listener is used to notify about subscribed [VssNode]. If a [VssNode] has children + * then [onNodeChanged] will be called on every value change for every children. */ -interface VssSpecificationListener { +interface VssNodeListener : Listener { /** - * Will be triggered with the [vssSpecification] when the underlying vssPath changed it's value or to inform about + * Will be triggered with the [vssNode] when the underlying vssPath changed it's value or to inform about * the initial state. */ - fun onSpecificationChanged(vssSpecification: T) + fun onNodeChanged(vssNode: T) /** * Will be triggered when an error happens during subscription and forwards the [throwable]. diff --git a/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/model/Property.kt b/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/connectivity/databroker/request/DataBrokerRequest.kt similarity index 63% rename from kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/model/Property.kt rename to kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/connectivity/databroker/request/DataBrokerRequest.kt index b5bffda8..dfb44e1d 100644 --- a/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/model/Property.kt +++ b/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/connectivity/databroker/request/DataBrokerRequest.kt @@ -14,25 +14,25 @@ * limitations under the License. * * SPDX-License-Identifier: Apache-2.0 - * */ -package org.eclipse.kuksa.model +package org.eclipse.kuksa.connectivity.databroker.request import org.eclipse.kuksa.proto.v1.Types.Field import org.eclipse.kuksa.proto.v1.Types.Field.FIELD_VALUE /** - * A DataBroker Property. + * Consists of request information for the [org.eclipse.kuksa.connectivity.databroker.DataBrokerConnection]. */ -data class Property( +interface DataBrokerRequest { /** - * The VehicleSignalSpecification path. + * The VehicleSignalSpecification (VSS) path. */ - val vssPath: String, + val vssPath: String /** - * The corresponding field type of the Property. The default is [FIELD_VALUE]. + * The corresponding field type(s) of the [vssPath] request. The [fields] can be used to subscribe to different + * information. The default is [FIELD_VALUE]. */ - val fields: Collection = listOf(FIELD_VALUE), -) + val fields: Array +} diff --git a/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/connectivity/databroker/request/FetchRequest.kt b/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/connectivity/databroker/request/FetchRequest.kt new file mode 100644 index 00000000..db7a7cca --- /dev/null +++ b/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/connectivity/databroker/request/FetchRequest.kt @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.eclipse.kuksa.connectivity.databroker.request + +import org.eclipse.kuksa.proto.v1.Types + +/** + * Used for fetch requests with [org.eclipse.kuksa.connectivity.databroker.DataBrokerConnection.fetch]. + */ +open class FetchRequest @JvmOverloads constructor( + override val vssPath: String, + override vararg val fields: Types.Field = arrayOf(Types.Field.FIELD_VALUE), +) : DataBrokerRequest diff --git a/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/connectivity/databroker/request/SubscribeRequest.kt b/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/connectivity/databroker/request/SubscribeRequest.kt new file mode 100644 index 00000000..f0201598 --- /dev/null +++ b/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/connectivity/databroker/request/SubscribeRequest.kt @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.eclipse.kuksa.connectivity.databroker.request + +import org.eclipse.kuksa.proto.v1.Types + +/** + * Used for subscribe requests with [org.eclipse.kuksa.connectivity.databroker.DataBrokerConnection.subscribe]. + */ +open class SubscribeRequest @JvmOverloads constructor( + override val vssPath: String, + override vararg val fields: Types.Field = arrayOf(Types.Field.FIELD_VALUE), +) : DataBrokerRequest diff --git a/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/connectivity/databroker/request/UpdateRequest.kt b/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/connectivity/databroker/request/UpdateRequest.kt new file mode 100644 index 00000000..310b6c4e --- /dev/null +++ b/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/connectivity/databroker/request/UpdateRequest.kt @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.eclipse.kuksa.connectivity.databroker.request + +import org.eclipse.kuksa.proto.v1.Types +import org.eclipse.kuksa.proto.v1.Types.Datapoint + +/** + * Used for update requests with [org.eclipse.kuksa.connectivity.databroker.DataBrokerConnection.update]. + */ +open class UpdateRequest @JvmOverloads constructor( + override val vssPath: String, + val dataPoint: Datapoint, + override vararg val fields: Types.Field = arrayOf(Types.Field.FIELD_VALUE), +) : DataBrokerRequest diff --git a/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/connectivity/databroker/request/VssNodeDataBrokerRequest.kt b/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/connectivity/databroker/request/VssNodeDataBrokerRequest.kt new file mode 100644 index 00000000..c4afd34f --- /dev/null +++ b/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/connectivity/databroker/request/VssNodeDataBrokerRequest.kt @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.eclipse.kuksa.connectivity.databroker.request + +import org.eclipse.kuksa.vsscore.model.VssNode + +/** + * Uses [VssNode] models to request information for the [org.eclipse.kuksa.connectivity.databroker.DataBrokerConnection] + * which are generated by the [org.eclipse.kuksa.vsscore.annotation.VssModelGenerator]. + */ +interface VssNodeDataBrokerRequest : DataBrokerRequest { + /** + * A generated [VssNode] model. + */ + val vssNode: T +} diff --git a/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/extension/VssSpecificationExtension.kt b/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/connectivity/databroker/request/VssNodeFetchRequest.kt similarity index 56% rename from kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/extension/VssSpecificationExtension.kt rename to kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/connectivity/databroker/request/VssNodeFetchRequest.kt index e7c9a579..6e0f3ab4 100644 --- a/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/extension/VssSpecificationExtension.kt +++ b/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/connectivity/databroker/request/VssNodeFetchRequest.kt @@ -16,20 +16,19 @@ * SPDX-License-Identifier: Apache-2.0 */ -package org.eclipse.kuksa.extension +package org.eclipse.kuksa.connectivity.databroker.request -import org.eclipse.kuksa.model.Property import org.eclipse.kuksa.proto.v1.Types -import org.eclipse.kuksa.vsscore.model.VssProperty -import org.eclipse.kuksa.vsscore.model.VssSpecification -import org.eclipse.kuksa.vsscore.model.vssProperties +import org.eclipse.kuksa.vsscore.model.VssNode /** - * Finds all [VssProperty] heirs for the [VssSpecification] and converts them into a collection of [Property]. + * Used for fetch requests with a generated [VssNode] model and + * [org.eclipse.kuksa.connectivity.databroker.DataBrokerConnection.fetch]. */ -fun VssSpecification.createProperties( - vararg fields: Types.Field = arrayOf(Types.Field.FIELD_VALUE), -): Collection { - return vssProperties - .map { Property(it.vssPath, fields.toSet()) } +class VssNodeFetchRequest @JvmOverloads constructor( + override val vssNode: T, + override vararg val fields: Types.Field = arrayOf(Types.Field.FIELD_VALUE), +) : VssNodeDataBrokerRequest { + override val vssPath: String + get() = vssNode.vssPath } diff --git a/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/connectivity/databroker/request/VssNodeSubscribeRequest.kt b/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/connectivity/databroker/request/VssNodeSubscribeRequest.kt new file mode 100644 index 00000000..76a806a2 --- /dev/null +++ b/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/connectivity/databroker/request/VssNodeSubscribeRequest.kt @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.eclipse.kuksa.connectivity.databroker.request + +import org.eclipse.kuksa.proto.v1.Types +import org.eclipse.kuksa.vsscore.model.VssNode + +/** + * Used for subscribe requests with a generated [VssNode] model and + * [org.eclipse.kuksa.connectivity.databroker.DataBrokerConnection.subscribe]. + */ +class VssNodeSubscribeRequest @JvmOverloads constructor( + override val vssNode: T, + override vararg val fields: Types.Field = arrayOf(Types.Field.FIELD_VALUE), +) : VssNodeDataBrokerRequest { + override val vssPath: String + get() = vssNode.vssPath +} diff --git a/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/connectivity/databroker/request/VssNodeUpdateRequest.kt b/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/connectivity/databroker/request/VssNodeUpdateRequest.kt new file mode 100644 index 00000000..fb8d69d8 --- /dev/null +++ b/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/connectivity/databroker/request/VssNodeUpdateRequest.kt @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.eclipse.kuksa.connectivity.databroker.request + +import org.eclipse.kuksa.proto.v1.Types +import org.eclipse.kuksa.vsscore.model.VssNode + +/** + * Used for update requests with a generated [VssNode] model and + * [org.eclipse.kuksa.connectivity.databroker.DataBrokerConnection.update]. + */ +class VssNodeUpdateRequest @JvmOverloads constructor( + override val vssNode: T, + override vararg val fields: Types.Field = arrayOf(Types.Field.FIELD_VALUE), +) : VssNodeDataBrokerRequest { + override val vssPath: String + get() = vssNode.vssPath +} diff --git a/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/connectivity/databroker/subscription/DataBrokerSubscriber.kt b/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/connectivity/databroker/subscription/DataBrokerSubscriber.kt new file mode 100644 index 00000000..6cd42e4c --- /dev/null +++ b/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/connectivity/databroker/subscription/DataBrokerSubscriber.kt @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.eclipse.kuksa.connectivity.databroker.subscription + +import android.util.Log +import org.eclipse.kuksa.connectivity.databroker.DataBrokerException +import org.eclipse.kuksa.connectivity.databroker.DataBrokerTransporter +import org.eclipse.kuksa.connectivity.databroker.listener.VssNodeListener +import org.eclipse.kuksa.connectivity.databroker.listener.VssPathListener +import org.eclipse.kuksa.extension.TAG +import org.eclipse.kuksa.proto.v1.Types +import org.eclipse.kuksa.proto.v1.Types.Field +import org.eclipse.kuksa.vsscore.model.VssNode +import org.eclipse.kuksa.vsscore.model.VssSignal + +/** + * Creates [DataBrokerSubscription]s to the DataBroker to get notified about changes on the underlying vssPaths and + * fields.If no [DataBrokerSubscription] for a given vssPath and field does exist the DataBrokerSubscriber will create + * a new one. If it was already requested before, the same [DataBrokerSubscription] will be re-used. When the last + * [VssPathListener] of a [DataBrokerSubscription] unsubscribes the [DataBrokerSubscription] will be automatically + * canceled and removed from the active [DataBrokerSubscription]s. + */ +internal class DataBrokerSubscriber(private val dataBrokerTransporter: DataBrokerTransporter) { + private val subscriptions = + mutableMapOf() // String(Subscription#identifier) -> Subscription + + /** + * Checks if the SDK is already subscribed to the corresponding [vssPath] and [field], if the SDK is already + * subscribed it will simply add the 3rd-party [listener] to the current subscription. If not, a new + * Subscription is made and the [listener] is added to it. + */ + fun subscribe(vssPath: String, field: Field, listener: VssPathListener) { + val identifier = createIdentifier(vssPath, field) + var subscription = subscriptions[identifier] + if (subscription == null) { + subscription = dataBrokerTransporter.subscribe(vssPath, field) + subscriptions[identifier] = subscription + Log.v(TAG, "Created $subscription") + } + + subscription.listeners.register(listener) + } + + /** + * Removes the specified [listener] for the specified [vssPath] and [field] from an already existing + * Subscription to the DataBroker. If the given Subscription has no more Listeners after unsubscribing it will be + * canceled and removed. Gracefully ignores invalid input, e.g. when a [vssPath] and [field] of a non-subscribed + * [vssPath] is provided. + */ + fun unsubscribe(vssPath: String, field: Field, listener: VssPathListener) { + val identifier = createIdentifier(vssPath, field) + val subscription = subscriptions[identifier] ?: return + subscription.listeners.unregister(listener) + + if (subscription.listeners.isEmpty()) { + Log.v(TAG, "Removing $subscription: no more listeners") + subscription.cancel() + subscriptions.remove(identifier) + } + } + + /** + * Subscribes to the specified [VssNode] with the provided [VssNodeListener]. Only a [VssSignal] + * can be subscribed because they have an actual value. When provided with any parent [VssNode] then this + * [subscribe] method will find all [VssSignal] children and subscribes them instead. Once subscribed the + * application will be notified about any changes to every subscribed [VssSignal]. The [field] can be used to + * subscribe to different information of the [node]. The default for the [field] parameter is a single + * [Types.Field.FIELD_VALUE] entry. + * + * @throws DataBrokerException in case the connection to the DataBroker is no longer active + */ + fun subscribe( + node: T, + field: Field = Field.FIELD_VALUE, + listener: VssNodeListener, + ) { + val vssPath = node.vssPath + + val vssNodePathListener = VssNodePathListener(node, listener) + subscribe(vssPath, field, vssNodePathListener) + } + + /** + * Removes the specified [listener] for the specified [node] and [field] from an already existing + * Subscription to the DataBroker. If the given Subscription has no more Listeners after unsubscribing it will be + * canceled and removed. Gracefully ignores invalid input, e.g. when a [node] and [field] of a + * non-subscribed [VssNode] is provided. + */ + fun unsubscribe( + node: T, + field: Field = Field.FIELD_VALUE, + listener: VssNodeListener, + ) { + val vssPath = node.vssPath + + val vssNodePathListener = VssNodePathListener(node, listener) + unsubscribe(vssPath, field, vssNodePathListener) + } + + private companion object { + private fun createIdentifier(vssPath: String, field: Field): String { + return "$vssPath#${field.name}" + } + } +} diff --git a/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/subscription/Subscription.kt b/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/connectivity/databroker/subscription/DataBrokerSubscription.kt similarity index 86% rename from kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/subscription/Subscription.kt rename to kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/connectivity/databroker/subscription/DataBrokerSubscription.kt index 6318bfab..461b737b 100644 --- a/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/subscription/Subscription.kt +++ b/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/connectivity/databroker/subscription/DataBrokerSubscription.kt @@ -14,20 +14,19 @@ * limitations under the License. * * SPDX-License-Identifier: Apache-2.0 - * */ -package org.eclipse.kuksa.subscription +package org.eclipse.kuksa.connectivity.databroker.subscription import io.grpc.Context -import org.eclipse.kuksa.PropertyListener +import org.eclipse.kuksa.connectivity.databroker.listener.VssPathListener import org.eclipse.kuksa.pattern.listener.MultiListener import org.eclipse.kuksa.proto.v1.KuksaValV1.SubscribeResponse import org.eclipse.kuksa.proto.v1.Types.Field /** * Denotes a Subscription to the DataBroker. Will be notified about changes w.r.t. the specified [vssPath] and [field]. - * To get informed about these changes it is required to register an [PropertyListener] using [listeners]. + * To get informed about these changes it is required to register an [VssPathListener] using [listeners]. * [cancellableContext] is used to cancel the subscription. When the Subscription is canceled the communication channel * to the DataBroker is closed, no more updates will be received from that point on. * @@ -35,12 +34,12 @@ import org.eclipse.kuksa.proto.v1.Types.Field * existing, resp. add the observer to the corresponding Subscription. If all Listeners are unregistered the * Subscription will be automatically canceled. */ -internal class Subscription( +internal class DataBrokerSubscription( val vssPath: String, val field: Field, private val cancellableContext: Context.CancellableContext, ) { - val listeners: MultiListener = MultiListener( + val listeners: MultiListener = MultiListener( onRegistered = { observer -> // initial update on registration if (lastThrowable != null) { @@ -48,7 +47,7 @@ internal class Subscription( } else { val lastSubscribeResponse = lastSubscribeResponse ?: return@MultiListener - observer.onPropertyChanged(lastSubscribeResponse.updatesList) + observer.onEntryChanged(lastSubscribeResponse.updatesList) } }, ) diff --git a/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/subscription/SpecificationPropertyListener.kt b/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/connectivity/databroker/subscription/VssNodePathListener.kt similarity index 61% rename from kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/subscription/SpecificationPropertyListener.kt rename to kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/connectivity/databroker/subscription/VssNodePathListener.kt index b93dfe65..4c078a4a 100644 --- a/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/subscription/SpecificationPropertyListener.kt +++ b/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/connectivity/databroker/subscription/VssNodePathListener.kt @@ -14,44 +14,43 @@ * limitations under the License. * * SPDX-License-Identifier: Apache-2.0 - * */ -package org.eclipse.kuksa.subscription +package org.eclipse.kuksa.connectivity.databroker.subscription -import org.eclipse.kuksa.PropertyListener -import org.eclipse.kuksa.VssSpecificationListener -import org.eclipse.kuksa.extension.copy +import org.eclipse.kuksa.connectivity.databroker.listener.VssNodeListener +import org.eclipse.kuksa.connectivity.databroker.listener.VssPathListener +import org.eclipse.kuksa.extension.vss.copy import org.eclipse.kuksa.proto.v1.KuksaValV1 -import org.eclipse.kuksa.vsscore.model.VssSpecification +import org.eclipse.kuksa.vsscore.model.VssNode -internal class SpecificationPropertyListener( - specification: T, - private val listener: VssSpecificationListener, -) : PropertyListener { +internal class VssNodePathListener( + node: T, + private val listener: VssNodeListener, +) : VssPathListener { // This is currently needed because we get multiple subscribe responses for every heir. Otherwise we // would override the last heir value with every new response. - private var updatedVssSpecification: T = specification + private var updatedVssNode: T = node - override fun onPropertyChanged(entryUpdates: List) { + override fun onEntryChanged(entryUpdates: List) { entryUpdates.forEach { entryUpdate -> val dataEntry = entryUpdate.entry - updatedVssSpecification = updatedVssSpecification.copy(dataEntry.path, dataEntry.value) + updatedVssNode = updatedVssNode.copy(dataEntry.path, dataEntry.value) } - listener.onSpecificationChanged(updatedVssSpecification) + listener.onNodeChanged(updatedVssNode) } override fun onError(throwable: Throwable) { listener.onError(throwable) } - // Two SpecificationObserverWrapper instances are equal if they have the same observer set! + // Two VssNodePathListener instances are equal if they have the same observer set! override fun equals(other: Any?): Boolean { if (this === other) return true if (javaClass != other?.javaClass) return false - other as SpecificationPropertyListener<*> + other as VssNodePathListener<*> return listener == other.listener } diff --git a/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/CoroutineCallback.kt b/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/coroutine/CoroutineCallback.kt similarity index 97% rename from kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/CoroutineCallback.kt rename to kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/coroutine/CoroutineCallback.kt index 6f312508..cbaf40b1 100644 --- a/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/CoroutineCallback.kt +++ b/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/coroutine/CoroutineCallback.kt @@ -17,7 +17,7 @@ * */ -package org.eclipse.kuksa +package org.eclipse.kuksa.coroutine import kotlin.coroutines.Continuation import kotlin.coroutines.CoroutineContext diff --git a/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/extension/DataPointExtension.kt b/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/extension/DataPointExtension.kt index 8c008667..8254d28d 100644 --- a/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/extension/DataPointExtension.kt +++ b/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/extension/DataPointExtension.kt @@ -24,7 +24,7 @@ import org.eclipse.kuksa.proto.v1.Types import org.eclipse.kuksa.proto.v1.Types.BoolArray import org.eclipse.kuksa.proto.v1.Types.Datapoint import org.eclipse.kuksa.proto.v1.Types.Datapoint.ValueCase -import org.eclipse.kuksa.vsscore.model.VssProperty +import org.eclipse.kuksa.vsscore.model.VssSignal private const val CSV_DELIMITER = "," @@ -35,11 +35,11 @@ val Types.Metadata.valueType: ValueCase get() = dataType.dataPointValueCase /** - * Converts the [VssProperty.value] into a [Datapoint] object. + * Converts the [VssSignal.value] into a [Datapoint] object. * - * @throws IllegalArgumentException if the [VssProperty] could not be converted to a [Datapoint]. + * @throws IllegalArgumentException if the [VssSignal] could not be converted to a [Datapoint]. */ -val VssProperty.datapoint: Datapoint +val VssSignal.datapoint: Datapoint get() { val stringValue = value.toString() return when (value::class) { diff --git a/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/extension/vssProperty/VssPropertyBooleanExtension.kt b/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/extension/vss/VssLeafBooleanExtension.kt similarity index 82% rename from kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/extension/vssProperty/VssPropertyBooleanExtension.kt rename to kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/extension/vss/VssLeafBooleanExtension.kt index 8544834c..c62c1df9 100644 --- a/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/extension/vssProperty/VssPropertyBooleanExtension.kt +++ b/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/extension/vss/VssLeafBooleanExtension.kt @@ -16,10 +16,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -package org.eclipse.kuksa.extension.vssProperty +package org.eclipse.kuksa.extension.vss -import org.eclipse.kuksa.extension.copy -import org.eclipse.kuksa.vsscore.model.VssProperty +import org.eclipse.kuksa.vsscore.model.VssSignal /** * Convenience operator for [copy] with a [Boolean] value which will be inverted. @@ -27,6 +26,6 @@ import org.eclipse.kuksa.vsscore.model.VssProperty * @throws [IllegalArgumentException] if the copied types do not match. * @throws [NoSuchElementException] if no copy method was found for the class. */ -operator fun VssProperty.not(): VssProperty { +operator fun VssSignal.not(): VssSignal { return copy(!value) } diff --git a/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/extension/vssProperty/VssPropertyDoubleExtension.kt b/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/extension/vss/VssLeafDoubleExtension.kt similarity index 62% rename from kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/extension/vssProperty/VssPropertyDoubleExtension.kt rename to kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/extension/vss/VssLeafDoubleExtension.kt index 37ef935b..08e55bae 100644 --- a/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/extension/vssProperty/VssPropertyDoubleExtension.kt +++ b/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/extension/vss/VssLeafDoubleExtension.kt @@ -16,89 +16,88 @@ * SPDX-License-Identifier: Apache-2.0 */ -package org.eclipse.kuksa.extension.vssProperty +package org.eclipse.kuksa.extension.vss -import org.eclipse.kuksa.extension.copy -import org.eclipse.kuksa.vsscore.model.VssProperty +import org.eclipse.kuksa.vsscore.model.VssSignal /** - * Convenience operator for [copy] which updates the [VssProperty.value] by adding [value] to it. + * Convenience operator for [copy] which updates the [VssSignal.value] by adding [value] to it. * * @throws [IllegalArgumentException] if the copied types do not match. * @throws [NoSuchElementException] if no copy method was found for the class. */ -operator fun VssProperty.plusAssign(value: Number) { +operator fun VssSignal.plusAssign(value: Number) { copy(this.value + value.toDouble()) } /** - * Convenience operator for [copy] which updates the [VssProperty.value] by adding [value] to it. + * Convenience operator for [copy] which updates the [VssSignal.value] by adding [value] to it. * * @throws [IllegalArgumentException] if the copied types do not match. * @throws [NoSuchElementException] if no copy method was found for the class. */ -operator fun VssProperty.plus(value: Number): VssProperty { +operator fun VssSignal.plus(value: Number): VssSignal { return copy(this.value + value.toDouble()) } /** - * Convenience operator for [copy] which updates the [VssProperty.value] by subtracting [value] to it. + * Convenience operator for [copy] which updates the [VssSignal.value] by subtracting [value] to it. * * @throws [IllegalArgumentException] if the copied types do not match. * @throws [NoSuchElementException] if no copy method was found for the class. */ -operator fun VssProperty.minusAssign(value: Number) { +operator fun VssSignal.minusAssign(value: Number) { copy(this.value - value.toDouble()) } /** - * Convenience operator for [copy] which updates the [VssProperty.value] by subtracting [value] to it. + * Convenience operator for [copy] which updates the [VssSignal.value] by subtracting [value] to it. * * @throws [IllegalArgumentException] if the copied types do not match. * @throws [NoSuchElementException] if no copy method was found for the class. */ -operator fun VssProperty.minus(value: Number): VssProperty { +operator fun VssSignal.minus(value: Number): VssSignal { return copy(this.value - value.toDouble()) } /** - * Convenience operator for [copy] which updates the [VssProperty.value] by dividing [value] to it. + * Convenience operator for [copy] which updates the [VssSignal.value] by dividing [value] to it. * * @throws [IllegalArgumentException] if the copied types do not match. * @throws [NoSuchElementException] if no copy method was found for the class. * @throws [ArithmeticException] if divided by zero. */ -operator fun VssProperty.divAssign(value: Number) { +operator fun VssSignal.divAssign(value: Number) { copy(this.value / value.toDouble()) } /** - * Convenience operator for [copy] which updates the [VssProperty.value] by dividing [value] to it. + * Convenience operator for [copy] which updates the [VssSignal.value] by dividing [value] to it. * * @throws [IllegalArgumentException] if the copied types do not match. * @throws [NoSuchElementException] if no copy method was found for the class. * @throws [ArithmeticException] if divided by zero. */ -operator fun VssProperty.div(value: Number): VssProperty { +operator fun VssSignal.div(value: Number): VssSignal { return copy(this.value / value.toDouble()) } /** - * Convenience operator for [copy] which updates the [VssProperty.value] by multiplying [value] to it. + * Convenience operator for [copy] which updates the [VssSignal.value] by multiplying [value] to it. * * @throws [IllegalArgumentException] if the copied types do not match. * @throws [NoSuchElementException] if no copy method was found for the class. */ -operator fun VssProperty.timesAssign(value: Number) { +operator fun VssSignal.timesAssign(value: Number) { copy(this.value * value.toDouble()) } /** - * Convenience operator for [copy] which updates the [VssProperty.value] by multiplying [value] to it. + * Convenience operator for [copy] which updates the [VssSignal.value] by multiplying [value] to it. * * @throws [IllegalArgumentException] if the copied types do not match. * @throws [NoSuchElementException] if no copy method was found for the class. */ -operator fun VssProperty.times(value: Number): VssProperty { +operator fun VssSignal.times(value: Number): VssSignal { return copy(this.value * value.toDouble()) } diff --git a/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/extension/vssProperty/VssPropertyFloatExtension.kt b/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/extension/vss/VssLeafFloatExtension.kt similarity index 62% rename from kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/extension/vssProperty/VssPropertyFloatExtension.kt rename to kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/extension/vss/VssLeafFloatExtension.kt index 91d090f0..68463dc9 100644 --- a/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/extension/vssProperty/VssPropertyFloatExtension.kt +++ b/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/extension/vss/VssLeafFloatExtension.kt @@ -16,89 +16,88 @@ * SPDX-License-Identifier: Apache-2.0 */ -package org.eclipse.kuksa.extension.vssProperty +package org.eclipse.kuksa.extension.vss -import org.eclipse.kuksa.extension.copy -import org.eclipse.kuksa.vsscore.model.VssProperty +import org.eclipse.kuksa.vsscore.model.VssSignal /** - * Convenience operator for [copy] which updates the [VssProperty.value] by adding [value] to it. + * Convenience operator for [copy] which updates the [VssSignal.value] by adding [value] to it. * * @throws [IllegalArgumentException] if the copied types do not match. * @throws [NoSuchElementException] if no copy method was found for the class. */ -operator fun VssProperty.plusAssign(value: Number) { +operator fun VssSignal.plusAssign(value: Number) { copy(this.value + value.toFloat()) } /** - * Convenience operator for [copy] which updates the [VssProperty.value] by adding [value] to it. + * Convenience operator for [copy] which updates the [VssSignal.value] by adding [value] to it. * * @throws [IllegalArgumentException] if the copied types do not match. * @throws [NoSuchElementException] if no copy method was found for the class. */ -operator fun VssProperty.plus(value: Number): VssProperty { +operator fun VssSignal.plus(value: Number): VssSignal { return copy(this.value + value.toFloat()) } /** - * Convenience operator for [copy] which updates the [VssProperty.value] by subtracting [value] to it. + * Convenience operator for [copy] which updates the [VssSignal.value] by subtracting [value] to it. * * @throws [IllegalArgumentException] if the copied types do not match. * @throws [NoSuchElementException] if no copy method was found for the class. */ -operator fun VssProperty.minusAssign(value: Number) { +operator fun VssSignal.minusAssign(value: Number) { copy(this.value - +value.toFloat()) } /** - * Convenience operator for [copy] which updates the [VssProperty.value] by subtracting [value] to it. + * Convenience operator for [copy] which updates the [VssSignal.value] by subtracting [value] to it. * * @throws [IllegalArgumentException] if the copied types do not match. * @throws [NoSuchElementException] if no copy method was found for the class. */ -operator fun VssProperty.minus(value: Number): VssProperty { +operator fun VssSignal.minus(value: Number): VssSignal { return copy(this.value - +value.toFloat()) } /** - * Convenience operator for [copy] which updates the [VssProperty.value] by dividing [value] to it. + * Convenience operator for [copy] which updates the [VssSignal.value] by dividing [value] to it. * * @throws [IllegalArgumentException] if the copied types do not match. * @throws [NoSuchElementException] if no copy method was found for the class. * @throws [ArithmeticException] if divided by zero. */ -operator fun VssProperty.divAssign(value: Number) { +operator fun VssSignal.divAssign(value: Number) { copy(this.value / +value.toFloat()) } /** - * Convenience operator for [copy] which updates the [VssProperty.value] by dividing [value] to it. + * Convenience operator for [copy] which updates the [VssSignal.value] by dividing [value] to it. * * @throws [IllegalArgumentException] if the copied types do not match. * @throws [NoSuchElementException] if no copy method was found for the class. * @throws [ArithmeticException] if divided by zero. */ -operator fun VssProperty.div(value: Number): VssProperty { +operator fun VssSignal.div(value: Number): VssSignal { return copy(this.value / +value.toFloat()) } /** - * Convenience operator for [copy] which updates the [VssProperty.value] by dividing [value] to it. + * Convenience operator for [copy] which updates the [VssSignal.value] by dividing [value] to it. * * @throws [IllegalArgumentException] if the copied types do not match. * @throws [NoSuchElementException] if no copy method was found for the class. */ -operator fun VssProperty.timesAssign(value: Number) { +operator fun VssSignal.timesAssign(value: Number) { copy(this.value * +value.toFloat()) } /** - * Convenience operator for [copy] which updates the [VssProperty.value] by dividing [value] to it. + * Convenience operator for [copy] which updates the [VssSignal.value] by dividing [value] to it. * * @throws [IllegalArgumentException] if the copied types do not match. * @throws [NoSuchElementException] if no copy method was found for the class. */ -operator fun VssProperty.times(value: Number): VssProperty { +operator fun VssSignal.times(value: Number): VssSignal { return copy(this.value * +value.toFloat()) } diff --git a/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/extension/vssProperty/VssPropertyIntExtension.kt b/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/extension/vss/VssLeafIntExtension.kt similarity index 62% rename from kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/extension/vssProperty/VssPropertyIntExtension.kt rename to kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/extension/vss/VssLeafIntExtension.kt index 3a8baf41..bd0464fd 100644 --- a/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/extension/vssProperty/VssPropertyIntExtension.kt +++ b/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/extension/vss/VssLeafIntExtension.kt @@ -16,89 +16,88 @@ * SPDX-License-Identifier: Apache-2.0 */ -package org.eclipse.kuksa.extension.vssProperty +package org.eclipse.kuksa.extension.vss -import org.eclipse.kuksa.extension.copy -import org.eclipse.kuksa.vsscore.model.VssProperty +import org.eclipse.kuksa.vsscore.model.VssSignal /** - * Convenience operator for [copy] which updates the [VssProperty.value] by adding [value] to it. + * Convenience operator for [copy] which updates the [VssSignal.value] by adding [value] to it. * * @throws [IllegalArgumentException] if the copied types do not match. * @throws [NoSuchElementException] if no copy method was found for the class. */ -operator fun VssProperty.plusAssign(value: Number) { +operator fun VssSignal.plusAssign(value: Number) { copy(this.value + value.toInt()) } /** - * Convenience operator for [copy] which updates the [VssProperty.value] by adding [value] to it. + * Convenience operator for [copy] which updates the [VssSignal.value] by adding [value] to it. * * @throws [IllegalArgumentException] if the copied types do not match. * @throws [NoSuchElementException] if no copy method was found for the class. */ -operator fun VssProperty.plus(value: Number): VssProperty { +operator fun VssSignal.plus(value: Number): VssSignal { return copy(this.value + value.toInt()) } /** - * Convenience operator for [copy] which updates the [VssProperty.value] by subtracting [value] to it. + * Convenience operator for [copy] which updates the [VssSignal.value] by subtracting [value] to it. * * @throws [IllegalArgumentException] if the copied types do not match. * @throws [NoSuchElementException] if no copy method was found for the class. */ -operator fun VssProperty.minusAssign(value: Number) { +operator fun VssSignal.minusAssign(value: Number) { copy(this.value - value.toInt()) } /** - * Convenience operator for [copy] which updates the [VssProperty.value] by subtracting [value] to it. + * Convenience operator for [copy] which updates the [VssSignal.value] by subtracting [value] to it. * * @throws [IllegalArgumentException] if the copied types do not match. * @throws [NoSuchElementException] if no copy method was found for the class. */ -operator fun VssProperty.minus(value: Number): VssProperty { +operator fun VssSignal.minus(value: Number): VssSignal { return copy(this.value - value.toInt()) } /** - * Convenience operator for [copy] which updates the [VssProperty.value] by dividing [value] to it. + * Convenience operator for [copy] which updates the [VssSignal.value] by dividing [value] to it. * * @throws [IllegalArgumentException] if the copied types do not match. * @throws [NoSuchElementException] if no copy method was found for the class. * @throws [ArithmeticException] if divided by zero. */ -operator fun VssProperty.divAssign(value: Number) { +operator fun VssSignal.divAssign(value: Number) { copy(this.value / value.toInt()) } /** - * Convenience operator for [copy] which updates the [VssProperty.value] by dividing [value] to it. + * Convenience operator for [copy] which updates the [VssSignal.value] by dividing [value] to it. * * @throws [IllegalArgumentException] if the copied types do not match. * @throws [NoSuchElementException] if no copy method was found for the class. * @throws [ArithmeticException] if divided by zero. */ -operator fun VssProperty.div(value: Number): VssProperty { +operator fun VssSignal.div(value: Number): VssSignal { return copy(this.value / value.toInt()) } /** - * Convenience operator for [copy] which updates the [VssProperty.value] by multiplying [value] to it. + * Convenience operator for [copy] which updates the [VssSignal.value] by multiplying [value] to it. * * @throws [IllegalArgumentException] if the copied types do not match. * @throws [NoSuchElementException] if no copy method was found for the class. */ -operator fun VssProperty.timesAssign(value: Number) { +operator fun VssSignal.timesAssign(value: Number) { copy(this.value * value.toInt()) } /** - * Convenience operator for [copy] which updates the [VssProperty.value] by multiplying [value] to it. + * Convenience operator for [copy] which updates the [VssSignal.value] by multiplying [value] to it. * * @throws [IllegalArgumentException] if the copied types do not match. * @throws [NoSuchElementException] if no copy method was found for the class. */ -operator fun VssProperty.times(value: Number): VssProperty { +operator fun VssSignal.times(value: Number): VssSignal { return copy(this.value * value.toInt()) } diff --git a/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/extension/vssProperty/VssPropertyLongExtension.kt b/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/extension/vss/VssLeafLongExtension.kt similarity index 62% rename from kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/extension/vssProperty/VssPropertyLongExtension.kt rename to kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/extension/vss/VssLeafLongExtension.kt index 716d3b19..0e819187 100644 --- a/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/extension/vssProperty/VssPropertyLongExtension.kt +++ b/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/extension/vss/VssLeafLongExtension.kt @@ -16,89 +16,88 @@ * SPDX-License-Identifier: Apache-2.0 */ -package org.eclipse.kuksa.extension.vssProperty +package org.eclipse.kuksa.extension.vss -import org.eclipse.kuksa.extension.copy -import org.eclipse.kuksa.vsscore.model.VssProperty +import org.eclipse.kuksa.vsscore.model.VssSignal /** - * Convenience operator for [copy] which updates the [VssProperty.value] by adding [value] to it. + * Convenience operator for [copy] which updates the [VssSignal.value] by adding [value] to it. * * @throws [IllegalArgumentException] if the copied types do not match. * @throws [NoSuchElementException] if no copy method was found for the class. */ -operator fun VssProperty.plusAssign(value: Number) { +operator fun VssSignal.plusAssign(value: Number) { copy(this.value + value.toLong()) } /** - * Convenience operator for [copy] which updates the [VssProperty.value] by adding [value] to it. + * Convenience operator for [copy] which updates the [VssSignal.value] by adding [value] to it. * * @throws [IllegalArgumentException] if the copied types do not match. * @throws [NoSuchElementException] if no copy method was found for the class. */ -operator fun VssProperty.plus(value: Number): VssProperty { +operator fun VssSignal.plus(value: Number): VssSignal { return copy(this.value + value.toLong()) } /** - * Convenience operator for [copy] which updates the [VssProperty.value] by subtracting [value] to it. + * Convenience operator for [copy] which updates the [VssSignal.value] by subtracting [value] to it. * * @throws [IllegalArgumentException] if the copied types do not match. * @throws [NoSuchElementException] if no copy method was found for the class. */ -operator fun VssProperty.minusAssign(value: Number) { +operator fun VssSignal.minusAssign(value: Number) { copy(this.value - value.toLong()) } /** - * Convenience operator for [copy] which updates the [VssProperty.value] by subtracting [value] to it. + * Convenience operator for [copy] which updates the [VssSignal.value] by subtracting [value] to it. * * @throws [IllegalArgumentException] if the copied types do not match. * @throws [NoSuchElementException] if no copy method was found for the class. */ -operator fun VssProperty.minus(value: Number): VssProperty { +operator fun VssSignal.minus(value: Number): VssSignal { return copy(this.value - value.toLong()) } /** - * Convenience operator for [copy] which updates the [VssProperty.value] by dividing [value] to it. + * Convenience operator for [copy] which updates the [VssSignal.value] by dividing [value] to it. * * @throws [IllegalArgumentException] if the copied types do not match. * @throws [NoSuchElementException] if no copy method was found for the class. * @throws [ArithmeticException] if divided by zero. */ -operator fun VssProperty.divAssign(value: Number) { +operator fun VssSignal.divAssign(value: Number) { copy(this.value / value.toLong()) } /** - * Convenience operator for [copy] which updates the [VssProperty.value] by dividing [value] to it. + * Convenience operator for [copy] which updates the [VssSignal.value] by dividing [value] to it. * * @throws [IllegalArgumentException] if the copied types do not match. * @throws [NoSuchElementException] if no copy method was found for the class. * @throws [ArithmeticException] if divided by zero. */ -operator fun VssProperty.div(value: Number): VssProperty { +operator fun VssSignal.div(value: Number): VssSignal { return copy(this.value / value.toLong()) } /** - * Convenience operator for [copy] which updates the [VssProperty.value] by multiplying [value] to it. + * Convenience operator for [copy] which updates the [VssSignal.value] by multiplying [value] to it. * * @throws [IllegalArgumentException] if the copied types do not match. * @throws [NoSuchElementException] if no copy method was found for the class. */ -operator fun VssProperty.timesAssign(value: Number) { +operator fun VssSignal.timesAssign(value: Number) { copy(this.value * value.toLong()) } /** - * Convenience operator for [copy] which updates the [VssProperty.value] by multiplying [value] to it. + * Convenience operator for [copy] which updates the [VssSignal.value] by multiplying [value] to it. * * @throws [IllegalArgumentException] if the copied types do not match. * @throws [NoSuchElementException] if no copy method was found for the class. */ -operator fun VssProperty.times(value: Number): VssProperty { +operator fun VssSignal.times(value: Number): VssSignal { return copy(this.value * value.toLong()) } diff --git a/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/extension/VssSpecificationCopyExtension.kt b/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/extension/vss/VssNodeCopyExtension.kt similarity index 69% rename from kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/extension/VssSpecificationCopyExtension.kt rename to kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/extension/vss/VssNodeCopyExtension.kt index 8a35c233..a128d35e 100644 --- a/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/extension/VssSpecificationCopyExtension.kt +++ b/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/extension/vss/VssNodeCopyExtension.kt @@ -14,23 +14,23 @@ * limitations under the License. * * SPDX-License-Identifier: Apache-2.0 - * */ -package org.eclipse.kuksa.extension +package org.eclipse.kuksa.extension.vss +import org.eclipse.kuksa.extension.copy import org.eclipse.kuksa.proto.v1.Types import org.eclipse.kuksa.proto.v1.Types.Datapoint import org.eclipse.kuksa.proto.v1.Types.Datapoint.ValueCase.* -import org.eclipse.kuksa.vsscore.model.VssProperty -import org.eclipse.kuksa.vsscore.model.VssSpecification +import org.eclipse.kuksa.vsscore.model.VssNode +import org.eclipse.kuksa.vsscore.model.VssSignal import org.eclipse.kuksa.vsscore.model.findHeritageLine import org.eclipse.kuksa.vsscore.model.heritage import org.eclipse.kuksa.vsscore.model.variableName import kotlin.reflect.full.memberProperties /** - * Creates a copy of the [VssSpecification] where the whole [VssSpecification.findHeritageLine] is replaced + * Creates a copy of the [VssNode] where the whole [VssNode.findHeritageLine] is replaced * with modified heirs. * * ### Example: @@ -40,7 +40,7 @@ import kotlin.reflect.full.memberProperties * * A deep copy is necessary for a nested history tree with at least two generations. The VssWindowChildLockEngaged * is replaced inside VssCabin where this again is replaced inside VssVehicle. Use the [generation] to start copying - * from the [VssSpecification] to the [deepCopy]. Returns a copy where every heir in the given [changedHeritage] is + * from the [VssNode] to the [deepCopy]. Returns a copy where every heir in the given [changedHeritage] is * replaced with another copy. * * @throws [IllegalArgumentException] if the copied types do not match. This can happen if the [heritage] is not @@ -49,33 +49,43 @@ import kotlin.reflect.full.memberProperties */ // The suggested method to improve the performance can't be used here because we are already working with a full array. // https://detekt.dev/docs/rules/performance/ -@Suppress("performance:SpreadOperator") -fun T.deepCopy(generation: Int = 0, vararg changedHeritage: VssSpecification): T { - if (generation == changedHeritage.size) { // Reached the end, use the changed VssProperty +fun T.deepCopy(generation: Int = 0, changedHeritage: List): T { + if (generation == changedHeritage.size) { // Reached the end, use the changed VssSignal return this } - // Create the missing link between this (VssSpecification) and the given property (VssSpecifications inbetween) + // Create the missing link between this [VssNode] and the given node inbetween var heritageLine = changedHeritage if (changedHeritage.size == 1) { heritageLine = findHeritageLine(changedHeritage.first(), true) - .toTypedArray() + .toList() .ifEmpty { changedHeritage } } - val childSpecification = heritageLine[generation] - val childCopy = childSpecification.deepCopy(generation + 1, *heritageLine) - val parameterNameToChild = mapOf(childSpecification.variableName to childCopy) + val childNode = heritageLine[generation] + val childCopy = childNode.deepCopy(generation + 1, heritageLine) + val parameterNameToChild = mapOf(childNode.variableName to childCopy) return copy(parameterNameToChild) } /** - * Creates a copy of a [VssProperty] where the [VssProperty.value] is changed to the given [Datapoint]. + * Convenience method for [deepCopy] with a [VssNode]. It will return the [VssNode] with the updated + * [VssNode]. + * + * @throws [IllegalArgumentException] if the copied types do not match. + * @throws [NoSuchElementException] if no copy method was found for the class. + */ +fun T.deepCopy(vararg vssNodes: VssNode): T { + return deepCopy(0, vssNodes.toList()) +} + +/** + * Creates a copy of a [VssSignal] where the [VssSignal.value] is changed to the given [Datapoint]. */ // The actual value type is unknown but it is expected that the casted [valueCase] is valid if no exception was thrown. @Suppress("UNCHECKED_CAST") -fun VssProperty.copy(datapoint: Datapoint): VssProperty { +fun VssSignal.copy(datapoint: Datapoint): VssSignal { with(datapoint) { val value: Any = when (valueCase) { STRING -> string @@ -123,13 +133,13 @@ fun VssProperty.copy(datapoint: Datapoint): VssProperty { } /** - * Calls the generated copy method of the data class for the [VssProperty] and returns a new copy with the new [value]. + * Calls the generated copy method of the data class for the [VssSignal] and returns a new copy with the new [value]. * * @throws [IllegalArgumentException] if the copied types do not match. * @throws [NoSuchElementException] if no copy method was found for the class. */ -fun VssProperty.copy(value: T): VssProperty { - val memberProperties = VssProperty::class.memberProperties +fun VssSignal.copy(value: T): VssSignal { + val memberProperties = VssSignal::class.memberProperties val firstPropertyName = memberProperties.first().name val valueMap = mapOf(firstPropertyName to value) @@ -137,46 +147,46 @@ fun VssProperty.copy(value: T): VssProperty { } /** - * Creates a copy of the [VssSpecification] where the heir with a matching [vssPath] is replaced with the + * Creates a copy of the [VssNode] where the heir with a matching [vssPath] is replaced with the * [updatedValue]. * - * @param consideredHeritage the heritage of the [VssSpecification] which is considered for searching. The default - * will always generate the up to date heritage of the current [VssSpecification]. For performance reason it may make + * @param consideredHeritage the heritage of the [VssNode] which is considered for searching. The default + * will always generate the up to date heritage of the current [VssNode]. For performance reason it may make * sense to cache the input and reuse the [Collection] here. * * @throws [IllegalArgumentException] if the copied types do not match. * @throws [NoSuchElementException] if no copy method was found for the class. */ @Suppress("UNCHECKED_CAST") -fun T.copy( +fun T.copy( vssPath: String, updatedValue: Datapoint, - consideredHeritage: Collection = heritage, + consideredHeritage: Collection = heritage, ): T { - val vssSpecifications = consideredHeritage + this - val vssProperty = vssSpecifications - .filterIsInstance>() + val vssNodes = consideredHeritage + this + val vssNode = vssNodes + .filterIsInstance>() .find { it.vssPath == vssPath } ?: return this - val updatedVssProperty = vssProperty.copy(updatedValue) + val updatedVssNode = vssNode.copy(updatedValue) - // Same property with no heirs, no deep copy is needed - if (this.vssPath == updatedVssProperty.vssPath) return updatedVssProperty as T + // Same node with no heirs, no deep copy is needed + if (this.vssPath == updatedVssNode.vssPath) return updatedVssNode as T - return deepCopy(0, updatedVssProperty) + return deepCopy(updatedVssNode) } // region Operators /** - * Convenience operator for [deepCopy] with a [VssSpecification]. It will return the [VssSpecification] with the updated - * [VssSpecification]. + * Convenience operator for [deepCopy] with a [VssNode]. It will return the [VssNode] with the updated + * [VssNode]. * * @throws [IllegalArgumentException] if the copied types do not match. * @throws [NoSuchElementException] if no copy method was found for the class. */ -operator fun T.invoke(vararg property: VssSpecification): T { - return deepCopy(0, *property) +operator fun T.invoke(vararg vssNodes: VssNode): T { + return deepCopy(0, vssNodes.toList()) } /** @@ -185,7 +195,7 @@ operator fun T.invoke(vararg property: VssSpecification): * @throws [IllegalArgumentException] if the copied types do not match. * @throws [NoSuchElementException] if no copy method was found for the class. */ -operator fun VssProperty.invoke(value: T): VssProperty { +operator fun VssSignal.invoke(value: T): VssSignal { return copy(value) } diff --git a/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/TimeoutConfig.kt b/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/model/TimeoutConfig.kt similarity index 97% rename from kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/TimeoutConfig.kt rename to kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/model/TimeoutConfig.kt index 6956e47e..f1d0f02d 100644 --- a/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/TimeoutConfig.kt +++ b/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/model/TimeoutConfig.kt @@ -17,7 +17,7 @@ * */ -package org.eclipse.kuksa +package org.eclipse.kuksa.model import java.util.concurrent.TimeUnit import javax.annotation.concurrent.Immutable diff --git a/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/subscription/DataBrokerSubscriber.kt b/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/subscription/DataBrokerSubscriber.kt deleted file mode 100644 index f95678fe..00000000 --- a/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/subscription/DataBrokerSubscriber.kt +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (c) 2023 Contributors to the Eclipse Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - * - */ - -package org.eclipse.kuksa.subscription - -import android.util.Log -import org.eclipse.kuksa.DataBrokerException -import org.eclipse.kuksa.DataBrokerTransporter -import org.eclipse.kuksa.PropertyListener -import org.eclipse.kuksa.VssSpecificationListener -import org.eclipse.kuksa.extension.TAG -import org.eclipse.kuksa.proto.v1.Types -import org.eclipse.kuksa.proto.v1.Types.Field -import org.eclipse.kuksa.vsscore.model.VssProperty -import org.eclipse.kuksa.vsscore.model.VssSpecification - -/** - * Creates [Subscription]s to the DataBroker to get notified about changes on the underlying vssPaths and fields. - * If no [Subscription] for a given vssPath and field does exist the DataBrokerSubscriber will create a new one. If it - * was already requested before, the same [Subscription] will be re-used. - * When the last [PropertyListener] of a [Subscription] unsubscribes the [Subscription] will be automatically canceled - * and removed from the active [Subscription]s. - */ -internal class DataBrokerSubscriber(private val dataBrokerTransporter: DataBrokerTransporter) { - private val subscriptions = mutableMapOf() // String(Subscription#identifier) -> Subscription - - /** - * Checks if the SDK is already subscribed to the corresponding [vssPath] and [field], if the SDK is already - * subscribed it will simply add the 3rd-party [propertyListener] to the current subscription. If not, a new - * Subscription is made and the [propertyListener] is added to it. - */ - fun subscribe(vssPath: String, field: Field, propertyListener: PropertyListener) { - val identifier = createIdentifier(vssPath, field) - var subscription = subscriptions[identifier] - if (subscription == null) { - subscription = dataBrokerTransporter.subscribe(vssPath, field) - subscriptions[identifier] = subscription - Log.v(TAG, "Created $subscription") - } - - subscription.listeners.register(propertyListener) - } - - /** - * Removes the specified [propertyListener] for the specified [vssPath] and [field] from an already existing - * Subscription to the DataBroker. If the given Subscription has no more Listeners after unsubscribing it will be - * canceled and removed. Gracefully ignores invalid input, e.g. when a [vssPath] and [field] of a non-subscribed - * property is provided. - */ - fun unsubscribe(vssPath: String, field: Field, propertyListener: PropertyListener) { - val identifier = createIdentifier(vssPath, field) - val subscription = subscriptions[identifier] ?: return - subscription.listeners.unregister(propertyListener) - - if (subscription.listeners.isEmpty()) { - Log.v(TAG, "Removing $subscription: no more listeners") - subscription.cancel() - subscriptions.remove(identifier) - } - } - - /** - * Subscribes to the specified [VssSpecification] with the provided [VssSpecificationListener]. Only a [VssProperty] - * can be subscribed because they have an actual value. When provided with any parent [VssSpecification] then this - * [subscribe] method will find all [VssProperty] children and subscribes them instead. Once subscribed the - * application will be notified about any changes to every subscribed [VssProperty]. The [field] can be used to - * subscribe to different information of the [specification]. The default for the [field] parameter is a single - * [Types.Field.FIELD_VALUE] entry. - * - * @throws DataBrokerException in case the connection to the DataBroker is no longer active - */ - fun subscribe( - specification: T, - field: Field = Field.FIELD_VALUE, - listener: VssSpecificationListener, - ) { - val vssPath = specification.vssPath - - val specificationPropertyListener = SpecificationPropertyListener(specification, listener) - subscribe(vssPath, field, specificationPropertyListener) - } - - /** - * Removes the specified [listener] for the specified [specification] and [field] from an already existing - * Subscription to the DataBroker. If the given Subscription has no more Listeners after unsubscribing it will be - * canceled and removed. Gracefully ignores invalid input, e.g. when a [specification] and [field] of a - * non-subscribed property is provided. - */ - fun unsubscribe( - specification: T, - field: Field = Field.FIELD_VALUE, - listener: VssSpecificationListener, - ) { - val vssPath = specification.vssPath - - val specificationPropertyListener = SpecificationPropertyListener(specification, listener) - unsubscribe(vssPath, field, specificationPropertyListener) - } - - private companion object { - private fun createIdentifier(vssPath: String, field: Field): String { - return "$vssPath#${field.name}" - } - } -} diff --git a/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/DataBrokerConnectorAuthenticationTest.kt b/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/connectivity/authentication/DataBrokerConnectorAuthenticationTest.kt similarity index 78% rename from kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/DataBrokerConnectorAuthenticationTest.kt rename to kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/connectivity/authentication/DataBrokerConnectorAuthenticationTest.kt index bcf7c653..cfa5e847 100644 --- a/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/DataBrokerConnectorAuthenticationTest.kt +++ b/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/connectivity/authentication/DataBrokerConnectorAuthenticationTest.kt @@ -14,15 +14,15 @@ * limitations under the License. * * SPDX-License-Identifier: Apache-2.0 - * */ -package org.eclipse.kuksa +package org.eclipse.kuksa.connectivity.authentication import io.kotest.core.spec.style.BehaviorSpec import io.kotest.matchers.shouldBe -import org.eclipse.kuksa.databroker.DataBrokerConnectorProvider -import org.eclipse.kuksa.model.Property +import org.eclipse.kuksa.connectivity.databroker.DataBrokerConnectorProvider +import org.eclipse.kuksa.connectivity.databroker.request.FetchRequest +import org.eclipse.kuksa.connectivity.databroker.request.UpdateRequest import org.eclipse.kuksa.proto.v1.Types import org.eclipse.kuksa.test.TestResourceFile import org.eclipse.kuksa.test.kotest.Authentication @@ -44,6 +44,7 @@ class DataBrokerConnectorAuthenticationTest : BehaviorSpec({ given("A DataBrokerConnectorProvider") { val dataBrokerConnectorProvider = DataBrokerConnectorProvider() + val speedVssPath = "Vehicle.Speed" and("an insecure DataBrokerConnector with a READ_WRITE_ALL JWT") { val jwtFileStream = JwtType.READ_WRITE_ALL.asInputStream() @@ -51,10 +52,10 @@ class DataBrokerConnectorAuthenticationTest : BehaviorSpec({ and("a successfully established connection") { val connection = dataBrokerConnector.connect() - val property = Property("Vehicle.Speed") `when`("Reading Vehicle.Speed") { - val response = connection.fetch(property) + val fetchRequest = FetchRequest(speedVssPath) + val response = connection.fetch(fetchRequest) then("No error should occur") { response.errorsList.size shouldBe 0 @@ -64,8 +65,9 @@ class DataBrokerConnectorAuthenticationTest : BehaviorSpec({ `when`("Writing the VALUE of Vehicle.Speed") { val nextFloat = random.nextFloat() * 100F val datapoint = Types.Datapoint.newBuilder().setFloat(nextFloat).build() + val updateRequest = UpdateRequest(speedVssPath, datapoint) - val response = connection.update(property, datapoint) + val response = connection.update(updateRequest) then("No error should occur") { response.errorsList.size shouldBe 0 @@ -80,10 +82,10 @@ class DataBrokerConnectorAuthenticationTest : BehaviorSpec({ and("a successfully established connection") { val connection = dataBrokerConnector.connect() - val property = Property("Vehicle.Speed") `when`("Reading Vehicle.Speed") { - val response = connection.fetch(property) + val fetchRequest = FetchRequest(speedVssPath) + val response = connection.fetch(fetchRequest) then("No error should appear") { response.errorsList.size shouldBe 0 @@ -93,8 +95,8 @@ class DataBrokerConnectorAuthenticationTest : BehaviorSpec({ `when`("Writing the VALUE of Vehicle.Speed") { val nextFloat = random.nextFloat() * 100F val datapoint = Types.Datapoint.newBuilder().setFloat(nextFloat).build() - - val response = connection.update(property, datapoint) + val updateRequest = UpdateRequest(speedVssPath, datapoint) + val response = connection.update(updateRequest) then("An error should occur") { response.errorsList.size shouldBe 1 @@ -109,13 +111,12 @@ class DataBrokerConnectorAuthenticationTest : BehaviorSpec({ and("a successfully established connection") { val connection = dataBrokerConnector.connect() - val actuatorTargetProperty = Property( - "Vehicle.Body.Mirrors.DriverSide.Pan", - listOf(Types.Field.FIELD_ACTUATOR_TARGET), - ) + val panVssPath = "Vehicle.Body.Mirrors.DriverSide.Pan" + val actuatorTargetField = Types.Field.FIELD_ACTUATOR_TARGET `when`("Reading the ACTUATOR_TARGET of Vehicle.Body.Mirrors.DriverSide.Pan") { - val response = connection.fetch(actuatorTargetProperty) + val fetchRequest = FetchRequest(panVssPath, actuatorTargetField) + val response = connection.fetch(fetchRequest) then("No error should occur") { response.errorsList.size shouldBe 0 @@ -125,21 +126,18 @@ class DataBrokerConnectorAuthenticationTest : BehaviorSpec({ `when`("Writing to the ACTUATOR_TARGET of Vehicle.Body.Mirrors.DriverSide.Pan") { val nextInt = random.nextInt(-100..100) val datapoint = Types.Datapoint.newBuilder().setInt32(nextInt).build() + val updateRequest = UpdateRequest(panVssPath, datapoint, actuatorTargetField) - val response = connection.update(actuatorTargetProperty, datapoint) + val response = connection.update(updateRequest) then("An error should occur") { response.errorsList.size shouldBe 1 } } - val valueProperty = Property( - "Vehicle.Speed", - listOf(Types.Field.FIELD_VALUE), - ) - `when`("Reading the VALUE of Vehicle.Speed") { - val response = connection.fetch(valueProperty) + val fetchRequest = FetchRequest(speedVssPath) + val response = connection.fetch(fetchRequest) then("No error should occur") { response.errorsList.size shouldBe 0 @@ -149,8 +147,9 @@ class DataBrokerConnectorAuthenticationTest : BehaviorSpec({ `when`("Writing the VALUE of Vehicle.Speed") { val nextFloat = random.nextFloat() * 100F val datapoint = Types.Datapoint.newBuilder().setFloat(nextFloat).build() + val updateRequest = UpdateRequest(speedVssPath, datapoint) - val response = connection.update(valueProperty, datapoint) + val response = connection.update(updateRequest) then("No error should occur") { response.errorsList.size shouldBe 0 diff --git a/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/databroker/DataBrokerConfig.kt b/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/connectivity/databroker/DataBrokerConfig.kt similarity index 95% rename from kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/databroker/DataBrokerConfig.kt rename to kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/connectivity/databroker/DataBrokerConfig.kt index 255e3d81..4c754649 100644 --- a/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/databroker/DataBrokerConfig.kt +++ b/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/connectivity/databroker/DataBrokerConfig.kt @@ -14,10 +14,9 @@ * limitations under the License. * * SPDX-License-Identifier: Apache-2.0 - * */ -package org.eclipse.kuksa.databroker +package org.eclipse.kuksa.connectivity.databroker import java.util.concurrent.TimeUnit diff --git a/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/DataBrokerConnectionTest.kt b/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/connectivity/databroker/DataBrokerConnectionTest.kt similarity index 55% rename from kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/DataBrokerConnectionTest.kt rename to kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/connectivity/databroker/DataBrokerConnectionTest.kt index 8ce05047..3a992aa6 100644 --- a/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/DataBrokerConnectionTest.kt +++ b/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/connectivity/databroker/DataBrokerConnectionTest.kt @@ -14,10 +14,9 @@ * limitations under the License. * * SPDX-License-Identifier: Apache-2.0 - * */ -package org.eclipse.kuksa +package org.eclipse.kuksa.connectivity.databroker import io.grpc.ConnectivityState import io.grpc.ManagedChannel @@ -31,18 +30,20 @@ import io.mockk.mockk import io.mockk.slot import io.mockk.verify import kotlinx.coroutines.runBlocking -import org.eclipse.kuksa.databroker.DataBrokerConnectorProvider -import org.eclipse.kuksa.mocking.FriendlyVssSpecificationListener -import org.eclipse.kuksa.model.Property +import org.eclipse.kuksa.connectivity.databroker.listener.DisconnectListener +import org.eclipse.kuksa.connectivity.databroker.listener.VssPathListener +import org.eclipse.kuksa.connectivity.databroker.request.FetchRequest +import org.eclipse.kuksa.connectivity.databroker.request.SubscribeRequest +import org.eclipse.kuksa.connectivity.databroker.request.UpdateRequest +import org.eclipse.kuksa.connectivity.databroker.request.VssNodeFetchRequest +import org.eclipse.kuksa.connectivity.databroker.request.VssNodeSubscribeRequest +import org.eclipse.kuksa.mocking.FriendlyVssNodeListener import org.eclipse.kuksa.proto.v1.KuksaValV1 import org.eclipse.kuksa.proto.v1.Types -import org.eclipse.kuksa.proto.v1.Types.Datapoint import org.eclipse.kuksa.test.kotest.DefaultDatabroker import org.eclipse.kuksa.test.kotest.Integration -import org.eclipse.kuksa.vssSpecification.VssDriver -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Assertions.assertFalse -import org.junit.jupiter.api.Assertions.assertTrue +import org.eclipse.kuksa.vssNode.VssDriver +import org.junit.jupiter.api.Assertions import kotlin.random.Random import kotlin.time.Duration.Companion.seconds @@ -52,19 +53,19 @@ class DataBrokerConnectionTest : BehaviorSpec({ given("A successfully established connection to the DataBroker") { val dataBrokerConnection = connectToDataBrokerBlocking() - and("A Property with a valid VSS Path") { + and("A request with a valid VSS Path") { val vssPath = "Vehicle.Acceleration.Lateral" - val fields = listOf(Types.Field.FIELD_VALUE) - val property = Property(vssPath, fields) + val field = Types.Field.FIELD_VALUE + val subscribeRequest = SubscribeRequest(vssPath, field) - `when`("Subscribing to the Property") { - val propertyListener = mockk(relaxed = true) - dataBrokerConnection.subscribe(property, propertyListener) + `when`("Subscribing to the VSS path") { + val vssPathListener = mockk(relaxed = true) + dataBrokerConnection.subscribe(subscribeRequest, vssPathListener) - then("The #onPropertyChanged method is triggered") { + then("The #onEntryChanged method is triggered") { val capturingSlot = slot>() verify(timeout = 100L) { - propertyListener.onPropertyChanged(capture(capturingSlot)) + vssPathListener.onEntryChanged(capture(capturingSlot)) } val entryUpdates = capturingSlot.captured @@ -72,58 +73,62 @@ class DataBrokerConnectionTest : BehaviorSpec({ entryUpdates[0].entry.path shouldBe vssPath } - `when`("The observed Property changes") { - clearMocks(propertyListener) + `when`("The observed VSS path changes") { + clearMocks(vssPathListener) val random = Random(System.currentTimeMillis()) val newValue = random.nextFloat() - val datapoint = Datapoint.newBuilder().setFloat(newValue).build() - dataBrokerConnection.update(property, datapoint) + val datapoint = Types.Datapoint.newBuilder().setFloat(newValue).build() + val updateRequest = UpdateRequest(vssPath, datapoint, field) + dataBrokerConnection.update(updateRequest) - then("The #onPropertyChanged callback is triggered with the new value") { + then("The #onEntryChanged callback is triggered with the new value") { val capturingSlot = slot>() verify(timeout = 100) { - propertyListener.onPropertyChanged(capture(capturingSlot)) + vssPathListener.onEntryChanged(capture(capturingSlot)) } val entryUpdates = capturingSlot.captured val capturedDatapoint = entryUpdates[0].entry.value val float = capturedDatapoint.float - assertEquals(newValue, float, 0.0001f) + Assertions.assertEquals(newValue, float, 0.0001f) } } } val validDatapoint = createRandomFloatDatapoint() - `when`("Updating the Property with a valid Datapoint") { + `when`("Updating the DataBroker property (VSS path) with a valid Datapoint") { // make sure that the value is set and known to us - val response = dataBrokerConnection.update(property, validDatapoint) + val updateRequest = UpdateRequest(vssPath, validDatapoint, field) + val response = dataBrokerConnection.update(updateRequest) then("No error should appear") { - assertFalse(response.hasError()) + Assertions.assertFalse(response.hasError()) } and("When fetching it afterwards") { - val response1 = dataBrokerConnection.fetch(property) + val fetchRequest = FetchRequest(vssPath) + val response1 = dataBrokerConnection.fetch(fetchRequest) then("The response contains the correctly set value") { val entriesList = response1.entriesList val first = entriesList.first() val capturedValue = first.value - assertEquals(validDatapoint.float, capturedValue.float, 0.0001F) + Assertions.assertEquals(validDatapoint.float, capturedValue.float, 0.0001F) } } } - `when`("Updating the Property with a Datapoint of a wrong/different type") { + `when`("Updating the DataBroker property (VSS path) with a Datapoint of a wrong/different type") { val datapoint = createRandomIntDatapoint() - val response = dataBrokerConnection.update(property, datapoint) + val updateRequest = UpdateRequest(vssPath, datapoint) + val response = dataBrokerConnection.update(updateRequest) then("It should fail with an errorCode 400 (type mismatch)") { val errorsList = response.errorsList - assertTrue(errorsList.size > 0) + Assertions.assertTrue(errorsList.size > 0) val error = errorsList[0].error @@ -131,32 +136,35 @@ class DataBrokerConnectionTest : BehaviorSpec({ } and("Fetching it afterwards") { - val getResponse = dataBrokerConnection.fetch(property) + val fetchRequest = FetchRequest(vssPath) + val getResponse = dataBrokerConnection.fetch(fetchRequest) then("The response contains the correctly set value") { val entriesList = getResponse.entriesList val first = entriesList.first() val capturedValue = first.value - assertEquals(validDatapoint.float, capturedValue.float, 0.0001F) + Assertions.assertEquals(validDatapoint.float, capturedValue.float, 0.0001F) } } } } - and("A Specification") { - val specification = VssDriver() - val property = Property(specification.heartRate.vssPath) + and("A VssNode") { + val vssDriver = VssDriver() - `when`("Fetching the specification") { + `when`("Fetching the node") { and("The initial value is different from the default for a child") { val newHeartRateValue = 60 - val datapoint = Datapoint.newBuilder().setUint32(newHeartRateValue).build() + val datapoint = Types.Datapoint.newBuilder().setUint32(newHeartRateValue).build() + val updateRequest = UpdateRequest(vssDriver.heartRate.vssPath, datapoint) + + dataBrokerConnection.update(updateRequest) - dataBrokerConnection.update(property, datapoint) + val fetchRequest = VssNodeFetchRequest(vssDriver) + val updatedDriver = dataBrokerConnection.fetch(fetchRequest) - then("Every child property has been updated with the correct value") { - val updatedDriver = dataBrokerConnection.fetch(specification) + then("Every child node has been updated with the correct value") { val heartRate = updatedDriver.heartRate heartRate.value shouldBe newHeartRateValue @@ -164,46 +172,49 @@ class DataBrokerConnectionTest : BehaviorSpec({ } } - `when`("Subscribing to the specification") { - val specificationListener = FriendlyVssSpecificationListener() - dataBrokerConnection.subscribe(specification, listener = specificationListener) + `when`("Subscribing to the node") { + val vssNodeListener = FriendlyVssNodeListener() + val subscribeRequest = VssNodeSubscribeRequest(vssDriver) + dataBrokerConnection.subscribe(subscribeRequest, listener = vssNodeListener) - then("The #onSpecificationChanged method is triggered") { + then("The #onNodeChanged method is triggered") { eventually(1.seconds) { - specificationListener.updatedSpecifications.size shouldBe 1 + vssNodeListener.updatedVssNodes.size shouldBe 1 } } and("The initial value is different from the default for a child") { val newHeartRateValue = 70 - val datapoint = Datapoint.newBuilder().setUint32(newHeartRateValue).build() + val datapoint = Types.Datapoint.newBuilder().setUint32(newHeartRateValue).build() + val updateRequest = UpdateRequest(vssDriver.heartRate.vssPath, datapoint) - dataBrokerConnection.update(property, datapoint) + dataBrokerConnection.update(updateRequest) - then("Every child property has been updated with the correct value") { + then("Every child node has been updated with the correct value") { eventually(1.seconds) { - specificationListener.updatedSpecifications.size shouldBe 2 + vssNodeListener.updatedVssNodes.size shouldBe 2 } - val updatedDriver = specificationListener.updatedSpecifications.last() + val updatedDriver = vssNodeListener.updatedVssNodes.last() val heartRate = updatedDriver.heartRate heartRate.value shouldBe newHeartRateValue } } - and("Any subscribed Property was changed") { + and("Any subscribed node was changed") { val newHeartRateValue = 50 - val datapoint = Datapoint.newBuilder().setUint32(newHeartRateValue).build() + val datapoint = Types.Datapoint.newBuilder().setUint32(newHeartRateValue).build() + val updateRequest = UpdateRequest(vssDriver.heartRate.vssPath, datapoint) - dataBrokerConnection.update(property, datapoint) + dataBrokerConnection.update(updateRequest) - then("The subscribed Specification should be updated") { + then("The subscribed vssNode should be updated") { eventually(1.seconds) { - specificationListener.updatedSpecifications.size shouldBe 3 + vssNodeListener.updatedVssNodes.size shouldBe 3 } - val updatedDriver = specificationListener.updatedSpecifications.last() + val updatedDriver = vssNodeListener.updatedVssNodes.last() val heartRate = updatedDriver.heartRate heartRate.value shouldBe newHeartRateValue @@ -212,30 +223,31 @@ class DataBrokerConnectionTest : BehaviorSpec({ } } - and("A Property with an INVALID VSS Path") { - val fields = listOf(Types.Field.FIELD_VALUE) - val property = Property("Vehicle.Some.Unknown.Path", fields) + and("An INVALID VSS Path") { + val invalidVssPath = "Vehicle.Some.Unknown.Path" - `when`("Trying to subscribe to the INVALID Property") { - val propertyListener = mockk(relaxed = true) - dataBrokerConnection.subscribe(property, propertyListener) + `when`("Trying to subscribe to the INVALID VSS path") { + val vssPathListener = mockk(relaxed = true) + val subscribeRequest = SubscribeRequest(invalidVssPath) + dataBrokerConnection.subscribe(subscribeRequest, vssPathListener) - then("The PropertyListener#onError method should be triggered with 'NOT_FOUND' (Path not found)") { + then("The VssPathListener#onError method should be triggered with 'NOT_FOUND' (Path not found)") { val capturingSlot = slot() - verify(timeout = 100L) { propertyListener.onError(capture(capturingSlot)) } + verify(timeout = 100L) { vssPathListener.onError(capture(capturingSlot)) } val capturedThrowable = capturingSlot.captured capturedThrowable.message shouldContain "NOT_FOUND" } } - `when`("Trying to update the INVALID property") { + `when`("Trying to update the INVALID VSS Path") { // make sure that the value is set and known to us val datapoint = createRandomFloatDatapoint() - val response = dataBrokerConnection.update(property, datapoint) + val updateRequest = UpdateRequest(invalidVssPath, datapoint) + val response = dataBrokerConnection.update(updateRequest) then("It should fail with an errorCode 404 (path not found)") { val errorsList = response.errorsList - assertTrue(errorsList.size > 0) + Assertions.assertTrue(errorsList.size > 0) val error = errorsList[0].error @@ -243,16 +255,17 @@ class DataBrokerConnectionTest : BehaviorSpec({ } } - `when`("Trying to fetch the INVALID property") { - val response = dataBrokerConnection.fetch(property) + `when`("Trying to fetch the INVALID VSS path") { + val fetchRequest = FetchRequest(invalidVssPath) + val response = dataBrokerConnection.fetch(fetchRequest) then("The response should not contain any entries") { - assertEquals(0, response.entriesList.size) + Assertions.assertEquals(0, response.entriesList.size) } then("The response should contain an error with errorCode 404 (path not found)") { val errorsList = response.errorsList - assertTrue(errorsList.size > 0) + Assertions.assertTrue(errorsList.size > 0) val error = errorsList[0].error @@ -292,16 +305,16 @@ class DataBrokerConnectionTest : BehaviorSpec({ } }) -private fun createRandomFloatDatapoint(): Datapoint { +private fun createRandomFloatDatapoint(): Types.Datapoint { val random = Random(System.currentTimeMillis()) val newValue = random.nextFloat() - return Datapoint.newBuilder().setFloat(newValue).build() + return Types.Datapoint.newBuilder().setFloat(newValue).build() } -private fun createRandomIntDatapoint(): Datapoint { +private fun createRandomIntDatapoint(): Types.Datapoint { val random = Random(System.currentTimeMillis()) val newValue = random.nextInt() - return Datapoint.newBuilder().setInt32(newValue).build() + return Types.Datapoint.newBuilder().setInt32(newValue).build() } private fun connectToDataBrokerBlocking(): DataBrokerConnection { diff --git a/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/databroker/DataBrokerConnectorProvider.kt b/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/connectivity/databroker/DataBrokerConnectorProvider.kt similarity index 94% rename from kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/databroker/DataBrokerConnectorProvider.kt rename to kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/connectivity/databroker/DataBrokerConnectorProvider.kt index 327833bb..53df36bb 100644 --- a/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/databroker/DataBrokerConnectorProvider.kt +++ b/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/connectivity/databroker/DataBrokerConnectorProvider.kt @@ -14,19 +14,17 @@ * limitations under the License. * * SPDX-License-Identifier: Apache-2.0 - * */ -package org.eclipse.kuksa.databroker +package org.eclipse.kuksa.connectivity.databroker import io.grpc.ChannelCredentials import io.grpc.Grpc import io.grpc.ManagedChannel import io.grpc.ManagedChannelBuilder import io.grpc.TlsChannelCredentials -import org.eclipse.kuksa.DataBrokerConnector -import org.eclipse.kuksa.TimeoutConfig -import org.eclipse.kuksa.authentication.JsonWebToken +import org.eclipse.kuksa.connectivity.authentication.JsonWebToken +import org.eclipse.kuksa.model.TimeoutConfig import java.io.IOException import java.io.InputStream diff --git a/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/DataBrokerConnectorSecureTest.kt b/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/connectivity/databroker/DataBrokerConnectorSecureTest.kt similarity index 89% rename from kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/DataBrokerConnectorSecureTest.kt rename to kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/connectivity/databroker/DataBrokerConnectorSecureTest.kt index f9d7b073..0ff03f1f 100644 --- a/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/DataBrokerConnectorSecureTest.kt +++ b/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/connectivity/databroker/DataBrokerConnectorSecureTest.kt @@ -14,22 +14,17 @@ * limitations under the License. * * SPDX-License-Identifier: Apache-2.0 - * */ -package org.eclipse.kuksa +package org.eclipse.kuksa.connectivity.databroker import io.kotest.core.spec.style.BehaviorSpec -import org.eclipse.kuksa.databroker.DataBrokerConnectorProvider import org.eclipse.kuksa.test.TestResourceFile import org.eclipse.kuksa.test.kotest.CustomDatabroker import org.eclipse.kuksa.test.kotest.Integration import org.eclipse.kuksa.test.kotest.Secure import org.junit.jupiter.api.Assertions -// DataBroker must be started with TLS enabled: -// databroker --tls-cert /certs/Server.pem --tls-private-key /certs/Server.key" - // run command: ./gradlew clean test -Dkotest.tags="Secure" class DataBrokerConnectorSecureTest : BehaviorSpec({ tags(Integration, Secure, CustomDatabroker) diff --git a/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/DataBrokerConnectorTest.kt b/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/connectivity/databroker/DataBrokerConnectorTest.kt similarity index 95% rename from kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/DataBrokerConnectorTest.kt rename to kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/connectivity/databroker/DataBrokerConnectorTest.kt index b825a5d4..63e30827 100644 --- a/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/DataBrokerConnectorTest.kt +++ b/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/connectivity/databroker/DataBrokerConnectorTest.kt @@ -14,15 +14,13 @@ * limitations under the License. * * SPDX-License-Identifier: Apache-2.0 - * */ -package org.eclipse.kuksa +package org.eclipse.kuksa.connectivity.databroker import io.kotest.assertions.throwables.shouldThrow import io.kotest.core.spec.style.BehaviorSpec import io.kotest.matchers.shouldNotBe -import org.eclipse.kuksa.databroker.DataBrokerConnectorProvider import org.eclipse.kuksa.test.kotest.DefaultDatabroker import org.eclipse.kuksa.test.kotest.Insecure import org.eclipse.kuksa.test.kotest.Integration diff --git a/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/DataBrokerTransporterTest.kt b/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/connectivity/databroker/DataBrokerTransporterTest.kt similarity index 82% rename from kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/DataBrokerTransporterTest.kt rename to kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/connectivity/databroker/DataBrokerTransporterTest.kt index fc3200f7..21b6cdeb 100644 --- a/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/DataBrokerTransporterTest.kt +++ b/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/connectivity/databroker/DataBrokerTransporterTest.kt @@ -14,10 +14,9 @@ * limitations under the License. * * SPDX-License-Identifier: Apache-2.0 - * */ -package org.eclipse.kuksa +package org.eclipse.kuksa.connectivity.databroker import io.grpc.ManagedChannelBuilder import io.kotest.core.spec.style.BehaviorSpec @@ -26,12 +25,10 @@ import io.kotest.matchers.shouldNotBe import io.kotest.matchers.types.instanceOf import io.mockk.mockk import io.mockk.verify -import org.eclipse.kuksa.databroker.DataBrokerConnectorProvider +import org.eclipse.kuksa.connectivity.databroker.listener.VssPathListener import org.eclipse.kuksa.extensions.updateRandomFloatValue import org.eclipse.kuksa.proto.v1.KuksaValV1 -import org.eclipse.kuksa.proto.v1.KuksaValV1.SetResponse import org.eclipse.kuksa.proto.v1.Types -import org.eclipse.kuksa.proto.v1.Types.Datapoint import org.eclipse.kuksa.test.kotest.DefaultDatabroker import org.eclipse.kuksa.test.kotest.Insecure import org.eclipse.kuksa.test.kotest.Integration @@ -50,14 +47,14 @@ class DataBrokerTransporterTest : BehaviorSpec({ and("Some VSS-related data") { val vssPath = "Vehicle.ADAS.CruiseControl.SpeedSet" - val fields = listOf(Types.Field.FIELD_VALUE) + val fields = setOf(Types.Field.FIELD_VALUE) val random = Random(System.currentTimeMillis()) val valueToSet = random.nextInt(250).toFloat() `when`("Updating the $fields of $vssPath to $valueToSet km/h") { - val updatedDatapoint = Datapoint.newBuilder().setFloat(valueToSet).build() + val updatedDatapoint = Types.Datapoint.newBuilder().setFloat(valueToSet).build() val result = kotlin.runCatching { - classUnderTest.update(vssPath, fields, updatedDatapoint) + classUnderTest.update(vssPath, updatedDatapoint, fields) } then("No Exception should be thrown") { @@ -67,7 +64,7 @@ class DataBrokerTransporterTest : BehaviorSpec({ then("It should return a valid SetResponse") { val response = result.getOrNull() response shouldNotBe null - response shouldBe instanceOf(SetResponse::class) + response shouldBe instanceOf(KuksaValV1.SetResponse::class) } } @@ -104,29 +101,32 @@ class DataBrokerTransporterTest : BehaviorSpec({ `when`("Subscribing to the vssPath using FIELD_VALUE") { val subscription = classUnderTest.subscribe(vssPath, Types.Field.FIELD_VALUE) - val propertyListener = mockk(relaxed = true) - subscription.listeners.register(propertyListener) + val vssPathListener = mockk(relaxed = true) + subscription.listeners.register(vssPathListener) and("The value of the vssPath is updated") { classUnderTest.updateRandomFloatValue(vssPath) - then("The PropertyListener should be notified") { + then("The listener should be notified") { verify { - propertyListener.onPropertyChanged(any()) + vssPathListener.onEntryChanged(any()) } } } } `when`("Subscribing to an invalid vssPath") { - val subscription = classUnderTest.subscribe("Vehicle.Some.Invalid.Path", Types.Field.FIELD_VALUE) + val subscription = classUnderTest.subscribe( + "Vehicle.Some.Invalid.Path", + Types.Field.FIELD_VALUE, + ) - val propertyListener = mockk(relaxed = true) - subscription.listeners.register(propertyListener) + val vssPathListener = mockk(relaxed = true) + subscription.listeners.register(vssPathListener) then("An Error should be triggered") { verify(timeout = 100L) { - propertyListener.onError(any()) + vssPathListener.onError(any()) } } } diff --git a/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/subscription/DataBrokerSubscriberTest.kt b/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/connectivity/databroker/subscription/DataBrokerSubscriberTest.kt similarity index 65% rename from kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/subscription/DataBrokerSubscriberTest.kt rename to kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/connectivity/databroker/subscription/DataBrokerSubscriberTest.kt index 52f2c58b..3c174221 100644 --- a/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/subscription/DataBrokerSubscriberTest.kt +++ b/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/connectivity/databroker/subscription/DataBrokerSubscriberTest.kt @@ -14,10 +14,9 @@ * limitations under the License. * * SPDX-License-Identifier: Apache-2.0 - * */ -package org.eclipse.kuksa.subscription +package org.eclipse.kuksa.connectivity.databroker.subscription import io.kotest.assertions.nondeterministic.continually import io.kotest.assertions.nondeterministic.eventually @@ -28,21 +27,21 @@ import io.mockk.every import io.mockk.mockk import io.mockk.verify import kotlinx.coroutines.delay -import org.eclipse.kuksa.DataBrokerTransporter -import org.eclipse.kuksa.PropertyListener -import org.eclipse.kuksa.databroker.DataBrokerConnectorProvider +import org.eclipse.kuksa.connectivity.databroker.DataBrokerConnectorProvider +import org.eclipse.kuksa.connectivity.databroker.DataBrokerTransporter +import org.eclipse.kuksa.connectivity.databroker.listener.VssPathListener import org.eclipse.kuksa.extensions.toggleBoolean import org.eclipse.kuksa.extensions.updateRandomFloatValue import org.eclipse.kuksa.extensions.updateRandomUint32Value -import org.eclipse.kuksa.mocking.FriendlyPropertyListener -import org.eclipse.kuksa.mocking.FriendlyVssSpecificationListener +import org.eclipse.kuksa.mocking.FriendlyVssNodeListener +import org.eclipse.kuksa.mocking.FriendlyVssPathListener import org.eclipse.kuksa.pattern.listener.MultiListener import org.eclipse.kuksa.pattern.listener.count import org.eclipse.kuksa.proto.v1.Types import org.eclipse.kuksa.test.kotest.DefaultDatabroker import org.eclipse.kuksa.test.kotest.Insecure import org.eclipse.kuksa.test.kotest.Integration -import org.eclipse.kuksa.vssSpecification.VssDriver +import org.eclipse.kuksa.vssNode.VssDriver import kotlin.time.Duration.Companion.milliseconds import kotlin.time.Duration.Companion.seconds @@ -62,34 +61,34 @@ class DataBrokerSubscriberTest : BehaviorSpec({ `when`("Subscribing to VSS_PATH (Branch) 'Vehicle.ADAS.ABS'") { val vssPath = "Vehicle.ADAS.ABS" val fieldValue = Types.Field.FIELD_VALUE - val propertyListener = FriendlyPropertyListener() - classUnderTest.subscribe(vssPath, fieldValue, propertyListener) + val vssPathListener = FriendlyVssPathListener() + classUnderTest.subscribe(vssPath, fieldValue, vssPathListener) - then("The PropertyListener should send out ONE update containing ALL children") { + then("The VssPathListener should send out ONE update containing ALL children") { eventually(1.seconds) { - propertyListener.updates.size shouldBe 1 + vssPathListener.updates.size shouldBe 1 } - val entryUpdates = propertyListener.updates[0] - propertyListener.updates.size shouldBe 1 // ONE update + val entryUpdates = vssPathListener.updates[0] + vssPathListener.updates.size shouldBe 1 // ONE update entryUpdates.size shouldBe 3 // all children: IsEnabled, IsEngaged, IsError entryUpdates.all { it.entry.path.startsWith(vssPath) } shouldBe true } `when`("Any child changes it's value") { - propertyListener.reset() + vssPathListener.reset() val vssPathIsError = "Vehicle.ADAS.ABS.IsError" val newValueIsError = databrokerTransporter.toggleBoolean(vssPathIsError) val vssPathIsEngaged = "Vehicle.ADAS.ABS.IsEngaged" val newValueIsEngaged = databrokerTransporter.toggleBoolean(vssPathIsEngaged) - then("The PropertyListener should be notified about it") { + then("The VssPathListener should be notified about it") { eventually(1.seconds) { - propertyListener.updates.size shouldBe 2 + vssPathListener.updates.size shouldBe 2 } - val entryUpdates = propertyListener.updates.flatten() + val entryUpdates = vssPathListener.updates.flatten() entryUpdates.count { val path = it.entry.path val entry = it.entry @@ -109,31 +108,31 @@ class DataBrokerSubscriberTest : BehaviorSpec({ `when`("Subscribing using VSS_PATH to Vehicle.Speed with FIELD_VALUE") { val vssPath = "Vehicle.Speed" val fieldValue = Types.Field.FIELD_VALUE - val propertyListener = FriendlyPropertyListener() - classUnderTest.subscribe(vssPath, fieldValue, propertyListener) + val vssPathListener = FriendlyVssPathListener() + classUnderTest.subscribe(vssPath, fieldValue, vssPathListener) and("When the FIELD_VALUE of Vehicle.Speed is updated") { val updateRandomFloatValue = databrokerTransporter.updateRandomFloatValue(vssPath) - then("The PropertyListener is notified about the change") { + then("The VssPathListener is notified about the change") { eventually(1.seconds) { - propertyListener.updates.size shouldBe 2 + vssPathListener.updates.size shouldBe 2 } - propertyListener.updates.flatten() + vssPathListener.updates.flatten() .count { val dataEntry = it.entry val datapoint = dataEntry.value dataEntry.path == vssPath && datapoint.float == updateRandomFloatValue } shouldBe 1 - propertyListener.updates.clear() + vssPathListener.updates.clear() } } - `when`("Subscribing the same PropertyListener to a different vssPath") { + `when`("Subscribing the same VssPathListener to a different vssPath") { val otherVssPath = "Vehicle.ADAS.CruiseControl.SpeedSet" - classUnderTest.subscribe(otherVssPath, fieldValue, propertyListener) + classUnderTest.subscribe(otherVssPath, fieldValue, vssPathListener) and("Both values are updated") { val updatedValueVssPath = databrokerTransporter.updateRandomFloatValue(vssPath) @@ -141,10 +140,10 @@ class DataBrokerSubscriberTest : BehaviorSpec({ then("The Observer is notified about both changes") { eventually(1.seconds) { - propertyListener.updates.size shouldBe 3 // 1 from subscribe(otherVssPath) + 2 updates + vssPathListener.updates.size shouldBe 3 // 1 from subscribe(otherVssPath) + 2 updates } - val entryUpdates = propertyListener.updates.flatten() + val entryUpdates = vssPathListener.updates.flatten() entryUpdates .count { val path = it.entry.path @@ -163,25 +162,25 @@ class DataBrokerSubscriberTest : BehaviorSpec({ } } - `when`("Subscribing multiple (different) PropertyListener to $vssPath") { - val friendlyPropertyListeners = mutableListOf() + `when`("Subscribing multiple (different) VssPathListener to $vssPath") { + val friendlyVssPathListeners = mutableListOf() repeat(10) { - val otherPropertyListenerMock = FriendlyPropertyListener() - friendlyPropertyListeners.add(otherPropertyListenerMock) + val otherVssPathListenerMock = FriendlyVssPathListener() + friendlyVssPathListeners.add(otherVssPathListenerMock) - classUnderTest.subscribe(vssPath, fieldValue, otherPropertyListenerMock) + classUnderTest.subscribe(vssPath, fieldValue, otherVssPathListenerMock) } and("When the FIELD_VALUE of Vehicle.Speed is updated") { val randomFloatValue = databrokerTransporter.updateRandomFloatValue(vssPath) - then("Each PropertyListener is only notified once") { - friendlyPropertyListeners.forEach { friendlyPropertyListener -> + then("Each VssPathListener is only notified once") { + friendlyVssPathListeners.forEach { listener -> eventually(1.seconds) { - friendlyPropertyListener.updates.size shouldBe 2 + listener.updates.size shouldBe 2 } - val count = friendlyPropertyListener.updates + val count = listener.updates .count { it[0].entry.value.float == randomFloatValue } count shouldBe 1 } @@ -189,94 +188,94 @@ class DataBrokerSubscriberTest : BehaviorSpec({ } } - `when`("Unsubscribing the previously registered PropertyListener") { - propertyListener.reset() - classUnderTest.unsubscribe(vssPath, fieldValue, propertyListener) + `when`("Unsubscribing the previously registered VssPathListener") { + vssPathListener.reset() + classUnderTest.unsubscribe(vssPath, fieldValue, vssPathListener) and("When the FIELD_VALUE of Vehicle.Speed is updated") { databrokerTransporter.updateRandomFloatValue(vssPath) delay(100) - then("The PropertyListener is not notified") { + then("The VssPathListener is not notified") { continually(100.milliseconds) { - propertyListener.updates.size shouldBe 0 + vssPathListener.updates.size shouldBe 0 } } } } } - `when`("Subscribing the same PropertyListener twice using VSS_PATH to Vehicle.Speed with FIELD_VALUE") { + `when`("Subscribing the same VssPathListener twice using VSS_PATH to Vehicle.Speed with FIELD_VALUE") { val vssPath = "Vehicle.Speed" val fieldValue = Types.Field.FIELD_VALUE - val friendlyPropertyListener = FriendlyPropertyListener() - classUnderTest.subscribe(vssPath, fieldValue, friendlyPropertyListener) - classUnderTest.subscribe(vssPath, fieldValue, friendlyPropertyListener) + val vssPathListener = FriendlyVssPathListener() + classUnderTest.subscribe(vssPath, fieldValue, vssPathListener) + classUnderTest.subscribe(vssPath, fieldValue, vssPathListener) and("When the FIELD_VALUE of Vehicle.Speed is updated") { val randomFloatValue = databrokerTransporter.updateRandomFloatValue(vssPath) - then("The PropertyListener is only notified once") { + then("The VssPathListener is only notified once") { eventually(1.seconds) { - friendlyPropertyListener.updates.size shouldBe 2 + vssPathListener.updates.size shouldBe 2 } - val count = friendlyPropertyListener.updates + val count = vssPathListener.updates .count { it[0].entry.value.float == randomFloatValue } count shouldBe 1 } } } - val specification = VssDriver.VssHeartRate() + val vssHeartRate = VssDriver.VssHeartRate() - `when`("Subscribing using VssSpecification to Vehicle.Driver.HeartRate with Field FIELD_VALUE") { - val friendlyVssSpecificationListener = FriendlyVssSpecificationListener() + `when`("Subscribing using a VSS node to Vehicle.Driver.HeartRate with Field FIELD_VALUE") { + val friendlyVssNodeListener = FriendlyVssNodeListener() classUnderTest.subscribe( - specification, + vssHeartRate, Types.Field.FIELD_VALUE, - friendlyVssSpecificationListener, + friendlyVssNodeListener, ) and("The value of Vehicle.Driver.HeartRate changes") { val randomIntValue = - databrokerTransporter.updateRandomUint32Value(specification.vssPath) + databrokerTransporter.updateRandomUint32Value(vssHeartRate.vssPath) then("The Observer should be triggered") { eventually(1.seconds) { - friendlyVssSpecificationListener.updatedSpecifications.size shouldBe 2 + friendlyVssNodeListener.updatedVssNodes.size shouldBe 2 } - val count = friendlyVssSpecificationListener.updatedSpecifications + val count = friendlyVssNodeListener.updatedVssNodes .count { it.value == randomIntValue } count shouldBe 1 } } } - `when`("Subscribing the same SpecificationObserver twice to Vehicle.Driver.HeartRate") { - val specificationObserverMock = FriendlyVssSpecificationListener() + `when`("Subscribing the same VssNodeObserver twice to Vehicle.Driver.HeartRate") { + val nodeListenerMock = FriendlyVssNodeListener() classUnderTest.subscribe( - specification, + vssHeartRate, Types.Field.FIELD_VALUE, - specificationObserverMock, + nodeListenerMock, ) classUnderTest.subscribe( - specification, + vssHeartRate, Types.Field.FIELD_VALUE, - specificationObserverMock, + nodeListenerMock, ) and("The value of Vehicle.Driver.HeartRate changes") { val randomIntValue = - databrokerTransporter.updateRandomUint32Value(specification.vssPath) + databrokerTransporter.updateRandomUint32Value(vssHeartRate.vssPath) then("The Observer is only notified once") { eventually(1.seconds) { - specificationObserverMock.updatedSpecifications.size shouldBe 2 + nodeListenerMock.updatedVssNodes.size shouldBe 2 } - val count = specificationObserverMock.updatedSpecifications.count { it.value == randomIntValue } + val count = nodeListenerMock.updatedVssNodes.count { it.value == randomIntValue } count shouldBe 1 } } @@ -285,9 +284,9 @@ class DataBrokerSubscriberTest : BehaviorSpec({ } given("An Instance of DataBrokerSubscriber with a mocked DataBrokerTransporter") { - val subscriptionMock = mockk(relaxed = true) + val subscriptionMock = mockk(relaxed = true) val dataBrokerTransporterMock = mockk(relaxed = true) - val multiListener = MultiListener() + val multiListener = MultiListener() every { dataBrokerTransporterMock.subscribe(any(), any()) } returns subscriptionMock every { subscriptionMock.listeners } returns multiListener val classUnderTest = DataBrokerSubscriber(dataBrokerTransporterMock) @@ -295,23 +294,23 @@ class DataBrokerSubscriberTest : BehaviorSpec({ `when`("Subscribing for the first time to a vssPath and field") { val vssPath = "Vehicle.Speed" val field = Types.Field.FIELD_VALUE - val propertyListenerMock1 = mockk() - val propertyListenerMock2 = mockk() - classUnderTest.subscribe(vssPath, field, propertyListenerMock1) + val vssPathListenerMock1 = mockk() + val vssPathListenerMock2 = mockk() + classUnderTest.subscribe(vssPath, field, vssPathListenerMock1) - then("A new Subscription is created and the PropertyListener is added to the list of Listeners") { + then("A new Subscription is created and the VssPathListener is added to the list of Listeners") { verify { dataBrokerTransporterMock.subscribe(vssPath, field) } multiListener.count() shouldBe 1 } - `when`("Another PropertyListener subscribes to the same vssPath and field") { + `when`("Another VssPathListener subscribes to the same vssPath and field") { clearMocks(dataBrokerTransporterMock) - classUnderTest.subscribe(vssPath, field, propertyListenerMock2) + classUnderTest.subscribe(vssPath, field, vssPathListenerMock2) - then("No new Subscription is created and the PropertyListener is added to the list of Listeners") { + then("No new Subscription is created and the VssPathListener is added to the list of Listeners") { verify(exactly = 0) { dataBrokerTransporterMock.subscribe(vssPath, field) } @@ -319,8 +318,8 @@ class DataBrokerSubscriberTest : BehaviorSpec({ } } - `when`("One of two PropertyListeners unsubscribes") { - classUnderTest.unsubscribe(vssPath, field, propertyListenerMock1) + `when`("One of two VssPathListeners unsubscribes") { + classUnderTest.unsubscribe(vssPath, field, vssPathListenerMock1) then("The Subscription is not canceled") { verify(exactly = 0) { @@ -329,8 +328,8 @@ class DataBrokerSubscriberTest : BehaviorSpec({ multiListener.count() shouldBe 1 } - `when`("The last PropertyListener unsubscribes as well") { - classUnderTest.unsubscribe(vssPath, field, propertyListenerMock2) + `when`("The last VssPathListener unsubscribes as well") { + classUnderTest.unsubscribe(vssPath, field, vssPathListenerMock2) then("There should be no more listeners registered") { multiListener.count() shouldBe 0 diff --git a/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/extensions/DataBrokerTransporterExtensions.kt b/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/extensions/DataBrokerTransporterExtensions.kt index 8fc91105..4f85f2f1 100644 --- a/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/extensions/DataBrokerTransporterExtensions.kt +++ b/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/extensions/DataBrokerTransporterExtensions.kt @@ -20,7 +20,7 @@ package org.eclipse.kuksa.extensions import io.kotest.assertions.fail -import org.eclipse.kuksa.DataBrokerTransporter +import org.eclipse.kuksa.connectivity.databroker.DataBrokerTransporter import org.eclipse.kuksa.proto.v1.Types import kotlin.random.Random @@ -33,10 +33,8 @@ internal suspend fun DataBrokerTransporter.updateRandomFloatValue( val randomFloat = randomValue.toFloat() val updatedDatapoint = Types.Datapoint.newBuilder().setFloat(randomFloat).build() - val fields = listOf(Types.Field.FIELD_VALUE) - try { - update(vssPath, fields, updatedDatapoint) + update(vssPath, updatedDatapoint, setOf(Types.Field.FIELD_VALUE)) } catch (e: Exception) { fail("Updating $vssPath to $randomFloat failed: $e") } @@ -53,7 +51,7 @@ internal suspend fun DataBrokerTransporter.updateRandomUint32Value( val updatedDatapoint = Types.Datapoint.newBuilder().setUint32(randomValue).build() try { - update(vssPath, listOf(Types.Field.FIELD_VALUE), updatedDatapoint) + update(vssPath, updatedDatapoint, setOf(Types.Field.FIELD_VALUE)) } catch (e: Exception) { fail("Updating $vssPath to $randomValue failed: $e") } @@ -62,7 +60,7 @@ internal suspend fun DataBrokerTransporter.updateRandomUint32Value( } internal suspend fun DataBrokerTransporter.toggleBoolean(vssPath: String): Boolean { - val fields = listOf(Types.Field.FIELD_VALUE) + val fields = setOf(Types.Field.FIELD_VALUE) var newBoolean: Boolean? = null try { @@ -72,7 +70,7 @@ internal suspend fun DataBrokerTransporter.toggleBoolean(vssPath: String): Boole newBoolean = !currentBool val newDatapoint = Types.Datapoint.newBuilder().setBool(newBoolean).build() - update(vssPath, fields, newDatapoint) + update(vssPath, newDatapoint, fields) } catch (e: Exception) { fail("Updating $vssPath to $newBoolean failed: $e") } diff --git a/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/mocking/FriendlyVssSpecificationListener.kt b/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/mocking/FriendlyVssNodeListener.kt similarity index 68% rename from kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/mocking/FriendlyVssSpecificationListener.kt rename to kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/mocking/FriendlyVssNodeListener.kt index e9a45990..ce4c7acb 100644 --- a/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/mocking/FriendlyVssSpecificationListener.kt +++ b/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/mocking/FriendlyVssNodeListener.kt @@ -19,14 +19,15 @@ package org.eclipse.kuksa.mocking -import org.eclipse.kuksa.VssSpecificationListener -import org.eclipse.kuksa.vsscore.model.VssSpecification +import org.eclipse.kuksa.connectivity.databroker.listener.VssNodeListener +import org.eclipse.kuksa.vsscore.model.VssNode -class FriendlyVssSpecificationListener : VssSpecificationListener { - val updatedSpecifications = mutableListOf() +class FriendlyVssNodeListener : VssNodeListener { + val updatedVssNodes = mutableListOf() val errors = mutableListOf() - override fun onSpecificationChanged(vssSpecification: T) { - updatedSpecifications.add(vssSpecification) + + override fun onNodeChanged(vssNode: T) { + updatedVssNodes.add(vssNode) } override fun onError(throwable: Throwable) { @@ -34,7 +35,7 @@ class FriendlyVssSpecificationListener : VssSpecificationL } fun reset() { - updatedSpecifications.clear() + updatedVssNodes.clear() errors.clear() } } diff --git a/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/mocking/FriendlyPropertyListener.kt b/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/mocking/FriendlyVssPathListener.kt similarity index 84% rename from kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/mocking/FriendlyPropertyListener.kt rename to kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/mocking/FriendlyVssPathListener.kt index 514f1f5b..b3354096 100644 --- a/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/mocking/FriendlyPropertyListener.kt +++ b/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/mocking/FriendlyVssPathListener.kt @@ -19,13 +19,13 @@ package org.eclipse.kuksa.mocking -import org.eclipse.kuksa.PropertyListener +import org.eclipse.kuksa.connectivity.databroker.listener.VssPathListener import org.eclipse.kuksa.proto.v1.KuksaValV1 -class FriendlyPropertyListener : PropertyListener { +class FriendlyVssPathListener : VssPathListener { val updates = mutableListOf>() val errors = mutableListOf() - override fun onPropertyChanged(entryUpdates: List) { + override fun onEntryChanged(entryUpdates: List) { updates.add(entryUpdates) } diff --git a/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/vssSpecification/VssDriver.kt b/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/vssNode/VssDriver.kt similarity index 88% rename from kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/vssSpecification/VssDriver.kt rename to kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/vssNode/VssDriver.kt index 6d3f6999..d81fc36f 100644 --- a/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/vssSpecification/VssDriver.kt +++ b/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/vssNode/VssDriver.kt @@ -17,10 +17,10 @@ * */ -package org.eclipse.kuksa.vssSpecification +package org.eclipse.kuksa.vssNode -import org.eclipse.kuksa.vsscore.model.VssProperty -import org.eclipse.kuksa.vsscore.model.VssSpecification +import org.eclipse.kuksa.vsscore.model.VssNode +import org.eclipse.kuksa.vsscore.model.VssSignal import kotlin.reflect.KClass data class VssDriver @JvmOverloads constructor( @@ -31,7 +31,7 @@ data class VssDriver @JvmOverloads constructor( val identifier: VssIdentifier = VssIdentifier(), val isEyesOnRoad: VssIsEyesOnRoad = VssIsEyesOnRoad(), val isHandsOnWheel: VssIsHandsOnWheel = VssIsHandsOnWheel(), -) : VssSpecification { +) : VssNode { override val comment: String get() = "" @@ -47,7 +47,7 @@ data class VssDriver @JvmOverloads constructor( override val vssPath: String get() = "Vehicle.Driver" - override val children: Set + override val children: Set get() = setOf( attentiveProbability, distractionLevel, @@ -63,7 +63,7 @@ data class VssDriver @JvmOverloads constructor( data class VssHeartRate @JvmOverloads constructor( override val `value`: Int = 0, - ) : VssProperty { + ) : VssSignal { override val comment: String get() = "" @@ -79,7 +79,7 @@ data class VssDriver @JvmOverloads constructor( override val vssPath: String get() = "Vehicle.Driver.HeartRate" - override val children: Set + override val children: Set get() = setOf() override val parentClass: KClass<*> @@ -89,7 +89,7 @@ data class VssDriver @JvmOverloads constructor( data class VssAttentiveProbability @JvmOverloads constructor( override val `value`: Float = 0f, -) : VssProperty { +) : VssSignal { override val comment: String get() = "" @@ -105,7 +105,7 @@ data class VssAttentiveProbability @JvmOverloads constructor( override val vssPath: String get() = "Vehicle.Driver.AttentiveProbability" - override val children: Set + override val children: Set get() = setOf() override val parentClass: KClass<*> @@ -114,7 +114,7 @@ data class VssAttentiveProbability @JvmOverloads constructor( data class VssDistractionLevel @JvmOverloads constructor( override val `value`: Float = 0f, -) : VssProperty { +) : VssSignal { override val comment: String get() = "" @@ -130,7 +130,7 @@ data class VssDistractionLevel @JvmOverloads constructor( override val vssPath: String get() = "Vehicle.Driver.DistractionLevel" - override val children: Set + override val children: Set get() = setOf() override val parentClass: KClass<*> @@ -139,7 +139,7 @@ data class VssDistractionLevel @JvmOverloads constructor( data class VssFatigueLevel @JvmOverloads constructor( override val `value`: Float = 0f, -) : VssProperty { +) : VssSignal { override val comment: String get() = "" @@ -155,7 +155,7 @@ data class VssFatigueLevel @JvmOverloads constructor( override val vssPath: String get() = "Vehicle.Driver.FatigueLevel" - override val children: Set + override val children: Set get() = setOf() override val parentClass: KClass<*> @@ -165,7 +165,7 @@ data class VssFatigueLevel @JvmOverloads constructor( data class VssIdentifier @JvmOverloads constructor( val issuer: VssIssuer = VssIssuer(), val subject: VssSubject = VssSubject(), -) : VssSpecification { +) : VssNode { override val comment: String get() = "" @@ -181,7 +181,7 @@ data class VssIdentifier @JvmOverloads constructor( override val vssPath: String get() = "Vehicle.Driver.Identifier" - override val children: Set + override val children: Set get() = setOf(issuer, subject) override val parentClass: KClass<*> @@ -190,7 +190,7 @@ data class VssIdentifier @JvmOverloads constructor( data class VssIssuer @JvmOverloads constructor( override val `value`: String = "", -) : VssProperty { +) : VssSignal { override val comment: String get() = "" @@ -207,7 +207,7 @@ data class VssIssuer @JvmOverloads constructor( override val vssPath: String get() = "Vehicle.Driver.Identifier.Issuer" - override val children: Set + override val children: Set get() = setOf() override val parentClass: KClass<*> @@ -216,7 +216,7 @@ data class VssIssuer @JvmOverloads constructor( data class VssSubject @JvmOverloads constructor( override val `value`: String = "", -) : VssProperty { +) : VssSignal { override val comment: String get() = "" @@ -232,7 +232,7 @@ data class VssSubject @JvmOverloads constructor( override val vssPath: String get() = "Vehicle.Driver.Identifier.Subject" - override val children: Set + override val children: Set get() = setOf() override val parentClass: KClass<*> @@ -241,7 +241,7 @@ data class VssSubject @JvmOverloads constructor( data class VssIsEyesOnRoad @JvmOverloads constructor( override val `value`: Boolean = false, -) : VssProperty { +) : VssSignal { override val comment: String get() = "" @@ -257,7 +257,7 @@ data class VssIsEyesOnRoad @JvmOverloads constructor( override val vssPath: String get() = "Vehicle.Driver.IsEyesOnRoad" - override val children: Set + override val children: Set get() = setOf() override val parentClass: KClass<*> @@ -266,7 +266,7 @@ data class VssIsEyesOnRoad @JvmOverloads constructor( data class VssIsHandsOnWheel @JvmOverloads constructor( override val `value`: Boolean = false, -) : VssProperty { +) : VssSignal { override val comment: String get() = "" @@ -282,7 +282,7 @@ data class VssIsHandsOnWheel @JvmOverloads constructor( override val vssPath: String get() = "Vehicle.Driver.IsHandsOnWheel" - override val children: Set + override val children: Set get() = setOf() override val parentClass: KClass<*> diff --git a/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/vssSpecification/VssSpecificationArithmeticTest.kt b/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/vssNode/VssNodeArithmeticTest.kt similarity index 89% rename from kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/vssSpecification/VssSpecificationArithmeticTest.kt rename to kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/vssNode/VssNodeArithmeticTest.kt index 22de38a2..04387424 100644 --- a/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/vssSpecification/VssSpecificationArithmeticTest.kt +++ b/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/vssNode/VssNodeArithmeticTest.kt @@ -16,22 +16,22 @@ * SPDX-License-Identifier: Apache-2.0 */ -package org.eclipse.kuksa.vssSpecification +package org.eclipse.kuksa.vssNode import io.kotest.assertions.throwables.shouldThrow import io.kotest.core.spec.style.BehaviorSpec import io.kotest.matchers.shouldBe import io.kotest.matchers.string.shouldStartWith -import org.eclipse.kuksa.extension.vssProperty.div -import org.eclipse.kuksa.extension.vssProperty.minus -import org.eclipse.kuksa.extension.vssProperty.plus -import org.eclipse.kuksa.extension.vssProperty.times +import org.eclipse.kuksa.extension.vss.div +import org.eclipse.kuksa.extension.vss.minus +import org.eclipse.kuksa.extension.vss.plus +import org.eclipse.kuksa.extension.vss.times import org.eclipse.kuksa.test.kotest.Unit -class VssSpecificationArithmeticTest : BehaviorSpec({ +class VssNodeArithmeticTest : BehaviorSpec({ tags(Unit) - given("VssSpecification values for arithmetic operations") { + given("VssNode values for arithmetic operations") { val valueInt = VssValueInt(value = 100) val valueFloat = VssValueFloat(value = 100f) val valueLong = VssValueLong(value = 100L) diff --git a/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/vssSpecification/VssSpecificationCopyTest.kt b/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/vssNode/VssNodeCopyTest.kt similarity index 70% rename from kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/vssSpecification/VssSpecificationCopyTest.kt rename to kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/vssNode/VssNodeCopyTest.kt index 8499acd2..3e711dd6 100644 --- a/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/vssSpecification/VssSpecificationCopyTest.kt +++ b/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/vssNode/VssNodeCopyTest.kt @@ -17,37 +17,37 @@ * */ -package org.eclipse.kuksa.vssSpecification +package org.eclipse.kuksa.vssNode import io.kotest.assertions.throwables.shouldThrow import io.kotest.core.spec.style.BehaviorSpec import io.kotest.matchers.shouldBe import io.kotest.matchers.string.shouldStartWith import io.kotest.matchers.types.shouldBeSameInstanceAs -import org.eclipse.kuksa.extension.copy -import org.eclipse.kuksa.extension.deepCopy -import org.eclipse.kuksa.extension.invoke -import org.eclipse.kuksa.extension.vssProperty.not +import org.eclipse.kuksa.extension.vss.copy +import org.eclipse.kuksa.extension.vss.deepCopy +import org.eclipse.kuksa.extension.vss.invoke +import org.eclipse.kuksa.extension.vss.not import org.eclipse.kuksa.proto.v1.Types import org.eclipse.kuksa.test.kotest.Unit -import org.eclipse.kuksa.vsscore.model.VssProperty +import org.eclipse.kuksa.vsscore.model.VssSignal -class VssSpecificationCopyTest : BehaviorSpec({ +class VssNodeCopyTest : BehaviorSpec({ tags(Unit) - given("A specification") { + given("A VssNode") { val vehicle = VssVehicle() - val driverHeartRate: VssProperty = vehicle.driver.heartRate + val driverHeartRate: VssSignal = vehicle.driver.heartRate and("a changed heritage line") { val newValue = 70 val updatedHeartRate = VssDriver.VssHeartRate(value = newValue) `when`("a deep copy is done with a changed heritage line") { - val deepCopiedSpecification = vehicle.deepCopy(0, updatedHeartRate) + val deepCopiedNode = vehicle.deepCopy(updatedHeartRate) then("it should return the new children as a copy") { - val heartRate = deepCopiedSpecification.driver.heartRate + val heartRate = deepCopiedNode.driver.heartRate heartRate shouldBeSameInstanceAs updatedHeartRate } @@ -55,10 +55,10 @@ class VssSpecificationCopyTest : BehaviorSpec({ } `when`("a value copy is done via the not operator") { - val invertedSpecification = !vehicle.driver.isEyesOnRoad + val invertedNode = !vehicle.driver.isEyesOnRoad then("it should return a copy with the inverted value") { - invertedSpecification.value shouldBe true + invertedNode.value shouldBe true } } @@ -67,10 +67,10 @@ class VssSpecificationCopyTest : BehaviorSpec({ `when`("a deep copy is done via the invoke operator") { val copiedHeartRate = driverHeartRate(newValue) - val copiedSpecification = vehicle(copiedHeartRate) + val copiedNode = vehicle(copiedHeartRate) then("it should return a copy with the updated value") { - val heartRate = copiedSpecification.driver.heartRate + val heartRate = copiedNode.driver.heartRate heartRate shouldBeSameInstanceAs copiedHeartRate } @@ -82,10 +82,10 @@ class VssSpecificationCopyTest : BehaviorSpec({ val datapoint = Types.Datapoint.newBuilder().setInt32(newValue).build() `when`("a copy is done") { - val copiedSpecification = driverHeartRate.copy(datapoint) + val copiedNode = driverHeartRate.copy(datapoint) then("it should return a copy with the updated value") { - val heartRateValue = copiedSpecification.value + val heartRateValue = copiedNode.value heartRateValue shouldBe newValue } @@ -95,10 +95,10 @@ class VssSpecificationCopyTest : BehaviorSpec({ val vssPath = driverHeartRate.vssPath `when`("a copy is done") { - val copiedSpecification = driverHeartRate.copy(vssPath, datapoint) + val copiedNode = driverHeartRate.copy(vssPath, datapoint) then("it should return a copy with the updated value") { - val heartRateValue = copiedSpecification.value + val heartRateValue = copiedNode.value heartRateValue shouldBe newValue } @@ -109,12 +109,12 @@ class VssSpecificationCopyTest : BehaviorSpec({ and("an empty DataPoint") { val datapoint = Types.Datapoint.newBuilder().build() - and("an invalid VssSpecification") { - val invalidSpecification = VssInvalid() + and("an invalid VssSignal") { + val vssInvalid = VssInvalid() `when`("a copy is done") { val exception = shouldThrow { - invalidSpecification.copy(datapoint) + vssInvalid.copy(datapoint) } then("it should throw an IllegalArgumentException") { diff --git a/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/vssSpecification/VssVehicle.kt b/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/vssNode/VssVehicle.kt similarity index 88% rename from kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/vssSpecification/VssVehicle.kt rename to kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/vssNode/VssVehicle.kt index 6abebf3d..ff1cad8a 100644 --- a/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/vssSpecification/VssVehicle.kt +++ b/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/vssNode/VssVehicle.kt @@ -17,10 +17,10 @@ * */ -package org.eclipse.kuksa.vssSpecification +package org.eclipse.kuksa.vssNode -import org.eclipse.kuksa.vsscore.model.VssProperty -import org.eclipse.kuksa.vsscore.model.VssSpecification +import org.eclipse.kuksa.vsscore.model.VssNode +import org.eclipse.kuksa.vsscore.model.VssSignal import kotlin.reflect.KClass data class VssVehicle( @@ -32,8 +32,8 @@ data class VssVehicle( override val description: String = "High-level vehicle data.", override val type: String = "branch", override val comment: String = "", -) : VssSpecification { - override val children: Set +) : VssNode { + override val children: Set get() = setOf(driver, passenger, body) } @@ -43,7 +43,7 @@ data class VssBody( override val description: String = "All body components.", override val type: String = "branch", override val comment: String = "", -) : VssSpecification { +) : VssNode { override val parentClass: KClass<*> get() = VssVehicle::class } @@ -55,8 +55,8 @@ data class VssPassenger( override val description: String = "Passenger data", override val type: String = "branch", override val comment: String = "", -) : VssSpecification { - override val children: Set +) : VssNode { + override val children: Set get() = setOf(heartRate) override val parentClass: KClass<*> get() = VssVehicle::class @@ -68,7 +68,7 @@ data class VssPassenger( override val type: String = "sensor", override val comment: String = "", override val value: Int = 80, - ) : VssProperty { + ) : VssSignal { override val parentClass: KClass<*> get() = VssPassenger::class } @@ -81,7 +81,7 @@ data class VssValueInt( override val type: String = "sensor", override val comment: String = "", override val value: Int = 0, -) : VssProperty +) : VssSignal data class VssValueFloat( override val uuid: String = "Value", @@ -90,7 +90,7 @@ data class VssValueFloat( override val type: String = "sensor", override val comment: String = "", override val value: Float = 0f, -) : VssProperty +) : VssSignal data class VssValueDouble( override val uuid: String = "Value", @@ -99,7 +99,7 @@ data class VssValueDouble( override val type: String = "sensor", override val comment: String = "", override val value: Double = 0.0, -) : VssProperty +) : VssSignal data class VssValueLong( override val uuid: String = "Value", @@ -108,7 +108,7 @@ data class VssValueLong( override val type: String = "sensor", override val comment: String = "", override val value: Long = 0L, -) : VssProperty +) : VssSignal data class VssInvalid( override val uuid: String = "Invalid", @@ -117,4 +117,4 @@ data class VssInvalid( override val type: String = "", override val comment: String = "", override val value: Exception = Exception(), -) : VssProperty +) : VssSignal diff --git a/kuksa-sdk/src/test/kotlin/org/integration-tests-guide.md b/kuksa-sdk/src/test/kotlin/org/integration-tests-guide.md index ed2f35e3..a3b6a904 100644 --- a/kuksa-sdk/src/test/kotlin/org/integration-tests-guide.md +++ b/kuksa-sdk/src/test/kotlin/org/integration-tests-guide.md @@ -3,14 +3,15 @@ corresponding SubscriptionChannels run asynchronously. Between the update execut here and the propagation to the subscribed observers a small timeframe may pass. Therefore things like this most likely won't work because of the behavior described above: ``` - subscribe("Vehicle.Speed", observer) + val request = SubscribeRequest("Vehicle.Speed") + subscribe(request, observer) updateRandomFloatValue("Vehicle.Speed") - verify { observer.onPropertyChanged("Vehicle.Speed", any())} + verify { observer.onEntryChanged("Vehicle.Speed", any())} ``` Keep in mind, that when calling DataBrokerConnection#subscribe an initial update with the current value is send, to the subscribing observer, meaning that checking for a successful update requires at least two calls to the -#onPropertyChanged method. One for the initial update and one for the consecutive, real update of the value. +#onEntryChanged method. One for the initial update and one for the consecutive, real update of the value. Using verify(timeout = 100\[, exactly = 1\]) alone won't fix this issue, because "exactly = 1" will always be fulfilled by the initial update mentioned above. It therefore needs to be something like @@ -18,13 +19,14 @@ verify(timeout = 100, exactly = 2) if an update is expected. A better example therefore would be: ``` - subscribe("Vehicle.Speed", observer) // triggers first #onPropertyChanged with initial value - val value = updateRandomFloatValue() // triggers second #onPropertyChanged with updated value + val request = SubscribeRequest("Vehicle.Speed") + subscribe(request, observer) // triggers first #onEntryChanged with initial value + val value = updateRandomFloatValue() // triggers second #onEntryChanged with updated value val dataEntries = mutableListOf() verify (timeout = 100, exactly = 2) { // initial update + "updateRandomFloatValue") - observer.onPropertyChanged("Vehicle.Speed", capture(dataEntries)) + observer.onEntryChanged("Vehicle.Speed", capture(dataEntries)) } val count = dataEntries.count { it.value.float == randomFloatValue } diff --git a/samples/src/main/java/com/example/sample/JavaActivity.java b/samples/src/main/java/com/example/sample/JavaActivity.java index 1ef1dd08..1271330f 100644 --- a/samples/src/main/java/com/example/sample/JavaActivity.java +++ b/samples/src/main/java/com/example/sample/JavaActivity.java @@ -22,14 +22,19 @@ import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; -import org.eclipse.kuksa.CoroutineCallback; -import org.eclipse.kuksa.DataBrokerConnection; -import org.eclipse.kuksa.DataBrokerConnector; -import org.eclipse.kuksa.DisconnectListener; -import org.eclipse.kuksa.PropertyListener; -import org.eclipse.kuksa.VssSpecificationListener; -import org.eclipse.kuksa.authentication.JsonWebToken; -import org.eclipse.kuksa.model.Property; +import org.eclipse.kuksa.connectivity.authentication.JsonWebToken; +import org.eclipse.kuksa.connectivity.databroker.DataBrokerConnection; +import org.eclipse.kuksa.connectivity.databroker.DataBrokerConnector; +import org.eclipse.kuksa.connectivity.databroker.listener.DisconnectListener; +import org.eclipse.kuksa.connectivity.databroker.listener.VssPathListener; +import org.eclipse.kuksa.connectivity.databroker.listener.VssNodeListener; +import org.eclipse.kuksa.connectivity.databroker.request.FetchRequest; +import org.eclipse.kuksa.connectivity.databroker.request.SubscribeRequest; +import org.eclipse.kuksa.connectivity.databroker.request.UpdateRequest; +import org.eclipse.kuksa.connectivity.databroker.request.VssNodeFetchRequest; +import org.eclipse.kuksa.connectivity.databroker.request.VssNodeSubscribeRequest; +import org.eclipse.kuksa.connectivity.databroker.request.VssNodeUpdateRequest; +import org.eclipse.kuksa.coroutine.CoroutineCallback; import org.eclipse.kuksa.proto.v1.KuksaValV1; import org.eclipse.kuksa.proto.v1.KuksaValV1.GetResponse; import org.eclipse.kuksa.proto.v1.Types; @@ -39,7 +44,6 @@ import java.io.IOException; import java.io.InputStream; import java.util.Collection; -import java.util.Collections; import java.util.List; import javax.annotation.Nullable; @@ -53,7 +57,7 @@ /** * @noinspection unused */ -//@VssDefinition // Commented out to prevent conflicts with the Kotlin activity +//@VssModelGenerator // Commented out to prevent conflicts with the Kotlin activity public class JavaActivity extends AppCompatActivity { private final DisconnectListener disconnectListener = () -> { @@ -135,10 +139,11 @@ public void disconnect() { dataBrokerConnection = null; } - public void fetchProperty(Property property) { + public void fetch() { if (dataBrokerConnection == null) return; - dataBrokerConnection.fetch(property, new CoroutineCallback() { + FetchRequest request = new FetchRequest("Vehicle.Speed", Types.Field.FIELD_VALUE); + dataBrokerConnection.fetch(request, new CoroutineCallback() { @Override public void onSuccess(GetResponse result) { // handle result @@ -151,10 +156,14 @@ public void onError(@NonNull Throwable throwable) { }); } - public void updateProperty(Property property, Datapoint datapoint) { + public void update() { if (dataBrokerConnection == null) return; - dataBrokerConnection.update(property, datapoint, new CoroutineCallback() { + Datapoint datapoint = Datapoint.newBuilder() + .setFloat(50f) + .build(); + UpdateRequest request = new UpdateRequest("Vehicle.Speed", datapoint, Types.Field.FIELD_VALUE); + dataBrokerConnection.update(request, new CoroutineCallback() { @Override public void onSuccess(KuksaValV1.SetResponse result) { // handle result @@ -167,16 +176,17 @@ public void onError(@NonNull Throwable error) { }); } - public void subscribeProperty(Property property) { + public void subscribe() { if (dataBrokerConnection == null) return; - dataBrokerConnection.subscribe(property, new PropertyListener() { + SubscribeRequest request = new SubscribeRequest("Vehicle.Speed", Types.Field.FIELD_VALUE); + dataBrokerConnection.subscribe(request, new VssPathListener() { @Override - public void onPropertyChanged(@NonNull List entryUpdates) { + public void onEntryChanged(@NonNull List entryUpdates) { for (KuksaValV1.EntryUpdate entryUpdate : entryUpdates) { Types.DataEntry updatedValue = entryUpdate.getEntry(); - // handle property change + // handle value change //noinspection SwitchStatementWithTooFewBranches switch (updatedValue.getPath()) { case "VSS.Speed": @@ -193,14 +203,17 @@ public void onError(@NonNull Throwable throwable) { }); } - // region: Specifications - public void fetchSpecification() { + // region: VSS generated models + public void fetchNode() { if (dataBrokerConnection == null) return; VssVehicle.VssSpeed vssSpeed = new VssVehicle.VssSpeed(); - dataBrokerConnection.fetch( + VssNodeFetchRequest request = new VssNodeFetchRequest<>( vssSpeed, - Collections.singleton(Types.Field.FIELD_VALUE), + Types.Field.FIELD_VALUE + ); + dataBrokerConnection.fetch( + request, new CoroutineCallback() { @Override public void onSuccess(@Nullable VssVehicle.VssSpeed result) { @@ -217,13 +230,16 @@ public void onError(@NonNull Throwable error) { ); } - public void updateSpecification() { + public void updateNode() { if (dataBrokerConnection == null) return; VssVehicle.VssSpeed vssSpeed = new VssVehicle.VssSpeed(100f); - dataBrokerConnection.update( + VssNodeUpdateRequest request = new VssNodeUpdateRequest<>( vssSpeed, - Collections.singleton(Types.Field.FIELD_VALUE), + Types.Field.FIELD_VALUE + ); + dataBrokerConnection.update( + request, new CoroutineCallback>() { @Override public void onSuccess(@Nullable Collection result) { @@ -238,17 +254,20 @@ public void onError(@NonNull Throwable error) { ); } - public void subscribeSpecification() { + public void subscribeNode() { if (dataBrokerConnection == null) return; VssVehicle.VssSpeed vssSpeed = new VssVehicle.VssSpeed(); - dataBrokerConnection.subscribe( + VssNodeSubscribeRequest request = new VssNodeSubscribeRequest<>( vssSpeed, - Collections.singleton(Types.Field.FIELD_VALUE), - new VssSpecificationListener() { + Types.Field.FIELD_VALUE + ); + dataBrokerConnection.subscribe( + request, + new VssNodeListener() { @Override - public void onSpecificationChanged(@NonNull VssVehicle.VssSpeed vssSpecification) { - Float speed = vssSpecification.getValue(); + public void onNodeChanged(@NonNull VssVehicle.VssSpeed vssNode) { + Float speed = vssNode.getValue(); } @Override diff --git a/samples/src/main/kotlin/com/example/sample/KotlinActivity.kt b/samples/src/main/kotlin/com/example/sample/KotlinActivity.kt index 12536968..b1c64fce 100644 --- a/samples/src/main/kotlin/com/example/sample/KotlinActivity.kt +++ b/samples/src/main/kotlin/com/example/sample/KotlinActivity.kt @@ -26,23 +26,28 @@ import io.grpc.Grpc import io.grpc.ManagedChannelBuilder import io.grpc.TlsChannelCredentials import kotlinx.coroutines.launch -import org.eclipse.kuksa.DataBrokerConnection -import org.eclipse.kuksa.DataBrokerConnector -import org.eclipse.kuksa.DataBrokerException -import org.eclipse.kuksa.DisconnectListener -import org.eclipse.kuksa.PropertyListener -import org.eclipse.kuksa.VssSpecificationListener -import org.eclipse.kuksa.authentication.JsonWebToken -import org.eclipse.kuksa.model.Property +import org.eclipse.kuksa.connectivity.authentication.JsonWebToken +import org.eclipse.kuksa.connectivity.databroker.DataBrokerConnection +import org.eclipse.kuksa.connectivity.databroker.DataBrokerConnector +import org.eclipse.kuksa.connectivity.databroker.DataBrokerException +import org.eclipse.kuksa.connectivity.databroker.listener.DisconnectListener +import org.eclipse.kuksa.connectivity.databroker.listener.VssNodeListener +import org.eclipse.kuksa.connectivity.databroker.listener.VssPathListener +import org.eclipse.kuksa.connectivity.databroker.request.FetchRequest +import org.eclipse.kuksa.connectivity.databroker.request.SubscribeRequest +import org.eclipse.kuksa.connectivity.databroker.request.UpdateRequest +import org.eclipse.kuksa.connectivity.databroker.request.VssNodeFetchRequest +import org.eclipse.kuksa.connectivity.databroker.request.VssNodeSubscribeRequest +import org.eclipse.kuksa.connectivity.databroker.request.VssNodeUpdateRequest import org.eclipse.kuksa.proto.v1.KuksaValV1 import org.eclipse.kuksa.proto.v1.Types import org.eclipse.kuksa.proto.v1.Types.Datapoint import org.eclipse.kuksa.vss.VssVehicle -import org.eclipse.kuksa.vsscore.annotation.VssDefinition +import org.eclipse.kuksa.vsscore.annotation.VssModelGenerator import java.io.IOException @Suppress("UNUSED_VARIABLE", "SwallowedException") -@VssDefinition +@VssModelGenerator class KotlinActivity : AppCompatActivity() { private var disconnectListener = DisconnectListener { @@ -114,10 +119,11 @@ class KotlinActivity : AppCompatActivity() { dataBrokerConnection = null } - fun fetchProperty(property: Property) { + fun fetchProperty() { + val request = FetchRequest("Vehicle.Speed", Types.Field.FIELD_VALUE) lifecycleScope.launch { try { - val response = dataBrokerConnection?.fetch(property) ?: return@launch + val response = dataBrokerConnection?.fetch(request) ?: return@launch // handle response } catch (e: DataBrokerException) { // handle error @@ -125,10 +131,16 @@ class KotlinActivity : AppCompatActivity() { } } - fun updateProperty(property: Property, datapoint: Datapoint) { + private val newSpeed = 50f + + fun updateProperty() { + val datapoint = Datapoint.newBuilder() + .setFloat(newSpeed) + .build() + val request = UpdateRequest("Vehicle.Speed", datapoint, Types.Field.FIELD_VALUE) lifecycleScope.launch { try { - val response = dataBrokerConnection?.update(property, datapoint) ?: return@launch + val response = dataBrokerConnection?.update(request) ?: return@launch // handle response } catch (e: DataBrokerException) { // handle error @@ -136,13 +148,14 @@ class KotlinActivity : AppCompatActivity() { } } - fun subscribeProperty(property: Property) { - val propertyListener = object : PropertyListener { - override fun onPropertyChanged(entryUpdates: List) { + fun subscribeProperty() { + val request = SubscribeRequest("Vehicle.Speed", Types.Field.FIELD_VALUE) + val vssPathListener = object : VssPathListener { + override fun onEntryChanged(entryUpdates: List) { entryUpdates.forEach { entryUpdate -> val updatedValue = entryUpdate.entry - // handle property change + // handle value change when (updatedValue.path) { "Vehicle.Speed" -> { val speed = updatedValue.value.float @@ -156,15 +169,16 @@ class KotlinActivity : AppCompatActivity() { } } - dataBrokerConnection?.subscribe(property, propertyListener) + dataBrokerConnection?.subscribe(request, vssPathListener) } - // region: Specifications - fun fetchSpecification() { + // region: VSS generated models + fun fetchNode() { lifecycleScope.launch { try { val vssSpeed = VssVehicle.VssSpeed() - val updatedSpeed = dataBrokerConnection?.fetch(vssSpeed, listOf(Types.Field.FIELD_VALUE)) + val request = VssNodeFetchRequest(vssSpeed, Types.Field.FIELD_VALUE) + val updatedSpeed = dataBrokerConnection?.fetch(request) val speed = updatedSpeed?.value } catch (e: DataBrokerException) { // handle error @@ -172,20 +186,21 @@ class KotlinActivity : AppCompatActivity() { } } - fun updateSpecification() { + fun updateNode() { lifecycleScope.launch { val vssSpeed = VssVehicle.VssSpeed(value = 100f) - dataBrokerConnection?.update(vssSpeed, listOf(Types.Field.FIELD_VALUE)) + val request = VssNodeUpdateRequest(vssSpeed, Types.Field.FIELD_VALUE) + dataBrokerConnection?.update(request) } } - fun subscribeSpecification() { + fun subscribeNode() { val vssSpeed = VssVehicle.VssSpeed(value = 100f) + val request = VssNodeSubscribeRequest(vssSpeed, Types.Field.FIELD_VALUE) dataBrokerConnection?.subscribe( - vssSpeed, - listOf(Types.Field.FIELD_VALUE), - listener = object : VssSpecificationListener { - override fun onSpecificationChanged(vssSpecification: VssVehicle.VssSpeed) { + request, + object : VssNodeListener { + override fun onNodeChanged(vssNode: VssVehicle.VssSpeed) { val speed = vssSpeed.value } diff --git a/vss-core/src/main/kotlin/org/eclipse/kuksa/vsscore/annotation/VssDefinition.kt b/vss-core/src/main/kotlin/org/eclipse/kuksa/vsscore/annotation/VssModelGenerator.kt similarity index 92% rename from vss-core/src/main/kotlin/org/eclipse/kuksa/vsscore/annotation/VssDefinition.kt rename to vss-core/src/main/kotlin/org/eclipse/kuksa/vsscore/annotation/VssModelGenerator.kt index cc9215d8..8cf89281 100644 --- a/vss-core/src/main/kotlin/org/eclipse/kuksa/vsscore/annotation/VssDefinition.kt +++ b/vss-core/src/main/kotlin/org/eclipse/kuksa/vsscore/annotation/VssModelGenerator.kt @@ -21,9 +21,9 @@ package org.eclipse.kuksa.vsscore.annotation /** * Add this annotation to any class to trigger the model generation (Kotlin Symbol Processing) for the given - * Vehicle Signal Specification definition file by the "vss-processor-plugin". Currently VSS specification files in + * Vehicle Signal Specification definition file by the "vss-processor-plugin". Currently VSS files in * .yaml and .json format are supported by the vss-processor. - * Use the "VSS Processor Plugin" to provide the Symbol Processor with the necessary definition file(s). + * Use the "VSS Processor Plugin" to provide the Symbol Processor with the necessary VSS file(s). * * ### Plugin Example * @@ -42,7 +42,7 @@ package org.eclipse.kuksa.vsscore.annotation * ### Annotation Example * * ``` - * @VssDefinition + * @VssModelGenerator * class Activity * ``` * @@ -56,4 +56,4 @@ package org.eclipse.kuksa.vsscore.annotation */ @Target(AnnotationTarget.CLASS) @Retention(AnnotationRetention.SOURCE) -annotation class VssDefinition +annotation class VssModelGenerator diff --git a/vss-core/src/main/kotlin/org/eclipse/kuksa/vsscore/model/VssSpecification.kt b/vss-core/src/main/kotlin/org/eclipse/kuksa/vsscore/model/VssNode.kt similarity index 56% rename from vss-core/src/main/kotlin/org/eclipse/kuksa/vsscore/model/VssSpecification.kt rename to vss-core/src/main/kotlin/org/eclipse/kuksa/vsscore/model/VssNode.kt index 48747baa..b613f616 100644 --- a/vss-core/src/main/kotlin/org/eclipse/kuksa/vsscore/model/VssSpecification.kt +++ b/vss-core/src/main/kotlin/org/eclipse/kuksa/vsscore/model/VssNode.kt @@ -24,13 +24,42 @@ import kotlin.reflect.KClass import kotlin.reflect.full.declaredMemberProperties /** - * Represents a node inside a VSS specification file. + * Represents a node inside a VSS (file) data structure. it represents the most common properties. */ interface VssNode { + /** + * The [uuid] is a mandatory field and should never be empty. + */ + val uuid: String + + /** + * Defines the path to the [VssNode] inside the VSS tree structure. + * Example: Vehicle.Body.Horn.IsActive + */ + val vssPath: String + + /** + * Simple description of the [VssNode]. + */ + val description: String + + /** + * Most relevant for [VssSignal] nodes. They can be of the type "Sensor" or "Actuator". + * For Nodes with children this will always be "branch". + */ + val type: String + + /** + * An optional additional comment for the [VssNode]. + */ + val comment: String + /** * A collection of all initialized children. */ - val children: Set + // Always empty for VssSignals. If this is moved to the VssBranch interface then it introduces some API + // inconveniences because most search API return a VssNode so a cast is always necessary for further searches. + val children: Set get() = emptySet() /** @@ -41,35 +70,36 @@ interface VssNode { } /** - * In addition of being a [VssNode] it represents the most common properties of a VSS specification. The [uuid] is a - * mandatory field and should never be empty. + * Defines a [VssNode] which is not a [VssSignal] and only acts as a branch with one or more children. The [type] is + * always "branch". */ -interface VssSpecification : VssNode { - val uuid: String - val vssPath: String - val description: String - val type: String - val comment: String +interface VssBranch : VssNode { + override val type: String + get() = "branch" } /** - * Some [VssSpecification] may have an additional [value] property. These are children which are not parents. + * Some [VssNode] may have an additional [value] property. These are children [VssSignal] which do not have other + * children. */ -interface VssProperty : VssSpecification { +interface VssSignal : VssNode { + /** + * A primitive type value. + */ val value: T } /** - * Splits the [VssSpecification.vssPath] into its parts. + * Splits the [VssNode.vssPath] into its parts. */ -val VssSpecification.vssPathComponents: List +val VssNode.vssPathComponents: List get() = vssPath.split(".") /** - * Generates a heritage line with the [VssSpecification.vssPath] from the most known parent. + * Generates a heritage line with the [VssNode.vssPath] from the most known parent. * E.g. "Vehicle.OBD.Catalyst" -> [Vehicle, Vehicle.OBD, Vehicle.OBD.Catalyst] */ -val VssSpecification.vssPathHeritageLine: List +val VssNode.vssPathHeritageLine: List get() { val components = vssPathComponents return components.foldIndexed(emptyList()) { index, accumulation, _ -> @@ -79,21 +109,21 @@ val VssSpecification.vssPathHeritageLine: List } /** - * Parses a name from the [VssSpecification.vssPath]. + * Parses a name from the [VssNode.vssPath]. */ -val VssSpecification.name: String +val VssNode.name: String get() = vssPath.substringAfterLast(".") /** - * Return the parent [VssSpecification.vssPath]. + * Return the parent [VssNode.vssPath]. */ -val VssSpecification.parentVssPath: String +val VssNode.parentVssPath: String get() = vssPath.substringBeforeLast(".", "") /** - * Returns the parent key depending on the [VssSpecification.vssPath]. + * Returns the parent key depending on the [VssNode.vssPath]. */ -val VssSpecification.parentKey: String +val VssNode.parentKey: String get() { val keys = vssPathComponents if (keys.size < 2) return "" @@ -104,7 +134,7 @@ val VssSpecification.parentKey: String /** * Similar to the [variableName] but for the parent and does not lowercase the [name] wherever necessary. */ -val VssSpecification.parentClassName: String +val VssNode.parentClassName: String get() { if (parentKey.isEmpty()) return "" @@ -112,24 +142,25 @@ val VssSpecification.parentClassName: String } /** - * Iterates through all nested children which also may have children and aggregates them into one big collection. + * Iterates through all nested children of the [VssNode] which also may have children and aggregates them into one + * big collection. */ -val VssSpecification.heritage: Collection +val VssNode.heritage: Collection get() = children.toList() + children.flatMap { it.heritage } /** - * Finds the latest generation in the form of [VssProperty] for the current [VssSpecification]. + * Finds the latest generation in the form of [VssSignal] for the current [VssNode]. */ -val VssSpecification.vssProperties: Collection> +val VssNode.vssSignals: Collection> get() = heritage .ifEmpty { setOf(this) } - .filterIsInstance>() + .filterIsInstance>() /** * Uses the [variablePrefix] to generate a unique variable name. The first character is at least lowercased. * If the [name] is something like "ABS" then it is converted to "abs" instead of "aBS". */ -val VssSpecification.variableName: String // Fixes duplicates e.g. type as variable and nested type +val VssNode.variableName: String // Fixes duplicates e.g. type as variable and nested type get() { val fullName = (variablePrefix + name).toCamelCase @@ -139,7 +170,7 @@ val VssSpecification.variableName: String // Fixes duplicates e.g. type as varia /** * Similar to the [variableName] but does not lowercase the [name] wherever necessary. */ -val VssSpecification.className: String +val VssNode.className: String get() { return (classNamePrefix + name).toCamelCase.replaceFirstChar { it.uppercase() } } @@ -147,15 +178,15 @@ val VssSpecification.className: String /** * Used in case of conflicted naming with child properties. */ -private val VssSpecification.variablePrefix: String +private val VssNode.variablePrefix: String get() = if (isVariableOccupied) classNamePrefix else "" /** * True if the [name] clashes with a property name. */ -private val VssSpecification.isVariableOccupied: Boolean +private val VssNode.isVariableOccupied: Boolean get() { - val declaredMemberProperties = VssSpecification::class.declaredMemberProperties + val declaredMemberProperties = VssNode::class.declaredMemberProperties return declaredMemberProperties.find { member -> member.name.equals(name, true) } != null @@ -166,19 +197,19 @@ private val classNamePrefix: String /** * Creates an inheritance line to the given [heir]. Similar to [vssPathHeritageLine] but the other way around. It - * returns a [Collection] of the full heritage line in the form of [VssSpecification]. + * returns a [Collection] of the full heritage line in the form of [VssNode]. * * ### Hint - * The given heir is only used to find the heir inside the [VssSpecification]. It may differ from the one which is + * The given heir is only used to find the heir inside the [VssNode]. It may differ from the one which is * returned. If you want the heritage replaced by the given [heir] parameter then use the [isReplacingHeir] parameter. */ -fun VssSpecification.findHeritageLine( - heir: VssSpecification, +fun VssNode.findHeritageLine( + heir: VssNode, isReplacingHeir: Boolean = false, -): Collection { - val specificationKeys = heir.vssPathHeritageLine +): Collection { + val vssNodeKeys = heir.vssPathHeritageLine val heritageLine = heritage.filter { child -> - specificationKeys.contains(child.vssPath) + vssNodeKeys.contains(child.vssPath) }.toMutableList() if (isReplacingHeir && heritageLine.isNotEmpty()) { @@ -190,21 +221,19 @@ fun VssSpecification.findHeritageLine( } /** - * Finds the given [property] inside the current [VssSpecification]. + * Finds the given [signal] inside the current [VssNode]. */ -fun , V : Any> VssSpecification.findProperty(property: VssProperty): VssProperty { +inline fun , V : Any> VssNode.findSignal(signal: T): VssNode { return heritage - .filterIsInstance>() - .first { it.uuid == property.uuid } + .first { it.uuid == signal.uuid } } /** - * Finds all [VssProperty] which matches the given [KClass.simpleName]. This is useful when multiple nested objects - * with the same Name exists but are pretty much the same besides the [VssSpecification.vssPath] etc. + * Finds all [VssSignal] which matches the given [KClass.simpleName]. This is useful when multiple nested objects + * with the same Name exists but are pretty much the same besides the [VssNode.vssPath] etc. */ -fun , V : Any> VssSpecification.findProperties(type: KClass): Map> { +inline fun , V : Any> VssNode.findSignal(type: KClass): Map { return heritage - .filterIsInstance>() .filter { it::class.simpleName == type.simpleName } .associateBy { it.vssPath } } diff --git a/vss-core/src/test/kotlin/org/eclipse/kuksa/vsscore/model/VssSpecificationTest.kt b/vss-core/src/test/kotlin/org/eclipse/kuksa/vsscore/model/VssNodeTest.kt similarity index 89% rename from vss-core/src/test/kotlin/org/eclipse/kuksa/vsscore/model/VssSpecificationTest.kt rename to vss-core/src/test/kotlin/org/eclipse/kuksa/vsscore/model/VssNodeTest.kt index 6611a4af..51ee4ec2 100644 --- a/vss-core/src/test/kotlin/org/eclipse/kuksa/vsscore/model/VssSpecificationTest.kt +++ b/vss-core/src/test/kotlin/org/eclipse/kuksa/vsscore/model/VssNodeTest.kt @@ -21,8 +21,8 @@ package org.eclipse.kuksa.vsscore.model import io.kotest.core.spec.style.BehaviorSpec import io.kotest.matchers.shouldBe -class VssSpecificationTest : BehaviorSpec({ - given("The root level specification") { +class VssNodeTest : BehaviorSpec({ + given("The root level VssNode") { val vssVehicle = VssVehicle() `when`("finding the whole heritage") { val heritage = vssVehicle.heritage @@ -42,10 +42,10 @@ class VssSpecificationTest : BehaviorSpec({ heritageLine shouldBe listOf(vssVehicle.driver, vssVehicle.driver.heartRate) } } - `when`("finding specific properties") { - val properties = vssVehicle.findProperties(VssDriver.VssHeartRate::class) - then("it should return all VssProperties which fit the class") { - properties.size shouldBe 2 + `when`("finding specific leafs") { + val leafs = vssVehicle.findSignal(VssDriver.VssHeartRate::class) + then("it should return all leafs which fit the class") { + leafs.size shouldBe 2 } } `when`("getting the variable name") { @@ -62,7 +62,7 @@ class VssSpecificationTest : BehaviorSpec({ } } - given("A child VssSpecification") { + given("A child VssNode") { val vssDriver = VssDriver() `when`("splitting it into path components") { val pathComponents = vssDriver.vssPathComponents diff --git a/vss-core/src/test/kotlin/org/eclipse/kuksa/vsscore/model/VssVehicle.kt b/vss-core/src/test/kotlin/org/eclipse/kuksa/vsscore/model/VssVehicle.kt index 68e0bac9..eb237186 100644 --- a/vss-core/src/test/kotlin/org/eclipse/kuksa/vsscore/model/VssVehicle.kt +++ b/vss-core/src/test/kotlin/org/eclipse/kuksa/vsscore/model/VssVehicle.kt @@ -30,8 +30,8 @@ data class VssVehicle( override val description: String = "High-level vehicle data.", override val type: String = "branch", override val comment: String = "", -) : VssSpecification { - override val children: Set +) : VssNode { + override val children: Set get() = setOf(driver, passenger, body) } @@ -41,7 +41,7 @@ data class VssBody( override val description: String = "All body components.", override val type: String = "branch", override val comment: String = "", -) : VssSpecification { +) : VssNode { override val parentClass: KClass<*> get() = VssVehicle::class } @@ -53,8 +53,8 @@ data class VssDriver( override val description: String = "Driver data.", override val type: String = "branch", override val comment: String = "", -) : VssSpecification { - override val children: Set +) : VssNode { + override val children: Set get() = setOf(heartRate) override val parentClass: KClass<*> get() = VssVehicle::class @@ -66,7 +66,7 @@ data class VssDriver( override val type: String = "sensor", override val comment: String = "", override val value: Int = 100, - ) : VssProperty { + ) : VssSignal { override val parentClass: KClass<*> get() = VssDriver::class } @@ -79,8 +79,8 @@ data class VssPassenger( override val description: String = "Passenger data", override val type: String = "branch", override val comment: String = "", -) : VssSpecification { - override val children: Set +) : VssNode { + override val children: Set get() = setOf(heartRate) override val parentClass: KClass<*> get() = VssVehicle::class @@ -92,7 +92,7 @@ data class VssPassenger( override val type: String = "sensor", override val comment: String = "", override val value: Int = 80, - ) : VssProperty { + ) : VssSignal { override val parentClass: KClass<*> get() = VssPassenger::class } diff --git a/vss-processor-plugin/src/main/java/org/eclipse/kuksa/vssprocessor/plugin/VssProcessorPlugin.kt b/vss-processor-plugin/src/main/java/org/eclipse/kuksa/vssprocessor/plugin/VssProcessorPlugin.kt index 22004206..747916f7 100644 --- a/vss-processor-plugin/src/main/java/org/eclipse/kuksa/vssprocessor/plugin/VssProcessorPlugin.kt +++ b/vss-processor-plugin/src/main/java/org/eclipse/kuksa/vssprocessor/plugin/VssProcessorPlugin.kt @@ -54,7 +54,7 @@ internal constructor(objectFactory: ObjectFactory) { private val fileSeparator = File.separator /** - * This Plugin searches for compatible specification files and copies them into an input folder for the + * This Plugin searches for compatible VSS files and copies them into an input folder for the * KSP VSS Processor. This is necessary because the Symbol Processor does not have access to the android assets folder. */ class VssProcessorPlugin : Plugin { @@ -67,15 +67,15 @@ class VssProcessorPlugin : Plugin { val buildDirPath = buildDir.absolutePath val vssDir = "${rootDir}${fileSeparator}$VSS_FOLDER_NAME" - val provideVssDefinitionTask = - project.tasks.register(PROVIDE_VSS_DEFINITION_TASK_NAME) { + val provideVssFilesTask = + project.tasks.register(PROVIDE_VSS_FILES_TASK_NAME) { val searchPath = extension.searchPath.get().ifEmpty { vssDir } - val vssDefinitionFilePath = StringBuilder(buildDirPath) + val vssFilePath = StringBuilder(buildDirPath) .append(fileSeparator) .append(KSP_INPUT_BUILD_DIRECTORY) .append(fileSeparator) .toString() - val vssDefinitionBuildFile = File(vssDefinitionFilePath) + val vssFile = File(vssFilePath) logger.info("Searching directory $searchPath for VSS definitions") @@ -88,11 +88,11 @@ class VssProcessorPlugin : Plugin { } inputDir.set(searchDir) - outputDir.set(vssDefinitionBuildFile) + outputDir.set(vssFile) } tasks.getByName("preBuild").dependsOn( - provideVssDefinitionTask.get(), + provideVssFilesTask.get(), ) } } @@ -100,17 +100,17 @@ class VssProcessorPlugin : Plugin { companion object { private const val KSP_INPUT_BUILD_DIRECTORY = "kspInput" private const val EXTENSION_NAME = "vssProcessor" - private const val PROVIDE_VSS_DEFINITION_TASK_NAME = "provideVssDefinition" + private const val PROVIDE_VSS_FILES_TASK_NAME = "provideVssFiles" private const val VSS_FOLDER_NAME = "vss" } } /** - * This task takes an input directory [inputDir] which should contain all available VSS definition files and an + * This task takes an input directory [inputDir] which should contain all available VSS files and an * output directory [outputDir] where all files are copied to so the VSSProcessor can work with them. */ @CacheableTask -private abstract class ProvideVssDefinitionTask : DefaultTask() { +private abstract class ProvideVssFilesTask : DefaultTask() { @get:Incremental @get:IgnoreEmptyDirectories @get:PathSensitive(PathSensitivity.NAME_ONLY) diff --git a/vss-processor-plugin/src/test/kotlin/org/eclipse/kuksa/vssprocessor/plugin/VssProcessorPluginTest.kt b/vss-processor-plugin/src/test/kotlin/org/eclipse/kuksa/vssprocessor/plugin/VssProcessorPluginTest.kt index 5dbd4a45..42db208c 100644 --- a/vss-processor-plugin/src/test/kotlin/org/eclipse/kuksa/vssprocessor/plugin/VssProcessorPluginTest.kt +++ b/vss-processor-plugin/src/test/kotlin/org/eclipse/kuksa/vssprocessor/plugin/VssProcessorPluginTest.kt @@ -75,13 +75,13 @@ class VssProcessorPluginTest : BehaviorSpec({ pluginProject.refresh() // So the plugin project does not have 2 :lib includes } - `when`("the ProvideVssDefinitionTask is executed without correct input") { + `when`("the provideVssFiles task is executed without correct input") { val result = gradleRunner - .withArguments("provideVssDefinition") + .withArguments(PROVIDE_VSS_FILES_TASK_NAME) .buildAndFail() then("it should throw an Exception") { - result.output shouldContain "Could not create task ':lib:provideVssDefinition'" + result.output shouldContain "Could not create task ':lib:provideVssFiles'" } } } @@ -104,46 +104,46 @@ class VssProcessorPluginTest : BehaviorSpec({ pluginProject.add(vssProcessorProject) - `when`("the ProvideVssDefinitionTask is executed with build cache the #1 time") { + `when`("the provideVssFiles task is executed with build cache the #1 time") { pluginProject.localCacheFolder.deleteRecursively() val result = gradleRunner - .withArguments("clean", "--build-cache", "provideVssDefinition") + .withArguments("clean", "--build-cache", PROVIDE_VSS_FILES_TASK_NAME) .build() - println("ProvideVssDefinitionTask + Build Cache #1 output: ${result.output}") + println("ProvideVssFilesTask + Build Cache #1 output: ${result.output}") then("it should build successfully") { - val outcome = result.task(":lib:provideVssDefinition")?.outcome + val outcome = result.task(":lib:$PROVIDE_VSS_FILES_TASK_NAME")?.outcome outcome shouldBe TaskOutcome.SUCCESS } } - `when`("the ProvideVssDefinitionTask is executed with build cache the #2 time") { + `when`("the provideVssFiles task is executed with build cache the #2 time") { val result = gradleRunner - .withArguments("clean", "--build-cache", "provideVssDefinition") + .withArguments("clean", "--build-cache", PROVIDE_VSS_FILES_TASK_NAME) .build() - println("ProvideVssDefinitionTask + Build Cache #2 output: ${result.output}") + println("ProvideVssFilesTask + Build Cache #2 output: ${result.output}") then("it should build from cache") { - val outcome = result.task(":lib:provideVssDefinition")?.outcome + val outcome = result.task(":lib:$PROVIDE_VSS_FILES_TASK_NAME")?.outcome outcome shouldBe TaskOutcome.FROM_CACHE } } - `when`("the ProvideVssDefinitionTask is executed with build cache the #3 time") { + `when`("the provideVssFiles task is executed with build cache the #3 time") { val kspInputDir = vssProcessorProject.buildDir.resolve(KSP_INPUT_BUILD_DIRECTORY) val result = gradleRunner - .withArguments("--build-cache", "provideVssDefinition") + .withArguments("--build-cache", PROVIDE_VSS_FILES_TASK_NAME) .build() - println("ProvideVssDefinitionTask + Build Cache #3 output: ${result.output}") + println("ProvideVssFilesTask + Build Cache #3 output: ${result.output}") then("it should be up to date") { - val outcome = result.task(":lib:provideVssDefinition")?.outcome + val outcome = result.task(":lib:$PROVIDE_VSS_FILES_TASK_NAME")?.outcome outcome shouldBe TaskOutcome.UP_TO_DATE } @@ -155,7 +155,7 @@ class VssProcessorPluginTest : BehaviorSpec({ } } - `when`("the input of the ProvideVssDefinitionTask changes") { + `when`("the input of the ProvideVssFilesTask changes") { val projectVssDir2 = vssDir2Path.substringAfter(TEST_FOLDER_NAME_DEFAULT) vssProcessorProject.generate( """ @@ -166,28 +166,28 @@ class VssProcessorPluginTest : BehaviorSpec({ ) val result = gradleRunner - .withArguments("--build-cache", "provideVssDefinition") + .withArguments("--build-cache", PROVIDE_VSS_FILES_TASK_NAME) .build() - println("ProvideVssDefinitionTask + Build Cache #4 output: ${result.output}") + println("ProvideVssFilesTask + Build Cache #4 output: ${result.output}") then("it should build successfully") { - val outcome = result.task(":lib:provideVssDefinition")?.outcome + val outcome = result.task(":lib:$PROVIDE_VSS_FILES_TASK_NAME")?.outcome outcome shouldBe TaskOutcome.SUCCESS } } - `when`("the name of the input of the ProvideVssDefinitionTask changes") { + `when`("the name of the input of the ProvideVssFilesTask changes") { vssFile2.renameTo(File("$vssDir2Path/vss_rel_4.0_test_renamed.yml")) val result = gradleRunner - .withArguments("--build-cache", "provideVssDefinition") + .withArguments("--build-cache", PROVIDE_VSS_FILES_TASK_NAME) .build() - println("ProvideVssDefinitionTask + Build Cache #5 output: ${result.output}") + println("ProvideVssFilesTask + Build Cache #5 output: ${result.output}") then("it should build successfully") { - val outcome = result.task(":lib:provideVssDefinition")?.outcome + val outcome = result.task(":lib:$PROVIDE_VSS_FILES_TASK_NAME")?.outcome outcome shouldBe TaskOutcome.SUCCESS } @@ -198,6 +198,7 @@ class VssProcessorPluginTest : BehaviorSpec({ }) { companion object { private const val VSS_TEST_FILE_NAME = "vss_rel_4.0_test.yaml" + private const val PROVIDE_VSS_FILES_TASK_NAME = "provideVssFiles" private const val VSS_TEST_FILE_MINIMAL_NAME = "vss_rel_4.0_test_minimal.yaml" private const val GRADLE_VERSION_TEST = "8.6" private const val KSP_INPUT_BUILD_DIRECTORY = "kspInput" diff --git a/vss-processor/src/main/kotlin/org/eclipse/kuksa/vssprocessor/VssModelGeneratorProcessor.kt b/vss-processor/src/main/kotlin/org/eclipse/kuksa/vssprocessor/VssModelGeneratorProcessor.kt index a0ff282a..0a36d472 100644 --- a/vss-processor/src/main/kotlin/org/eclipse/kuksa/vssprocessor/VssModelGeneratorProcessor.kt +++ b/vss-processor/src/main/kotlin/org/eclipse/kuksa/vssprocessor/VssModelGeneratorProcessor.kt @@ -36,8 +36,7 @@ import com.google.devtools.ksp.validate import com.squareup.kotlinpoet.FileSpec import com.squareup.kotlinpoet.ksp.toClassName import com.squareup.kotlinpoet.ksp.writeTo -import org.eclipse.kuksa.vsscore.annotation.VssDefinition -import org.eclipse.kuksa.vsscore.model.VssNode +import org.eclipse.kuksa.vsscore.annotation.VssModelGenerator import org.eclipse.kuksa.vsscore.model.parentClassName import org.eclipse.kuksa.vssprocessor.parser.factory.VssParserFactory import org.eclipse.kuksa.vssprocessor.spec.VssNodeSpecModel @@ -45,8 +44,8 @@ import org.eclipse.kuksa.vssprocessor.spec.VssPath import java.io.File /** - * Generates a [VssNode] for every specification listed in the input file. - * These nodes are a usable kotlin data class reflection of the specification. + * Generates a [org.eclipse.kuksa.vsscore.model.VssNode] for every entry listed in the input file. + * These nodes are a usable kotlin data class reflection of the element. * * @param codeGenerator to generate class files with * @param logger to log output with @@ -55,11 +54,11 @@ class VssModelGeneratorProcessor( private val codeGenerator: CodeGenerator, private val logger: KSPLogger, ) : SymbolProcessor { - private val visitor = VssDefinitionVisitor() + private val visitor = VssModelGeneratorVisitor() private val vssParserFactory = VssParserFactory() override fun process(resolver: Resolver): List { - val symbols = resolver.getSymbolsWithAnnotation(VssDefinition::class.qualifiedName.toString()) + val symbols = resolver.getSymbolsWithAnnotation(VssModelGenerator::class.qualifiedName.toString()) val deferredSymbols = symbols.filterNot { it.validate() } symbols.forEach { it.accept(visitor, Unit) } @@ -68,7 +67,7 @@ class VssModelGeneratorProcessor( return emptyList() } - private inner class VssDefinitionVisitor : KSVisitorVoid() { + private inner class VssModelGeneratorVisitor : KSVisitorVoid() { override fun visitClassDeclaration(classDeclaration: KSClassDeclaration, data: Unit) { val containingFile = classDeclaration.containingFile ?: return @@ -80,30 +79,30 @@ class VssModelGeneratorProcessor( annotatedProcessorFileName, ) - val definitionFiles = loadVssDefinitionFiles() - if (definitionFiles.isEmpty()) { - logger.error("No VSS definition files were found! Is the plugin correctly configured?") + val vssFiles = loadVssFiles() + if (vssFiles.isEmpty()) { + logger.error("No VSS files were found! Is the plugin correctly configured?") return } - val simpleSpecificationElements = mutableListOf() - definitionFiles.forEach { definitionFile -> + val simpleNodeElements = mutableListOf() + vssFiles.forEach { definitionFile -> logger.info("Parsing models for definition file: ${definitionFile.name}") - val vssDefinitionParser = vssParserFactory.create(definitionFile) - val specModels = vssDefinitionParser.parseNodes(definitionFile) + val vssParser = vssParserFactory.create(definitionFile) + val specModels = vssParser.parseNodes(definitionFile) - simpleSpecificationElements.addAll(specModels) + simpleNodeElements.addAll(specModels) } - val vssPathToSpecificationElement = simpleSpecificationElements + val vssPathToVssNodeElement = simpleNodeElements .distinctBy { it.uuid } .associateBy({ VssPath(it.vssPath) }, { it }) - generateModelFiles(vssPathToSpecificationElement) + generateModelFiles(vssPathToVssNodeElement) } // Uses the default file path for generated files (from the code generator) and searches for the given file. - private fun loadVssDefinitionFiles(): Collection { + private fun loadVssFiles(): Collection { val generatedFile = codeGenerator.generatedFile.firstOrNull() ?: return emptySet() val generationPath = generatedFile.absolutePath val buildPath = generationPath.replaceAfterLast("$BUILD_FOLDER_NAME$fileSeparator", "") @@ -116,26 +115,26 @@ class VssModelGeneratorProcessor( .toSet() } - private fun generateModelFiles(vssPathToSpecification: Map) { - val duplicateSpecificationNames = vssPathToSpecification.keys + private fun generateModelFiles(vssPathToVssNode: Map) { + val duplicateNodeNames = vssPathToVssNode.keys .groupBy { it.leaf } .filter { it.value.size > 1 } .keys - logger.info("Ambiguous specifications - Generate nested classes: $duplicateSpecificationNames") + logger.info("Ambiguous VssNode - Generate nested classes: $duplicateNodeNames") val generatedFilesVssPathToClassName = mutableMapOf() - for ((vssPath, specModel) in vssPathToSpecification) { + for ((vssPath, specModel) in vssPathToVssNode) { // Every duplicate is produced as a nested class - No separate file should be generated - if (duplicateSpecificationNames.contains(vssPath.leaf)) { + if (duplicateNodeNames.contains(vssPath.leaf)) { continue } specModel.logger = logger val classSpec = specModel.createClassSpec( PACKAGE_NAME, - vssPathToSpecification.values, - duplicateSpecificationNames, + vssPathToVssNode.values, + duplicateNodeNames, ) val className = classSpec.name ?: throw NoSuchFieldException("Class spec $classSpec has no name field!") @@ -202,7 +201,7 @@ class VssModelGeneratorProcessor( /** * Provides the environment for the [VssModelGeneratorProcessor]. */ -class VssDefinitionProcessorProvider : SymbolProcessorProvider { +class VssModelGeneratorProcessorProvider : SymbolProcessorProvider { override fun create( environment: SymbolProcessorEnvironment, ): SymbolProcessor { diff --git a/vss-processor/src/main/kotlin/org/eclipse/kuksa/vssprocessor/parser/VssParser.kt b/vss-processor/src/main/kotlin/org/eclipse/kuksa/vssprocessor/parser/VssParser.kt index 52ea9e01..8cffcdd3 100644 --- a/vss-processor/src/main/kotlin/org/eclipse/kuksa/vssprocessor/parser/VssParser.kt +++ b/vss-processor/src/main/kotlin/org/eclipse/kuksa/vssprocessor/parser/VssParser.kt @@ -24,9 +24,9 @@ import java.io.File internal interface VssParser { /** - * @param definitionFile to parse [VssNodeSpecModel] with + * Uses the given [vssFile] to parse [VssNodeSpecModel] with. * * @throws java.io.IOException will be thrown when parsing the SpecModels failed */ - fun parseNodes(definitionFile: File): List + fun parseNodes(vssFile: File): List } diff --git a/vss-processor/src/main/kotlin/org/eclipse/kuksa/vssprocessor/parser/json/JsonVssParser.kt b/vss-processor/src/main/kotlin/org/eclipse/kuksa/vssprocessor/parser/json/JsonVssParser.kt index 37b0f94b..d00fff11 100644 --- a/vss-processor/src/main/kotlin/org/eclipse/kuksa/vssprocessor/parser/json/JsonVssParser.kt +++ b/vss-processor/src/main/kotlin/org/eclipse/kuksa/vssprocessor/parser/json/JsonVssParser.kt @@ -46,11 +46,11 @@ internal class JsonVssParser : VssParser { KEY_DATA_CHILDREN, ) - override fun parseNodes(definitionFile: File): List { + override fun parseNodes(vssFile: File): List { val vssNodeSpecModels = mutableListOf() try { - val jsonStreamReader = definitionFile.reader() + val jsonStreamReader = vssFile.reader() val gson = Gson() val rootJsonObject = gson.fromJson(jsonStreamReader, JsonObject::class.java) @@ -59,10 +59,10 @@ internal class JsonVssParser : VssParser { val vehicleJsonObject = rootJsonObject.getAsJsonObject(ROOT_KEY_VEHICLE) vssNodeSpecModels += parseSpecModels(ROOT_KEY_VEHICLE, vehicleJsonObject) } else { - throw IOException("Invalid VSS Specification file '${definitionFile.path}'") + throw IOException("Invalid VSS file '${vssFile.path}'") } } catch (e: JsonParseException) { - throw IOException("Invalid VSS Specification file '${definitionFile.path}'", e) + throw IOException("Invalid VSS file '${vssFile.path}'", e) } return vssNodeSpecModels.toList() diff --git a/vss-processor/src/main/kotlin/org/eclipse/kuksa/vssprocessor/parser/yaml/YamlVssParser.kt b/vss-processor/src/main/kotlin/org/eclipse/kuksa/vssprocessor/parser/yaml/YamlVssParser.kt index b952d8a8..fe502cdb 100644 --- a/vss-processor/src/main/kotlin/org/eclipse/kuksa/vssprocessor/parser/yaml/YamlVssParser.kt +++ b/vss-processor/src/main/kotlin/org/eclipse/kuksa/vssprocessor/parser/yaml/YamlVssParser.kt @@ -19,7 +19,7 @@ package org.eclipse.kuksa.vssprocessor.parser.yaml -import org.eclipse.kuksa.vsscore.model.VssSpecification +import org.eclipse.kuksa.vsscore.model.VssNode import org.eclipse.kuksa.vssprocessor.parser.VssParser import org.eclipse.kuksa.vssprocessor.spec.VssNodeSpecModel import java.io.File @@ -28,15 +28,15 @@ import kotlin.reflect.KProperty import kotlin.reflect.full.memberProperties internal class YamlVssParser(private val elementDelimiter: String = "") : VssParser { - override fun parseNodes(definitionFile: File): List { - val specificationElements = mutableListOf() - definitionFile.useLines { lines -> + override fun parseNodes(vssFile: File): List { + val vssNodeElements = mutableListOf() + vssFile.useLines { lines -> val yamlAttributes = mutableListOf() for (line in lines.toList()) { val trimmedLine = line.trim() if (trimmedLine == elementDelimiter) { // A new element will follow after the delimiter - parseYamlElement(yamlAttributes)?.let { specificationElement -> - specificationElements.add(specificationElement) + parseYamlElement(yamlAttributes)?.let { element -> + vssNodeElements.add(element) } yamlAttributes.clear() @@ -48,12 +48,12 @@ internal class YamlVssParser(private val elementDelimiter: String = "") : VssPar } // Add the last element because no empty line will follow - parseYamlElement(yamlAttributes)?.let { specificationElement -> - specificationElements.add(specificationElement) + parseYamlElement(yamlAttributes)?.let { element -> + vssNodeElements.add(element) } } - return specificationElements + return vssNodeElements } // Example .yaml element: @@ -90,12 +90,12 @@ internal class YamlVssParser(private val elementDelimiter: String = "") : VssPar fieldsToSet.add(fieldInfo) } - val vssSpecificationMember = VssNodeSpecModel() - vssSpecificationMember.setFields(fieldsToSet) + val vssNodeSpec = VssNodeSpecModel() + vssNodeSpec.setFields(fieldsToSet) - if (vssSpecificationMember.uuid.isEmpty()) return null + if (vssNodeSpec.uuid.isEmpty()) return null - return vssSpecificationMember + return vssNodeSpec } } @@ -103,7 +103,7 @@ internal class YamlVssParser(private val elementDelimiter: String = "") : VssPar * @param fields to set via reflection. Pair. * @param remapNames which can be used if the propertyName does not match with the input name */ -private fun VssSpecification.setFields( +private fun VssNode.setFields( fields: List>, remapNames: Map = emptyMap(), ) { diff --git a/vss-processor/src/main/kotlin/org/eclipse/kuksa/vssprocessor/spec/SpecModel.kt b/vss-processor/src/main/kotlin/org/eclipse/kuksa/vssprocessor/spec/SpecModel.kt index ecb3e0f3..facfd69d 100644 --- a/vss-processor/src/main/kotlin/org/eclipse/kuksa/vssprocessor/spec/SpecModel.kt +++ b/vss-processor/src/main/kotlin/org/eclipse/kuksa/vssprocessor/spec/SpecModel.kt @@ -28,14 +28,14 @@ import com.squareup.kotlinpoet.TypeSpec internal interface SpecModel> { /** * @param packageName to use for the generated class specs. - * @param relatedSpecifications which can be used to generate children dependencies for the current and all + * @param relatedNodes which can be used to generate children dependencies for the current and all * [nestedClasses] models. The information used are depended on [T]. * @param nestedClasses which can be used to create a class spec with nested classes. The string can be used as * identifier for finding the nested classes. */ fun createClassSpec( packageName: String, - relatedSpecifications: Collection = emptyList(), + relatedNodes: Collection = emptyList(), nestedClasses: Collection = emptySet(), ): TypeSpec } diff --git a/vss-processor/src/main/kotlin/org/eclipse/kuksa/vssprocessor/spec/VssNodeSpecModel.kt b/vss-processor/src/main/kotlin/org/eclipse/kuksa/vssprocessor/spec/VssNodeSpecModel.kt index 53de8ffa..29eebf43 100644 --- a/vss-processor/src/main/kotlin/org/eclipse/kuksa/vssprocessor/spec/VssNodeSpecModel.kt +++ b/vss-processor/src/main/kotlin/org/eclipse/kuksa/vssprocessor/spec/VssNodeSpecModel.kt @@ -32,9 +32,9 @@ import com.squareup.kotlinpoet.TypeName import com.squareup.kotlinpoet.TypeSpec import com.squareup.kotlinpoet.asClassName import com.squareup.kotlinpoet.asTypeName +import org.eclipse.kuksa.vsscore.model.VssBranch import org.eclipse.kuksa.vsscore.model.VssNode -import org.eclipse.kuksa.vsscore.model.VssProperty -import org.eclipse.kuksa.vsscore.model.VssSpecification +import org.eclipse.kuksa.vsscore.model.VssSignal import org.eclipse.kuksa.vsscore.model.className import org.eclipse.kuksa.vsscore.model.name import org.eclipse.kuksa.vsscore.model.parentClassName @@ -44,8 +44,8 @@ import kotlin.reflect.KClass import kotlin.reflect.KProperty1 import kotlin.reflect.full.declaredMemberProperties -// Reflects the specification file as a model and is filled via reflection. That is why the variable names -// should exactly match the names inside the specification file and be of a string type. +// Reflects the VSS file as a model and is filled via reflection. That is why the variable names +// should exactly match the names inside the VSS file and be of a string type. internal class VssNodeSpecModel( override var uuid: String = "", override var vssPath: String = "", @@ -53,9 +53,13 @@ internal class VssNodeSpecModel( override var type: String = "", override var comment: String = "", @Suppress("MemberVisibilityCanBePrivate") var datatype: String = "", -) : VssSpecification, SpecModel { +) : VssNode, SpecModel { var logger: KSPLogger? = null + private val stringTypeName = String::class.asTypeName() + private val vssNodeSetTypeName = Set::class.parameterizedBy(VssNode::class) + private val genericClassTypeName = KClass::class.asClassName().parameterizedBy(STAR).copy(nullable = true) + private val datatypeProperty: TypeName get() { return when (datatype) { @@ -99,65 +103,65 @@ internal class VssNodeSpecModel( override fun createClassSpec( packageName: String, - relatedSpecifications: Collection, + relatedNodes: Collection, nestedClasses: Collection, ): TypeSpec { - val childSpecifications = relatedSpecifications.filter { it.parentKey == name } + val childNodes = relatedNodes.filter { it.parentKey == name } // Every specification has only one parent, do not check again for heirs - val reducedRelatedSpecifications = relatedSpecifications - childSpecifications.toSet() - this + val reducedRelatedNodes = relatedNodes - childNodes.toSet() - this val nestedChildSpecs = mutableListOf() val constructorBuilder = FunSpec.constructorBuilder() .addAnnotation(JvmOverloads::class) val propertySpecs = mutableListOf() - val superInterfaces = mutableSetOf(VssSpecification::class.asTypeName()) + val superInterfaces = mutableSetOf(VssBranch::class.asTypeName()) // The last element in the chain should have a value like "isLocked". - if (childSpecifications.isEmpty()) { + if (childNodes.isEmpty()) { val (valuePropertySpec, parameterSpec) = createValueSpec() constructorBuilder.addParameter(parameterSpec) propertySpecs.add(valuePropertySpec) - // Final leafs should ONLY implement the vss property interface + // Final leafs should ONLY implement the VssSignal interface superInterfaces.clear() - val vssPropertyInterface = VssProperty::class + val vssSignalInterface = VssSignal::class .asTypeName() .plusParameter(datatypeProperty) - superInterfaces.add(vssPropertyInterface) + superInterfaces.add(vssSignalInterface) } - val propertySpec = createVssSpecificationSpecs(className, packageName = packageName) + val propertySpec = createVssNodeSpecs(className, packageName = packageName) propertySpecs.addAll(propertySpec) - // Parses all specifications into properties - childSpecifications.forEach { childSpecification -> - // This nested specification has an ambiguous name, add as nested class + // Parses all VssNodes into properties + childNodes.forEach { childNode -> + // This nested node has an ambiguous name, add as nested class // The package name is different for nested classes (no qualifier) - val hasAmbiguousName = nestedClasses.contains(childSpecification.name) + val hasAmbiguousName = nestedClasses.contains(childNode.name) val uniquePackageName = if (hasAmbiguousName) "" else packageName - val childPropertySpec = createVssSpecificationSpecs(className, uniquePackageName, childSpecification) + val childPropertySpec = createVssNodeSpecs(className, uniquePackageName, childNode) propertySpecs.addAll(childPropertySpec) - // Nested VssSpecification properties should be added as constructor parameters + // Nested VssNode properties should be added as constructor parameters val mainClassPropertySpec = childPropertySpec.first() if (mainClassPropertySpec.initializer != null) { // Only add a default for initializer if (hasAmbiguousName) { // All children should contain the prefix vssPath (improves performance) - val relevantRelatedSpecifications = reducedRelatedSpecifications.filter { - it.vssPath.contains(childSpecification.vssPath + ".") + val relevantRelatedNodes = reducedRelatedNodes.filter { + it.vssPath.contains(childNode.vssPath + ".") } - val childSpec = childSpecification.createClassSpec( + val childSpec = childNode.createClassSpec( packageName, - relevantRelatedSpecifications, + relevantRelatedNodes, nestedClasses, ) nestedChildSpecs.add(childSpec) } - val defaultClassName = childSpecification.className + val defaultClassName = childNode.className val defaultParameter = createDefaultParameterSpec( mainClassPropertySpec.name, defaultClassName, @@ -168,7 +172,7 @@ internal class VssNodeSpecModel( } } - val nodeSpecs = createVssNodeSpecs(childSpecifications) + val nodeSpecs = createVssNodeTreeSpecs(childNodes) propertySpecs.addAll(nodeSpecs) val className = ClassName(packageName, className) @@ -207,18 +211,18 @@ internal class VssNodeSpecModel( .defaultValue("%L()", defaultClassName) .build() - private fun createVssSpecificationSpecs( + private fun createVssNodeSpecs( className: String, packageName: String, - specification: VssNodeSpecModel = this, + specModel: VssNodeSpecModel = this, ): List { val propertySpecs = mutableListOf() - val members = VssSpecification::class.declaredMemberProperties + val members = VssNode::class.declaredMemberProperties - fun createInterfaceDataTypeSpec(member: KProperty1): PropertySpec { + fun createInterfaceDataTypeSpec(member: KProperty1): PropertySpec { val memberName = member.name val memberType = member.returnType.asTypeName() - val initialValue = member.get(specification) ?: "" + val initialValue = member.get(specModel) ?: "" return PropertySpec .builder(memberName, memberType) @@ -232,11 +236,11 @@ internal class VssNodeSpecModel( } fun createObjectTypeSpec( - specification: VssNodeSpecModel, + nodeSpecModel: VssNodeSpecModel, packageName: String, ): PropertySpec { - val prefixedTypeName = ClassName(packageName, specification.className) - val variableName = specification.variableName + val prefixedTypeName = ClassName(packageName, nodeSpecModel.className) + val variableName = nodeSpecModel.variableName return PropertySpec .builder(variableName, prefixedTypeName) @@ -245,23 +249,27 @@ internal class VssNodeSpecModel( } // Add primitive data types - if (specification.className == className) { + if (specModel.className == className) { members.forEach { member -> - val primitiveDataTypeSpec = createInterfaceDataTypeSpec(member) - propertySpecs.add(primitiveDataTypeSpec) + when (member.returnType.asTypeName()) { + stringTypeName -> createInterfaceDataTypeSpec(member) + else -> null + }?.let { primitiveDataTypeSpec -> + propertySpecs.add(primitiveDataTypeSpec) + } } return propertySpecs } // Add nested child classes - val objectTypeSpec = createObjectTypeSpec(specification, packageName) + val objectTypeSpec = createObjectTypeSpec(specModel, packageName) return listOf(objectTypeSpec) } - private fun createVssNodeSpecs(childSpecifications: List): List { + private fun createVssNodeTreeSpecs(childNodes: List): List { fun createSetSpec(memberName: String, memberType: TypeName): PropertySpec { - val specificationNamesJoined = childSpecifications.joinToString(", ") { it.variableName } + val vssNodeNamesJoined = childNodes.joinToString(", ") { it.variableName } return PropertySpec .builder(memberName, memberType) @@ -269,7 +277,7 @@ internal class VssNodeSpecModel( .addModifiers(KModifier.OVERRIDE) .getter( FunSpec.getterBuilder() - .addStatement("return setOf(%L)", specificationNamesJoined) + .addStatement("return setOf(%L)", vssNodeNamesJoined) .build(), ) .build() @@ -294,17 +302,13 @@ internal class VssNodeSpecModel( val members = VssNode::class.declaredMemberProperties members.forEach { member -> val memberName = member.name - val memberType = member.returnType.asTypeName() - - val setTypeName = Set::class.parameterizedBy(VssSpecification::class) - val classTypeName = KClass::class.asClassName().parameterizedBy(STAR).copy(nullable = true) - val propertySpec: PropertySpec? = when (memberType) { - setTypeName -> createSetSpec(memberName, memberType) - classTypeName -> createParentSpec(memberName, memberType) + when (val memberType = member.returnType.asTypeName()) { + vssNodeSetTypeName -> createSetSpec(memberName, memberType) + genericClassTypeName -> createParentSpec(memberName, memberType) else -> null + }?.let { propertySpec -> + propertySpecs.add(propertySpec) } - - propertySpec?.let { propertySpecs.add(it) } } return propertySpecs diff --git a/vss-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider b/vss-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider index ffd9900e..2c9bd87d 100644 --- a/vss-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider +++ b/vss-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider @@ -1 +1 @@ -org.eclipse.kuksa.vssprocessor.VssDefinitionProcessorProvider +org.eclipse.kuksa.vssprocessor.VssModelGeneratorProcessorProvider diff --git a/vss-processor/src/test/kotlin/org/eclipse/kuksa/vssprocessor/parser/factory/VssParserFactoryTest.kt b/vss-processor/src/test/kotlin/org/eclipse/kuksa/vssprocessor/parser/factory/VssParserFactoryTest.kt index 5bc43236..8b635e76 100644 --- a/vss-processor/src/test/kotlin/org/eclipse/kuksa/vssprocessor/parser/factory/VssParserFactoryTest.kt +++ b/vss-processor/src/test/kotlin/org/eclipse/kuksa/vssprocessor/parser/factory/VssParserFactoryTest.kt @@ -28,39 +28,39 @@ import java.io.File class VssParserFactoryTest : BehaviorSpec({ - given("An instance of DefinitionParserFactory") { + given("An instance of VssParserFactory") { val classUnderTest = VssParserFactory() `when`("Calling create with supported extension 'json'") { - val vssDefinitionParser = classUnderTest.create("json") + val vssParser = classUnderTest.create("json") - then("It should return a JsonDefinitionParser") { - vssDefinitionParser shouldBe instanceOf(JsonVssParser::class) + then("It should return a parser for JSON VSS files") { + vssParser shouldBe instanceOf(JsonVssParser::class) } } `when`("Calling create with supported extension 'yaml'") { - val vssDefinitionParser = classUnderTest.create("yaml") + val vssParser = classUnderTest.create("yaml") - then("It should return a YamlDefinitionParser") { - vssDefinitionParser shouldBe instanceOf(YamlVssParser::class) + then("It should return a parser for YAML VSS files") { + vssParser shouldBe instanceOf(YamlVssParser::class) } } `when`("Calling create with supported extension 'yml'") { - val vssDefinitionParser = classUnderTest.create("yml") + val vssParser = classUnderTest.create("yml") - then("It should return a YamlDefinitionParser") { - vssDefinitionParser shouldBe instanceOf(YamlVssParser::class) + then("It should return a parser for YAML VSS files") { + vssParser shouldBe instanceOf(YamlVssParser::class) } } `when`("Calling create with a File with a supported file extension") { val supportedFile = File("someVssFile.with-multiple-dots.json") - val vssDefinitionParser = classUnderTest.create(supportedFile) + val vssParser = classUnderTest.create(supportedFile) - then("It should correctly extract the extension and return the corresponding VssDefinitionParser") { - vssDefinitionParser shouldBe instanceOf(JsonVssParser::class) + then("It should correctly extract the extension and return the corresponding VssParser") { + vssParser shouldBe instanceOf(JsonVssParser::class) } } diff --git a/vss-processor/src/test/kotlin/org/eclipse/kuksa/vssprocessor/parser/json/JsonVssParserTest.kt b/vss-processor/src/test/kotlin/org/eclipse/kuksa/vssprocessor/parser/json/JsonVssParserTest.kt index 4c3c9235..1cd14443 100644 --- a/vss-processor/src/test/kotlin/org/eclipse/kuksa/vssprocessor/parser/json/JsonVssParserTest.kt +++ b/vss-processor/src/test/kotlin/org/eclipse/kuksa/vssprocessor/parser/json/JsonVssParserTest.kt @@ -32,8 +32,8 @@ class JsonVssParserTest : BehaviorSpec({ val classUnderTest = JsonVssParser() `when`("Parsing the SpecModels of vss_rel_4.0.partial.json") { - val partialSpecFile = TestResourceFile("json/vss_rel_4.0.partial.json") - val specModels = classUnderTest.parseNodes(partialSpecFile) + val partialVssFile = TestResourceFile("json/vss_rel_4.0.partial.json") + val specModels = classUnderTest.parseNodes(partialVssFile) then("The following SpecModels should be parsed") { val validVssPaths = listOf( @@ -90,7 +90,7 @@ class JsonVssParserTest : BehaviorSpec({ val fullSpecFile = TestResourceFile("json/vss_rel_4.0.json") val specModels = classUnderTest.parseNodes(fullSpecFile) - then("the correct number of specification models should be parsed") { + then("the correct number of models models should be parsed") { specModels.size shouldBe 1197 // counted occurrences of '"uuid":' in specFile } } diff --git a/vss-processor/src/test/kotlin/org/eclipse/kuksa/vssprocessor/parser/yaml/YamlVssParserTest.kt b/vss-processor/src/test/kotlin/org/eclipse/kuksa/vssprocessor/parser/yaml/YamlVssParserTest.kt index d935d915..fd6674a4 100644 --- a/vss-processor/src/test/kotlin/org/eclipse/kuksa/vssprocessor/parser/yaml/YamlVssParserTest.kt +++ b/vss-processor/src/test/kotlin/org/eclipse/kuksa/vssprocessor/parser/yaml/YamlVssParserTest.kt @@ -27,13 +27,13 @@ class YamlVssParserTest : BehaviorSpec({ given("A parser for yaml files") { val parser = YamlVssParser() - and("a specification file of version 4") { + and("a VSS file of version 4") { val fullSpecificationFile = TestResourceFile("yaml/vss_rel_4.0.yaml") `when`("parsing the file") { val parsedSpecifications = parser.parseNodes(fullSpecificationFile) - then("the correct number of specification models should be parsed") { + then("the correct number of VSS models should be parsed") { parsedSpecifications.size shouldBe 1197 // counted occurrences of '"uuid":' in specFile } } diff --git a/vss-processor/src/test/kotlin/org/eclipse/kuksa/vssprocessor/spec/VssNodeSpecModelTest.kt b/vss-processor/src/test/kotlin/org/eclipse/kuksa/vssprocessor/spec/VssNodeSpecModelTest.kt index b296003d..a8c0751d 100644 --- a/vss-processor/src/test/kotlin/org/eclipse/kuksa/vssprocessor/spec/VssNodeSpecModelTest.kt +++ b/vss-processor/src/test/kotlin/org/eclipse/kuksa/vssprocessor/spec/VssNodeSpecModelTest.kt @@ -149,9 +149,9 @@ class VssNodeSpecModelTest : BehaviorSpec({ exception shouldNotBe null } } - and("related specifications") { + and("related nodes") { val vehicleSpeedSpecModel = VssNodeSpecModel(datatype = "float", vssPath = "Vehicle.Speed") - val relatedSpecifications = listOf( + val relatedVssNodes = listOf( VssNodeSpecModel(vssPath = "Vehicle.SmartphoneProjection"), VssNodeSpecModel(datatype = "boolean", vssPath = "Vehicle.IsBrokenDown"), vehicleSpeedSpecModel, @@ -161,7 +161,7 @@ class VssNodeSpecModelTest : BehaviorSpec({ ) `when`("creating a class spec with children") { - val rootClassSpec = specModel.createClassSpec("test", relatedSpecifications) + val rootClassSpec = specModel.createClassSpec("test", relatedVssNodes) then("it should contain the child properties") { val isBrokenDownPropertySpec = rootClassSpec.propertySpecs.find { it.name == "isBrokenDown" } @@ -190,14 +190,14 @@ class VssNodeSpecModelTest : BehaviorSpec({ } } } - and("nested specifications") { - val nestedSpecifications = listOf("Speed") + and("nested nodes") { + val nestedVssNodes = listOf("Speed") `when`("creating a child class spec with nested children") { val classSpec = specModel.createClassSpec( "test", - relatedSpecifications, - nestedSpecifications, + relatedVssNodes, + nestedVssNodes, ) then("it should contain the nested children") {