ni / measurement-plugin-python

Python framework to develop measurement plug-ins for NI application software. Contains sample measurement plug-ins for InstrumentStudio and TestStand.
MIT License
19 stars 14 forks source link

niscope_acquire_waveform example fails with "unsupported operand type(s)" error #923

Open bkeryan opened 10 hours ago

bkeryan commented 10 hours ago

Bug Report

niscope_acquire_waveform fails with this error:

An error occurred while running the measurement. Exception iterating responses: unsupported operand type(s) for +: 'google._upb._message.RepeatedScalarContainer' and 'list'.

This may be related to #767

Repro or Code Sample

Install example (with install_examples.py), install .env.simulation as .env, and run it.

Expected Behavior

It works.

Current Behavior

It fails with this error: image

Possible Solution

Context

Your Environment

AB#2864650

bkeryan commented 10 hours ago

DiscoveryService log:

===== 09/23/2024 16:02:53 ===== Information: HTTP "POST" "/ni.measurementlink.discovery.v1.DiscoveryService/ResolveServiceWithInformation" responded 200 in 4543.5198 ms
===== 09/23/2024 16:02:53 ===== Information: "NI-SCOPE Acquire Waveform (Py)": "2024-09-23 16:02:53,199 INFO: Successfully registered with discovery service."
===== 09/23/2024 16:02:53 ===== Information: "NI-SCOPE Acquire Waveform (Py)": "Press enter to close the measurement service."
===== 09/23/2024 16:02:53 ===== Information: "NI-SCOPE Acquire Waveform (Py)": "2024-09-23 16:02:53,202 INFO: gRPC server call /ni.measurementlink.measurement.v2.MeasurementService/GetMetadata responded OK in 1.1393 ms"
===== 09/23/2024 16:02:53 ===== Information: "PinMapService": "[16:02:53 INF] HTTP POST /ni.measurementlink.pinmap.v1.PinMapService/QueryPins responded 200 in 13.1701 ms"
===== 09/23/2024 16:02:53 ===== Information: ResolveService "ni.measurementlink.monitoring.v1.MonitoringService": Local IP: "127.0.0.1" Remote IP: "127.0.0.1" DeploymentTarget: ""
===== 09/23/2024 16:02:53 ===== Information: Resolved "127.0.0.1" to HostName: "replicas"
===== 09/23/2024 16:02:53 ===== Information: HTTP "POST" "/ni.measurementlink.discovery.v1.DiscoveryService/ResolveServiceWithInformation" responded 200 in 0.5455 ms
===== 09/23/2024 16:02:55 ===== Information: "NI-SCOPE Acquire Waveform (Py)": "2024-09-23 16:02:55,670 INFO: Starting acquisition: measurement_pins=['PinGroup1'] vertical_range=5 trigger_pin=Pin1 trigger_level=0.5"
===== 09/23/2024 16:02:55 ===== Information: "NI-SCOPE Acquire Waveform (Py)": "2024-09-23 16:02:55,670 INFO: gRPC server call /ni.measurementlink.measurement.v2.MeasurementService/Measure responded UNKNOWN in 0.8941 ms"
===== 09/23/2024 16:02:55 ===== Information: "NI-SCOPE Acquire Waveform (Py)": "2024-09-23 16:02:55,670 ERROR: Exception iterating responses: unsupported operand type(s) for +: 'google._upb._message.RepeatedScalarContainer' and 'list'"
===== 09/23/2024 16:02:55 ===== Information: "NI-SCOPE Acquire Waveform (Py)": "Traceback (most recent call last):"
===== 09/23/2024 16:02:55 ===== Information: "NI-SCOPE Acquire Waveform (Py)": "  File \"C:\ProgramData\National Instruments\Plug-Ins\Measurements\niscope_acquire_waveform\.venv\lib\site-packages\grpc\_server.py\", line 654, in _take_response_from_response_iterator"
===== 09/23/2024 16:02:55 ===== Information: "NI-SCOPE Acquire Waveform (Py)": "    return next(response_iterator), True"
===== 09/23/2024 16:02:55 ===== Information: "NI-SCOPE Acquire Waveform (Py)": "  File \"C:\ProgramData\National Instruments\Plug-Ins\Measurements\niscope_acquire_waveform\.venv\lib\site-packages\ni_measurement_plugin_sdk_service\grpc\loggers.py\", line 391, in __next__"
===== 09/23/2024 16:02:55 ===== Information: "NI-SCOPE Acquire Waveform (Py)": "    response = next(self._inner_iterator)"
===== 09/23/2024 16:02:55 ===== Information: "NI-SCOPE Acquire Waveform (Py)": "  File \"C:\ProgramData\National Instruments\Plug-Ins\Measurements\niscope_acquire_waveform\.venv\lib\site-packages\ni_measurement_plugin_sdk_service\_internal\grpc_servicer.py\", line 371, in Measure"
===== 09/23/2024 16:02:55 ===== Information: "NI-SCOPE Acquire Waveform (Py)": "    return_value = self._measure_function(**mapping_by_variable_name)"
===== 09/23/2024 16:02:55 ===== Information: "NI-SCOPE Acquire Waveform (Py)": "  File \"C:\ProgramData\National Instruments\Plug-Ins\Measurements\niscope_acquire_waveform\measurement.py\", line 93, in measure"
===== 09/23/2024 16:02:55 ===== Information: "NI-SCOPE Acquire Waveform (Py)": "    measurement_pins + [trigger_pin]"
===== 09/23/2024 16:02:55 ===== Information: "NI-SCOPE Acquire Waveform (Py)": "TypeError: unsupported operand type(s) for +: 'google._upb._message.RepeatedScalarContainer' and 'list'"
bkeryan commented 10 hours ago

I think deserialize_parameters needs another special case to convert from a protobuf repeated object to a list: https://github.com/ni/measurement-plugin-python/blob/main/packages/service/ni_measurement_plugin_sdk_service/_internal/parameter/decoder.py#L44

bkeryan commented 9 hours ago

Hmm, sample_measurement uses a type hint of Iterable[T] instead of List[T]: https://github.com/ni/measurement-plugin-python/blob/main/examples/sample_measurement/measurement.py#L65

Maybe niscope_acquire_waveform should do the same, and explicitly convert to list if it wants a list.

@dixonjoel What do you think?

bkeryan commented 9 hours ago

Converting RepeatedScalarContainer to list would look something like this:

diff --git a/packages/service/ni_measurement_plugin_sdk_service/_internal/parameter/decoder.py b/packages/service/ni_measurement_plugin_sdk_service/_internal/parameter/decoder.py
index b11c7cb2..277f52a0 100644
--- a/packages/service/ni_measurement_plugin_sdk_service/_internal/parameter/decoder.py
+++ b/packages/service/ni_measurement_plugin_sdk_service/_internal/parameter/decoder.py
@@ -49,6 +49,9 @@ def deserialize_parameters(
             and value.ByteSize() == 0
         ):
             parameter_values[i] = None
+        elif parameter_metadata.repeated:
+            # Convert RepeatedScalarContainer to list.
+            parameter_values[i] = list(value)
         else:
             parameter_values[i] = value
     return parameter_values
dixonjoel commented 9 hours ago

Hmm, sample_measurement uses a type hint of Iterable[T] instead of List[T]: https://github.com/ni/measurement-plugin-python/blob/main/examples/sample_measurement/measurement.py#L65

Maybe niscope_acquire_waveform should do the same, and explicitly convert to list if it wants a list.

@dixonjoel What do you think?

If we already have a pattern that works for the measurement service, I would just change this to Iterable[str]. Does that make it work?