Skip to content

Commit f77dd57

Browse files
authored
nirfsg: Enable complex number support for gRPC and update system tests (#2172)
1 parent 448825e commit f77dd57

12 files changed

Lines changed: 950 additions & 457 deletions

File tree

build/templates/_grpc_stub_interpreter.py.mako

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ module_name = config['module_name']
88
proto_name = config.get('proto_name', module_name)
99
service_class_prefix = config['grpc_service_class_prefix']
1010
functions = helper.filter_codegen_functions(config['functions'])
11+
are_complex_parameters_used = helper.are_complex_parameters_used(functions)
1112
%>\
1213

1314
import grpc
@@ -19,6 +20,9 @@ import warnings
1920
from . import enums as enums # noqa: F401
2021
% endif
2122
from . import errors as errors
23+
% if are_complex_parameters_used:
24+
from . import nidevice_pb2 as grpc_complex_types # noqa: F401
25+
% endif
2226
from . import ${proto_name}_pb2 as grpc_types
2327
from . import ${proto_name}_pb2_grpc as ${module_name}_grpc
2428
from . import session_pb2 as session_grpc_types
Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,62 @@
11
<%page args="f, config, method_template"/>\
2-
## numpy_read and numpy_write are identical for gRPC -- both return a NotImplementedError
3-
<%include file="/_grpc_stub_interpreter.py/numpy_read_method.py.mako" args="f=f, config=config, method_template=method_template" />\
2+
<%
3+
'''Renders a GrpcStubInterpreter method for numpy write operations with complex number support.'''
4+
5+
import build.helper as helper
6+
7+
full_func_name = f['interpreter_name'] + method_template['method_python_name_suffix']
8+
method_decl_params = helper.get_params_snippet(f, helper.ParameterUsageOptions.INTERPRETER_METHOD_DECLARATION)
9+
grpc_name = f.get('grpc_name', f['name'])
10+
grpc_request_args = helper.get_params_snippet(f, helper.ParameterUsageOptions.GRPC_REQUEST_PARAMETERS)
11+
return_statement = helper.get_grpc_interpreter_method_return_snippet(f['parameters'], config)
12+
if return_statement == 'return':
13+
return_statement = None
14+
capture_response = 'response = ' if return_statement else ''
15+
included_in_proto = f.get('included_in_proto', True)
16+
numpy_complex_params = helper.filter_parameters(f['parameters'], helper.ParameterUsageOptions.COMPLEX_NUMBER_PARAMETERS)
17+
for param in numpy_complex_params:
18+
grpc_request_args = grpc_request_args.replace(
19+
param['grpc_name'] + '=' + param['python_name'],
20+
param['grpc_name'] + '=' + param['python_name'] + '_list'
21+
)
22+
%>\
23+
24+
def ${full_func_name}(${method_decl_params}): # noqa: N802
25+
% if included_in_proto:
26+
% if numpy_complex_params:
27+
# Use ravel() so that gRPC always receives a flat numpy array, regardless of input dimensions.
28+
% for param in numpy_complex_params:
29+
% if param['original_type'] == 'NIComplexNumber[]':
30+
${param['python_name']}_list = [
31+
grpc_complex_types.NIComplexNumber(real=val.real, imaginary=val.imag)
32+
for val in ${param['python_name']}.ravel()
33+
]
34+
% elif param['original_type'] == 'NIComplexNumberF32[]':
35+
${param['python_name']}_list = [
36+
grpc_complex_types.NIComplexNumberF32(real=val.real, imaginary=val.imag)
37+
for val in ${param['python_name']}.ravel()
38+
]
39+
% elif param['original_type'] == 'NIComplexI16[]':
40+
arr = ${param['python_name']}.ravel()
41+
if arr.size % 2 != 0:
42+
raise ValueError("Interleaved int16 array must have even length (real/imag pairs)")
43+
arr_pairs = arr.reshape(-1, 2)
44+
${param['python_name']}_list = [
45+
grpc_complex_types.NIComplexI16(real=int(pair[0]), imaginary=int(pair[1]))
46+
for pair in arr_pairs
47+
]
48+
% endif
49+
% endfor
50+
${capture_response}self._invoke(
51+
self._client.${grpc_name},
52+
grpc_types.${grpc_name}Request(${grpc_request_args}),
53+
)
54+
% if return_statement:
55+
${return_statement}
56+
% endif
57+
% else:
58+
raise NotImplementedError('numpy-specific methods are not supported over gRPC')
59+
% endif
60+
% else:
61+
raise NotImplementedError('${full_func_name} is not supported over gRPC')
62+
% endif

generated/nifake/nifake/_grpc_stub_interpreter.py

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
from . import enums as enums # noqa: F401
1010
from . import errors as errors
11+
from . import nidevice_pb2 as grpc_complex_types # noqa: F401
1112
from . import nifake_pb2 as grpc_types
1213
from . import nifake_pb2_grpc as nifake_grpc
1314
from . import session_pb2 as session_grpc_types
@@ -145,7 +146,15 @@ def fetch_waveform_into(self, number_of_samples): # noqa: N802
145146
raise NotImplementedError('numpy-specific methods are not supported over gRPC')
146147

147148
def function_with_3d_numpy_array_of_numpy_complex128_input_parameter(self, multidimensional_array): # noqa: N802
148-
raise NotImplementedError('numpy-specific methods are not supported over gRPC')
149+
# Use ravel() so that gRPC always receives a flat numpy array, regardless of input dimensions.
150+
multidimensional_array_list = [
151+
grpc_complex_types.NIComplexNumber(real=val.real, imaginary=val.imag)
152+
for val in multidimensional_array.ravel()
153+
]
154+
self._invoke(
155+
self._client.FunctionWith3dNumpyArrayOfNumpyComplex128InputParameter,
156+
grpc_types.FunctionWith3dNumpyArrayOfNumpyComplex128InputParameterRequest(vi=self._vi, multidimensional_array=multidimensional_array_list),
157+
)
149158

150159
def function_with_intflag_parameter(self, flag): # noqa: N802
151160
self._invoke(
@@ -512,13 +521,41 @@ def write_waveform_numpy(self, waveform): # noqa: N802
512521
raise NotImplementedError('numpy-specific methods are not supported over gRPC')
513522

514523
def write_waveform_numpy_complex128(self, waveform_data_array): # noqa: N802
515-
raise NotImplementedError('numpy-specific methods are not supported over gRPC')
524+
# Use ravel() so that gRPC always receives a flat numpy array, regardless of input dimensions.
525+
waveform_data_array_list = [
526+
grpc_complex_types.NIComplexNumber(real=val.real, imaginary=val.imag)
527+
for val in waveform_data_array.ravel()
528+
]
529+
self._invoke(
530+
self._client.WriteWaveformNumpyComplex128,
531+
grpc_types.WriteWaveformNumpyComplex128Request(vi=self._vi, waveform_data_array=waveform_data_array_list),
532+
)
516533

517534
def write_waveform_numpy_complex64(self, waveform_data_array): # noqa: N802
518-
raise NotImplementedError('numpy-specific methods are not supported over gRPC')
535+
# Use ravel() so that gRPC always receives a flat numpy array, regardless of input dimensions.
536+
waveform_data_array_list = [
537+
grpc_complex_types.NIComplexNumberF32(real=val.real, imaginary=val.imag)
538+
for val in waveform_data_array.ravel()
539+
]
540+
self._invoke(
541+
self._client.WriteWaveformNumpyComplex64,
542+
grpc_types.WriteWaveformNumpyComplex64Request(vi=self._vi, waveform_data_array=waveform_data_array_list),
543+
)
519544

520545
def write_waveform_numpy_complex_interleaved_i16(self, waveform_data_array): # noqa: N802
521-
raise NotImplementedError('numpy-specific methods are not supported over gRPC')
546+
# Use ravel() so that gRPC always receives a flat numpy array, regardless of input dimensions.
547+
arr = waveform_data_array.ravel()
548+
if arr.size % 2 != 0:
549+
raise ValueError("Interleaved int16 array must have even length (real/imag pairs)")
550+
arr_pairs = arr.reshape(-1, 2)
551+
waveform_data_array_list = [
552+
grpc_complex_types.NIComplexI16(real=int(pair[0]), imaginary=int(pair[1]))
553+
for pair in arr_pairs
554+
]
555+
self._invoke(
556+
self._client.WriteWaveformNumpyComplexInterleavedI16,
557+
grpc_types.WriteWaveformNumpyComplexInterleavedI16Request(vi=self._vi, waveform_data_array=waveform_data_array_list),
558+
)
522559

523560
def close(self): # noqa: N802
524561
self._invoke(

generated/nifake/nifake/nifake_pb2.py

Lines changed: 451 additions & 433 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

generated/nifake/nifake/nifake_pb2_grpc.py

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,21 @@ def __init__(self, channel):
229229
request_serializer=nifake__pb2.WriteWaveformRequest.SerializeToString,
230230
response_deserializer=nifake__pb2.WriteWaveformResponse.FromString,
231231
)
232+
self.WriteWaveformNumpyComplex128 = channel.unary_unary(
233+
'/nifake_grpc.NiFake/WriteWaveformNumpyComplex128',
234+
request_serializer=nifake__pb2.WriteWaveformNumpyComplex128Request.SerializeToString,
235+
response_deserializer=nifake__pb2.WriteWaveformNumpyComplex128Response.FromString,
236+
)
237+
self.WriteWaveformNumpyComplex64 = channel.unary_unary(
238+
'/nifake_grpc.NiFake/WriteWaveformNumpyComplex64',
239+
request_serializer=nifake__pb2.WriteWaveformNumpyComplex64Request.SerializeToString,
240+
response_deserializer=nifake__pb2.WriteWaveformNumpyComplex64Response.FromString,
241+
)
242+
self.WriteWaveformNumpyComplexInterleavedI16 = channel.unary_unary(
243+
'/nifake_grpc.NiFake/WriteWaveformNumpyComplexInterleavedI16',
244+
request_serializer=nifake__pb2.WriteWaveformNumpyComplexInterleavedI16Request.SerializeToString,
245+
response_deserializer=nifake__pb2.WriteWaveformNumpyComplexInterleavedI16Response.FromString,
246+
)
232247
self.SetCustomType = channel.unary_unary(
233248
'/nifake_grpc.NiFake/SetCustomType',
234249
request_serializer=nifake__pb2.SetCustomTypeRequest.SerializeToString,
@@ -319,6 +334,11 @@ def __init__(self, channel):
319334
request_serializer=nifake__pb2.FunctionWithOverriddenGrpcName2xRequest.SerializeToString,
320335
response_deserializer=nifake__pb2.FunctionWithOverriddenGrpcName2xResponse.FromString,
321336
)
337+
self.FunctionWith3dNumpyArrayOfNumpyComplex128InputParameter = channel.unary_unary(
338+
'/nifake_grpc.NiFake/FunctionWith3dNumpyArrayOfNumpyComplex128InputParameter',
339+
request_serializer=nifake__pb2.FunctionWith3dNumpyArrayOfNumpyComplex128InputParameterRequest.SerializeToString,
340+
response_deserializer=nifake__pb2.FunctionWith3dNumpyArrayOfNumpyComplex128InputParameterResponse.FromString,
341+
)
322342
self.StringValuedEnumNoEnumGenerated = channel.unary_unary(
323343
'/nifake_grpc.NiFake/StringValuedEnumNoEnumGenerated',
324344
request_serializer=nifake__pb2.StringValuedEnumNoEnumGeneratedRequest.SerializeToString,
@@ -772,6 +792,24 @@ def WriteWaveform(self, request, context):
772792
context.set_details('Method not implemented!')
773793
raise NotImplementedError('Method not implemented!')
774794

795+
def WriteWaveformNumpyComplex128(self, request, context):
796+
"""Missing associated documentation comment in .proto file."""
797+
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
798+
context.set_details('Method not implemented!')
799+
raise NotImplementedError('Method not implemented!')
800+
801+
def WriteWaveformNumpyComplex64(self, request, context):
802+
"""Missing associated documentation comment in .proto file."""
803+
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
804+
context.set_details('Method not implemented!')
805+
raise NotImplementedError('Method not implemented!')
806+
807+
def WriteWaveformNumpyComplexInterleavedI16(self, request, context):
808+
"""Missing associated documentation comment in .proto file."""
809+
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
810+
context.set_details('Method not implemented!')
811+
raise NotImplementedError('Method not implemented!')
812+
775813
def SetCustomType(self, request, context):
776814
"""Missing associated documentation comment in .proto file."""
777815
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
@@ -880,6 +918,12 @@ def FunctionWithOverriddenGrpcName2x(self, request, context):
880918
context.set_details('Method not implemented!')
881919
raise NotImplementedError('Method not implemented!')
882920

921+
def FunctionWith3dNumpyArrayOfNumpyComplex128InputParameter(self, request, context):
922+
"""Missing associated documentation comment in .proto file."""
923+
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
924+
context.set_details('Method not implemented!')
925+
raise NotImplementedError('Method not implemented!')
926+
883927
def StringValuedEnumNoEnumGenerated(self, request, context):
884928
"""Missing associated documentation comment in .proto file."""
885929
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
@@ -1326,6 +1370,21 @@ def add_NiFakeServicer_to_server(servicer, server):
13261370
request_deserializer=nifake__pb2.WriteWaveformRequest.FromString,
13271371
response_serializer=nifake__pb2.WriteWaveformResponse.SerializeToString,
13281372
),
1373+
'WriteWaveformNumpyComplex128': grpc.unary_unary_rpc_method_handler(
1374+
servicer.WriteWaveformNumpyComplex128,
1375+
request_deserializer=nifake__pb2.WriteWaveformNumpyComplex128Request.FromString,
1376+
response_serializer=nifake__pb2.WriteWaveformNumpyComplex128Response.SerializeToString,
1377+
),
1378+
'WriteWaveformNumpyComplex64': grpc.unary_unary_rpc_method_handler(
1379+
servicer.WriteWaveformNumpyComplex64,
1380+
request_deserializer=nifake__pb2.WriteWaveformNumpyComplex64Request.FromString,
1381+
response_serializer=nifake__pb2.WriteWaveformNumpyComplex64Response.SerializeToString,
1382+
),
1383+
'WriteWaveformNumpyComplexInterleavedI16': grpc.unary_unary_rpc_method_handler(
1384+
servicer.WriteWaveformNumpyComplexInterleavedI16,
1385+
request_deserializer=nifake__pb2.WriteWaveformNumpyComplexInterleavedI16Request.FromString,
1386+
response_serializer=nifake__pb2.WriteWaveformNumpyComplexInterleavedI16Response.SerializeToString,
1387+
),
13291388
'SetCustomType': grpc.unary_unary_rpc_method_handler(
13301389
servicer.SetCustomType,
13311390
request_deserializer=nifake__pb2.SetCustomTypeRequest.FromString,
@@ -1416,6 +1475,11 @@ def add_NiFakeServicer_to_server(servicer, server):
14161475
request_deserializer=nifake__pb2.FunctionWithOverriddenGrpcName2xRequest.FromString,
14171476
response_serializer=nifake__pb2.FunctionWithOverriddenGrpcName2xResponse.SerializeToString,
14181477
),
1478+
'FunctionWith3dNumpyArrayOfNumpyComplex128InputParameter': grpc.unary_unary_rpc_method_handler(
1479+
servicer.FunctionWith3dNumpyArrayOfNumpyComplex128InputParameter,
1480+
request_deserializer=nifake__pb2.FunctionWith3dNumpyArrayOfNumpyComplex128InputParameterRequest.FromString,
1481+
response_serializer=nifake__pb2.FunctionWith3dNumpyArrayOfNumpyComplex128InputParameterResponse.SerializeToString,
1482+
),
14191483
'StringValuedEnumNoEnumGenerated': grpc.unary_unary_rpc_method_handler(
14201484
servicer.StringValuedEnumNoEnumGenerated,
14211485
request_deserializer=nifake__pb2.StringValuedEnumNoEnumGeneratedRequest.FromString,
@@ -2347,6 +2411,57 @@ def WriteWaveform(request,
23472411
options, channel_credentials,
23482412
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
23492413

2414+
@staticmethod
2415+
def WriteWaveformNumpyComplex128(request,
2416+
target,
2417+
options=(),
2418+
channel_credentials=None,
2419+
call_credentials=None,
2420+
insecure=False,
2421+
compression=None,
2422+
wait_for_ready=None,
2423+
timeout=None,
2424+
metadata=None):
2425+
return grpc.experimental.unary_unary(request, target, '/nifake_grpc.NiFake/WriteWaveformNumpyComplex128',
2426+
nifake__pb2.WriteWaveformNumpyComplex128Request.SerializeToString,
2427+
nifake__pb2.WriteWaveformNumpyComplex128Response.FromString,
2428+
options, channel_credentials,
2429+
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
2430+
2431+
@staticmethod
2432+
def WriteWaveformNumpyComplex64(request,
2433+
target,
2434+
options=(),
2435+
channel_credentials=None,
2436+
call_credentials=None,
2437+
insecure=False,
2438+
compression=None,
2439+
wait_for_ready=None,
2440+
timeout=None,
2441+
metadata=None):
2442+
return grpc.experimental.unary_unary(request, target, '/nifake_grpc.NiFake/WriteWaveformNumpyComplex64',
2443+
nifake__pb2.WriteWaveformNumpyComplex64Request.SerializeToString,
2444+
nifake__pb2.WriteWaveformNumpyComplex64Response.FromString,
2445+
options, channel_credentials,
2446+
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
2447+
2448+
@staticmethod
2449+
def WriteWaveformNumpyComplexInterleavedI16(request,
2450+
target,
2451+
options=(),
2452+
channel_credentials=None,
2453+
call_credentials=None,
2454+
insecure=False,
2455+
compression=None,
2456+
wait_for_ready=None,
2457+
timeout=None,
2458+
metadata=None):
2459+
return grpc.experimental.unary_unary(request, target, '/nifake_grpc.NiFake/WriteWaveformNumpyComplexInterleavedI16',
2460+
nifake__pb2.WriteWaveformNumpyComplexInterleavedI16Request.SerializeToString,
2461+
nifake__pb2.WriteWaveformNumpyComplexInterleavedI16Response.FromString,
2462+
options, channel_credentials,
2463+
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
2464+
23502465
@staticmethod
23512466
def SetCustomType(request,
23522467
target,
@@ -2653,6 +2768,23 @@ def FunctionWithOverriddenGrpcName2x(request,
26532768
options, channel_credentials,
26542769
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
26552770

2771+
@staticmethod
2772+
def FunctionWith3dNumpyArrayOfNumpyComplex128InputParameter(request,
2773+
target,
2774+
options=(),
2775+
channel_credentials=None,
2776+
call_credentials=None,
2777+
insecure=False,
2778+
compression=None,
2779+
wait_for_ready=None,
2780+
timeout=None,
2781+
metadata=None):
2782+
return grpc.experimental.unary_unary(request, target, '/nifake_grpc.NiFake/FunctionWith3dNumpyArrayOfNumpyComplex128InputParameter',
2783+
nifake__pb2.FunctionWith3dNumpyArrayOfNumpyComplex128InputParameterRequest.SerializeToString,
2784+
nifake__pb2.FunctionWith3dNumpyArrayOfNumpyComplex128InputParameterResponse.FromString,
2785+
options, channel_credentials,
2786+
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
2787+
26562788
@staticmethod
26572789
def StringValuedEnumNoEnumGenerated(request,
26582790
target,

0 commit comments

Comments
 (0)