diff --git a/src/nitypes/waveform/_analog_waveform.py b/src/nitypes/waveform/_analog_waveform.py index f947b0d5..392e6b70 100644 --- a/src/nitypes/waveform/_analog_waveform.py +++ b/src/nitypes/waveform/_analog_waveform.py @@ -3,7 +3,7 @@ import datetime as dt import sys import warnings -from collections.abc import Sequence +from collections.abc import Mapping, Sequence from typing import Any, Generic, SupportsIndex, TypeVar, Union, cast, overload import hightime as ht @@ -17,6 +17,7 @@ CHANNEL_NAME, UNIT_DESCRIPTION, ExtendedPropertyDictionary, + ExtendedPropertyValue, ) from nitypes.waveform._scaling import NO_SCALING, ScaleMode from nitypes.waveform._timing import BaseTiming, PrecisionTiming, Timing, convert_timing @@ -83,6 +84,9 @@ def from_array_1d( copy: bool = ..., start_index: SupportsIndex | None = ..., sample_count: SupportsIndex | None = ..., + extended_properties: Mapping[str, ExtendedPropertyValue] | None = ..., + timing: Timing | PrecisionTiming | None = ..., + scale_mode: ScaleMode | None = ..., ) -> AnalogWaveform[_ScalarType]: ... @overload @@ -94,6 +98,9 @@ def from_array_1d( copy: bool = ..., start_index: SupportsIndex | None = ..., sample_count: SupportsIndex | None = ..., + extended_properties: Mapping[str, ExtendedPropertyValue] | None = ..., + timing: Timing | PrecisionTiming | None = ..., + scale_mode: ScaleMode | None = ..., ) -> AnalogWaveform[_ScalarType]: ... @overload @@ -105,6 +112,9 @@ def from_array_1d( copy: bool = ..., start_index: SupportsIndex | None = ..., sample_count: SupportsIndex | None = ..., + extended_properties: Mapping[str, ExtendedPropertyValue] | None = ..., + timing: Timing | PrecisionTiming | None = ..., + scale_mode: ScaleMode | None = ..., ) -> AnalogWaveform[Any]: ... @staticmethod @@ -115,6 +125,9 @@ def from_array_1d( copy: bool = True, start_index: SupportsIndex | None = 0, sample_count: SupportsIndex | None = None, + extended_properties: Mapping[str, ExtendedPropertyValue] | None = None, + timing: Timing | PrecisionTiming | None = None, + scale_mode: ScaleMode | None = None, ) -> AnalogWaveform[_ScalarType]: """Construct an analog waveform from a one-dimensional array or sequence. @@ -125,6 +138,9 @@ def from_array_1d( copy: Specifies whether to copy the array or save a reference to it. start_index: The sample index at which the analog waveform data begins. sample_count: The number of samples in the analog waveform. + extended_properties: The extended properties of the analog waveform. + timing: The timing information of the analog waveform. + scale_mode: The scale mode of the analog waveform. Returns: An analog waveform containing the specified data. @@ -143,9 +159,12 @@ def from_array_1d( raise invalid_arg_type("input array", "one-dimensional array or sequence", array) return AnalogWaveform( - _data=np.asarray(array, dtype, copy=copy), + raw_data=np.asarray(array, dtype, copy=copy), start_index=start_index, sample_count=sample_count, + extended_properties=extended_properties, + timing=timing, + scale_mode=scale_mode, ) @overload @@ -157,6 +176,9 @@ def from_array_2d( copy: bool = ..., start_index: SupportsIndex | None = ..., sample_count: SupportsIndex | None = ..., + extended_properties: Mapping[str, ExtendedPropertyValue] | None = ..., + timing: Timing | PrecisionTiming | None = ..., + scale_mode: ScaleMode | None = ..., ) -> list[AnalogWaveform[_ScalarType]]: ... @overload @@ -168,6 +190,9 @@ def from_array_2d( copy: bool = ..., start_index: SupportsIndex | None = ..., sample_count: SupportsIndex | None = ..., + extended_properties: Mapping[str, ExtendedPropertyValue] | None = ..., + timing: Timing | PrecisionTiming | None = ..., + scale_mode: ScaleMode | None = ..., ) -> list[AnalogWaveform[_ScalarType]]: ... @overload @@ -179,6 +204,9 @@ def from_array_2d( copy: bool = ..., start_index: SupportsIndex | None = ..., sample_count: SupportsIndex | None = ..., + extended_properties: Mapping[str, ExtendedPropertyValue] | None = ..., + timing: Timing | PrecisionTiming | None = ..., + scale_mode: ScaleMode | None = ..., ) -> list[AnalogWaveform[Any]]: ... @staticmethod @@ -189,6 +217,9 @@ def from_array_2d( copy: bool = True, start_index: SupportsIndex | None = 0, sample_count: SupportsIndex | None = None, + extended_properties: Mapping[str, ExtendedPropertyValue] | None = None, + timing: Timing | PrecisionTiming | None = None, + scale_mode: ScaleMode | None = None, ) -> list[AnalogWaveform[_ScalarType]]: """Construct a list of analog waveforms from a two-dimensional array or nested sequence. @@ -199,9 +230,16 @@ def from_array_2d( copy: Specifies whether to copy the array or save a reference to it. start_index: The sample index at which the analog waveform data begins. sample_count: The number of samples in the analog waveform. + extended_properties: The extended properties of the analog waveform. + timing: The timing information of the analog waveform. + scale_mode: The scale mode of the analog waveform. Returns: A list containing an analog waveform for each row of the specified data. + + When constructing multiple analog waveforms, the same extended properties, timing + information, and scale mode are applied to all analog waveforms. Consider assigning + these properties after construction. """ if isinstance(array, np.ndarray): if array.ndim != 2: @@ -218,9 +256,12 @@ def from_array_2d( return [ AnalogWaveform( - _data=np.asarray(array[i], dtype, copy=copy), + raw_data=np.asarray(array[i], dtype, copy=copy), start_index=start_index, sample_count=sample_count, + extended_properties=extended_properties, + timing=timing, + scale_mode=scale_mode, ) for i in range(len(array)) ] @@ -251,9 +292,12 @@ def __init__( # noqa: D107 - Missing docstring in __init__ (auto-generated noqa sample_count: SupportsIndex | None = ..., dtype: None = ..., *, + raw_data: None = ..., start_index: SupportsIndex | None = ..., capacity: SupportsIndex | None = ..., - _data: None = ..., + extended_properties: Mapping[str, ExtendedPropertyValue] | None = ..., + timing: Timing | PrecisionTiming | None = ..., + scale_mode: ScaleMode | None = ..., ) -> None: ... @overload @@ -262,9 +306,12 @@ def __init__( # noqa: D107 - Missing docstring in __init__ (auto-generated noqa sample_count: SupportsIndex | None = ..., dtype: type[_ScalarType_co] | np.dtype[_ScalarType_co] = ..., *, + raw_data: None = ..., start_index: SupportsIndex | None = ..., capacity: SupportsIndex | None = ..., - _data: None = ..., + extended_properties: Mapping[str, ExtendedPropertyValue] | None = ..., + timing: Timing | PrecisionTiming | None = ..., + scale_mode: ScaleMode | None = ..., ) -> None: ... @overload @@ -273,9 +320,12 @@ def __init__( # noqa: D107 - Missing docstring in __init__ (auto-generated noqa sample_count: SupportsIndex | None = ..., dtype: None = ..., *, + raw_data: npt.NDArray[_ScalarType_co] | None = ..., start_index: SupportsIndex | None = ..., capacity: SupportsIndex | None = ..., - _data: npt.NDArray[_ScalarType_co] | None = ..., + extended_properties: Mapping[str, ExtendedPropertyValue] | None = ..., + timing: Timing | PrecisionTiming | None = ..., + scale_mode: ScaleMode | None = ..., ) -> None: ... @overload @@ -284,9 +334,12 @@ def __init__( # noqa: D107 - Missing docstring in __init__ (auto-generated noqa sample_count: SupportsIndex | None = ..., dtype: npt.DTypeLike = ..., *, + raw_data: npt.NDArray[Any] | None = ..., start_index: SupportsIndex | None = ..., capacity: SupportsIndex | None = ..., - _data: npt.NDArray[Any] | None = ..., + extended_properties: Mapping[str, ExtendedPropertyValue] | None = ..., + timing: Timing | PrecisionTiming | None = ..., + scale_mode: ScaleMode | None = ..., ) -> None: ... def __init__( @@ -294,9 +347,12 @@ def __init__( sample_count: SupportsIndex | None = None, dtype: npt.DTypeLike = None, *, + raw_data: npt.NDArray[_ScalarType_co] | None = None, start_index: SupportsIndex | None = None, capacity: SupportsIndex | None = None, - _data: npt.NDArray[_ScalarType_co] | None = None, + extended_properties: Mapping[str, ExtendedPropertyValue] | None = None, + timing: Timing | PrecisionTiming | None = None, + scale_mode: ScaleMode | None = None, ) -> None: """Construct an analog waveform. @@ -304,25 +360,45 @@ def __init__( sample_count: The number of samples in the analog waveform. dtype: The NumPy data type for the analog waveform data. If not specified, the data type defaults to np.float64. + raw_data: A NumPy ndarray to use for sample storage. The analog waveform takes ownership + of this array. If not specified, an ndarray is created based on the specified dtype, + start index, sample count, and capacity. start_index: The sample index at which the analog waveform data begins. sample_count: The number of samples in the analog waveform. capacity: The number of samples to allocate. Pre-allocating a larger buffer optimizes appending samples to the waveform. + extended_properties: The extended properties of the analog waveform. + timing: The timing information of the analog waveform. + scale_mode: The scale mode of the analog waveform. Returns: An analog waveform. - - Arguments that are prefixed with an underscore are internal implementation details and are - subject to change. """ - if _data is None: + if raw_data is None: self._init_with_new_array( sample_count, dtype, start_index=start_index, capacity=capacity ) - else: + elif isinstance(raw_data, np.ndarray): self._init_with_provided_array( - _data, dtype, start_index=start_index, sample_count=sample_count, capacity=capacity + raw_data, + dtype, + start_index=start_index, + sample_count=sample_count, + capacity=capacity, ) + else: + raise invalid_arg_type("raw data", "NumPy ndarray", raw_data) + + self._extended_properties = ExtendedPropertyDictionary(extended_properties) + + if timing is None: + timing = Timing.empty + self._timing = timing + self._converted_timing_cache = {} + + if scale_mode is None: + scale_mode = NO_SCALING + self._scale_mode = scale_mode def _init_with_new_array( self, @@ -357,10 +433,6 @@ def _init_with_new_array( self._data = np.zeros(capacity, dtype) self._start_index = start_index self._sample_count = sample_count - self._extended_properties = ExtendedPropertyDictionary() - self._timing = Timing.empty - self._converted_timing_cache = {} - self._scale_mode = NO_SCALING def _init_with_provided_array( self, @@ -414,10 +486,6 @@ def _init_with_provided_array( self._data = data self._start_index = start_index self._sample_count = sample_count - self._extended_properties = ExtendedPropertyDictionary() - self._timing = Timing.empty - self._converted_timing_cache = {} - self._scale_mode = NO_SCALING @property def raw_data(self) -> npt.NDArray[_ScalarType_co]: @@ -784,3 +852,32 @@ def _increase_capacity(self, amount: int) -> None: new_capacity = self._start_index + self._sample_count + amount if new_capacity > self.capacity: self.capacity = new_capacity + + def __eq__(self, value: object, /) -> bool: + """Return self==value.""" + if not isinstance(value, self.__class__): + return NotImplemented + return ( + self.dtype == value.dtype + and np.array_equal(self.raw_data, value.raw_data) + and self._extended_properties == value._extended_properties + and self._timing == value._timing + and self._scale_mode == value._scale_mode + ) + + def __repr__(self) -> str: + """Return repr(self).""" + args = [f"{self._sample_count}"] + if self.dtype != np.float64: + args.append(f"{self.dtype.name}") + # start_index and capacity are not shown because they are allocation details. raw_data hides + # the unused data before start_index and after start_index+sample_count. + if self._sample_count > 0: + args.append(f"raw_data={self.raw_data!r}") + if self._extended_properties: + args.append(f"extended_properties={self._extended_properties._properties!r}") + if self._timing is not Timing.empty and self._timing is not PrecisionTiming.empty: + args.append(f"timing={self._timing!r}") + if self._scale_mode is not NO_SCALING: + args.append(f"scale_mode={self._scale_mode}") + return f"{self.__class__.__module__}.{self.__class__.__name__}({', '.join(args)})" diff --git a/src/nitypes/waveform/_extended_properties.py b/src/nitypes/waveform/_extended_properties.py index 987d023b..8a4bad7f 100644 --- a/src/nitypes/waveform/_extended_properties.py +++ b/src/nitypes/waveform/_extended_properties.py @@ -1,6 +1,7 @@ from __future__ import annotations import operator +from collections.abc import Mapping from typing import Iterator, MutableMapping, Union from nitypes._typing import TypeAlias @@ -18,40 +19,40 @@ class ExtendedPropertyDictionary(MutableMapping[str, ExtendedPropertyValue]): """A dictionary of extended properties.""" - def __init__(self) -> None: + def __init__(self, properties: Mapping[str, ExtendedPropertyValue] | None = None, /) -> None: """Construct an ExtendedPropertyDictionary.""" self._properties: dict[str, ExtendedPropertyValue] = {} + if properties is not None: + self._properties.update(properties) - def __len__( # noqa: D105 - Missing docstring in magic method (auto-generated noqa) - self, - ) -> int: + def __len__(self) -> int: + """Return len(self).""" return len(self._properties) - def __iter__( # noqa: D105 - Missing docstring in magic method (auto-generated noqa) - self, - ) -> Iterator[str]: + def __iter__(self) -> Iterator[str]: + """Implement iter(self).""" return iter(self._properties) - def __contains__( # noqa: D105 - Missing docstring in magic method (auto-generated noqa) - self, x: object, / - ) -> bool: - return operator.contains(self._properties, x) + def __contains__(self, value: object, /) -> bool: + """Implement value in self.""" + return operator.contains(self._properties, value) - def __getitem__( # noqa: D105 - Missing docstring in magic method (auto-generated noqa) - self, key: str, / - ) -> ExtendedPropertyValue: + def __getitem__(self, key: str, /) -> ExtendedPropertyValue: + """Get self[key].""" return operator.getitem(self._properties, key) - def __setitem__( # noqa: D105 - Missing docstring in magic method (auto-generated noqa) - self, key: str, value: ExtendedPropertyValue, / - ) -> None: + def __setitem__(self, key: str, value: ExtendedPropertyValue, /) -> None: + """Set self[key] to value.""" operator.setitem(self._properties, key, value) - def __delitem__( # noqa: D105 - Missing docstring in magic method (auto-generated noqa) - self, key: str, / - ) -> None: + def __delitem__(self, key: str, /) -> None: + """Delete self[key].""" operator.delitem(self._properties, key) def _merge(self, other: ExtendedPropertyDictionary) -> None: for key, value in other.items(): self._properties.setdefault(key, value) + + def __repr__(self) -> str: + """Return repr(self).""" + return f"{self.__class__.__module__}.{self.__class__.__name__}({self._properties!r})" diff --git a/src/nitypes/waveform/_scaling/_linear.py b/src/nitypes/waveform/_scaling/_linear.py index a1c32646..cf662141 100644 --- a/src/nitypes/waveform/_scaling/_linear.py +++ b/src/nitypes/waveform/_scaling/_linear.py @@ -44,7 +44,14 @@ def _transform_data(self, data: npt.NDArray[_ScalarType]) -> npt.NDArray[_Scalar # npt.NDArray[np.float32] with a float promotes dtype to Any or np.float64 return data * self._gain + self._offset # type: ignore[operator,no-any-return] - def __repr__( # noqa: D105 - Missing docstring in magic method (auto-generated noqa) + def __eq__(self, value: object, /) -> bool: + """Return self==value.""" + if not isinstance(value, self.__class__): + return NotImplemented + return self._gain == value._gain and self._offset == value._offset + + def __repr__( self, ) -> str: + """Return repr(self).""" return f"{self.__class__.__module__}.{self.__class__.__name__}({self.gain}, {self.offset})" diff --git a/src/nitypes/waveform/_scaling/_none.py b/src/nitypes/waveform/_scaling/_none.py index daeaf004..64d7ce1d 100644 --- a/src/nitypes/waveform/_scaling/_none.py +++ b/src/nitypes/waveform/_scaling/_none.py @@ -13,7 +13,14 @@ class NoneScaleMode(ScaleMode): def _transform_data(self, data: npt.NDArray[_ScalarType]) -> npt.NDArray[_ScalarType]: return data - def __repr__( # noqa: D105 - Missing docstring in magic method (auto-generated noqa) + def __eq__(self, value: object, /) -> bool: + """Return self==value.""" + if not isinstance(value, self.__class__): + return NotImplemented + return True + + def __repr__( self, ) -> str: + """Return repr(self).""" return f"{self.__class__.__module__}.{self.__class__.__name__}()" diff --git a/src/nitypes/waveform/_timing/_base.py b/src/nitypes/waveform/_timing/_base.py index 7980c1b9..0f71b08f 100644 --- a/src/nitypes/waveform/_timing/_base.py +++ b/src/nitypes/waveform/_timing/_base.py @@ -200,7 +200,8 @@ def get_timestamps( return self._sample_interval_strategy.get_timestamps(self, start_index, count) - def __eq__(self, value: object) -> bool: # noqa: D105 - Missing docstring in magic method + def __eq__(self, value: object, /) -> bool: + """Return self==value.""" if not isinstance(value, self.__class__): return NotImplemented return ( @@ -211,7 +212,8 @@ def __eq__(self, value: object) -> bool: # noqa: D105 - Missing docstring in ma and self._timestamps == value._timestamps ) - def __repr__(self) -> str: # noqa: D105 - Missing docstring in magic method + def __repr__(self) -> str: + """Return repr(self).""" # For Enum, __str__ is an unqualified ctor expression like E.V and __repr__ is . args = [f"{self.sample_interval_mode.__class__.__module__}.{self.sample_interval_mode}"] if self._timestamp is not None: diff --git a/tests/unit/waveform/test_analog_waveform.py b/tests/unit/waveform/test_analog_waveform.py index 53dff0d5..513de08e 100644 --- a/tests/unit/waveform/test_analog_waveform.py +++ b/tests/unit/waveform/test_analog_waveform.py @@ -1272,7 +1272,6 @@ def test___regular_waveform_and_regular_waveform_with_different_extended_propert assert waveform.extended_properties == {"A": 1, "B": 2, "C": 4} -@pytest.mark.xfail(reason="Needs __eq__ from https://github.com/ni/nitypes-python/pull/11") def test___regular_waveform_and_regular_waveform_with_different_scale_mode___append___appends_waveform_with_scaling_mismatch_warning() -> ( None ): @@ -1418,3 +1417,228 @@ def test___regular_waveform_and_irregular_waveform_list___append___raises_runtim assert list(waveform.raw_data) == [0, 1, 2] assert waveform.timing.sample_interval_mode == SampleIntervalMode.REGULAR assert waveform.timing.sample_interval == dt.timedelta(milliseconds=1) + + +############################################################################### +# magic methods +############################################################################### +@pytest.mark.parametrize( + "left, right", + [ + (AnalogWaveform(), AnalogWaveform()), + (AnalogWaveform(10), AnalogWaveform(10)), + (AnalogWaveform(10, np.float64), AnalogWaveform(10, np.float64)), + (AnalogWaveform(10, np.int32), AnalogWaveform(10, np.int32)), + ( + AnalogWaveform(10, np.int32, start_index=5, capacity=20), + AnalogWaveform(10, np.int32, start_index=5, capacity=20), + ), + ( + AnalogWaveform.from_array_1d([1, 2, 3], np.float64), + AnalogWaveform.from_array_1d([1, 2, 3], np.float64), + ), + ( + AnalogWaveform.from_array_1d([1, 2, 3], np.int32), + AnalogWaveform.from_array_1d([1, 2, 3], np.int32), + ), + ( + AnalogWaveform( + timing=Timing.create_with_regular_interval(dt.timedelta(milliseconds=1)) + ), + AnalogWaveform( + timing=Timing.create_with_regular_interval(dt.timedelta(milliseconds=1)) + ), + ), + ( + AnalogWaveform( + timing=PrecisionTiming.create_with_regular_interval(ht.timedelta(milliseconds=1)) + ), + AnalogWaveform( + timing=PrecisionTiming.create_with_regular_interval(ht.timedelta(milliseconds=1)) + ), + ), + ( + AnalogWaveform( + extended_properties={"NI_ChannelName": "Dev1/ai0", "NI_UnitDescription": "Volts"} + ), + AnalogWaveform( + extended_properties={"NI_ChannelName": "Dev1/ai0", "NI_UnitDescription": "Volts"} + ), + ), + ( + AnalogWaveform(scale_mode=LinearScaleMode(2.0, 1.0)), + AnalogWaveform(scale_mode=LinearScaleMode(2.0, 1.0)), + ), + # start_index and capacity may differ as long as raw_data and sample_count are the same. + ( + AnalogWaveform(10, np.int32, start_index=5, capacity=20), + AnalogWaveform(10, np.int32, start_index=10, capacity=25), + ), + ( + AnalogWaveform.from_array_1d( + [0, 0, 1, 2, 3, 4, 5, 0], np.int32, start_index=2, sample_count=5 + ), + AnalogWaveform.from_array_1d( + [0, 1, 2, 3, 4, 5, 0, 0, 0], np.int32, start_index=1, sample_count=5 + ), + ), + ], +) +def test___same_value___equality___equal( + left: AnalogWaveform[Any], right: AnalogWaveform[Any] +) -> None: + assert left == right + assert not (left != right) + + +@pytest.mark.parametrize( + "left, right", + [ + (AnalogWaveform(), AnalogWaveform(10)), + (AnalogWaveform(10), AnalogWaveform(11)), + (AnalogWaveform(10, np.float64), AnalogWaveform(10, np.int32)), + ( + AnalogWaveform(15, np.int32, start_index=5, capacity=20), + AnalogWaveform(10, np.int32, start_index=5, capacity=20), + ), + ( + AnalogWaveform.from_array_1d([1, 4, 3], np.float64), + AnalogWaveform.from_array_1d([1, 2, 3], np.float64), + ), + ( + AnalogWaveform.from_array_1d([1, 2, 3], np.int32), + AnalogWaveform.from_array_1d([1, 2, 3], np.float64), + ), + ( + AnalogWaveform( + timing=Timing.create_with_regular_interval(dt.timedelta(milliseconds=1)) + ), + AnalogWaveform( + timing=Timing.create_with_regular_interval(dt.timedelta(milliseconds=2)) + ), + ), + ( + AnalogWaveform( + timing=PrecisionTiming.create_with_regular_interval(ht.timedelta(milliseconds=1)) + ), + AnalogWaveform( + timing=PrecisionTiming.create_with_regular_interval(ht.timedelta(milliseconds=2)) + ), + ), + ( + AnalogWaveform( + extended_properties={"NI_ChannelName": "Dev1/ai0", "NI_UnitDescription": "Volts"} + ), + AnalogWaveform( + extended_properties={"NI_ChannelName": "Dev1/ai0", "NI_UnitDescription": "Amps"} + ), + ), + ( + AnalogWaveform(scale_mode=LinearScaleMode(2.0, 1.0)), + AnalogWaveform(scale_mode=LinearScaleMode(2.0, 1.1)), + ), + ( + AnalogWaveform(scale_mode=NO_SCALING), + AnalogWaveform(scale_mode=LinearScaleMode(2.0, 1.0)), + ), + # __eq__ does not convert timing, even if the values are equivalent. + ( + AnalogWaveform( + timing=Timing.create_with_regular_interval(dt.timedelta(milliseconds=1)) + ), + AnalogWaveform( + timing=PrecisionTiming.create_with_regular_interval(ht.timedelta(milliseconds=1)) + ), + ), + ( + AnalogWaveform( + timing=PrecisionTiming.create_with_regular_interval(ht.timedelta(milliseconds=1)) + ), + AnalogWaveform( + timing=Timing.create_with_regular_interval(dt.timedelta(milliseconds=1)) + ), + ), + ], +) +def test___different_value___equality___not_equal( + left: AnalogWaveform[Any], right: AnalogWaveform[Any] +) -> None: + assert not (left == right) + assert left != right + + +@pytest.mark.parametrize( + "value, expected_repr", + [ + (AnalogWaveform(), "nitypes.waveform.AnalogWaveform(0)"), + ( + AnalogWaveform(5), + "nitypes.waveform.AnalogWaveform(5, raw_data=array([0., 0., 0., 0., 0.]))", + ), + ( + AnalogWaveform(5, np.float64), + "nitypes.waveform.AnalogWaveform(5, raw_data=array([0., 0., 0., 0., 0.]))", + ), + (AnalogWaveform(0, np.int32), "nitypes.waveform.AnalogWaveform(0, int32)"), + ( + AnalogWaveform(5, np.int32), + "nitypes.waveform.AnalogWaveform(5, int32, raw_data=array([0, 0, 0, 0, 0], dtype=int32))", + ), + ( + AnalogWaveform(5, np.int32, start_index=5, capacity=20), + "nitypes.waveform.AnalogWaveform(5, int32, raw_data=array([0, 0, 0, 0, 0], dtype=int32))", + ), + ( + AnalogWaveform.from_array_1d([1, 2, 3], np.float64), + "nitypes.waveform.AnalogWaveform(3, raw_data=array([1., 2., 3.]))", + ), + ( + AnalogWaveform.from_array_1d([1, 2, 3], np.int32), + "nitypes.waveform.AnalogWaveform(3, int32, raw_data=array([1, 2, 3], dtype=int32))", + ), + ( + AnalogWaveform( + timing=Timing.create_with_regular_interval(dt.timedelta(milliseconds=1)) + ), + "nitypes.waveform.AnalogWaveform(0, timing=nitypes.waveform.Timing(nitypes.waveform.SampleIntervalMode.REGULAR, sample_interval=datetime.timedelta(microseconds=1000)))", + ), + ( + AnalogWaveform( + timing=PrecisionTiming.create_with_regular_interval(ht.timedelta(milliseconds=1)) + ), + "nitypes.waveform.AnalogWaveform(0, timing=nitypes.waveform.PrecisionTiming(nitypes.waveform.SampleIntervalMode.REGULAR, sample_interval=hightime.timedelta(microseconds=1000)))", + ), + ( + AnalogWaveform( + extended_properties={"NI_ChannelName": "Dev1/ai0", "NI_UnitDescription": "Volts"} + ), + "nitypes.waveform.AnalogWaveform(0, extended_properties={'NI_ChannelName': 'Dev1/ai0', 'NI_UnitDescription': 'Volts'})", + ), + ( + AnalogWaveform(scale_mode=LinearScaleMode(2.0, 1.0)), + "nitypes.waveform.AnalogWaveform(0, scale_mode=nitypes.waveform.LinearScaleMode(2.0, 1.0))", + ), + ( + AnalogWaveform.from_array_1d( + [1, 2, 3], + np.int32, + timing=Timing.create_with_regular_interval(dt.timedelta(milliseconds=1)), + ), + "nitypes.waveform.AnalogWaveform(3, int32, raw_data=array([1, 2, 3], dtype=int32), timing=nitypes.waveform.Timing(nitypes.waveform.SampleIntervalMode.REGULAR, sample_interval=datetime.timedelta(microseconds=1000)))", + ), + ( + AnalogWaveform.from_array_1d( + [1, 2, 3], + np.int32, + extended_properties={"NI_ChannelName": "Dev1/ai0", "NI_UnitDescription": "Volts"}, + ), + "nitypes.waveform.AnalogWaveform(3, int32, raw_data=array([1, 2, 3], dtype=int32), extended_properties={'NI_ChannelName': 'Dev1/ai0', 'NI_UnitDescription': 'Volts'})", + ), + ( + AnalogWaveform.from_array_1d([1, 2, 3], np.int32, scale_mode=LinearScaleMode(2.0, 1.0)), + "nitypes.waveform.AnalogWaveform(3, int32, raw_data=array([1, 2, 3], dtype=int32), scale_mode=nitypes.waveform.LinearScaleMode(2.0, 1.0))", + ), + ], +) +def test___various_values___repr___looks_ok(value: AnalogWaveform[Any], expected_repr: str) -> None: + assert repr(value) == expected_repr