From 18f4d6c1bb892d472798620b132fe5599da7c3ba Mon Sep 17 00:00:00 2001 From: Ebrahim Ameen Date: Fri, 14 Oct 2022 00:44:11 +0200 Subject: [PATCH] feat: implement vehicle datapoint setter interface --- examples/array-datatype/__init__.py | 13 - examples/dog-mode/__init__.py | 13 - examples/dynamic-rule/__init__.py | 13 - examples/seat-adjuster/AppManifest.json | 2 +- examples/seat-adjuster/__init__.py | 13 - examples/static-rule/__init__.py | 13 - examples/vdb-queries/__init__.py | 13 - sdv/model.py | 264 +++++++++- sdv/proto/broker_pb2.py | 72 ++- sdv/proto/broker_pb2.pyi | 59 ++- sdv/proto/broker_pb2_grpc.py | 34 ++ sdv/proto/collector_pb2.pyi | 8 +- sdv/proto/collector_pb2_grpc.py | 16 +- sdv/proto/sdv/databroker/v1/broker.proto | 103 ++-- sdv/proto/sdv/databroker/v1/collector.proto | 120 ++--- sdv/proto/sdv/databroker/v1/types.proto | 180 +++---- sdv/vdb/client.py | 13 + setup.py | 2 +- tests/proto/collector.proto | 102 ---- tests/proto/common.proto | 100 ---- tests/unit/client_test.py | 53 +- tests/unit/model_test.py | 508 ++++++++++++++++++-- tests/vdb_broker.http | 51 ++ tests/vdb_collector.http | 6 +- 24 files changed, 1205 insertions(+), 566 deletions(-) delete mode 100644 examples/array-datatype/__init__.py delete mode 100644 examples/dog-mode/__init__.py delete mode 100644 examples/dynamic-rule/__init__.py delete mode 100644 examples/seat-adjuster/__init__.py delete mode 100644 examples/static-rule/__init__.py delete mode 100644 examples/vdb-queries/__init__.py delete mode 100644 tests/proto/collector.proto delete mode 100644 tests/proto/common.proto create mode 100644 tests/vdb_broker.http diff --git a/examples/array-datatype/__init__.py b/examples/array-datatype/__init__.py deleted file mode 100644 index b3251343..00000000 --- a/examples/array-datatype/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2022 Robert Bosch GmbH and Microsoft Corporation -# -# This program and the accompanying materials are made available under the -# terms of the Apache License, Version 2.0 which is available at -# https://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 diff --git a/examples/dog-mode/__init__.py b/examples/dog-mode/__init__.py deleted file mode 100644 index b3251343..00000000 --- a/examples/dog-mode/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2022 Robert Bosch GmbH and Microsoft Corporation -# -# This program and the accompanying materials are made available under the -# terms of the Apache License, Version 2.0 which is available at -# https://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 diff --git a/examples/dynamic-rule/__init__.py b/examples/dynamic-rule/__init__.py deleted file mode 100644 index b3251343..00000000 --- a/examples/dynamic-rule/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2022 Robert Bosch GmbH and Microsoft Corporation -# -# This program and the accompanying materials are made available under the -# terms of the Apache License, Version 2.0 which is available at -# https://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 diff --git a/examples/seat-adjuster/AppManifest.json b/examples/seat-adjuster/AppManifest.json index e2c1bd75..a6011d84 100644 --- a/examples/seat-adjuster/AppManifest.json +++ b/examples/seat-adjuster/AppManifest.json @@ -21,7 +21,7 @@ { "name": "databroker", "image": "ghcr.io/eclipse/kuksa.val/databroker", - "version": "v0.17.0" + "version": "master" }, { "name": "feedercan", diff --git a/examples/seat-adjuster/__init__.py b/examples/seat-adjuster/__init__.py deleted file mode 100644 index b3251343..00000000 --- a/examples/seat-adjuster/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2022 Robert Bosch GmbH and Microsoft Corporation -# -# This program and the accompanying materials are made available under the -# terms of the Apache License, Version 2.0 which is available at -# https://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 diff --git a/examples/static-rule/__init__.py b/examples/static-rule/__init__.py deleted file mode 100644 index b3251343..00000000 --- a/examples/static-rule/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2022 Robert Bosch GmbH and Microsoft Corporation -# -# This program and the accompanying materials are made available under the -# terms of the Apache License, Version 2.0 which is available at -# https://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 diff --git a/examples/vdb-queries/__init__.py b/examples/vdb-queries/__init__.py deleted file mode 100644 index b3251343..00000000 --- a/examples/vdb-queries/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2022 Robert Bosch GmbH and Microsoft Corporation -# -# This program and the accompanying materials are made available under the -# terms of the Apache License, Version 2.0 which is available at -# https://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 diff --git a/sdv/model.py b/sdv/model.py index 38aaa23f..99efcc93 100755 --- a/sdv/model.py +++ b/sdv/model.py @@ -22,7 +22,17 @@ import grpc from deprecated import deprecated +from sdv.proto.types_pb2 import BoolArray from sdv.proto.types_pb2 import Datapoint as BrokerDatapoint +from sdv.proto.types_pb2 import ( + DoubleArray, + FloatArray, + Int32Array, + Int64Array, + StringArray, + Uint32Array, + Uint64Array, +) from sdv.vdb.client import VehicleDataBrokerClient from sdv.vdb.subscriptions import SubscriptionManager, VdbSubscription @@ -229,6 +239,28 @@ async def get(self): ) raise + async def set(self, value): + """Override the data point setter for the target datapoint type. + - An error will be raised if the target value can NOT be set successfully. + """ + raise Exception(f"Unknown datpoint type for to set the value = {value}") + + async def _set(self, datapoint: BrokerDatapoint): + """Wrapper setter for the public set(value) with specific Datapoint type.""" + try: + path = self.get_path() + response = await self.get_client().SetDatapoints( + datapoints={path: datapoint} + ) + if response.errors: + raise TypeError( + f"set target value for non-actuator {path} is not allowed!" + ) + + except (grpc.aio.AioRpcError, Exception): # type: ignore + logger.error("Error occured in DataPoint.set") + raise + class DataPointBoolean(DataPoint): """A data point with a value of type bool.""" @@ -238,7 +270,16 @@ async def get(self) -> bool: response: BrokerDatapoint = await super().get() return response.bool_value except (grpc.aio.AioRpcError, Exception) as ex: # type: ignore - logger.error("Error occured in DataPointBool.get") + logger.error("Error occured in DataPointBoolean.get") + logger.exception(ex) + raise + + async def set(self, value: bool): + try: + datapoint = BrokerDatapoint(bool_value=value) + await self._set(datapoint) + except (grpc.aio.AioRpcError, Exception) as ex: # type: ignore + logger.error("Error occured in DataPointBoolean.set") logger.exception(ex) raise @@ -251,7 +292,17 @@ async def get(self) -> List[bool]: response: BrokerDatapoint = await super().get() return list(response.bool_array.values) except (grpc.aio.AioRpcError, Exception) as ex: # type: ignore - logger.error("Error occured in DataPointBoolArray.get") + logger.error("Error occured in DataPointBooleanArray.get") + logger.exception(ex) + raise + + async def set(self, value: List[bool]): + try: + array = BoolArray(values=value) + datapoint = BrokerDatapoint(bool_array=array) + await self._set(datapoint) + except (grpc.aio.AioRpcError, Exception) as ex: # type: ignore + logger.error("Error occured in DataPointBooleanArray.set") logger.exception(ex) raise @@ -268,6 +319,15 @@ async def get(self) -> int: logger.exception(ex) raise + async def set(self, value: int): + try: + datapoint = BrokerDatapoint(int32_value=value) + await self._set(datapoint) + except (grpc.aio.AioRpcError, Exception) as ex: # type: ignore + logger.error("Error occured in DataPointInt8.set") + logger.exception(ex) + raise + class DataPointInt8Array(DataPoint): """A data point array with a value of type int32.""" @@ -281,6 +341,16 @@ async def get(self) -> List[int]: logger.exception(ex) raise + async def set(self, value: List[int]): + try: + array = Int32Array(values=value) + datapoint = BrokerDatapoint(int32_array=array) + await self._set(datapoint) + except (grpc.aio.AioRpcError, Exception) as ex: # type: ignore + logger.error("Error occured in DataPointInt8Array.set") + logger.exception(ex) + raise + class DataPointInt16(DataPoint): """A data point with a value of type int32.""" @@ -294,6 +364,15 @@ async def get(self) -> int: logger.exception(ex) raise + async def set(self, value: int): + try: + datapoint = BrokerDatapoint(int32_value=value) + await self._set(datapoint) + except (grpc.aio.AioRpcError, Exception) as ex: # type: ignore + logger.error("Error occured in DataPointInt16.set") + logger.exception(ex) + raise + class DataPointInt16Array(DataPoint): """A data point array with a value of type int32.""" @@ -307,6 +386,16 @@ async def get(self) -> List[int]: logger.exception(ex) raise + async def set(self, value: List[int]): + try: + array = Int32Array(values=value) + datapoint = BrokerDatapoint(int32_array=array) + await self._set(datapoint) + except (grpc.aio.AioRpcError, Exception) as ex: # type: ignore + logger.error("Error occured in DataPointInt16Array.set") + logger.exception(ex) + raise + class DataPointInt32(DataPoint): """A data point with a value of type int32.""" @@ -320,6 +409,15 @@ async def get(self) -> int: logger.exception(ex) raise + async def set(self, value: int): + try: + datapoint = BrokerDatapoint(int32_value=value) + await self._set(datapoint) + except (grpc.aio.AioRpcError, Exception) as ex: # type: ignore + logger.error("Error occured in DataPointInt32.set") + logger.exception(ex) + raise + class DataPointInt32Array(DataPoint): """A data point array with a value of type int32.""" @@ -333,6 +431,16 @@ async def get(self) -> List[int]: logger.exception(ex) raise + async def set(self, value: List[int]): + try: + array = Int32Array(values=value) + datapoint = BrokerDatapoint(int32_array=array) + await self._set(datapoint) + except (grpc.aio.AioRpcError, Exception) as ex: # type: ignore + logger.error("Error occured in DataPointInt32Array.set") + logger.exception(ex) + raise + class DataPointInt64(DataPoint): """A data point with a value of type int64.""" @@ -346,6 +454,15 @@ async def get(self) -> int: logger.exception(ex) raise + async def set(self, value: int): + try: + datapoint = BrokerDatapoint(int64_value=value) + await self._set(datapoint) + except (grpc.aio.AioRpcError, Exception) as ex: # type: ignore + logger.error("Error occured in DataPointInt64.set") + logger.exception(ex) + raise + class DataPointInt64Array(DataPoint): """A data point array with a value of type int64.""" @@ -359,6 +476,16 @@ async def get(self) -> List[int]: logger.exception(ex) raise + async def set(self, value: List[int]): + try: + array = Int64Array(values=value) + datapoint = BrokerDatapoint(int64_array=array) + await self._set(datapoint) + except (grpc.aio.AioRpcError, Exception) as ex: # type: ignore + logger.error("Error occured in DataPointInt64Array.set") + logger.exception(ex) + raise + class DataPointUint8(DataPoint): """A data point with a value of type uint32.""" @@ -372,6 +499,15 @@ async def get(self) -> int: logger.exception(ex) raise + async def set(self, value: int): + try: + datapoint = BrokerDatapoint(uint32_value=value) + await self._set(datapoint) + except (grpc.aio.AioRpcError, Exception) as ex: # type: ignore + logger.error("Error occured in DataPointUint8.set") + logger.exception(ex) + raise + class DataPointUint8Array(DataPoint): """A data point array with a value of type unsigned uint32.""" @@ -385,6 +521,16 @@ async def get(self) -> List[int]: logger.exception(ex) raise + async def set(self, value: List[int]): + try: + array = Uint32Array(values=value) + datapoint = BrokerDatapoint(uint32_array=array) + await self._set(datapoint) + except (grpc.aio.AioRpcError, Exception) as ex: # type: ignore + logger.error("Error occured in DataPointUint8Array.set") + logger.exception(ex) + raise + class DataPointUint16(DataPoint): """A data point with a value of type uint32.""" @@ -398,6 +544,15 @@ async def get(self) -> int: logger.exception(ex) raise + async def set(self, value: int): + try: + datapoint: BrokerDatapoint = BrokerDatapoint(uint32_value=value) + await self._set(datapoint) + except (grpc.aio.AioRpcError, Exception) as ex: # type: ignore + logger.error("Error occured in DataPointUint16.set") + logger.exception(ex) + raise + class DataPointUint16Array(DataPoint): """A data point array with a value of type unsigned uint32.""" @@ -411,6 +566,16 @@ async def get(self) -> List[int]: logger.exception(ex) raise + async def set(self, value: List[int]): + try: + array = Uint32Array(values=value) + datapoint = BrokerDatapoint(uint32_array=array) + await self._set(datapoint) + except (grpc.aio.AioRpcError, Exception) as ex: # type: ignore + logger.error("Error occured in DataPointUint16Array.set") + logger.exception(ex) + raise + class DataPointUint32(DataPoint): """A data point with a value of type uint32.""" @@ -424,6 +589,15 @@ async def get(self) -> int: logger.exception(ex) raise + async def set(self, value: int): + try: + datapoint = BrokerDatapoint(uint32_value=value) + await self._set(datapoint) + except (grpc.aio.AioRpcError, Exception) as ex: # type: ignore + logger.error("Error occured in DataPointUint32.set") + logger.exception(ex) + raise + class DataPointUint32Array(DataPoint): """A data point array with a value of type unsigned uint32.""" @@ -437,6 +611,16 @@ async def get(self) -> List[int]: logger.exception(ex) raise + async def set(self, value: List[int]): + try: + array = Uint32Array(values=value) + datapoint = BrokerDatapoint(uint32_array=array) + await self._set(datapoint) + except (grpc.aio.AioRpcError, Exception) as ex: # type: ignore + logger.error("Error occured in DataPointUint32Array.set") + logger.exception(ex) + raise + class DataPointUint64(DataPoint): """A data point with a value of type unit64.""" @@ -450,6 +634,15 @@ async def get(self) -> int: logger.exception(ex) raise + async def set(self, value: int): + try: + datapoint = BrokerDatapoint(uint64_value=value) + await self._set(datapoint) + except (grpc.aio.AioRpcError, Exception) as ex: # type: ignore + logger.error("Error occured in DataPointUint64.set") + logger.exception(ex) + raise + class DataPointUint64Array(DataPoint): """A data point array with a value of type unsigned uint64.""" @@ -463,6 +656,16 @@ async def get(self) -> List[int]: logger.exception(ex) raise + async def set(self, value: List[int]): + try: + array = Uint64Array(values=value) + datapoint = BrokerDatapoint(uint64_array=array) + await self._set(datapoint) + except (grpc.aio.AioRpcError, Exception) as ex: # type: ignore + logger.error("Error occured in DataPointUint64Array.set") + logger.exception(ex) + raise + class DataPointFloat(DataPoint): """A data point with a value of type float.""" @@ -476,6 +679,15 @@ async def get(self) -> float: logger.exception(ex) raise + async def set(self, value: float): + try: + datapoint = BrokerDatapoint(float_value=value) + await self._set(datapoint) + except (grpc.aio.AioRpcError, Exception) as ex: # type: ignore + logger.error("Error occured in DataPointFloat.set") + logger.exception(ex) + raise + class DataPointFloatArray(DataPoint): """A data point array with a value of type float.""" @@ -489,6 +701,16 @@ async def get(self) -> List[float]: logger.exception(ex) raise + async def set(self, value: List[float]): + try: + array = FloatArray(values=value) + datapoint = BrokerDatapoint(float_array=array) + await self._set(datapoint) + except (grpc.aio.AioRpcError, Exception) as ex: # type: ignore + logger.error("Error occured in DataPointFloatArray.set") + logger.exception(ex) + raise + class DataPointDouble(DataPoint): """A data point with a value of type double.""" @@ -502,6 +724,15 @@ async def get(self) -> float: logger.exception(ex) raise + async def set(self, value: float): + try: + datapoint = BrokerDatapoint(double_value=value) + await self._set(datapoint) + except (grpc.aio.AioRpcError, Exception) as ex: # type: ignore + logger.error("Error occured in DataPointDouble.set") + logger.exception(ex) + raise + class DataPointDoubleArray(DataPoint): """A data point array with a value of type double.""" @@ -515,6 +746,16 @@ async def get(self) -> List[float]: logger.exception(ex) raise + async def set(self, value: List[float]): + try: + array = DoubleArray(values=value) + datapoint = BrokerDatapoint(double_array=array) + await self._set(datapoint) + except (grpc.aio.AioRpcError, Exception) as ex: # type: ignore + logger.error("Error occured in DataPointDoubleArray.set") + logger.exception(ex) + raise + class DataPointString(DataPoint): """A data point with a value of type string.""" @@ -528,6 +769,15 @@ async def get(self) -> str: logger.exception(ex) raise + async def set(self, value: str): + try: + datapoint = BrokerDatapoint(string_value=value) + await self._set(datapoint) + except (grpc.aio.AioRpcError, Exception) as ex: # type: ignore + logger.error("Error occured in DataPointString.set") + logger.exception(ex) + raise + class DataPointStringArray(DataPoint): """A data point array with a value of type String.""" @@ -540,3 +790,13 @@ async def get(self) -> List[str]: logger.error("Error occured in DataPointStringArray.get") logger.exception(ex) raise + + async def set(self, value: List[str]): + try: + array = StringArray(values=value) + datapoint = BrokerDatapoint(string_array=array) + await self._set(datapoint) + except (grpc.aio.AioRpcError, Exception) as ex: # type: ignore + logger.error("Error occured in DataPointStringArray.set") + logger.exception(ex) + raise diff --git a/sdv/proto/broker_pb2.py b/sdv/proto/broker_pb2.py index 9f6fb845..65e066f1 100644 --- a/sdv/proto/broker_pb2.py +++ b/sdv/proto/broker_pb2.py @@ -29,13 +29,17 @@ from sdv.proto import types_pb2 as sdv_dot_databroker_dot_v1_dot_types__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1esdv/databroker/v1/broker.proto\x12\x11sdv.databroker.v1\x1a\x1dsdv/databroker/v1/types.proto\"*\n\x14GetDatapointsRequest\x12\x12\n\ndatapoints\x18\x01 \x03(\t\"\xb0\x01\n\x12GetDatapointsReply\x12I\n\ndatapoints\x18\x01 \x03(\x0b\x32\x35.sdv.databroker.v1.GetDatapointsReply.DatapointsEntry\x1aO\n\x0f\x44\x61tapointsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12+\n\x05value\x18\x02 \x01(\x0b\x32\x1c.sdv.databroker.v1.Datapoint:\x02\x38\x01\"!\n\x10SubscribeRequest\x12\r\n\x05query\x18\x02 \x01(\t\"\x9c\x01\n\x0eSubscribeReply\x12=\n\x06\x66ields\x18\x01 \x03(\x0b\x32-.sdv.databroker.v1.SubscribeReply.FieldsEntry\x1aK\n\x0b\x46ieldsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12+\n\x05value\x18\x02 \x01(\x0b\x32\x1c.sdv.databroker.v1.Datapoint:\x02\x38\x01\"#\n\x12GetMetadataRequest\x12\r\n\x05names\x18\x01 \x03(\t\"=\n\x10GetMetadataReply\x12)\n\x04list\x18\x01 \x03(\x0b\x32\x1b.sdv.databroker.v1.Metadata2\x9b\x02\n\x06\x42roker\x12_\n\rGetDatapoints\x12\'.sdv.databroker.v1.GetDatapointsRequest\x1a%.sdv.databroker.v1.GetDatapointsReply\x12U\n\tSubscribe\x12#.sdv.databroker.v1.SubscribeRequest\x1a!.sdv.databroker.v1.SubscribeReply0\x01\x12Y\n\x0bGetMetadata\x12%.sdv.databroker.v1.GetMetadataRequest\x1a#.sdv.databroker.v1.GetMetadataReplyb\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1esdv/databroker/v1/broker.proto\x12\x11sdv.databroker.v1\x1a\x1dsdv/databroker/v1/types.proto\"*\n\x14GetDatapointsRequest\x12\x12\n\ndatapoints\x18\x01 \x03(\t\"\xb0\x01\n\x12GetDatapointsReply\x12I\n\ndatapoints\x18\x01 \x03(\x0b\x32\x35.sdv.databroker.v1.GetDatapointsReply.DatapointsEntry\x1aO\n\x0f\x44\x61tapointsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12+\n\x05value\x18\x02 \x01(\x0b\x32\x1c.sdv.databroker.v1.Datapoint:\x02\x38\x01\"\xb4\x01\n\x14SetDatapointsRequest\x12K\n\ndatapoints\x18\x01 \x03(\x0b\x32\x37.sdv.databroker.v1.SetDatapointsRequest.DatapointsEntry\x1aO\n\x0f\x44\x61tapointsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12+\n\x05value\x18\x02 \x01(\x0b\x32\x1c.sdv.databroker.v1.Datapoint:\x02\x38\x01\"\xa9\x01\n\x12SetDatapointsReply\x12\x41\n\x06\x65rrors\x18\x01 \x03(\x0b\x32\x31.sdv.databroker.v1.SetDatapointsReply.ErrorsEntry\x1aP\n\x0b\x45rrorsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x30\n\x05value\x18\x02 \x01(\x0e\x32!.sdv.databroker.v1.DatapointError:\x02\x38\x01\"!\n\x10SubscribeRequest\x12\r\n\x05query\x18\x02 \x01(\t\"\x9c\x01\n\x0eSubscribeReply\x12=\n\x06\x66ields\x18\x01 \x03(\x0b\x32-.sdv.databroker.v1.SubscribeReply.FieldsEntry\x1aK\n\x0b\x46ieldsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12+\n\x05value\x18\x02 \x01(\x0b\x32\x1c.sdv.databroker.v1.Datapoint:\x02\x38\x01\"#\n\x12GetMetadataRequest\x12\r\n\x05names\x18\x01 \x03(\t\"=\n\x10GetMetadataReply\x12)\n\x04list\x18\x01 \x03(\x0b\x32\x1b.sdv.databroker.v1.Metadata2\xfc\x02\n\x06\x42roker\x12_\n\rGetDatapoints\x12\'.sdv.databroker.v1.GetDatapointsRequest\x1a%.sdv.databroker.v1.GetDatapointsReply\x12_\n\rSetDatapoints\x12\'.sdv.databroker.v1.SetDatapointsRequest\x1a%.sdv.databroker.v1.SetDatapointsReply\x12U\n\tSubscribe\x12#.sdv.databroker.v1.SubscribeRequest\x1a!.sdv.databroker.v1.SubscribeReply0\x01\x12Y\n\x0bGetMetadata\x12%.sdv.databroker.v1.GetMetadataRequest\x1a#.sdv.databroker.v1.GetMetadataReplyb\x06proto3') _GETDATAPOINTSREQUEST = DESCRIPTOR.message_types_by_name['GetDatapointsRequest'] _GETDATAPOINTSREPLY = DESCRIPTOR.message_types_by_name['GetDatapointsReply'] _GETDATAPOINTSREPLY_DATAPOINTSENTRY = _GETDATAPOINTSREPLY.nested_types_by_name['DatapointsEntry'] +_SETDATAPOINTSREQUEST = DESCRIPTOR.message_types_by_name['SetDatapointsRequest'] +_SETDATAPOINTSREQUEST_DATAPOINTSENTRY = _SETDATAPOINTSREQUEST.nested_types_by_name['DatapointsEntry'] +_SETDATAPOINTSREPLY = DESCRIPTOR.message_types_by_name['SetDatapointsReply'] +_SETDATAPOINTSREPLY_ERRORSENTRY = _SETDATAPOINTSREPLY.nested_types_by_name['ErrorsEntry'] _SUBSCRIBEREQUEST = DESCRIPTOR.message_types_by_name['SubscribeRequest'] _SUBSCRIBEREPLY = DESCRIPTOR.message_types_by_name['SubscribeReply'] _SUBSCRIBEREPLY_FIELDSENTRY = _SUBSCRIBEREPLY.nested_types_by_name['FieldsEntry'] @@ -63,6 +67,36 @@ _sym_db.RegisterMessage(GetDatapointsReply) _sym_db.RegisterMessage(GetDatapointsReply.DatapointsEntry) +SetDatapointsRequest = _reflection.GeneratedProtocolMessageType('SetDatapointsRequest', (_message.Message,), { + + 'DatapointsEntry' : _reflection.GeneratedProtocolMessageType('DatapointsEntry', (_message.Message,), { + 'DESCRIPTOR' : _SETDATAPOINTSREQUEST_DATAPOINTSENTRY, + '__module__' : 'sdv.databroker.v1.broker_pb2' + # @@protoc_insertion_point(class_scope:sdv.databroker.v1.SetDatapointsRequest.DatapointsEntry) + }) + , + 'DESCRIPTOR' : _SETDATAPOINTSREQUEST, + '__module__' : 'sdv.databroker.v1.broker_pb2' + # @@protoc_insertion_point(class_scope:sdv.databroker.v1.SetDatapointsRequest) + }) +_sym_db.RegisterMessage(SetDatapointsRequest) +_sym_db.RegisterMessage(SetDatapointsRequest.DatapointsEntry) + +SetDatapointsReply = _reflection.GeneratedProtocolMessageType('SetDatapointsReply', (_message.Message,), { + + 'ErrorsEntry' : _reflection.GeneratedProtocolMessageType('ErrorsEntry', (_message.Message,), { + 'DESCRIPTOR' : _SETDATAPOINTSREPLY_ERRORSENTRY, + '__module__' : 'sdv.databroker.v1.broker_pb2' + # @@protoc_insertion_point(class_scope:sdv.databroker.v1.SetDatapointsReply.ErrorsEntry) + }) + , + 'DESCRIPTOR' : _SETDATAPOINTSREPLY, + '__module__' : 'sdv.databroker.v1.broker_pb2' + # @@protoc_insertion_point(class_scope:sdv.databroker.v1.SetDatapointsReply) + }) +_sym_db.RegisterMessage(SetDatapointsReply) +_sym_db.RegisterMessage(SetDatapointsReply.ErrorsEntry) + SubscribeRequest = _reflection.GeneratedProtocolMessageType('SubscribeRequest', (_message.Message,), { 'DESCRIPTOR' : _SUBSCRIBEREQUEST, '__module__' : 'sdv.databroker.v1.broker_pb2' @@ -105,6 +139,10 @@ DESCRIPTOR._options = None _GETDATAPOINTSREPLY_DATAPOINTSENTRY._options = None _GETDATAPOINTSREPLY_DATAPOINTSENTRY._serialized_options = b'8\001' + _SETDATAPOINTSREQUEST_DATAPOINTSENTRY._options = None + _SETDATAPOINTSREQUEST_DATAPOINTSENTRY._serialized_options = b'8\001' + _SETDATAPOINTSREPLY_ERRORSENTRY._options = None + _SETDATAPOINTSREPLY_ERRORSENTRY._serialized_options = b'8\001' _SUBSCRIBEREPLY_FIELDSENTRY._options = None _SUBSCRIBEREPLY_FIELDSENTRY._serialized_options = b'8\001' _GETDATAPOINTSREQUEST._serialized_start=84 @@ -113,16 +151,24 @@ _GETDATAPOINTSREPLY._serialized_end=305 _GETDATAPOINTSREPLY_DATAPOINTSENTRY._serialized_start=226 _GETDATAPOINTSREPLY_DATAPOINTSENTRY._serialized_end=305 - _SUBSCRIBEREQUEST._serialized_start=307 - _SUBSCRIBEREQUEST._serialized_end=340 - _SUBSCRIBEREPLY._serialized_start=343 - _SUBSCRIBEREPLY._serialized_end=499 - _SUBSCRIBEREPLY_FIELDSENTRY._serialized_start=424 - _SUBSCRIBEREPLY_FIELDSENTRY._serialized_end=499 - _GETMETADATAREQUEST._serialized_start=501 - _GETMETADATAREQUEST._serialized_end=536 - _GETMETADATAREPLY._serialized_start=538 - _GETMETADATAREPLY._serialized_end=599 - _BROKER._serialized_start=602 - _BROKER._serialized_end=885 + _SETDATAPOINTSREQUEST._serialized_start=308 + _SETDATAPOINTSREQUEST._serialized_end=488 + _SETDATAPOINTSREQUEST_DATAPOINTSENTRY._serialized_start=226 + _SETDATAPOINTSREQUEST_DATAPOINTSENTRY._serialized_end=305 + _SETDATAPOINTSREPLY._serialized_start=491 + _SETDATAPOINTSREPLY._serialized_end=660 + _SETDATAPOINTSREPLY_ERRORSENTRY._serialized_start=580 + _SETDATAPOINTSREPLY_ERRORSENTRY._serialized_end=660 + _SUBSCRIBEREQUEST._serialized_start=662 + _SUBSCRIBEREQUEST._serialized_end=695 + _SUBSCRIBEREPLY._serialized_start=698 + _SUBSCRIBEREPLY._serialized_end=854 + _SUBSCRIBEREPLY_FIELDSENTRY._serialized_start=779 + _SUBSCRIBEREPLY_FIELDSENTRY._serialized_end=854 + _GETMETADATAREQUEST._serialized_start=856 + _GETMETADATAREQUEST._serialized_end=891 + _GETMETADATAREPLY._serialized_start=893 + _GETMETADATAREPLY._serialized_end=954 + _BROKER._serialized_start=957 + _BROKER._serialized_end=1337 # @@protoc_insertion_point(module_scope) diff --git a/sdv/proto/broker_pb2.pyi b/sdv/proto/broker_pb2.pyi index db9313ac..dd2dd45d 100644 --- a/sdv/proto/broker_pb2.pyi +++ b/sdv/proto/broker_pb2.pyi @@ -72,6 +72,62 @@ class GetDatapointsReply(google.protobuf.message.Message): def ClearField(self, field_name: typing_extensions.Literal["datapoints",b"datapoints"]) -> None: ... global___GetDatapointsReply = GetDatapointsReply +class SetDatapointsRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + class DatapointsEntry(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + KEY_FIELD_NUMBER: builtins.int + VALUE_FIELD_NUMBER: builtins.int + key: typing.Text + @property + def value(self) -> sdv.databroker.v1.types_pb2.Datapoint: ... + def __init__(self, + *, + key: typing.Text = ..., + value: typing.Optional[sdv.databroker.v1.types_pb2.Datapoint] = ..., + ) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["value",b"value"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["key",b"key","value",b"value"]) -> None: ... + + DATAPOINTS_FIELD_NUMBER: builtins.int + @property + def datapoints(self) -> google.protobuf.internal.containers.MessageMap[typing.Text, sdv.databroker.v1.types_pb2.Datapoint]: + """A map of data points to set""" + pass + def __init__(self, + *, + datapoints: typing.Optional[typing.Mapping[typing.Text, sdv.databroker.v1.types_pb2.Datapoint]] = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["datapoints",b"datapoints"]) -> None: ... +global___SetDatapointsRequest = SetDatapointsRequest + +class SetDatapointsReply(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + class ErrorsEntry(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + KEY_FIELD_NUMBER: builtins.int + VALUE_FIELD_NUMBER: builtins.int + key: typing.Text + value: sdv.databroker.v1.types_pb2.DatapointError.ValueType + def __init__(self, + *, + key: typing.Text = ..., + value: sdv.databroker.v1.types_pb2.DatapointError.ValueType = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["key",b"key","value",b"value"]) -> None: ... + + ERRORS_FIELD_NUMBER: builtins.int + @property + def errors(self) -> google.protobuf.internal.containers.ScalarMap[typing.Text, sdv.databroker.v1.types_pb2.DatapointError.ValueType]: + """A map of errors (if any)""" + pass + def __init__(self, + *, + errors: typing.Optional[typing.Mapping[typing.Text, sdv.databroker.v1.types_pb2.DatapointError.ValueType]] = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["errors",b"errors"]) -> None: ... +global___SetDatapointsReply = SetDatapointsReply + class SubscribeRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor QUERY_FIELD_NUMBER: builtins.int @@ -127,8 +183,7 @@ class GetMetadataRequest(google.protobuf.message.Message): @property def names(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[typing.Text]: """Request metadata for a list of data points referenced by their names. - The names are dot separated strings, e.g. - "Vehicle.Cabin.Seat.Row1.Pos1.Position" or "Vehicle.Speed". + e.g. "Vehicle.Cabin.Seat.Row1.Pos1.Position" or "Vehicle.Speed". If no names are provided, metadata for all known data points will be returned. diff --git a/sdv/proto/broker_pb2_grpc.py b/sdv/proto/broker_pb2_grpc.py index 6cab7237..89a20836 100644 --- a/sdv/proto/broker_pb2_grpc.py +++ b/sdv/proto/broker_pb2_grpc.py @@ -33,6 +33,11 @@ def __init__(self, channel): request_serializer=sdv_dot_databroker_dot_v1_dot_broker__pb2.GetDatapointsRequest.SerializeToString, response_deserializer=sdv_dot_databroker_dot_v1_dot_broker__pb2.GetDatapointsReply.FromString, ) + self.SetDatapoints = channel.unary_unary( + '/sdv.databroker.v1.Broker/SetDatapoints', + request_serializer=sdv_dot_databroker_dot_v1_dot_broker__pb2.SetDatapointsRequest.SerializeToString, + response_deserializer=sdv_dot_databroker_dot_v1_dot_broker__pb2.SetDatapointsReply.FromString, + ) self.Subscribe = channel.unary_stream( '/sdv.databroker.v1.Broker/Subscribe', request_serializer=sdv_dot_databroker_dot_v1_dot_broker__pb2.SubscribeRequest.SerializeToString, @@ -59,6 +64,13 @@ def GetDatapoints(self, request, context): context.set_details('Method not implemented!') raise NotImplementedError('Method not implemented!') + def SetDatapoints(self, request, context): + """Set a datapoint (values) + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + def Subscribe(self, request, context): """Subscribe to a set of data points or conditional expressions using the Data Broker Query Syntax (described in QUERY.md) @@ -88,6 +100,11 @@ def add_BrokerServicer_to_server(servicer, server): request_deserializer=sdv_dot_databroker_dot_v1_dot_broker__pb2.GetDatapointsRequest.FromString, response_serializer=sdv_dot_databroker_dot_v1_dot_broker__pb2.GetDatapointsReply.SerializeToString, ), + 'SetDatapoints': grpc.unary_unary_rpc_method_handler( + servicer.SetDatapoints, + request_deserializer=sdv_dot_databroker_dot_v1_dot_broker__pb2.SetDatapointsRequest.FromString, + response_serializer=sdv_dot_databroker_dot_v1_dot_broker__pb2.SetDatapointsReply.SerializeToString, + ), 'Subscribe': grpc.unary_stream_rpc_method_handler( servicer.Subscribe, request_deserializer=sdv_dot_databroker_dot_v1_dot_broker__pb2.SubscribeRequest.FromString, @@ -125,6 +142,23 @@ def GetDatapoints(request, options, channel_credentials, insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + @staticmethod + def SetDatapoints(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/sdv.databroker.v1.Broker/SetDatapoints', + sdv_dot_databroker_dot_v1_dot_broker__pb2.SetDatapointsRequest.SerializeToString, + sdv_dot_databroker_dot_v1_dot_broker__pb2.SetDatapointsReply.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + @staticmethod def Subscribe(request, target, diff --git a/sdv/proto/collector_pb2.pyi b/sdv/proto/collector_pb2.pyi index b86bc229..708f829e 100644 --- a/sdv/proto/collector_pb2.pyi +++ b/sdv/proto/collector_pb2.pyi @@ -153,18 +153,12 @@ class RegistrationMetadata(google.protobuf.message.Message): DESCRIPTION_FIELD_NUMBER: builtins.int CHANGE_TYPE_FIELD_NUMBER: builtins.int name: typing.Text - """Name of the data point consisting of elements separated by a dot '.' + """Name of the data point (e.g. "Vehicle.Cabin.Seat.Row1.Pos1.Position" or "Vehicle.Speed") - allowing to define datapoints as a tree structure like define by - COVESA VSS (https://covesa.github.io/vehicle_signal_specification/). """ data_type: sdv.databroker.v1.types_pb2.DataType.ValueType description: typing.Text - """"Free text" description allowing to give additional details targeted - to an app developer. - """ - change_type: sdv.databroker.v1.types_pb2.ChangeType.ValueType def __init__(self, *, diff --git a/sdv/proto/collector_pb2_grpc.py b/sdv/proto/collector_pb2_grpc.py index 19c1b70f..53351e2c 100644 --- a/sdv/proto/collector_pb2_grpc.py +++ b/sdv/proto/collector_pb2_grpc.py @@ -49,8 +49,8 @@ class CollectorServicer(object): """Missing associated documentation comment in .proto file.""" def RegisterDatapoints(self, request, context): - """A feeder (provider) shall call this as a first step to announce its "owned" data points - to the Data Broker. + """Register new datapoint (metadata) + If the registration of at least one of the passed data point fails, the overall registration is rejected and the gRPC status code ABORTED is returned (to indicate the "aborted" registration). The details, which data point(s) caused the failure and the reason, is passed in back in human- @@ -67,17 +67,7 @@ def RegisterDatapoints(self, request, context): raise NotImplementedError('Method not implemented!') def UpdateDatapoints(self, request, context): - """TODO: Convert RegisterDatapointsReply into a stream in order to be able to communicate - subscription state (i.e. if there are subscribing clients) - or - Use a separate function (typically immediately) called after successful - registration of datapoints, e.g.: - - rpc GetSubscriptionStates() returns (stream SubscriptionStatesReply); - or - rpc ProvideDatapoints(ProvideDatapointsRequest) returns (stream ProvideDatapointsReply); - - Provide a set of updated datapoint values to the broker. + """Provide a set of updated datapoint values to the broker. This is the unary equivalent of `StreamDatapoints` below and is better suited for cases where the frequency of updates is rather low. diff --git a/sdv/proto/sdv/databroker/v1/broker.proto b/sdv/proto/sdv/databroker/v1/broker.proto index 9f6c3bb9..6fb69d8c 100644 --- a/sdv/proto/sdv/databroker/v1/broker.proto +++ b/sdv/proto/sdv/databroker/v1/broker.proto @@ -14,75 +14,84 @@ syntax = "proto3"; -import "sdv/databroker/v1/types.proto"; - package sdv.databroker.v1; +import "types.proto"; + service Broker { - // Request a set of datapoints (values) - // - // Returns a list of requested data points. - // - // InvalidArgument is returned if the request is malformed. - rpc GetDatapoints(GetDatapointsRequest) returns (GetDatapointsReply); + // Request a set of datapoints (values) + // + // Returns a list of requested data points. + // + // InvalidArgument is returned if the request is malformed. + rpc GetDatapoints(GetDatapointsRequest) returns (GetDatapointsReply); - // Subscribe to a set of data points or conditional expressions - // using the Data Broker Query Syntax (described in QUERY.md) - // - // Returns a stream of replies. - // - // InvalidArgument is returned if the request is malformed. - rpc Subscribe(SubscribeRequest) returns (stream SubscribeReply); + // Set a datapoint (values) + rpc SetDatapoints(SetDatapointsRequest) returns (SetDatapointsReply); - // Request the metadata of a set of datapoints - // - // Returns metadata of the requested data points that exist. - rpc GetMetadata(GetMetadataRequest) returns (GetMetadataReply); + // Subscribe to a set of data points or conditional expressions + // using the Data Broker Query Syntax (described in QUERY.md) + // + // Returns a stream of replies. + // + // InvalidArgument is returned if the request is malformed. + rpc Subscribe(SubscribeRequest) returns (stream SubscribeReply); - // TODO: Implement subscription of metadata - // rpc SubscribeMetadata(SubscribeMetadataRequest) returns (stream SubscribeMetadataReply) + // Request the metadata of a set of datapoints + // + // Returns metadata of the requested data points that exist. + rpc GetMetadata(GetMetadataRequest) returns (GetMetadataReply); } message GetDatapointsRequest { - // A list of requested data points. - repeated string datapoints = 1; + // A list of requested data points. + repeated string datapoints = 1; } message GetDatapointsReply { - // Contains the values of the requested data points. - // If a requested data point is not available, the corresponding Datapoint - // will have the respective failure value set. - map datapoints = 1; + // Contains the values of the requested data points. + // If a requested data point is not available, the corresponding Datapoint + // will have the respective failure value set. + map datapoints = 1; +} + +message SetDatapointsRequest { + // A map of data points to set + map datapoints = 1; +} + +message SetDatapointsReply { + // A map of errors (if any) + map errors = 1; } message SubscribeRequest { - // Subscribe to a set of data points (or expressions) described - // by the provided query. - // The query syntax is a subset of SQL and is described in more - // detail in the QUERY.md file. - string query = 2; + // Subscribe to a set of data points (or expressions) described + // by the provided query. + // The query syntax is a subset of SQL and is described in more + // detail in the QUERY.md file. + string query = 2; } message SubscribeReply { - // Contains the fields specified by the query. - // If a requested data point value is not available, the corresponding - // Datapoint will have it's respective failure value set. - map fields = 1; + // Contains the fields specified by the query. + // If a requested data point value is not available, the corresponding + // Datapoint will have it's respective failure value set. + map fields = 1; } message GetMetadataRequest { - // Request metadata for a list of data points referenced by their names. - // The names are dot separated strings, e.g. - // "Vehicle.Cabin.Seat.Row1.Pos1.Position" or "Vehicle.Speed". - // - // If no names are provided, metadata for all known data points will be - // returned. - repeated string names = 1; + // Request metadata for a list of data points referenced by their names. + // e.g. "Vehicle.Cabin.Seat.Row1.Pos1.Position" or "Vehicle.Speed". + // + // If no names are provided, metadata for all known data points will be + // returned. + repeated string names = 1; } message GetMetadataReply { - // Contains metadata of the requested data points. If a data point - // doesn't exist (i.e. not known to the Data Broker) the corresponding - // Metadata isn't part of the returned list. - repeated Metadata list = 1; + // Contains metadata of the requested data points. If a data point + // doesn't exist (i.e. not known to the Data Broker) the corresponding + // Metadata isn't part of the returned list. + repeated Metadata list = 1; } diff --git a/sdv/proto/sdv/databroker/v1/collector.proto b/sdv/proto/sdv/databroker/v1/collector.proto index 64d3c984..cfb81aff 100644 --- a/sdv/proto/sdv/databroker/v1/collector.proto +++ b/sdv/proto/sdv/databroker/v1/collector.proto @@ -14,99 +14,85 @@ syntax = "proto3"; -import "sdv/databroker/v1/types.proto"; +import "types.proto"; package sdv.databroker.v1; service Collector { - // A feeder (provider) shall call this as a first step to announce its "owned" data points - // to the Data Broker. - // If the registration of at least one of the passed data point fails, the overall registration - // is rejected and the gRPC status code ABORTED is returned (to indicate the "aborted" registration). - // The details, which data point(s) caused the failure and the reason, is passed in back in human- - // readable form in the status message. Possible failure resaons are: - // * PERMISSION_DENIED - Not allowed to register this name - // * ALREADY_REGISTERED - The data point is already registered by some other feeder - // * RE_REGISTRATION_MISMATCH - Already registered by this feeder but with differing metadata - // * INVALID_NAME - The passed name of the datapoint has an invalid structure - // * INVALID_VALUE_TYPE - The passed ValueType is not supported - // * INVALID_CHANGE_TYPE - The passed ChangeType is not supported - rpc RegisterDatapoints(RegisterDatapointsRequest) returns (RegisterDatapointsReply); + // Register new datapoint (metadata) + // + // If the registration of at least one of the passed data point fails, the overall registration + // is rejected and the gRPC status code ABORTED is returned (to indicate the "aborted" registration). + // The details, which data point(s) caused the failure and the reason, is passed in back in human- + // readable form in the status message. Possible failure resaons are: + // * PERMISSION_DENIED - Not allowed to register this name + // * ALREADY_REGISTERED - The data point is already registered by some other feeder + // * RE_REGISTRATION_MISMATCH - Already registered by this feeder but with differing metadata + // * INVALID_NAME - The passed name of the datapoint has an invalid structure + // * INVALID_VALUE_TYPE - The passed ValueType is not supported + // * INVALID_CHANGE_TYPE - The passed ChangeType is not supported + rpc RegisterDatapoints(RegisterDatapointsRequest) returns (RegisterDatapointsReply); - // TODO: Convert RegisterDatapointsReply into a stream in order to be able to communicate - // subscription state (i.e. if there are subscribing clients) - // or - // Use a separate function (typically immediately) called after successful - // registration of datapoints, e.g.: - // - // rpc GetSubscriptionStates() returns (stream SubscriptionStatesReply); - // or - // rpc ProvideDatapoints(ProvideDatapointsRequest) returns (stream ProvideDatapointsReply); + // Provide a set of updated datapoint values to the broker. + // This is the unary equivalent of `StreamDatapoints` below and is better suited for cases + // where the frequency of updates is rather low. + // + // NOTE: The values provided in a single request are handled as a single update in the + // data broker. This ensures that any clients requesting (or subscribing to) a set of + // datapoints will get a consistent update, i.e. that either all values are updated or + // none are. + // + // Returns: any errors encountered updating the datapoints + // + rpc UpdateDatapoints(UpdateDatapointsRequest) returns (UpdateDatapointsReply); - // Provide a set of updated datapoint values to the broker. - // This is the unary equivalent of `StreamDatapoints` below and is better suited for cases - // where the frequency of updates is rather low. - // - // NOTE: The values provided in a single request are handled as a single update in the - // data broker. This ensures that any clients requesting (or subscribing to) a set of - // datapoints will get a consistent update, i.e. that either all values are updated or - // none are. - // - // Returns: any errors encountered updating the datapoints - // - rpc UpdateDatapoints(UpdateDatapointsRequest) returns (UpdateDatapointsReply); - - // Provide a stream with updated datapoint values to the broker. - // This is the streaming equivalent of `UpdateDatapoints` above and is better suited for - // cases where the frequency of updates is high. - // - // NOTE: The values provided in a single request are handled as a single update in the - // data broker. This ensures that any clients requesting (or subscribing to) a set of - // datapoints will get a consistent update, i.e. that either all values are updated or - // none are. - // - // Returns: any errors encountered updating the datapoints - // - rpc StreamDatapoints(stream StreamDatapointsRequest) returns (stream StreamDatapointsReply); + // Provide a stream with updated datapoint values to the broker. + // This is the streaming equivalent of `UpdateDatapoints` above and is better suited for + // cases where the frequency of updates is high. + // + // NOTE: The values provided in a single request are handled as a single update in the + // data broker. This ensures that any clients requesting (or subscribing to) a set of + // datapoints will get a consistent update, i.e. that either all values are updated or + // none are. + // + // Returns: any errors encountered updating the datapoints + // + rpc StreamDatapoints(stream StreamDatapointsRequest) returns (stream StreamDatapointsReply); } message UpdateDatapointsRequest { - map datapoints = 1; + map datapoints = 1; } message UpdateDatapointsReply { - map errors = 1; // If empty, everything went well + map errors = 1; // If empty, everything went well } message StreamDatapointsRequest { - map datapoints = 1; + map datapoints = 1; } message StreamDatapointsReply { - map errors = 1; // If empty, everything went well + map errors = 1; // If empty, everything went well } message RegisterDatapointsRequest { - repeated RegistrationMetadata list = 1; + repeated RegistrationMetadata list = 1; } message RegistrationMetadata { - // Name of the data point consisting of elements separated by a dot '.' - // (e.g. "Vehicle.Cabin.Seat.Row1.Pos1.Position" or "Vehicle.Speed") - // allowing to define datapoints as a tree structure like define by - // COVESA VSS (https://covesa.github.io/vehicle_signal_specification/). - string name = 1; - DataType data_type = 2; - // "Free text" description allowing to give additional details targeted - // to an app developer. - string description = 3; - ChangeType change_type = 4; + // Name of the data point + // (e.g. "Vehicle.Cabin.Seat.Row1.Pos1.Position" or "Vehicle.Speed") + string name = 1; + DataType data_type = 2; + string description = 3; + ChangeType change_type = 4; - // int32 min_update_hz = 10; // Only for CONTINUOUS - // int32 max_update_hz = 11; // Only for CONTINUOUS + // int32 min_update_hz = 10; // Only for CONTINUOUS + // int32 max_update_hz = 11; // Only for CONTINUOUS }; message RegisterDatapointsReply { - // Maps each data point name passed in RegisterDatapointsRequest to a data point id - map results = 1; + // Maps each data point name passed in RegisterDatapointsRequest to a data point id + map results = 1; } diff --git a/sdv/proto/sdv/databroker/v1/types.proto b/sdv/proto/sdv/databroker/v1/types.proto index a388247c..e103bb67 100644 --- a/sdv/proto/sdv/databroker/v1/types.proto +++ b/sdv/proto/sdv/databroker/v1/types.proto @@ -24,130 +24,130 @@ package sdv.databroker.v1; // These are mapped to sint32 and uint32 respectively. // enum DataType { - STRING = 0; - BOOL = 1; - INT8 = 2; - INT16 = 3; - INT32 = 4; - INT64 = 5; - UINT8 = 6; - UINT16 = 7; - UINT32 = 8; - UINT64 = 9; - FLOAT = 10; - DOUBLE = 11; - TIMESTAMP = 12; - STRING_ARRAY = 20; - BOOL_ARRAY = 21; - INT8_ARRAY = 22; - INT16_ARRAY = 23; - INT32_ARRAY = 24; - INT64_ARRAY = 25; - UINT8_ARRAY = 26; - UINT16_ARRAY = 27; - UINT32_ARRAY = 28; - UINT64_ARRAY = 29; - FLOAT_ARRAY = 30; - DOUBLE_ARRAY = 31; - TIMESTAMP_ARRAY = 32; + STRING = 0; + BOOL = 1; + INT8 = 2; + INT16 = 3; + INT32 = 4; + INT64 = 5; + UINT8 = 6; + UINT16 = 7; + UINT32 = 8; + UINT64 = 9; + FLOAT = 10; + DOUBLE = 11; + TIMESTAMP = 12; + STRING_ARRAY = 20; + BOOL_ARRAY = 21; + INT8_ARRAY = 22; + INT16_ARRAY = 23; + INT32_ARRAY = 24; + INT64_ARRAY = 25; + UINT8_ARRAY = 26; + UINT16_ARRAY = 27; + UINT32_ARRAY = 28; + UINT64_ARRAY = 29; + FLOAT_ARRAY = 30; + DOUBLE_ARRAY = 31; + TIMESTAMP_ARRAY = 32; } enum DatapointError { - UNKNOWN_DATAPOINT = 0; - INVALID_TYPE = 1; - ACCESS_DENIED = 2; - INTERNAL_ERROR = 3; - OUT_OF_BOUNDS = 4; + UNKNOWN_DATAPOINT = 0; + INVALID_TYPE = 1; + ACCESS_DENIED = 2; + INTERNAL_ERROR = 3; + OUT_OF_BOUNDS = 4; } enum ChangeType { - STATIC = 0; // Value never changes - ON_CHANGE = 1; // Updates are provided every time the value changes (i.e. - // window is open / closed) - CONTINUOUS = 2; // Value is updated continuously. Broker needs to tell - // provider the preferred (update) frequency. + STATIC = 0; // Value never changes + ON_CHANGE = 1; // Updates are provided every time the value changes (i.e. + // window is open / closed) + CONTINUOUS = 2; // Value is updated continuously. Broker needs to tell + // provider the preferred (update) frequency. } message StringArray { - repeated string values = 1; + repeated string values = 1; } message BoolArray { - repeated bool values = 1; + repeated bool values = 1; } message Int32Array { - repeated sint32 values = 1; + repeated sint32 values = 1; } message Int64Array { - repeated sint64 values = 1; + repeated sint64 values = 1; } message Uint32Array { - repeated uint32 values = 1; + repeated uint32 values = 1; } message Uint64Array { - repeated uint64 values = 1; + repeated uint64 values = 1; } message FloatArray { - repeated float values = 1; + repeated float values = 1; } message DoubleArray { - repeated double values = 1; + repeated double values = 1; } message Datapoint { - // Timestamp of the value - google.protobuf.Timestamp timestamp = 1; - - // values - oneof value { - Failure failure_value = 10; - string string_value = 11; - bool bool_value = 12; - sint32 int32_value = 13; - sint64 int64_value = 14; - uint32 uint32_value = 15; - uint64 uint64_value = 16; - float float_value = 17; - double double_value = 18; - StringArray string_array = 21; - BoolArray bool_array = 22; - Int32Array int32_array = 23; - Int64Array int64_array = 24; - Uint32Array uint32_array = 25; - Uint64Array uint64_array = 26; - FloatArray float_array = 27; - DoubleArray double_array = 28; - } - - enum Failure { - // The data point is known, but doesn't have a valid value - INVALID_VALUE = 0; - // The data point is known, but no value is available - NOT_AVAILABLE = 1; - // Unknown datapoint - UNKNOWN_DATAPOINT = 2; - // Access denied - ACCESS_DENIED = 3; - // Unexpected internal error - INTERNAL_ERROR = 4; - } + // Timestamp of the value + google.protobuf.Timestamp timestamp = 1; + + // values + oneof value { + Failure failure_value = 10; + string string_value = 11; + bool bool_value = 12; + sint32 int32_value = 13; + sint64 int64_value = 14; + uint32 uint32_value = 15; + uint64 uint64_value = 16; + float float_value = 17; + double double_value = 18; + StringArray string_array = 21; + BoolArray bool_array = 22; + Int32Array int32_array = 23; + Int64Array int64_array = 24; + Uint32Array uint32_array = 25; + Uint64Array uint64_array = 26; + FloatArray float_array = 27; + DoubleArray double_array = 28; + } + + enum Failure { + // The data point is known, but doesn't have a valid value + INVALID_VALUE = 0; + // The data point is known, but no value is available + NOT_AVAILABLE = 1; + // Unknown datapoint + UNKNOWN_DATAPOINT = 2; + // Access denied + ACCESS_DENIED = 3; + // Unexpected internal error + INTERNAL_ERROR = 4; + } } message Metadata { - // Id to be used in "get" and "subscribe" requests. Ids stay valid during - // one power cycle, only. - int32 id = 1; - string name = 4; - DataType data_type = 5; - ChangeType change_type = 6; // CONTINUOUS or STATIC or ON_CHANGE - string description = 7; - - // int32 min_update_hz = 10; // Only for CONTINUOUS - // int32 max_update_hz = 11; // Only for CONTINUOUS + // Id to be used in "get" and "subscribe" requests. Ids stay valid during + // one power cycle, only. + int32 id = 1; + string name = 4; + DataType data_type = 5; + ChangeType change_type = 6; // CONTINUOUS or STATIC or ON_CHANGE + string description = 7; + + // int32 min_update_hz = 10; // Only for CONTINUOUS + // int32 max_update_hz = 11; // Only for CONTINUOUS }; diff --git a/sdv/vdb/client.py b/sdv/vdb/client.py index 376f2363..75420d1c 100644 --- a/sdv/vdb/client.py +++ b/sdv/vdb/client.py @@ -25,6 +25,7 @@ from sdv.proto.broker_pb2 import ( GetDatapointsRequest, GetMetadataRequest, + SetDatapointsRequest, SubscribeRequest, ) from sdv.proto.broker_pb2_grpc import BrokerStub @@ -79,6 +80,18 @@ async def GetDatapoints(self, datapoints: List[str]): ) raise + async def SetDatapoints(self, datapoints): + try: + response = await self._stub.SetDatapoints( + SetDatapointsRequest(datapoints=datapoints), metadata=self._metadata + ) + return response + except grpc.aio.AioRpcError: # type: ignore + logger.exception( + "Error occured in VehicleDataBrokerClient.SetDatapoints", + ) + raise + def Subscribe(self, query: str): try: response = self._stub.Subscribe( diff --git a/setup.py b/setup.py index 55eac50b..85f134dd 100644 --- a/setup.py +++ b/setup.py @@ -69,7 +69,7 @@ setup( name="sdv", - version="0.5.1", + version="0.6.0", description="A Python SDK for Vehicle app", long_description=long_description, long_description_content_type="text/markdown", diff --git a/tests/proto/collector.proto b/tests/proto/collector.proto deleted file mode 100644 index 3198e1a0..00000000 --- a/tests/proto/collector.proto +++ /dev/null @@ -1,102 +0,0 @@ - -// Copyright (c) 2022 Robert Bosch GmbH and Microsoft Corporation -// -// This program and the accompanying materials are made available under the -// terms of the Apache License, Version 2.0 which is available at -// https://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 - -syntax = "proto3"; - -import "common.proto"; - -package sdv.edge.databroker.collector; - -service Collector { - // A feeder (provider) shall call this as a first step to announce its "owned" data points - // to the Data Broker. - // If the registration of at least one of the passed data point fails, the overall registration - // is rejected and the gRPC status code ABORTED is returned (to indicate the "aborted" registration). - // The details, which data point(s) caused the failure and the reason, is passed in back in human- - // readable form in the status message. Possible failure resaons are: - // * PERMISSION_DENIED - Not allowed to register this name - // * ALREADY_REGISTERED - The data point is already registered by some other feeder - // * RE_REGISTRATION_MISMATCH - Already registered by this feeder but with differing metadata - // * INVALID_NAME - The passed name of the datapoint has an invalid structure - // * INVALID_VALUE_TYPE - The passed ValueType is not supported - // * INVALID_CHANGE_TYPE - The passed ChangeType is not supported - rpc RegisterDatapoints(RegisterDatapointsRequest) returns (RegisterDatapointsReply); - - // TODO: Convert RegisterDatapointsReply into a stream in order to be able to communicate - // subscription state (i.e. if there are subscribing clients) - // or - // Use a separate function (typically immediately) called after successful - // registration of datapoints, e.g.: - // - // rpc GetSubscriptionStates() returns (stream SubscriptionStatesReply); - // or - // rpc ProvideDatapoints(ProvideDatapointsRequest) returns (stream ProvideDatapointsReply); - - // A feeder calls this to feed datapoint values to the broker that are updated on a very - // infrequent basis (e.g. less than once per second). - rpc UpdateDatapoints(UpdateDatapointsRequest) returns (UpdateDatapointsReply); - - // A feeder calls this to feed datapoint values to the broker that are updated on a - // frequent basis (e.g. at least once per second). - rpc StreamDatapoints(stream StreamDatapointsRequest) returns (stream StreamDatapointsReply); -} - -enum DatapointError { - UNKNOWN_DATAPOINT = 0; - INVALID_TYPE = 1; - ACCESS_DENIED = 2; - INTERNAL_ERROR = 3; -} - -message UpdateDatapointsRequest { - map datapoints = 1; -} - -message UpdateDatapointsReply { - map errors = 1; // If empty, everything went well -} - -message StreamDatapointsRequest { - map datapoints = 1; -} - -message StreamDatapointsReply { - map errors = 1; // If empty, everything went well -} - -message RegisterDatapointsRequest { - repeated RegistrationMetadata list = 1; -} - -message RegistrationMetadata { - // Name of the data point consisting of elements separated by a dot '.' - // (e.g. "Vehicle.Cabin.Seat.Row1.Pos1.Position" or "Vehicle.Speed") - // allowing to define datapoints as a tree structure like define by - // COVESA VSS (https://covesa.github.io/vehicle_signal_specification/). - string name = 1; - ValueType value_type = 2; - // "Free text" description allowing to give additional details targeted - // to an app developer. - string description = 3; - ChangeType change_type = 4; - - // int32 min_update_hz = 10; // Only for CONTINUOUS - // int32 max_update_hz = 11; // Only for CONTINUOUS -}; - -message RegisterDatapointsReply { - // Maps each data point name passed in RegisterDatapointsRequest to a data point id - map results = 1; -} diff --git a/tests/proto/common.proto b/tests/proto/common.proto deleted file mode 100644 index 488ce3b3..00000000 --- a/tests/proto/common.proto +++ /dev/null @@ -1,100 +0,0 @@ - -// Copyright (c) 2022 Robert Bosch GmbH and Microsoft Corporation -// -// This program and the accompanying materials are made available under the -// terms of the Apache License, Version 2.0 which is available at -// https://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 - -syntax = "proto3"; - -import "google/protobuf/timestamp.proto"; - -package sdv.edge.databroker; - -enum ValueType { - UNKNOWN = 0; - BOOL = 1; - INT32 = 2; - INT64 = 3; - UINT32 = 4; - UINT64 = 5; - SINT32 = 6; - SINT64 = 7; - FLOAT = 8; - FIXED32 = 9; - SFIXED32 = 10; - DOUBLE = 11; - FIXED64 = 12; - SFIXED64 = 13; - STRING = 14; - BYTES = 15; -} - -enum ChangeType { - STATIC = 0; // Value never changes - ON_CHANGE = 1; // Updates are provided every time the value changes (i.e. - // window is open / closed) - CONTINUOUS = 2; // Value is updated continuously. Broker needs to tell - // provider the preferred (update) frequency. -} - -message Datapoint { - // common data - - // time set in the Vehicle Data Broker - google.protobuf.Timestamp timestamp = 1; - - // values - oneof value { - Failure failure_value = 5; - bool bool_value = 6; - int32 int32_value = 7; - int64 int64_value = 8; - uint32 uint32_value = 9; - uint64 uint64_value = 10; - sint32 sint32_value = 11; - sint64 sint64_value = 12; - float float_value = 13; - fixed32 fixed32_value = 14; - sfixed32 sfixed32_value = 15; - double double_value = 16; - fixed64 fixed64_value = 17; - sfixed64 sfixed64_value = 18; - string string_value = 19; - bytes bytes_value = 20; - } - - enum Failure { - // The data point is available but does not have a valid/known value - INVALID_VALUE = 0; - // Datapoint temporary not available - NOT_AVAILABLE = 1; - // Unknown datapoint - UNKNOWN_DATAPOINT = 2; - // Access denied - ACCESS_DENIED = 3; - // Something unexpected has happened in Data Broker - INTERNAL_ERROR = 4; - } -} - -message Metadata { - // Id to be used in "get" and "subscribe" requests. Ids stay valid during - // one power cycle, only. - int32 id = 1; - string name = 4; - ValueType value_type = 5; - ChangeType change_type = 6; // CONTINUOUS or STATIC or ON_CHANGE - string description = 7; - - // int32 min_update_hz = 10; // Only for CONTINUOUS - // int32 max_update_hz = 11; // Only for CONTINUOUS -}; diff --git a/tests/unit/client_test.py b/tests/unit/client_test.py index e251fec2..109bbdea 100644 --- a/tests/unit/client_test.py +++ b/tests/unit/client_test.py @@ -21,7 +21,12 @@ import pytest -from sdv.proto.broker_pb2 import GetDatapointsReply, GetMetadataReply, SubscribeReply +from sdv.proto.broker_pb2 import ( + GetDatapointsReply, + GetMetadataReply, + SetDatapointsReply, + SubscribeReply, +) from sdv.proto.types_pb2 import Datapoint, DataType, Metadata from sdv.vdb.client import VehicleDataBrokerClient @@ -54,7 +59,7 @@ async def test_for_get_data_points(): new_callable=mock.AsyncMock, return_value=GetDatapointsReply(datapoints=get_fields()), ): - response = await client.GetDatapoints("Vehicle.Speed") + response = await client.GetDatapoints(["Vehicle.Speed"]) assert ( response.datapoints["Vehicle.Speed"].int32_value == get_sample_datapoint().int32_value @@ -62,6 +67,40 @@ async def test_for_get_data_points(): await client.close() +@pytest.mark.asyncio +async def test_for_set_datapoints(): + client = get_vehicle_client_instance() + + with mock.patch.object( + client._stub, + "SetDatapoints", + new_callable=mock.AsyncMock, + return_value=SetDatapointsReply(errors=datapoint_set_success()), + ): + datapoint = get_sample_datapoint() + response = await client.SetDatapoints(datapoints={"Vehicle.Speed": datapoint}) + # The response has an empty error + assert response.errors == {} + await client.close() + + +@pytest.mark.asyncio +async def test_for_set_datapoints_error(): + client = get_vehicle_client_instance() + + with mock.patch.object( + client._stub, + "SetDatapoints", + new_callable=mock.AsyncMock, + return_value=SetDatapointsReply(errors=datapoint_set_error()), + ): + datapoint = get_sample_datapoint() + response = await client.SetDatapoints(datapoints={"Vehicle.Speed": datapoint}) + # The response.errors is not empty. I.e. has an error = datapoint_set_error() + assert bool(response.errors) + await client.close() + + @pytest.mark.asyncio async def test_for_subscribe(): client = get_vehicle_client_instance() @@ -88,6 +127,10 @@ def get_fields() -> Mapping[Text, Datapoint]: return data +def datapoint_set_success() -> Mapping[Text, int]: + return {} + + def get_sample_datapoint() -> Datapoint: datapoint = Datapoint() datapoint.int32_value = 0 @@ -95,6 +138,12 @@ def get_sample_datapoint() -> Datapoint: return datapoint +def datapoint_set_error() -> Mapping[Text, Datapoint]: + error = {} + error["Vehicle.Speed"] = 0 + return error + + def get_vehicle_client_instance(): client = VehicleDataBrokerClient(50051) return client diff --git a/tests/unit/model_test.py b/tests/unit/model_test.py index e6add828..d7a30989 100755 --- a/tests/unit/model_test.py +++ b/tests/unit/model_test.py @@ -12,11 +12,11 @@ # # SPDX-License-Identifier: Apache-2.0 -# pylint: disable=C0103,R0902,R0912,E1101 - import asyncio + +# pylint: disable=C0103,R0902,R0912,E1101,C0302 import threading -from unittest import mock +from unittest import TestCase, mock import grpc import pytest @@ -52,7 +52,7 @@ ModelCollection, NamedRange, ) -from sdv.proto.broker_pb2 import GetDatapointsReply, SubscribeReply +from sdv.proto.broker_pb2 import GetDatapointsReply, SetDatapointsReply, SubscribeReply from sdv.proto.types_pb2 import Datapoint from sdv.vdb.client import VehicleDataBrokerClient @@ -139,7 +139,7 @@ async def test_get_exception(): @pytest.mark.asyncio -async def test_string_value(): +async def test_get_string(): vehicle = get_vehicle_instance() with mock.patch.object( @@ -155,7 +155,25 @@ async def test_string_value(): @pytest.mark.asyncio -async def test_string_array_value(): +async def test_set_string(): + vehicle = get_vehicle_instance() + + with mock.patch.object( + vehicle.String.get_client(), + "SetDatapoints", + new_callable=mock.AsyncMock, + return_value=SetDatapointsReply(errors={}), + ): + try: + await vehicle.String.set("New Value") + except TypeError as error: + TestCase.fail( + False, f"datapoint.set(new_value) raised an exception {error}" + ) + + +@pytest.mark.asyncio +async def test_get_string_array(): vehicle = get_vehicle_instance() with mock.patch.object( @@ -174,7 +192,25 @@ async def test_string_array_value(): @pytest.mark.asyncio -async def test_bool_value(): +async def test_set_string_array(): + vehicle = get_vehicle_instance() + + with mock.patch.object( + vehicle.StringArray.get_client(), + "SetDatapoints", + new_callable=mock.AsyncMock, + return_value=SetDatapointsReply(errors={}), + ): + try: + await vehicle.StringArray.set(["Yes", "No", "YO"]) + except TypeError as error: + TestCase.fail( + False, f"datapoint.set(new_value) raised an exception {error}" + ) + + +@pytest.mark.asyncio +async def test_get_bool(): vehicle = get_vehicle_instance() with mock.patch.object( @@ -188,7 +224,25 @@ async def test_bool_value(): @pytest.mark.asyncio -async def test_bool_array_value(): +async def test_set_bool(): + vehicle = get_vehicle_instance() + + with mock.patch.object( + vehicle.Bool.get_client(), + "SetDatapoints", + new_callable=mock.AsyncMock, + return_value=SetDatapointsReply(errors={}), + ): + try: + await vehicle.Bool.set(False) + except TypeError as error: + TestCase.fail( + False, f"datapoint.set(new_value) raised an exception {error}" + ) + + +@pytest.mark.asyncio +async def test_get_bool_array(): vehicle = get_vehicle_instance() with mock.patch.object( @@ -207,7 +261,25 @@ async def test_bool_array_value(): @pytest.mark.asyncio -async def test_int8_value(): +async def test_set_bool_array(): + vehicle = get_vehicle_instance() + + with mock.patch.object( + vehicle.BoolArray.get_client(), + "SetDatapoints", + new_callable=mock.AsyncMock, + return_value=SetDatapointsReply(errors={}), + ): + try: + await vehicle.BoolArray.set([False, True]) + except TypeError as error: + TestCase.fail( + False, f"datapoint.set(new_value) raised an exception {error}" + ) + + +@pytest.mark.asyncio +async def test_get_int8(): vehicle = get_vehicle_instance() with mock.patch.object( @@ -221,7 +293,25 @@ async def test_int8_value(): @pytest.mark.asyncio -async def test_int8_array_value(): +async def test_set_int8(): + vehicle = get_vehicle_instance() + + with mock.patch.object( + vehicle.Int8.get_client(), + "SetDatapoints", + new_callable=mock.AsyncMock, + return_value=SetDatapointsReply(errors={}), + ): + try: + await vehicle.Int8.set(10) + except TypeError as error: + TestCase.fail( + False, f"datapoint.set(new_value) raised an exception {error}" + ) + + +@pytest.mark.asyncio +async def test_get_int8_array(): vehicle = get_vehicle_instance() with mock.patch.object( @@ -240,7 +330,25 @@ async def test_int8_array_value(): @pytest.mark.asyncio -async def test_int16_value(): +async def test_set_int8_array(): + vehicle = get_vehicle_instance() + + with mock.patch.object( + vehicle.Int8Array.get_client(), + "SetDatapoints", + new_callable=mock.AsyncMock, + return_value=SetDatapointsReply(errors={}), + ): + try: + await vehicle.Int8Array.set([15, 16, 17]) + except TypeError as error: + TestCase.fail( + False, f"datapoint.set(new_value) raised an exception {error}" + ) + + +@pytest.mark.asyncio +async def test_get_int16(): vehicle = get_vehicle_instance() with mock.patch.object( @@ -254,7 +362,25 @@ async def test_int16_value(): @pytest.mark.asyncio -async def test_int16_array_value(): +async def test_set_int16(): + vehicle = get_vehicle_instance() + + with mock.patch.object( + vehicle.Int16.get_client(), + "SetDatapoints", + new_callable=mock.AsyncMock, + return_value=SetDatapointsReply(errors={}), + ): + try: + await vehicle.Int16.set(10) + except TypeError as error: + TestCase.fail( + False, f"datapoint.set(new_value) raised an exception {error}" + ) + + +@pytest.mark.asyncio +async def test_get_int16_array(): vehicle = get_vehicle_instance() with mock.patch.object( @@ -273,7 +399,25 @@ async def test_int16_array_value(): @pytest.mark.asyncio -async def test_int32_value(): +async def test_set_int16_array(): + vehicle = get_vehicle_instance() + + with mock.patch.object( + vehicle.Int16Array.get_client(), + "SetDatapoints", + new_callable=mock.AsyncMock, + return_value=SetDatapointsReply(errors={}), + ): + try: + await vehicle.Int16Array.set([10, 20, 30]) + except TypeError as error: + TestCase.fail( + False, f"datapoint.set(new_value) raised an exception {error}" + ) + + +@pytest.mark.asyncio +async def test_get_int32(): vehicle = get_vehicle_instance() with mock.patch.object( @@ -287,7 +431,25 @@ async def test_int32_value(): @pytest.mark.asyncio -async def test_int32_array_value(): +async def test_set_int32(): + vehicle = get_vehicle_instance() + + with mock.patch.object( + vehicle.Int32.get_client(), + "SetDatapoints", + new_callable=mock.AsyncMock, + return_value=SetDatapointsReply(errors={}), + ): + try: + await vehicle.Int32.set(10) + except TypeError as error: + TestCase.fail( + False, f"datapoint.set(new_value) raised an exception {error}" + ) + + +@pytest.mark.asyncio +async def test_get_int32_array(): vehicle = get_vehicle_instance() with mock.patch.object( @@ -306,7 +468,25 @@ async def test_int32_array_value(): @pytest.mark.asyncio -async def test_int64_value(): +async def test_set_int32_array(): + vehicle = get_vehicle_instance() + + with mock.patch.object( + vehicle.Int32Array.get_client(), + "SetDatapoints", + new_callable=mock.AsyncMock, + return_value=SetDatapointsReply(errors={}), + ): + try: + await vehicle.Int32Array.set([60, 70, 80]) + except TypeError as error: + TestCase.fail( + False, f"datapoint.set(new_value) raised an exception {error}" + ) + + +@pytest.mark.asyncio +async def test_get_int64(): vehicle = get_vehicle_instance() with mock.patch.object( @@ -320,7 +500,25 @@ async def test_int64_value(): @pytest.mark.asyncio -async def test_int64_array_value(): +async def test_set_int64(): + vehicle = get_vehicle_instance() + + with mock.patch.object( + vehicle.Int64.get_client(), + "SetDatapoints", + new_callable=mock.AsyncMock, + return_value=SetDatapointsReply(errors={}), + ): + try: + await vehicle.Int64.set(9999) + except TypeError as error: + TestCase.fail( + False, f"datapoint.set(new_value) raised an exception {error}" + ) + + +@pytest.mark.asyncio +async def test_get_int64_array(): vehicle = get_vehicle_instance() with mock.patch.object( @@ -339,7 +537,25 @@ async def test_int64_array_value(): @pytest.mark.asyncio -async def test_uint8_value(): +async def test_set_int64_array(): + vehicle = get_vehicle_instance() + + with mock.patch.object( + vehicle.Int64Array.get_client(), + "SetDatapoints", + new_callable=mock.AsyncMock, + return_value=SetDatapointsReply(errors={}), + ): + try: + await vehicle.Int64Array.set([1440, 90, 70]) + except TypeError as error: + TestCase.fail( + False, f"datapoint.set(new_value) raised an exception {error}" + ) + + +@pytest.mark.asyncio +async def test_get_uint8(): vehicle = get_vehicle_instance() with mock.patch.object( @@ -353,7 +569,62 @@ async def test_uint8_value(): @pytest.mark.asyncio -async def test_uint16_value(): +async def test_set_uint8(): + vehicle = get_vehicle_instance() + + with mock.patch.object( + vehicle.UInt8.get_client(), + "SetDatapoints", + new_callable=mock.AsyncMock, + return_value=SetDatapointsReply(errors={}), + ): + try: + await vehicle.UInt8.set(99) + except TypeError as error: + TestCase.fail( + False, f"datapoint.set(new_value) raised an exception {error}" + ) + + +@pytest.mark.asyncio +async def test_get_uint8_array(): + vehicle = get_vehicle_instance() + + with mock.patch.object( + vehicle.UInt8Array.get_client(), + "GetDatapoints", + new_callable=mock.AsyncMock, + return_value=GetDatapointsReply(datapoints=get_fields("all")), + ): + response = await vehicle.UInt8Array.get() + assert ( + response + == get_sample_datapoint( + "Vehicle.UInt8Array", [10, 20, 30] + ).uint32_array.values + ) + + +@pytest.mark.asyncio +async def test_set_uint8_array(): + vehicle = get_vehicle_instance() + + with mock.patch.object( + vehicle.UInt8Array.get_client(), + "SetDatapoints", + new_callable=mock.AsyncMock, + return_value=SetDatapointsReply(errors={}), + ): + try: + await vehicle.UInt8Array.set([40, 20, 10]) + except TypeError as error: + TestCase.fail( + False, f"datapoint.set(new_value) raised an exception {error}" + ) + + +@pytest.mark.asyncio +async def test_get_uint16(): vehicle = get_vehicle_instance() with mock.patch.object( @@ -367,7 +638,25 @@ async def test_uint16_value(): @pytest.mark.asyncio -async def test_uint16_array_value(): +async def test_set_uint16(): + vehicle = get_vehicle_instance() + + with mock.patch.object( + vehicle.UInt16.get_client(), + "SetDatapoints", + new_callable=mock.AsyncMock, + return_value=SetDatapointsReply(errors={}), + ): + try: + await vehicle.UInt16.set(188) + except TypeError as error: + TestCase.fail( + False, f"datapoint.set(new_value) raised an exception {error}" + ) + + +@pytest.mark.asyncio +async def test_get_uint16_array(): vehicle = get_vehicle_instance() with mock.patch.object( @@ -386,7 +675,25 @@ async def test_uint16_array_value(): @pytest.mark.asyncio -async def test_uint32_value(): +async def test_set_uint16_array(): + vehicle = get_vehicle_instance() + + with mock.patch.object( + vehicle.UInt16Array.get_client(), + "SetDatapoints", + new_callable=mock.AsyncMock, + return_value=SetDatapointsReply(errors={}), + ): + try: + await vehicle.UInt16Array.set([300, 400, 500]) + except TypeError as error: + TestCase.fail( + False, f"datapoint.set(new_value) raised an exception {error}" + ) + + +@pytest.mark.asyncio +async def test_get_uint32(): vehicle = get_vehicle_instance() with mock.patch.object( @@ -400,7 +707,25 @@ async def test_uint32_value(): @pytest.mark.asyncio -async def test_uint32_array_value(): +async def test_set_uint32(): + vehicle = get_vehicle_instance() + + with mock.patch.object( + vehicle.UInt32.get_client(), + "SetDatapoints", + new_callable=mock.AsyncMock, + return_value=SetDatapointsReply(errors={}), + ): + try: + await vehicle.UInt32.set(2345) + except TypeError as error: + TestCase.fail( + False, f"datapoint.set(new_value) raised an exception {error}" + ) + + +@pytest.mark.asyncio +async def test_get_uint32_array(): vehicle = get_vehicle_instance() with mock.patch.object( @@ -419,7 +744,25 @@ async def test_uint32_array_value(): @pytest.mark.asyncio -async def test_uint64_value(): +async def test_set_uint32_array(): + vehicle = get_vehicle_instance() + + with mock.patch.object( + vehicle.UInt32Array.get_client(), + "SetDatapoints", + new_callable=mock.AsyncMock, + return_value=SetDatapointsReply(errors={}), + ): + try: + await vehicle.UInt32Array.set([101, 102, 103]) + except TypeError as error: + TestCase.fail( + False, f"datapoint.set(new_value) raised an exception {error}" + ) + + +@pytest.mark.asyncio +async def test_get_uint64(): vehicle = get_vehicle_instance() with mock.patch.object( @@ -433,7 +776,25 @@ async def test_uint64_value(): @pytest.mark.asyncio -async def test_uint64_array_value(): +async def test_set_uint64(): + vehicle = get_vehicle_instance() + + with mock.patch.object( + vehicle.UInt64.get_client(), + "SetDatapoints", + new_callable=mock.AsyncMock, + return_value=SetDatapointsReply(errors={}), + ): + try: + await vehicle.UInt64.set(423) + except TypeError as error: + TestCase.fail( + False, f"datapoint.set(new_value) raised an exception {error}" + ) + + +@pytest.mark.asyncio +async def test_get_uint64_array(): vehicle = get_vehicle_instance() with mock.patch.object( @@ -452,7 +813,25 @@ async def test_uint64_array_value(): @pytest.mark.asyncio -async def test_float_value(): +async def test_set_uint64_array(): + vehicle = get_vehicle_instance() + + with mock.patch.object( + vehicle.UInt64Array.get_client(), + "SetDatapoints", + new_callable=mock.AsyncMock, + return_value=SetDatapointsReply(errors={}), + ): + try: + await vehicle.UInt64Array.set([100, 200, 300]) + except TypeError as error: + TestCase.fail( + False, f"datapoint.set(new_value) raised an exception {error}" + ) + + +@pytest.mark.asyncio +async def test_get_float(): vehicle = get_vehicle_instance() with mock.patch.object( @@ -466,7 +845,25 @@ async def test_float_value(): @pytest.mark.asyncio -async def test_float_array_value(): +async def test_set_float(): + vehicle = get_vehicle_instance() + + with mock.patch.object( + vehicle.Float.get_client(), + "SetDatapoints", + new_callable=mock.AsyncMock, + return_value=SetDatapointsReply(errors={}), + ): + try: + await vehicle.Float.set(423.123) + except TypeError as error: + TestCase.fail( + False, f"datapoint.set(new_value) raised an exception {error}" + ) + + +@pytest.mark.asyncio +async def test_get_float_array(): vehicle = get_vehicle_instance() with mock.patch.object( @@ -485,7 +882,25 @@ async def test_float_array_value(): @pytest.mark.asyncio -async def test_double_value(): +async def test_set_float_array(): + vehicle = get_vehicle_instance() + + with mock.patch.object( + vehicle.FloatArray.get_client(), + "SetDatapoints", + new_callable=mock.AsyncMock, + return_value=SetDatapointsReply(errors={}), + ): + try: + await vehicle.FloatArray.set([423.123, 1.1, 2.2]) + except TypeError as error: + TestCase.fail( + False, f"datapoint.set(new_value) raised an exception {error}" + ) + + +@pytest.mark.asyncio +async def test_get_double(): vehicle = get_vehicle_instance() with mock.patch.object( @@ -501,7 +916,25 @@ async def test_double_value(): @pytest.mark.asyncio -async def test_double_array_value(): +async def test_set_double(): + vehicle = get_vehicle_instance() + + with mock.patch.object( + vehicle.Double.get_client(), + "SetDatapoints", + new_callable=mock.AsyncMock, + return_value=SetDatapointsReply(errors={}), + ): + try: + await vehicle.Double.set(98765.123) + except TypeError as error: + TestCase.fail( + False, f"datapoint.set(new_value) raised an exception {error}" + ) + + +@pytest.mark.asyncio +async def test_get_double_array(): vehicle = get_vehicle_instance() with mock.patch.object( @@ -520,22 +953,21 @@ async def test_double_array_value(): @pytest.mark.asyncio -async def test_uint8_array_value(): +async def test_set_double_array(): vehicle = get_vehicle_instance() with mock.patch.object( - vehicle.UInt8Array.get_client(), - "GetDatapoints", + vehicle.DoubleArray.get_client(), + "SetDatapoints", new_callable=mock.AsyncMock, - return_value=GetDatapointsReply(datapoints=get_fields("all")), + return_value=SetDatapointsReply(errors={}), ): - response = await vehicle.UInt8Array.get() - assert ( - response - == get_sample_datapoint( - "Vehicle.UInt8Array", [10, 20, 30] - ).uint32_array.values - ) + try: + await vehicle.DoubleArray.set([321.456789, 190.1234567, 1100.01]) + except TypeError as error: + TestCase.fail( + False, f"datapoint.set(new_value) raised an exception {error}" + ) @pytest.mark.asyncio diff --git a/tests/vdb_broker.http b/tests/vdb_broker.http new file mode 100644 index 00000000..7413547d --- /dev/null +++ b/tests/vdb_broker.http @@ -0,0 +1,51 @@ +proto < ./../sdv/proto/sdv/databroker/v1/broker.proto +@port=55555 +@host=localhost:{{port}}/sdv.databroker.v1.Broker + +### + +# Test invalid SetDatapoints Request -> return empty dictionary. +GRPC /SetDatapoints +{ + "datapoints": { + "Vehicle.Cabin.Seat.Row1.Pos1.Position": { + "timestamp": { + "seconds": 1666270157, + "nanos": 226860106 + }, + "uint32Value": 500 + } + } +} + +# Test invalid SetDatapoints Request -> return error with code +GRPC /SetDatapoints +{ + "datapoints": { + "Vehicle.Cabin.Seat.Row1.Pos1.IsBelted": { + "timestamp": { + "seconds": 1666270157, + "nanos": 226860106 + }, + "boolValue": false + } + } +} + +# Test GetDatapoints Request. +GRPC /GetDatapoints +{ + "datapoints": ["Vehicle.Speed", "Vehicle.Cabin.Seat.Row1.Pos1.Position"] +} + +# Test GetMetadata Request of a single datapoint. +GRPC /GetMetadata +{ + "names": ["Vehicle.Speed"] +} + +# Test GetMetadata Request of all datapoints. +GRPC /GetMetadata +{ + "names": [] +} diff --git a/tests/vdb_collector.http b/tests/vdb_collector.http index 3e32d752..70531ee8 100644 --- a/tests/vdb_collector.http +++ b/tests/vdb_collector.http @@ -1,8 +1,8 @@ -proto < ./proto/collector.proto - +proto < ./../sdv/proto/sdv/databroker/v1/collector.proto @port=55555 +@host=localhost:{{port}}/sdv.databroker.v1.Collector -GRPC localhost:{{port}}/sdv.edge.databroker.collector.Collector/RegisterDatapoints +GRPC /RegisterDatapoints { "list": [ {