From 3cbb00bb1b003cd3d637c54f4e3c3b0cba7abfbe Mon Sep 17 00:00:00 2001 From: Michael Johansen Date: Fri, 20 Jun 2025 13:00:10 -0500 Subject: [PATCH] Add new protobuf converters to _convert.py. Signed-off-by: Michael Johansen --- src/nipanel/_convert.py | 4 ++ tests/unit/test_convert.py | 75 +++++++++++++++++++++++++++++++++----- 2 files changed, 70 insertions(+), 9 deletions(-) diff --git a/src/nipanel/_convert.py b/src/nipanel/_convert.py index 9488f54..11a0ec0 100644 --- a/src/nipanel/_convert.py +++ b/src/nipanel/_convert.py @@ -21,6 +21,7 @@ IntCollectionConverter, StrCollectionConverter, ) +from nipanel.converters.protobuf_types import DoubleAnalogWaveformConverter, ScalarConverter _logger = logging.getLogger(__name__) @@ -38,6 +39,9 @@ FloatCollectionConverter(), IntCollectionConverter(), StrCollectionConverter(), + # Protobuf Types + DoubleAnalogWaveformConverter(), + ScalarConverter(), ] _CONVERTIBLE_COLLECTION_TYPES = { diff --git a/tests/unit/test_convert.py b/tests/unit/test_convert.py index a14a359..a745cc8 100644 --- a/tests/unit/test_convert.py +++ b/tests/unit/test_convert.py @@ -1,8 +1,16 @@ -from typing import Any, Type, Union +from typing import Any, Union +import numpy as np import pytest from google.protobuf import any_pb2, wrappers_pb2 +from google.protobuf.message import Message +from ni.protobuf.types.scalar_pb2 import ScalarData from ni.pythonpanel.v1 import python_panel_types_pb2 +from ni_measurement_plugin_sdk_service._internal.stubs.ni.protobuf.types.waveform_pb2 import ( + DoubleAnalogWaveform, +) +from nitypes.scalar import Scalar +from nitypes.waveform import AnalogWaveform from typing_extensions import TypeAlias import nipanel._convert @@ -26,7 +34,7 @@ # ======================================================== -# Python to Protobuf +# Built-in Types: Python to Protobuf # ======================================================== @pytest.mark.parametrize( "proto_type, default_value, expected_value", @@ -39,7 +47,7 @@ ], ) def test___python_builtin_scalar___to_any___valid_wrapperpb2_value( - proto_type: Type[_AnyWrappersPb2], default_value: Any, expected_value: Any + proto_type: type[_AnyWrappersPb2], default_value: Any, expected_value: Any ) -> None: result = nipanel._convert.to_any(expected_value) unpack_dest = proto_type(value=default_value) @@ -60,7 +68,7 @@ def test___python_builtin_scalar___to_any___valid_wrapperpb2_value( ], ) def test___python_panel_collection___to_any___valid_paneltype_value( - proto_type: Type[_AnyPanelPbTypes], default_value: Any, expected_value: Any + proto_type: type[_AnyPanelPbTypes], default_value: Any, expected_value: Any ) -> None: result = nipanel._convert.to_any(expected_value) unpack_dest = proto_type(values=default_value) @@ -71,7 +79,7 @@ def test___python_panel_collection___to_any___valid_paneltype_value( # ======================================================== -# Protobuf to Python +# Built-in Types: Protobuf to Python # ======================================================== @pytest.mark.parametrize( "proto_type, expected_value", @@ -84,7 +92,7 @@ def test___python_panel_collection___to_any___valid_paneltype_value( ], ) def test___wrapperpb2_value___from_any___valid_python_value( - proto_type: Type[_AnyWrappersPb2], expected_value: Any + proto_type: type[_AnyWrappersPb2], expected_value: Any ) -> None: pb_value = proto_type(value=expected_value) packed_any = _pack_into_any(pb_value) @@ -106,7 +114,7 @@ def test___wrapperpb2_value___from_any___valid_python_value( ], ) def test___paneltype_value___from_any___valid_python_value( - proto_type: Type[_AnyPanelPbTypes], expected_value: Any + proto_type: type[_AnyPanelPbTypes], expected_value: Any ) -> None: pb_value = proto_type(values=expected_value) packed_any = _pack_into_any(pb_value) @@ -117,15 +125,64 @@ def test___paneltype_value___from_any___valid_python_value( assert result == expected_value +# ======================================================== +# Protobuf Types: Python to Protobuf +# ======================================================== +def test___python_scalar_object___to_any___valid_scalar_data_value() -> None: + scalar_obj = Scalar(1.0, "amps") + result = nipanel._convert.to_any(scalar_obj) + unpack_dest = ScalarData() + _assert_any_and_unpack(result, unpack_dest) + + assert isinstance(unpack_dest, ScalarData) + assert unpack_dest.double_value == 1.0 + assert unpack_dest.units == "amps" + + +def test___python_analog_waveform___to_any___valid_double_analog_waveform() -> None: + wfm_obj = AnalogWaveform(3) + result = nipanel._convert.to_any(wfm_obj) + unpack_dest = DoubleAnalogWaveform() + _assert_any_and_unpack(result, unpack_dest) + + assert isinstance(unpack_dest, DoubleAnalogWaveform) + assert list(unpack_dest.y_data) == [0.0, 0.0, 0.0] + + +# ======================================================== +# Protobuf Types: Protobuf to Python +# ======================================================== +def test___scalar_data___from_any___valid_python_scalar_object() -> None: + pb_value = ScalarData(units="amps", double_value=1.0) + packed_any = _pack_into_any(pb_value) + + result = nipanel._convert.from_any(packed_any) + + assert isinstance(result, Scalar) + assert result.value == 1.0 + assert result.units == "amps" + + +def test___double_analog_waveform___from_any___valid_python_analog_waveform() -> None: + pb_value = DoubleAnalogWaveform(y_data=[0.0, 0.0, 0.0]) + packed_any = _pack_into_any(pb_value) + + result = nipanel._convert.from_any(packed_any) + + assert isinstance(result, AnalogWaveform) + assert result.sample_count == result.capacity == len(result.raw_data) == 3 + assert result.dtype == np.float64 + + # ======================================================== # Pack/Unpack Helpers # ======================================================== -def _assert_any_and_unpack(packed_message: any_pb2.Any, unpack_destination: Any) -> None: +def _assert_any_and_unpack(packed_message: any_pb2.Any, unpack_destination: Message) -> None: assert isinstance(packed_message, any_pb2.Any) assert packed_message.Unpack(unpack_destination) -def _pack_into_any(proto_value: Union[_AnyWrappersPb2, _AnyPanelPbTypes]) -> any_pb2.Any: +def _pack_into_any(proto_value: Message) -> any_pb2.Any: as_any = any_pb2.Any() as_any.Pack(proto_value) return as_any