From 97d17b15148f20940e95281cca4b58a5b63c5809 Mon Sep 17 00:00:00 2001 From: Dennis Meister Date: Tue, 27 Sep 2022 18:49:19 +0200 Subject: [PATCH 1/5] Add dynamic datatype casting for vdb responses Signed-off-by: Dennis Meister --- .../array-datatype/requirements-links.txt | 2 +- examples/dog-mode/requirements-links.txt | 2 +- examples/dynamic-rule/requirements-links.txt | 2 +- examples/seat-adjuster/requirements-links.txt | 2 +- examples/seat-adjuster/src/main.py | 12 ++- examples/static-rule/requirements-links.txt | 2 +- examples/vdb-queries/requirements-links.txt | 2 +- sdv/datapointbase.py | 32 ++++++++ sdv/model.py | 3 +- sdv/vdb/reply.py | 79 +++++++++++++++++++ sdv/vdb/subscriptions.py | 7 +- setup.py | 2 +- 12 files changed, 134 insertions(+), 13 deletions(-) create mode 100644 sdv/datapointbase.py create mode 100644 sdv/vdb/reply.py diff --git a/examples/array-datatype/requirements-links.txt b/examples/array-datatype/requirements-links.txt index ca71b2d1..934722b2 100644 --- a/examples/array-datatype/requirements-links.txt +++ b/examples/array-datatype/requirements-links.txt @@ -1,2 +1,2 @@ -git+https://github.com/eclipse-velocitas/vehicle-app-python-sdk.git@v0.3.1 +git+https://github.com/eclipse-velocitas/vehicle-app-python-sdk.git@v0.4.0 git+https://github.com/eclipse-velocitas/vehicle-model-python.git@v0.2.0 diff --git a/examples/dog-mode/requirements-links.txt b/examples/dog-mode/requirements-links.txt index ca71b2d1..934722b2 100644 --- a/examples/dog-mode/requirements-links.txt +++ b/examples/dog-mode/requirements-links.txt @@ -1,2 +1,2 @@ -git+https://github.com/eclipse-velocitas/vehicle-app-python-sdk.git@v0.3.1 +git+https://github.com/eclipse-velocitas/vehicle-app-python-sdk.git@v0.4.0 git+https://github.com/eclipse-velocitas/vehicle-model-python.git@v0.2.0 diff --git a/examples/dynamic-rule/requirements-links.txt b/examples/dynamic-rule/requirements-links.txt index ca71b2d1..934722b2 100644 --- a/examples/dynamic-rule/requirements-links.txt +++ b/examples/dynamic-rule/requirements-links.txt @@ -1,2 +1,2 @@ -git+https://github.com/eclipse-velocitas/vehicle-app-python-sdk.git@v0.3.1 +git+https://github.com/eclipse-velocitas/vehicle-app-python-sdk.git@v0.4.0 git+https://github.com/eclipse-velocitas/vehicle-model-python.git@v0.2.0 diff --git a/examples/seat-adjuster/requirements-links.txt b/examples/seat-adjuster/requirements-links.txt index ca71b2d1..934722b2 100644 --- a/examples/seat-adjuster/requirements-links.txt +++ b/examples/seat-adjuster/requirements-links.txt @@ -1,2 +1,2 @@ -git+https://github.com/eclipse-velocitas/vehicle-app-python-sdk.git@v0.3.1 +git+https://github.com/eclipse-velocitas/vehicle-app-python-sdk.git@v0.4.0 git+https://github.com/eclipse-velocitas/vehicle-model-python.git@v0.2.0 diff --git a/examples/seat-adjuster/src/main.py b/examples/seat-adjuster/src/main.py index 69b81669..9168f2d1 100644 --- a/examples/seat-adjuster/src/main.py +++ b/examples/seat-adjuster/src/main.py @@ -29,6 +29,7 @@ get_opentelemetry_log_factory, get_opentelemetry_log_format, ) +from sdv.vdb.subscriptions import DataPointReply from sdv.vehicle_app import VehicleApp, subscribe_topic logging.setLogRecordFactory(get_opentelemetry_log_factory()) @@ -60,12 +61,17 @@ async def on_start(self): self.on_seat_position_changed ) - async def on_seat_position_changed(self, data): + async def on_seat_position_changed(self, data: DataPointReply): response_topic = "seatadjuster/currentPosition" - seat_path = self.Vehicle.Cabin.Seat.element_at(1, 1).Position.get_path() await self.publish_mqtt_event( response_topic, - json.dumps({"position": data.fields[seat_path].uint32_value}), + json.dumps( + { + "position": data.get( + self.Vehicle.Cabin.Seat.element_at(1, 1).Position + ) + } + ), ) @subscribe_topic("seatadjuster/setPosition/request") diff --git a/examples/static-rule/requirements-links.txt b/examples/static-rule/requirements-links.txt index ca71b2d1..934722b2 100644 --- a/examples/static-rule/requirements-links.txt +++ b/examples/static-rule/requirements-links.txt @@ -1,2 +1,2 @@ -git+https://github.com/eclipse-velocitas/vehicle-app-python-sdk.git@v0.3.1 +git+https://github.com/eclipse-velocitas/vehicle-app-python-sdk.git@v0.4.0 git+https://github.com/eclipse-velocitas/vehicle-model-python.git@v0.2.0 diff --git a/examples/vdb-queries/requirements-links.txt b/examples/vdb-queries/requirements-links.txt index ca71b2d1..934722b2 100644 --- a/examples/vdb-queries/requirements-links.txt +++ b/examples/vdb-queries/requirements-links.txt @@ -1,2 +1,2 @@ -git+https://github.com/eclipse-velocitas/vehicle-app-python-sdk.git@v0.3.1 +git+https://github.com/eclipse-velocitas/vehicle-app-python-sdk.git@v0.4.0 git+https://github.com/eclipse-velocitas/vehicle-model-python.git@v0.2.0 diff --git a/sdv/datapointbase.py b/sdv/datapointbase.py new file mode 100644 index 00000000..a46221dd --- /dev/null +++ b/sdv/datapointbase.py @@ -0,0 +1,32 @@ +# 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 + + +class DataPointBase: + """Base class for data points. Do not use for modelling directly.""" + + def join(self, *args): + pass + + def where(self, condition: str): + pass + + def get_query(self) -> str: + pass + + async def subscribe(self, on_update): + pass + + async def get(self): + pass diff --git a/sdv/model.py b/sdv/model.py index 3957f52d..9a162573 100755 --- a/sdv/model.py +++ b/sdv/model.py @@ -21,6 +21,7 @@ import grpc +from sdv.datapointbase import DataPointBase from sdv.proto.types_pb2 import Datapoint as BrokerDatapoint from sdv.vdb.client import VehicleDataBrokerClient from sdv.vdb.subscriptions import SubscriptionManager, VdbSubscription @@ -154,7 +155,7 @@ def __init__(self): self.metadata = conf.service_locator.get_metadata(self.name) -class DataPoint(Node): +class DataPoint(Node, DataPointBase): """Base class for data points. Do not use for modelling directly.""" def __init__(self, name: str, parent: Model): diff --git a/sdv/vdb/reply.py b/sdv/vdb/reply.py new file mode 100644 index 00000000..108dccca --- /dev/null +++ b/sdv/vdb/reply.py @@ -0,0 +1,79 @@ +# 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 + +# pylint: skip-file +# flake8: noqa + +from sdv.proto.types_pb2 import Datapoint as BrokerDatapoint + + +class DataPointReply: + def __init__(self, reply): + self._reply = reply + + def get(self, datapoint_object): + datapoint_type = datapoint_object.__class__.__name__ + datapoint: BrokerDatapoint = self._reply.fields[datapoint_object.get_path()] + try: + if datapoint_type == "DataPointBoolean": + return datapoint.bool_value + elif datapoint_type == "DataPointBooleanArray": + return list(datapoint.bool_array.values) + elif datapoint_type == "DataPointString": + return datapoint.string_value + elif datapoint_type == "DataPointStringArray": + return list(datapoint.string_array.values) + elif datapoint_type == "DataPointDouble": + return datapoint.double_value + elif datapoint_type == "DataPointDoubleArray": + return list(datapoint.double_array.values) + elif datapoint_type == "DataPointFloat": + return datapoint.float_value + elif datapoint_type == "DataPointFloatArray": + return list(datapoint.float_array.values) + elif datapoint_type == "DataPointInt8": + return datapoint.int32_value + elif datapoint_type == "DataPointInt8Array": + return list(datapoint.int32_array.values) + elif datapoint_type == "DataPointInt16": + return datapoint.int32_value + elif datapoint_type == "DataPointInt16Array": + return list(datapoint.int32_array.values) + elif datapoint_type == "DataPointInt32": + return datapoint.int32_value + elif datapoint_type == "DataPointInt32Array": + return list(datapoint.int32_array.values) + elif datapoint_type == "DataPointInt64": + return datapoint.int64_value + elif datapoint_type == "DataPointInt64Array": + return list(datapoint.int64_array.values) + elif datapoint_type == "DataPointUint8": + return datapoint.uint32_value + elif datapoint_type == "DataPointUint8Array": + return list(datapoint.uint32_array.values) + elif datapoint_type == "DataPointUint16": + return datapoint.uint32_value + elif datapoint_type == "DataPointUint16Array": + return list(datapoint.uint32_array.values) + elif datapoint_type == "DataPointUint32": + return datapoint.uint32_value + elif datapoint_type == "DataPointUint32Array": + return list(datapoint.uint32_array.values) + elif datapoint_type == "DataPointUint64": + return datapoint.uint64_value + elif datapoint_type == "DataPointUint64Array": + return list(datapoint.uint64_array.values) + + except Exception: + raise diff --git a/sdv/vdb/subscriptions.py b/sdv/vdb/subscriptions.py index a7ff5814..44684740 100644 --- a/sdv/vdb/subscriptions.py +++ b/sdv/vdb/subscriptions.py @@ -17,6 +17,8 @@ import grpc +from sdv.vdb.reply import DataPointReply + logger = logging.getLogger(__name__) @@ -78,10 +80,11 @@ def _add_subscription(vdb_sub): async def _subscribe_to_data_points(vdb_sub): try: async for reply in vdb_sub.vdb_client.Subscribe(vdb_sub.query): + reply_wrapper = DataPointReply(reply) if asyncio.iscoroutinefunction(vdb_sub.call_back): - await vdb_sub.call_back(reply) + await vdb_sub.call_back(reply_wrapper) else: - vdb_sub.call_back(reply) + vdb_sub.call_back(reply_wrapper) except (grpc.aio.AioRpcError, Exception): # type: ignore logger.exception( "Error occured in SubscriptionManager.subscribe_to_data_points." diff --git a/setup.py b/setup.py index 7332e386..51aa8ec6 100644 --- a/setup.py +++ b/setup.py @@ -67,7 +67,7 @@ setup( name="sdv", - version="0.3.1", + version="0.4.0", description="A Python SDK for Vehicle app", long_description=long_description, long_description_content_type="text/markdown", From dd735d84a611ed99e3bb5dd5da5d5b72194a863a Mon Sep 17 00:00:00 2001 From: Dennis Meister Date: Thu, 29 Sep 2022 09:52:09 +0000 Subject: [PATCH 2/5] Change if else to dictionary switch case approach Signed-off-by: Dennis Meister --- sdv/datapointbase.py | 32 ---------------- sdv/model.py | 3 +- sdv/vdb/reply.py | 90 ++++++++++++++++++-------------------------- 3 files changed, 37 insertions(+), 88 deletions(-) delete mode 100644 sdv/datapointbase.py diff --git a/sdv/datapointbase.py b/sdv/datapointbase.py deleted file mode 100644 index a46221dd..00000000 --- a/sdv/datapointbase.py +++ /dev/null @@ -1,32 +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 - - -class DataPointBase: - """Base class for data points. Do not use for modelling directly.""" - - def join(self, *args): - pass - - def where(self, condition: str): - pass - - def get_query(self) -> str: - pass - - async def subscribe(self, on_update): - pass - - async def get(self): - pass diff --git a/sdv/model.py b/sdv/model.py index 9a162573..3957f52d 100755 --- a/sdv/model.py +++ b/sdv/model.py @@ -21,7 +21,6 @@ import grpc -from sdv.datapointbase import DataPointBase from sdv.proto.types_pb2 import Datapoint as BrokerDatapoint from sdv.vdb.client import VehicleDataBrokerClient from sdv.vdb.subscriptions import SubscriptionManager, VdbSubscription @@ -155,7 +154,7 @@ def __init__(self): self.metadata = conf.service_locator.get_metadata(self.name) -class DataPoint(Node, DataPointBase): +class DataPoint(Node): """Base class for data points. Do not use for modelling directly.""" def __init__(self, name: str, parent: Model): diff --git a/sdv/vdb/reply.py b/sdv/vdb/reply.py index 108dccca..59f1809a 100644 --- a/sdv/vdb/reply.py +++ b/sdv/vdb/reply.py @@ -12,68 +12,50 @@ # # SPDX-License-Identifier: Apache-2.0 -# pylint: skip-file -# flake8: noqa - from sdv.proto.types_pb2 import Datapoint as BrokerDatapoint class DataPointReply: + """Wrapper for dynamic datatype casting of VDB reply.""" + def __init__(self, reply): self._reply = reply def get(self, datapoint_object): datapoint_type = datapoint_object.__class__.__name__ datapoint: BrokerDatapoint = self._reply.fields[datapoint_object.get_path()] - try: - if datapoint_type == "DataPointBoolean": - return datapoint.bool_value - elif datapoint_type == "DataPointBooleanArray": - return list(datapoint.bool_array.values) - elif datapoint_type == "DataPointString": - return datapoint.string_value - elif datapoint_type == "DataPointStringArray": - return list(datapoint.string_array.values) - elif datapoint_type == "DataPointDouble": - return datapoint.double_value - elif datapoint_type == "DataPointDoubleArray": - return list(datapoint.double_array.values) - elif datapoint_type == "DataPointFloat": - return datapoint.float_value - elif datapoint_type == "DataPointFloatArray": - return list(datapoint.float_array.values) - elif datapoint_type == "DataPointInt8": - return datapoint.int32_value - elif datapoint_type == "DataPointInt8Array": - return list(datapoint.int32_array.values) - elif datapoint_type == "DataPointInt16": - return datapoint.int32_value - elif datapoint_type == "DataPointInt16Array": - return list(datapoint.int32_array.values) - elif datapoint_type == "DataPointInt32": - return datapoint.int32_value - elif datapoint_type == "DataPointInt32Array": - return list(datapoint.int32_array.values) - elif datapoint_type == "DataPointInt64": - return datapoint.int64_value - elif datapoint_type == "DataPointInt64Array": - return list(datapoint.int64_array.values) - elif datapoint_type == "DataPointUint8": - return datapoint.uint32_value - elif datapoint_type == "DataPointUint8Array": - return list(datapoint.uint32_array.values) - elif datapoint_type == "DataPointUint16": - return datapoint.uint32_value - elif datapoint_type == "DataPointUint16Array": - return list(datapoint.uint32_array.values) - elif datapoint_type == "DataPointUint32": - return datapoint.uint32_value - elif datapoint_type == "DataPointUint32Array": - return list(datapoint.uint32_array.values) - elif datapoint_type == "DataPointUint64": - return datapoint.uint64_value - elif datapoint_type == "DataPointUint64Array": - return list(datapoint.uint64_array.values) + datapoint_values = { + "DataPointBoolean": datapoint.bool_value, + "DataPointBooleanArray": list(datapoint.bool_array.values), + "DataPointString": datapoint.string_value, + "DataPointStringArray": list(datapoint.string_array.values), + "DataPointDouble": datapoint.double_value, + "DataPointDoubleArray": list(datapoint.double_array.values), + "DataPointFloat": datapoint.float_value, + "DataPointFloatArray": list(datapoint.float_array.values), + "DataPointInt8": datapoint.int32_value, + "DataPointInt8Array": list(datapoint.int32_array.values), + "DataPointInt16": datapoint.int32_value, + "DataPointInt16Array": list(datapoint.int32_array.values), + "DataPointInt32": datapoint.int32_value, + "DataPointInt32Array": list(datapoint.int32_array.values), + "DataPointInt64": datapoint.int64_value, + "DataPointInt64Array": list(datapoint.int64_array.values), + "DataPointUint8": datapoint.uint32_value, + "DataPointUint8Array": list(datapoint.uint32_array.values), + "DataPointUint16": datapoint.uint32_value, + "DataPointUint16Array": list(datapoint.uint32_array.values), + "DataPointUint32": datapoint.uint32_value, + "DataPointUint32Array": list(datapoint.uint32_array.values), + "DataPointUint64": datapoint.uint64_value, + "DataPointUint64Array": list(datapoint.uint64_array.values), + } + datapoint_value = datapoint_values.get( + datapoint_type, + Exception(f"Datapoint of type {datapoint_type} has an unknown value"), + ) + + if isinstance(datapoint_value, Exception): + raise datapoint_value - except Exception: - raise + return datapoint_value From 417a1f6d150a5edefdbd7c4d5d004ee7f6cc58b7 Mon Sep 17 00:00:00 2001 From: "Ebrahim.A" Date: Wed, 5 Oct 2022 12:16:29 +0000 Subject: [PATCH 3/5] Fix linter issues --- examples/seat-adjuster/src/main.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/examples/seat-adjuster/src/main.py b/examples/seat-adjuster/src/main.py index 7edffeaf..a206429d 100644 --- a/examples/seat-adjuster/src/main.py +++ b/examples/seat-adjuster/src/main.py @@ -66,11 +66,7 @@ async def on_seat_position_changed(self, data: DataPointReply): await self.publish_mqtt_event( response_topic, json.dumps( - { - "position": data.get( - self.Vehicle.Cabin.Seat.Row(1).Pos(1).Position - ) - } + {"position": data.get(self.Vehicle.Cabin.Seat.Row(1).Pos(1).Position)} ), ) From 37466f5dfeba5b49d0c5e8d9ce13c51a302a87df Mon Sep 17 00:00:00 2001 From: "Ebrahim.A" Date: Fri, 7 Oct 2022 06:02:48 +0000 Subject: [PATCH 4/5] fix dogmode app --- examples/dog-mode/src/main.py | 23 ++++++++----------- examples/dog-mode/src/vehicle_model/sample.py | 2 +- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/examples/dog-mode/src/main.py b/examples/dog-mode/src/main.py index ab9f0ee5..7dcf45d3 100644 --- a/examples/dog-mode/src/main.py +++ b/examples/dog-mode/src/main.py @@ -26,6 +26,7 @@ from vehicle_model.sample import Vehicle, vehicle from sdv.util.log import get_default_date_format, get_default_log_format +from sdv.vdb.subscriptions import DataPointReply from sdv.vehicle_app import VehicleApp, subscribe_data_points logging.basicConfig(format=get_default_log_format(), datefmt=get_default_date_format()) @@ -47,9 +48,9 @@ class DogModeApp(VehicleApp): sent MQTT message to notify owner """ - def __init__(self, vehicle_client: Vehicle): + def __init__(self, vehicle: Vehicle): super().__init__() - self.vehicle_client = vehicle_client + self.vehicle = vehicle self.not_notified = True async def on_start(self): @@ -75,23 +76,19 @@ async def on_pt_battery_stateofcharge(self, stateOfCharge): Vehicle.Powertrain.Battery.StateOfCharge.Current, Vehicle.Cabin.AmbientAirTemperature""" ) - async def on_change(self, data): - dogModeTemperature = data.fields["Vehicle.Cabin.DogModeTemperature"].float_value - dogMode = data.fields["Vehicle.Cabin.DogMode"].bool_value - self.soc = data.fields[ - "Vehicle.Powertrain.Battery.StateOfCharge.Current" - ].float_value - self.temperature = data.fields[ - "Vehicle.Cabin.AmbientAirTemperature" - ].float_value + async def on_change(self, data: DataPointReply): + dogModeTemperature = data.get(self.vehicle.Cabin.DogModeTemperature) + dogMode = data.get(self.vehicle.Cabin.DogMode) + self.soc = data.get(self.vehicle.Powertrain.Battery.StateOfCharge.Current) + self.temperature = data.get(self.vehicle.Cabin.AmbientAirTemperature) logger.info( "Current temperature of the desired Vehicle is: %s", self.temperature ) - await self.vehicle_client.Cabin.HvacService.ToggleAcStatus(status=dogMode) + await self.vehicle.Cabin.HvacService.ToggleAcStatus(status=dogMode) if dogMode: - await self.vehicle_client.Cabin.HvacService.SetTemperature( + await self.vehicle.Cabin.HvacService.SetTemperature( temperature=dogModeTemperature ) diff --git a/examples/dog-mode/src/vehicle_model/sample.py b/examples/dog-mode/src/vehicle_model/sample.py index 9c2f91e3..14a1ed07 100644 --- a/examples/dog-mode/src/vehicle_model/sample.py +++ b/examples/dog-mode/src/vehicle_model/sample.py @@ -105,7 +105,7 @@ class Vehicle(Model): def __init__(self): super().__init__() - self.powertrain = Powertrain(self) + self.Powertrain = Powertrain(self) self.Speed = DataPointFloat("Speed", self) self.Cabin = Cabin(self) From 2ec4e8574ef31df133ea727fdc6fabfff71a0c2c Mon Sep 17 00:00:00 2001 From: "Ebrahim.A" Date: Fri, 7 Oct 2022 06:29:00 +0000 Subject: [PATCH 5/5] update the version to v0.5.0 --- examples/seat-adjuster/requirements-links.txt | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/seat-adjuster/requirements-links.txt b/examples/seat-adjuster/requirements-links.txt index 47e59363..c6609dcf 100644 --- a/examples/seat-adjuster/requirements-links.txt +++ b/examples/seat-adjuster/requirements-links.txt @@ -1,2 +1,2 @@ -git+https://github.com/eclipse-velocitas/vehicle-app-python-sdk.git@v0.4.0 +git+https://github.com/eclipse-velocitas/vehicle-app-python-sdk.git@v0.5.0 git+https://github.com/eclipse-velocitas/vehicle-model-python.git@v0.3.0 diff --git a/setup.py b/setup.py index 9da9be52..47dac050 100644 --- a/setup.py +++ b/setup.py @@ -69,7 +69,7 @@ setup( name="sdv", - version="0.4.0", + version="0.5.0", description="A Python SDK for Vehicle app", long_description=long_description, long_description_content_type="text/markdown",