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) 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/examples/seat-adjuster/src/main.py b/examples/seat-adjuster/src/main.py index 5eb84a1d..a206429d 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,13 @@ 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.Row(1).Pos(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.Row(1).Pos(1).Position)} + ), ) @subscribe_topic("seatadjuster/setPosition/request") diff --git a/sdv/vdb/reply.py b/sdv/vdb/reply.py new file mode 100644 index 00000000..59f1809a --- /dev/null +++ b/sdv/vdb/reply.py @@ -0,0 +1,61 @@ +# 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 + +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()] + 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 + + return datapoint_value 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 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",