diff --git a/.github/workflows/build_cmake.yml b/.github/workflows/build_cmake.yml index a2307235c..7d34895c2 100644 --- a/.github/workflows/build_cmake.yml +++ b/.github/workflows/build_cmake.yml @@ -155,7 +155,7 @@ jobs: - name: Upload Windows Server Information Artifact uses: actions/upload-artifact@v2 - if: ${{ (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/releases')) && (runner.os == 'Windows') }} + if: ${{ (runner.os == 'Windows') }} with: name: ni-grpc-device-server-windows-x64 path: | @@ -165,7 +165,7 @@ jobs: - name: Upload Windows Server Binaries Artifact uses: actions/upload-artifact@v2 - if: ${{ (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/releases')) && (runner.os == 'Windows') }} + if: ${{ (runner.os == 'Windows') }} with: name: ni-grpc-device-server-windows-x64 path: | @@ -175,7 +175,7 @@ jobs: - name: Upload Windows Test Binaries Artifact uses: actions/upload-artifact@v2 - if: ${{ (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/releases')) && (runner.os == 'Windows') }} + if: ${{ (runner.os == 'Windows') }} with: name: ni-grpc-device-tests-windows-x64 path: | diff --git a/.github/workflows/create_client_artifacts.yml b/.github/workflows/create_client_artifacts.yml index 2d04631d6..c05d38996 100644 --- a/.github/workflows/create_client_artifacts.yml +++ b/.github/workflows/create_client_artifacts.yml @@ -52,7 +52,7 @@ jobs: - name: Upload Windows Client Files Artifact uses: actions/upload-artifact@v2 - if: ${{ (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/releases')) && (runner.os == 'Windows') }} + if: ${{ (runner.os == 'Windows') }} with: name: ni-grpc-device-client path: | diff --git a/CMakeLists.txt b/CMakeLists.txt index d2316932b..083ac7b16 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -76,7 +76,7 @@ set(metadata_dir ${CMAKE_SOURCE_DIR}/source/codegen/metadata) set(service_output_dir ${CMAKE_SOURCE_DIR}/generated) set(codegen_dir ${CMAKE_SOURCE_DIR}/source/codegen) set(custom_dir ${CMAKE_SOURCE_DIR}/source/custom) -set(nidrivers "nidaqmx" "nidcpower" "nidigitalpattern" "nidmm" "nifake" "nifake_extension" "nifake_non_ivi" "nifgen" "nirfmxbluetooth" "nirfmxinstr" "nirfmxlte" "nirfmxnr" "nirfmxspecan" "nirfmxwlan" "nirfsa" "nirfsg" "niscope" "niswitch" "nisync" "nitclk" "nixnet" "nixnetsocket") +set(nidrivers "iotrace" "nidaqmx" "nidcpower" "nidigitalpattern" "nidmm" "nifake" "nifake_extension" "nifake_non_ivi" "nifgen" "nirfmxbluetooth" "nirfmxinstr" "nirfmxlte" "nirfmxnr" "nirfmxspecan" "nirfmxwlan" "nirfsa" "nirfsg" "niscope" "niswitch" "nisync" "nitclk" "nixnet" "nixnetsocket") #---------------------------------------------------------------------- # Create NI Driver APIs proto and server files @@ -307,9 +307,9 @@ target_link_libraries(ni_grpc_device_server set_target_properties(ni_grpc_device_server PROPERTIES BUILD_WITH_INSTALL_RPATH TRUE) if(WIN32) - target_link_libraries(ni_grpc_device_server Delayimp nidaqmx nidcpower niDigital nidmm niFgen niScope niRFmxBT niRFmxInstr niRFmxLTE niRFmxNR niRFmxSpecAn niRFmxWLAN niRFSA niRFSG niswitch nisync niTClk nixnet nixntipstack) - set_target_properties(ni_grpc_device_server PROPERTIES LINK_FLAGS "/DELAYLOAD:nicaiu.dll /DELAYLOAD:nidcpower.dll /DELAYLOAD:niDigital_64.dll /DELAYLOAD:nidmm_64.dll /DELAYLOAD:niFgen_64.dll /DELAYLOAD:niRFmxBT.dll /DELAYLOAD:niRFmxInstr.dll /DELAYLOAD:niRFmxLTE.dll /DELAYLOAD:niRFmxNR.dll /DELAYLOAD:niRFmxSpecAn.dll /DELAYLOAD:niRFmxWLAN.dll /DELAYLOAD:niRFSA_64.dll /DELAYLOAD:niRFSG_64.dll /DELAYLOAD:niScope.dll /DELAYLOAD:niswitch.dll /DELAYLOAD:nisync.dll /DELAYLOAD:niTClk_64.dll /DELAYLOAD:nixnet.dll /DELAYLOAD:nixntipstack.dll") - set(ni_grpc_device_server CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /DELAYLOAD:nicaiu.dll /DELAYLOAD:nidcpower.dll /DELAYLOAD:niDigital_64.dll /DELAYLOAD:nidmm_64.dll /DELAYLOAD:niFgen_64.dll /DELAYLOAD:niScope.dll /DELAYLOAD:niRFmxBT.dll /DELAYLOAD:niRFmxInstr.dll /DELAYLOAD:niRFmxLTE.dll /DELAYLOAD:niRFmxNR.dll /DELAYLOAD:niRFmxSpecAn.dll /DELAYLOAD:niRFmxWLAN.dll /DELAYLOAD:niRFSA_64.dll /DELAYLOAD:niRFSG_64.dll /DELAYLOAD:niswitch.dll /DELAYLOAD:nisync.dll /DELAYLOAD:niTClk_64.dll /DELAYLOAD:nixnet.dll /DELAYLOAD:nixntipstack.dll") + target_link_libraries(ni_grpc_device_server Delayimp nidaqmx nidcpower niDigital nidmm niFgen NiSpyLog niScope niRFmxBT niRFmxInstr niRFmxLTE niRFmxNR niRFmxSpecAn niRFmxWLAN niRFSA niRFSG niswitch nisync niTClk nixnet nixntipstack) + set_target_properties(ni_grpc_device_server PROPERTIES LINK_FLAGS "/DELAYLOAD:nicaiu.dll /DELAYLOAD:nidcpower.dll /DELAYLOAD:niDigital_64.dll /DELAYLOAD:nidmm_64.dll /DELAYLOAD:niFgen_64.dll /DELAYLOAD:NiSpyLog.dll /DELAYLOAD:niRFmxBT.dll /DELAYLOAD:niRFmxInstr.dll /DELAYLOAD:niRFmxLTE.dll /DELAYLOAD:niRFmxNR.dll /DELAYLOAD:niRFmxSpecAn.dll /DELAYLOAD:niRFmxWLAN.dll /DELAYLOAD:niRFSA_64.dll /DELAYLOAD:niRFSG_64.dll /DELAYLOAD:niScope.dll /DELAYLOAD:niswitch.dll /DELAYLOAD:nisync.dll /DELAYLOAD:niTClk_64.dll /DELAYLOAD:nixnet.dll /DELAYLOAD:nixntipstack.dll") + set(ni_grpc_device_server CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /DELAYLOAD:nicaiu.dll /DELAYLOAD:nidcpower.dll /DELAYLOAD:niDigital_64.dll /DELAYLOAD:nidmm_64.dll /DELAYLOAD:niFgen_64.dll /DELAYLOAD:NiSpyLog.dll /DELAYLOAD:niScope.dll /DELAYLOAD:niRFmxBT.dll /DELAYLOAD:niRFmxInstr.dll /DELAYLOAD:niRFmxLTE.dll /DELAYLOAD:niRFmxNR.dll /DELAYLOAD:niRFmxSpecAn.dll /DELAYLOAD:niRFmxWLAN.dll /DELAYLOAD:niRFSA_64.dll /DELAYLOAD:niRFSG_64.dll /DELAYLOAD:niswitch.dll /DELAYLOAD:nisync.dll /DELAYLOAD:niTClk_64.dll /DELAYLOAD:nixnet.dll /DELAYLOAD:nixntipstack.dll") endif() #---------------------------------------------------------------------- @@ -460,6 +460,7 @@ set(system_test_runner_sources "source/tests/system/nidmm_session_tests.cpp" "source/tests/system/nifgen_driver_api_tests.cpp" "source/tests/system/nifgen_session_tests.cpp" + "source/tests/system/io_trace_api_tests.cpp" "source/tests/system/nirfsa_driver_api_tests.cpp" "source/tests/system/nirfsg_driver_api_tests.cpp" "source/tests/system/nirfsg_session_tests.cpp" @@ -520,9 +521,9 @@ add_custom_command( $/) if(WIN32) - target_link_libraries(SystemTestsRunner Delayimp nidaqmx nidcpower niDigital nidmm niFgen niRFmxBT niRFmxInstr niRFmxLTE niRFmxNR niRFmxSpecAn niRFmxWLAN niRFSA niRFSG niScope niswitch nisync niTClk nixnet nixntipstack) - set_target_properties(SystemTestsRunner PROPERTIES LINK_FLAGS "/DELAYLOAD:nicaiu.dll /DELAYLOAD:nidcpower.dll /DELAYLOAD:niDigital_64.dll /DELAYLOAD:nidmm_64.dll /DELAYLOAD:niFgen_64.dll /DELAYLOAD:niRFmxBT.dll /DELAYLOAD:niRFmxInstr.dll /DELAYLOAD:niRFmxLTE.dll /DELAYLOAD:niRFmxNR.dll /DELAYLOAD:niRFmxSpecAn.dll /DELAYLOAD:niRFmxWLAN.dll /DELAYLOAD:niRFSA_64.dll /DELAYLOAD:niRFSG_64.dll /DELAYLOAD:niScope.dll /DELAYLOAD:niswitch.dll /DELAYLOAD:nisync.dll /DELAYLOAD:niTClk_64.dll /DELAYLOAD:nixnet.dll /DELAYLOAD:nixntipstack.dll") - set(SystemTestsRunner CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /DELAYLOAD:nicaiu.dll /DELAYLOAD:nidcpower.dll /DELAYLOAD:niDigital_64.dll /DELAYLOAD:nidmm_64.dll /DELAYLOAD:niFgen_64.dll /DELAYLOAD:niRFmxBT.dll /DELAYLOAD:niRFmxInstr.dll /DELAYLOAD:niRFmxLTE.dll /DELAYLOAD:niRFmxNR.dll /DELAYLOAD:niRFmxSpecAn.dll /DELAYLOAD:niRFmxWLAN.dll /DELAYLOAD:niRFSA_64.dll /DELAYLOAD:niRFSG_64.dll /DELAYLOAD:niScope.dll /DELAYLOAD:niswitch.dll /DELAYLOAD:nisync.dll /DELAYLOAD:niTClk_64.dll /DELAYLOAD:nixnet.dll /DELAYLOAD:nixntipstack.dll") + target_link_libraries(SystemTestsRunner Delayimp nidaqmx nidcpower niDigital nidmm niFgen NiSpyLog niRFmxBT niRFmxInstr niRFmxLTE niRFmxNR niRFmxSpecAn niRFmxWLAN niRFSA niRFSG niScope niswitch nisync niTClk nixnet nixntipstack) + set_target_properties(SystemTestsRunner PROPERTIES LINK_FLAGS "/DELAYLOAD:nicaiu.dll /DELAYLOAD:nidcpower.dll /DELAYLOAD:niDigital_64.dll /DELAYLOAD:nidmm_64.dll /DELAYLOAD:niFgen_64.dll /DELAYLOAD:NiSpyLog.dll /DELAYLOAD:niRFmxBT.dll /DELAYLOAD:niRFmxInstr.dll /DELAYLOAD:niRFmxLTE.dll /DELAYLOAD:niRFmxNR.dll /DELAYLOAD:niRFmxSpecAn.dll /DELAYLOAD:niRFmxWLAN.dll /DELAYLOAD:niRFSA_64.dll /DELAYLOAD:niRFSG_64.dll /DELAYLOAD:niScope.dll /DELAYLOAD:niswitch.dll /DELAYLOAD:nisync.dll /DELAYLOAD:niTClk_64.dll /DELAYLOAD:nixnet.dll /DELAYLOAD:nixntipstack.dll") + set(SystemTestsRunner CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /DELAYLOAD:nicaiu.dll /DELAYLOAD:nidcpower.dll /DELAYLOAD:niDigital_64.dll /DELAYLOAD:nidmm_64.dll /DELAYLOAD:niFgen_64.dll /DELAYLOAD:NiSpyLog.dll /DELAYLOAD:niRFmxBT.dll /DELAYLOAD:niRFmxInstr.dll /DELAYLOAD:niRFmxLTE.dll /DELAYLOAD:niRFmxNR.dll /DELAYLOAD:niRFmxSpecAn.dll /DELAYLOAD:niRFmxWLAN.dll /DELAYLOAD:niRFSA_64.dll /DELAYLOAD:niRFSG_64.dll /DELAYLOAD:niScope.dll /DELAYLOAD:niswitch.dll /DELAYLOAD:nisync.dll /DELAYLOAD:niTClk_64.dll /DELAYLOAD:nixnet.dll /DELAYLOAD:nixntipstack.dll") endif() # Hook up different google test runners to CTest diff --git a/examples/niscope/graph-measurement.py b/examples/niscope/graph-measurement.py index 78280c2b9..e12d54b2c 100644 --- a/examples/niscope/graph-measurement.py +++ b/examples/niscope/graph-measurement.py @@ -37,6 +37,8 @@ import matplotlib.pyplot as plt import niscope_pb2 as niscope_types import niscope_pb2_grpc as grpc_scope +import iotrace_pb2 as iotrace_types +import iotrace_pb2_grpc as grpc_iotrace SERVER_ADDRESS = "localhost" SERVER_PORT = "31763" @@ -67,13 +69,50 @@ def check_for_error(vi, status): raise Exception(error_message_response.error_message) +def simple_check_for_error(status, message): + """Raise an exception if the status indicates an error.""" + if status < 0: + raise Exception(f"Status check failed {status}: {message}") + + +def start_tracing(): + """Launch IO Trace and start tracing""" + print("Start tracing...") + open_response = iotrace_client.OpenIOTrace(iotrace_types.OpenIOTraceRequest()) + simple_check_for_error(open_response.status, "Launching IO Trace failed.") + time.sleep(4) + start_tracing_response = iotrace_client.StartTracing( + iotrace_types.StartTracingRequest( + log_file_setting=iotrace_types.LogFileSetting.LOG_FILE_SETTING_Spy, + file_path_string="C:\\dev\\traces\\graph-measurement.nitrace", + file_write_mode=iotrace_types.FileWriteMode.FILE_WRITE_MODE_CreateOrOverwrite, + ) + ) + simple_check_for_error(start_tracing_response.status, "Start tracing failed.") + + +def stop_tracing(): + """Stop tracing and close IO Trace""" + input("\nHit enter to stop tracing and close IO Trace...\n") + stop_tracing_response = iotrace_client.StopTracing(iotrace_types.StopTracingRequest()) + simple_check_for_error(stop_tracing_response.status, "Stop tracing failed.") + time.sleep(0.5) + close_io_trace_response = iotrace_client.CloseIOTrace(iotrace_types.CloseIOTraceRequest()) + simple_check_for_error(close_io_trace_response.status, "Close IO Trace failed.") + + # Create the communication channel for the remote host (in this case we are connecting to a local # server) and create a connection to the NI-SCOPE service channel = grpc.insecure_channel(f"{SERVER_ADDRESS}:{SERVER_PORT}") niscope_client = grpc_scope.NiScopeStub(channel) +iotrace_client = grpc_iotrace.IOTraceStub(channel) try: # Initialize the scope + start_tracing() + + input("\nHit enter to start measurement.\n") + init_result = niscope_client.InitWithOptions( niscope_types.InitWithOptionsRequest( session_name="demo", resource_name=RESOURCE, id_query=False, option_string=OPTIONS @@ -143,46 +182,47 @@ def check_for_error(vi, status): # Setup a plot to draw the captured waveform fig = plt.gcf() + plot_open = True fig.show() + + def on_close(event): + global plot_open + plot_open = False + + fig.canvas.mpl_connect("close_event", on_close) fig.canvas.draw() - print("\nReading values in loop. CTRL+C to stop.\n") - try: - while True: - # Clear the plot and setup the axis - plt.clf() - plt.axis([0, 100, -6, 6]) - # Read a waveform from the scope - read_result = niscope_client.Read( - niscope_types.ReadRequest( - vi=vi, channel_list=CHANNELS, timeout=1, num_samples=10000 - ) - ) - check_for_error(vi, read_result.status) - values = read_result.waveform[0:10] - print(values) - - # Update the plot with the new waveform - plt.plot(read_result.waveform[0:100]) - fig.canvas.draw() - plt.pause(0.001) - - # Fetch the measured average frequency - fetch_result = niscope_client.FetchMeasurementStats( - niscope_types.FetchMeasurementStatsRequest( - vi=vi, - channel_list=CHANNELS, - timeout=1, - scalar_meas_function=niscope_types.ScalarMeasurement.SCALAR_MEASUREMENT_NISCOPE_VAL_AVERAGE_FREQUENCY, - ) + while plot_open: + # Clear the plot and setup the axis + plt.clf() + plt.axis([0, 100, -6, 6]) + # Read a waveform from the scope + read_result = niscope_client.Read( + niscope_types.ReadRequest(vi=vi, channel_list=CHANNELS, timeout=1, num_samples=10000) + ) + check_for_error(vi, read_result.status) + values = read_result.waveform[0:10] + print(values) + + # Update the plot with the new waveform + plt.plot(read_result.waveform[0:100], color="green") + fig.canvas.draw() + plt.pause(0.001) + + # Fetch the measured average frequency + fetch_result = niscope_client.FetchMeasurementStats( + niscope_types.FetchMeasurementStatsRequest( + vi=vi, + channel_list=CHANNELS, + timeout=1, + scalar_meas_function=niscope_types.ScalarMeasurement.SCALAR_MEASUREMENT_NISCOPE_VAL_AVERAGE_FREQUENCY, ) - check_for_error(vi, fetch_result.status) - print("Average Frequency: " + str("%.2f" % round(fetch_result.result[0], 2)) + " Hz") - print("") + ) + check_for_error(vi, fetch_result.status) + print("Average Frequency: " + str("%.2f" % round(fetch_result.result[0], 2)) + " Hz") + print("") - time.sleep(0.1) - except KeyboardInterrupt: - pass + time.sleep(0.1) except grpc.RpcError as rpc_error: error_message = rpc_error.details() @@ -198,3 +238,4 @@ def check_for_error(vi, status): if "vi" in vars() and vi.id != 0: # close the session. check_for_error(vi, (niscope_client.Close(niscope_types.CloseRequest(vi=vi))).status) + stop_tracing() diff --git a/generated/iotrace/iotrace.proto b/generated/iotrace/iotrace.proto new file mode 100644 index 000000000..1174df97f --- /dev/null +++ b/generated/iotrace/iotrace.proto @@ -0,0 +1,96 @@ + +//--------------------------------------------------------------------- +// This file is automatically generated. All manual edits will be lost. +//--------------------------------------------------------------------- +// Proto file for the IO-TRACE Metadata +//--------------------------------------------------------------------- +syntax = "proto3"; + +option java_multiple_files = true; +option java_package = "com.ni.grpc.iotrace"; +option java_outer_classname = "IOTrace"; +option csharp_namespace = "NationalInstruments.Grpc.IOTrace"; + +package iotrace_grpc; + +import "session.proto"; + +service IOTrace { + rpc CloseIOTrace(CloseIOTraceRequest) returns (CloseIOTraceResponse); + rpc LogMessage(LogMessageRequest) returns (LogMessageResponse); + rpc OpenIOTrace(OpenIOTraceRequest) returns (OpenIOTraceResponse); + rpc StartTracing(StartTracingRequest) returns (StartTracingResponse); + rpc StopTracing(StopTracingRequest) returns (StopTracingResponse); +} + +enum IOTraceAttribute { + IOTRACE_ATTRIBUTE_UNSPECIFIED = 0; +} + +enum FileWriteMode { + FILE_WRITE_MODE_CreateOnly = 0; + FILE_WRITE_MODE_CreateOrAppend = 1; + FILE_WRITE_MODE_CreateOrOverwrite = 2; +} + +enum LogFileSetting { + LOG_FILE_SETTING_Spy = 0; + LOG_FILE_SETTING_NoFile = -1; + LOG_FILE_SETTING_PlainText = 1; + LOG_FILE_SETTING_CommaSeparated = 2; + LOG_FILE_SETTING_SettingXml = 3; +} + +enum CommandStatus { + COMMAND_STATUS_Success = 0; + COMMAND_STATUS_NoExecute = -303200; + COMMAND_STATUS_IncompatibleState = -303201; + COMMAND_STATUS_UnableToOpenLogFile = -303202; + COMMAND_STATUS_IOTraceClosed = -303203; + COMMAND_STATUS_InvalidSettings = -303204; + COMMAND_STATUS_BadParameter = -303205; + COMMAND_STATUS_InternalFailure = -303206; + COMMAND_STATUS_InvalidFileExtension = -303207; + COMMAND_STATUS_BufferTooSmall = -303208; + COMMAND_STATUS_FileAlreadyExists = -303209; +} + +message CloseIOTraceRequest { +} + +message CloseIOTraceResponse { + int32 status = 1; +} + +message LogMessageRequest { + string message = 1; +} + +message LogMessageResponse { + int32 status = 1; +} + +message OpenIOTraceRequest { +} + +message OpenIOTraceResponse { + int32 status = 1; +} + +message StartTracingRequest { + LogFileSetting log_file_setting = 1; + string file_path_string = 2; + FileWriteMode file_write_mode = 3; +} + +message StartTracingResponse { + int32 status = 1; +} + +message StopTracingRequest { +} + +message StopTracingResponse { + int32 status = 1; +} + diff --git a/generated/iotrace/iotrace_client.cpp b/generated/iotrace/iotrace_client.cpp new file mode 100644 index 000000000..45f2525f3 --- /dev/null +++ b/generated/iotrace/iotrace_client.cpp @@ -0,0 +1,100 @@ + +//--------------------------------------------------------------------- +// This file is automatically generated. All manual edits will be lost. +//--------------------------------------------------------------------- +// EXPERIMENTAL Client convenience wrapper for IO-TRACE. +//--------------------------------------------------------------------- +#include "iotrace_client.h" + +#include + +#include + +#include +#include +#include +#include + +namespace iotrace_grpc::experimental::client { + +CloseIOTraceResponse +close_io_trace(const StubPtr& stub) +{ + ::grpc::ClientContext context; + + auto request = CloseIOTraceRequest{}; + + auto response = CloseIOTraceResponse{}; + + raise_if_error( + stub->CloseIOTrace(&context, request, &response)); + + return response; +} + +LogMessageResponse +log_message(const StubPtr& stub, const pb::string& message) +{ + ::grpc::ClientContext context; + + auto request = LogMessageRequest{}; + request.set_message(message); + + auto response = LogMessageResponse{}; + + raise_if_error( + stub->LogMessage(&context, request, &response)); + + return response; +} + +OpenIOTraceResponse +open_io_trace(const StubPtr& stub) +{ + ::grpc::ClientContext context; + + auto request = OpenIOTraceRequest{}; + + auto response = OpenIOTraceResponse{}; + + raise_if_error( + stub->OpenIOTrace(&context, request, &response)); + + return response; +} + +StartTracingResponse +start_tracing(const StubPtr& stub, const LogFileSetting& log_file_setting, const pb::string& file_path_string, const FileWriteMode& file_write_mode) +{ + ::grpc::ClientContext context; + + auto request = StartTracingRequest{}; + request.set_log_file_setting(log_file_setting); + request.set_file_path_string(file_path_string); + request.set_file_write_mode(file_write_mode); + + auto response = StartTracingResponse{}; + + raise_if_error( + stub->StartTracing(&context, request, &response)); + + return response; +} + +StopTracingResponse +stop_tracing(const StubPtr& stub) +{ + ::grpc::ClientContext context; + + auto request = StopTracingRequest{}; + + auto response = StopTracingResponse{}; + + raise_if_error( + stub->StopTracing(&context, request, &response)); + + return response; +} + + +} // namespace iotrace_grpc::experimental::client diff --git a/generated/iotrace/iotrace_client.h b/generated/iotrace/iotrace_client.h new file mode 100644 index 000000000..f54d711d5 --- /dev/null +++ b/generated/iotrace/iotrace_client.h @@ -0,0 +1,33 @@ + +//--------------------------------------------------------------------- +// This file is automatically generated. All manual edits will be lost. +//--------------------------------------------------------------------- +// EXPERIMENTAL Client convenience wrapper for IO-TRACE. +//--------------------------------------------------------------------- +#ifndef IOTRACE_GRPC_CLIENT_H +#define IOTRACE_GRPC_CLIENT_H + +#include + +#include +#include + +#include +#include + +namespace iotrace_grpc::experimental::client { + +namespace pb = ::google::protobuf; +using StubPtr = std::unique_ptr; +using namespace nidevice_grpc::experimental::client; + + +CloseIOTraceResponse close_io_trace(const StubPtr& stub); +LogMessageResponse log_message(const StubPtr& stub, const pb::string& message); +OpenIOTraceResponse open_io_trace(const StubPtr& stub); +StartTracingResponse start_tracing(const StubPtr& stub, const LogFileSetting& log_file_setting, const pb::string& file_path_string, const FileWriteMode& file_write_mode); +StopTracingResponse stop_tracing(const StubPtr& stub); + +} // namespace iotrace_grpc::experimental::client + +#endif /* IOTRACE_GRPC_CLIENT_H */ diff --git a/generated/iotrace/iotrace_library.cpp b/generated/iotrace/iotrace_library.cpp new file mode 100644 index 000000000..db3805422 --- /dev/null +++ b/generated/iotrace/iotrace_library.cpp @@ -0,0 +1,98 @@ +//--------------------------------------------------------------------- +// This file is automatically generated. All manual edits will be lost. +//--------------------------------------------------------------------- +// Service implementation for the IO-TRACE Metadata +//--------------------------------------------------------------------- +#include "iotrace_library.h" + +#if defined(_MSC_VER) +static const char* kLibraryName = "NiSpyLog.dll"; +#else +static const char* kLibraryName = "liblibNiSpyLog.so"; +#endif + +namespace iotrace_grpc { + +IOTraceLibrary::IOTraceLibrary() : shared_library_(kLibraryName) +{ + shared_library_.load(); + bool loaded = shared_library_.is_loaded(); + memset(&function_pointers_, 0, sizeof(function_pointers_)); + if (!loaded) { + return; + } + function_pointers_.CloseIOTrace = reinterpret_cast(shared_library_.get_function_pointer("nispy_CloseSpy")); + function_pointers_.GetIOTracePath = reinterpret_cast(shared_library_.get_function_pointer("nispy_GetApplicationPath")); + function_pointers_.LogMessage = reinterpret_cast(shared_library_.get_function_pointer("nispy_WriteTextEntry")); + function_pointers_.StartTracing = reinterpret_cast(shared_library_.get_function_pointer("nispy_StartSpying")); + function_pointers_.StopTracing = reinterpret_cast(shared_library_.get_function_pointer("nispy_StopSpying")); +} + +IOTraceLibrary::~IOTraceLibrary() +{ +} + +::grpc::Status IOTraceLibrary::check_function_exists(std::string functionName) +{ + return shared_library_.function_exists(functionName.c_str()) + ? ::grpc::Status::OK + : ::grpc::Status(::grpc::NOT_FOUND, "Could not find the function " + functionName); +} + +eNiSpyAPICommandStatus IOTraceLibrary::CloseIOTrace() +{ + if (!function_pointers_.CloseIOTrace) { + throw nidevice_grpc::LibraryLoadException("Could not find nispy_CloseSpy."); + } +#if defined(_MSC_VER) + return nispy_CloseSpy(); +#else + return function_pointers_.CloseIOTrace(); +#endif +} + +eNiSpyAPICommandStatus IOTraceLibrary::GetIOTracePath(char pathString[256], int32_t pathStringSize) +{ + if (!function_pointers_.GetIOTracePath) { + throw nidevice_grpc::LibraryLoadException("Could not find nispy_GetApplicationPath."); + } + return function_pointers_.GetIOTracePath(pathString, pathStringSize); +} + +eNiSpyAPICommandStatus IOTraceLibrary::LogMessage(const char message[]) +{ + if (!function_pointers_.LogMessage) { + throw nidevice_grpc::LibraryLoadException("Could not find nispy_WriteTextEntry."); + } +#if defined(_MSC_VER) + return nispy_WriteTextEntry(message); +#else + return function_pointers_.LogMessage(message); +#endif +} + +eNiSpyAPICommandStatus IOTraceLibrary::StartTracing(eNiSpyLogFileSetting logFileSetting, const char filePathString[], eNiSpyAPIFileWriteMode fileWriteMode) +{ + if (!function_pointers_.StartTracing) { + throw nidevice_grpc::LibraryLoadException("Could not find nispy_StartSpying."); + } +#if defined(_MSC_VER) + return nispy_StartSpying(logFileSetting, filePathString, fileWriteMode); +#else + return function_pointers_.StartTracing(logFileSetting, filePathString, fileWriteMode); +#endif +} + +eNiSpyAPICommandStatus IOTraceLibrary::StopTracing() +{ + if (!function_pointers_.StopTracing) { + throw nidevice_grpc::LibraryLoadException("Could not find nispy_StopSpying."); + } +#if defined(_MSC_VER) + return nispy_StopSpying(); +#else + return function_pointers_.StopTracing(); +#endif +} + +} // namespace iotrace_grpc diff --git a/generated/iotrace/iotrace_library.h b/generated/iotrace/iotrace_library.h new file mode 100644 index 000000000..26d0085da --- /dev/null +++ b/generated/iotrace/iotrace_library.h @@ -0,0 +1,48 @@ +//--------------------------------------------------------------------- +// This file is automatically generated. All manual edits will be lost. +//--------------------------------------------------------------------- +// Real implementation of LibraryInterface for IO-TRACE +//--------------------------------------------------------------------- +#ifndef IOTRACE_GRPC_LIBRARY_H +#define IOTRACE_GRPC_LIBRARY_H + +#include "iotrace_library_interface.h" + +#include + +namespace iotrace_grpc { + +class IOTraceLibrary : public iotrace_grpc::IOTraceLibraryInterface { + public: + IOTraceLibrary(); + virtual ~IOTraceLibrary(); + + ::grpc::Status check_function_exists(std::string functionName); + eNiSpyAPICommandStatus CloseIOTrace(); + eNiSpyAPICommandStatus GetIOTracePath(char pathString[256], int32_t pathStringSize); + eNiSpyAPICommandStatus LogMessage(const char message[]); + eNiSpyAPICommandStatus StartTracing(eNiSpyLogFileSetting logFileSetting, const char filePathString[], eNiSpyAPIFileWriteMode fileWriteMode); + eNiSpyAPICommandStatus StopTracing(); + + private: + using CloseIOTracePtr = decltype(&nispy_CloseSpy); + using GetIOTracePathPtr = eNiSpyAPICommandStatus (*)(char pathString[256], int32_t pathStringSize); + using LogMessagePtr = decltype(&nispy_WriteTextEntry); + using StartTracingPtr = decltype(&nispy_StartSpying); + using StopTracingPtr = decltype(&nispy_StopSpying); + + typedef struct FunctionPointers { + CloseIOTracePtr CloseIOTrace; + GetIOTracePathPtr GetIOTracePath; + LogMessagePtr LogMessage; + StartTracingPtr StartTracing; + StopTracingPtr StopTracing; + } FunctionLoadStatus; + + nidevice_grpc::SharedLibrary shared_library_; + FunctionPointers function_pointers_; +}; + +} // namespace iotrace_grpc + +#endif // IOTRACE_GRPC_LIBRARY_H diff --git a/generated/iotrace/iotrace_library_interface.h b/generated/iotrace/iotrace_library_interface.h new file mode 100644 index 000000000..80e8cc278 --- /dev/null +++ b/generated/iotrace/iotrace_library_interface.h @@ -0,0 +1,26 @@ +//--------------------------------------------------------------------- +// This file is automatically generated. All manual edits will be lost. +//--------------------------------------------------------------------- +// Library wrapper for implementing interactions with IO-TRACE +//--------------------------------------------------------------------- +#ifndef IOTRACE_GRPC_LIBRARY_WRAPPER_H +#define IOTRACE_GRPC_LIBRARY_WRAPPER_H + +#include +#include + +namespace iotrace_grpc { + +class IOTraceLibraryInterface { + public: + virtual ~IOTraceLibraryInterface() {} + + virtual eNiSpyAPICommandStatus CloseIOTrace() = 0; + virtual eNiSpyAPICommandStatus GetIOTracePath(char pathString[256], int32_t pathStringSize) = 0; + virtual eNiSpyAPICommandStatus LogMessage(const char message[]) = 0; + virtual eNiSpyAPICommandStatus StartTracing(eNiSpyLogFileSetting logFileSetting, const char filePathString[], eNiSpyAPIFileWriteMode fileWriteMode) = 0; + virtual eNiSpyAPICommandStatus StopTracing() = 0; +}; + +} // namespace iotrace_grpc +#endif // IOTRACE_GRPC_LIBRARY_WRAPPER_H diff --git a/generated/iotrace/iotrace_mock_library.h b/generated/iotrace/iotrace_mock_library.h new file mode 100644 index 000000000..cbfd13dc3 --- /dev/null +++ b/generated/iotrace/iotrace_mock_library.h @@ -0,0 +1,30 @@ +//--------------------------------------------------------------------- +// This file is automatically generated. All manual edits will be lost. +//--------------------------------------------------------------------- +// Mock of LibraryInterface for IO-TRACE +//--------------------------------------------------------------------- +#ifndef IOTRACE_GRPC_MOCK_LIBRARY_H +#define IOTRACE_GRPC_MOCK_LIBRARY_H + +#include +#include + +#include "iotrace_library_interface.h" + +namespace ni { +namespace tests { +namespace unit { + +class IOTraceMockLibrary : public iotrace_grpc::IOTraceLibraryInterface { + public: + MOCK_METHOD(eNiSpyAPICommandStatus, CloseIOTrace, (), (override)); + MOCK_METHOD(eNiSpyAPICommandStatus, GetIOTracePath, (char pathString[256], int32_t pathStringSize), (override)); + MOCK_METHOD(eNiSpyAPICommandStatus, LogMessage, (const char message[]), (override)); + MOCK_METHOD(eNiSpyAPICommandStatus, StartTracing, (eNiSpyLogFileSetting logFileSetting, const char filePathString[], eNiSpyAPIFileWriteMode fileWriteMode), (override)); + MOCK_METHOD(eNiSpyAPICommandStatus, StopTracing, (), (override)); +}; + +} // namespace unit +} // namespace tests +} // namespace ni +#endif // IOTRACE_GRPC_MOCK_LIBRARY_H diff --git a/generated/iotrace/iotrace_service.cpp b/generated/iotrace/iotrace_service.cpp new file mode 100644 index 000000000..4ec341f98 --- /dev/null +++ b/generated/iotrace/iotrace_service.cpp @@ -0,0 +1,124 @@ + +//--------------------------------------------------------------------- +// This file is automatically generated. All manual edits will be lost. +//--------------------------------------------------------------------- +// Service implementation for the IO-TRACE Metadata +//--------------------------------------------------------------------- +#include "iotrace_service.h" + +#include +#include +#include +#include +#include +#include + +namespace iotrace_grpc { + + using nidevice_grpc::converters::allocate_output_storage; + using nidevice_grpc::converters::calculate_linked_array_size; + using nidevice_grpc::converters::convert_from_grpc; + using nidevice_grpc::converters::convert_to_grpc; + using nidevice_grpc::converters::MatchState; + + IOTraceService::IOTraceService( + IOTraceLibraryInterface* library, + ResourceRepositorySharedPtr resource_repository, + const IOTraceFeatureToggles& feature_toggles) + : library_(library), + session_repository_(resource_repository), + feature_toggles_(feature_toggles) + { + } + + IOTraceService::~IOTraceService() + { + } + + // Returns true if it's safe to use outputs of a method with the given status. + inline bool status_ok(int32 status) + { + return status >= 0; + } + + //--------------------------------------------------------------------- + //--------------------------------------------------------------------- + ::grpc::Status IOTraceService::CloseIOTrace(::grpc::ServerContext* context, const CloseIOTraceRequest* request, CloseIOTraceResponse* response) + { + if (context->IsCancelled()) { + return ::grpc::Status::CANCELLED; + } + try { + auto status = library_->CloseIOTrace(); + response->set_status(status); + return ::grpc::Status::OK; + } + catch (nidevice_grpc::LibraryLoadException& ex) { + return ::grpc::Status(::grpc::NOT_FOUND, ex.what()); + } + } + + //--------------------------------------------------------------------- + //--------------------------------------------------------------------- + ::grpc::Status IOTraceService::LogMessage(::grpc::ServerContext* context, const LogMessageRequest* request, LogMessageResponse* response) + { + if (context->IsCancelled()) { + return ::grpc::Status::CANCELLED; + } + try { + auto message = request->message().c_str(); + auto status = library_->LogMessage(message); + response->set_status(status); + return ::grpc::Status::OK; + } + catch (nidevice_grpc::LibraryLoadException& ex) { + return ::grpc::Status(::grpc::NOT_FOUND, ex.what()); + } + } + + //--------------------------------------------------------------------- + //--------------------------------------------------------------------- + ::grpc::Status IOTraceService::StartTracing(::grpc::ServerContext* context, const StartTracingRequest* request, StartTracingResponse* response) + { + if (context->IsCancelled()) { + return ::grpc::Status::CANCELLED; + } + try { + auto log_file_setting = convert_from_grpc(request->log_file_setting()); + auto file_path_string = request->file_path_string().c_str(); + auto file_write_mode = convert_from_grpc(request->file_write_mode()); + auto status = library_->StartTracing(log_file_setting, file_path_string, file_write_mode); + response->set_status(status); + return ::grpc::Status::OK; + } + catch (nidevice_grpc::LibraryLoadException& ex) { + return ::grpc::Status(::grpc::NOT_FOUND, ex.what()); + } + } + + //--------------------------------------------------------------------- + //--------------------------------------------------------------------- + ::grpc::Status IOTraceService::StopTracing(::grpc::ServerContext* context, const StopTracingRequest* request, StopTracingResponse* response) + { + if (context->IsCancelled()) { + return ::grpc::Status::CANCELLED; + } + try { + auto status = library_->StopTracing(); + response->set_status(status); + return ::grpc::Status::OK; + } + catch (nidevice_grpc::LibraryLoadException& ex) { + return ::grpc::Status(::grpc::NOT_FOUND, ex.what()); + } + } + + + IOTraceFeatureToggles::IOTraceFeatureToggles( + const nidevice_grpc::FeatureToggles& feature_toggles) + : is_enabled( + feature_toggles.is_feature_enabled("iotrace", CodeReadiness::kNextRelease)) + { + } +} // namespace iotrace_grpc + diff --git a/generated/iotrace/iotrace_service.h b/generated/iotrace/iotrace_service.h new file mode 100644 index 000000000..ce2658ae8 --- /dev/null +++ b/generated/iotrace/iotrace_service.h @@ -0,0 +1,58 @@ + +//--------------------------------------------------------------------- +// This file is automatically generated. All manual edits will be lost. +//--------------------------------------------------------------------- +// Service header for the IO-TRACE Metadata +//--------------------------------------------------------------------- +#ifndef IOTRACE_GRPC_SERVICE_H +#define IOTRACE_GRPC_SERVICE_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "iotrace_library_interface.h" + +namespace iotrace_grpc { + +struct IOTraceFeatureToggles +{ + using CodeReadiness = nidevice_grpc::FeatureToggles::CodeReadiness; + IOTraceFeatureToggles(const nidevice_grpc::FeatureToggles& feature_toggles = {}); + + bool is_enabled; +}; + +class IOTraceService final : public IOTrace::Service { +public: + using ResourceRepositorySharedPtr = std::shared_ptr>; + + IOTraceService( + IOTraceLibraryInterface* library, + ResourceRepositorySharedPtr resource_repository, + const IOTraceFeatureToggles& feature_toggles = {}); + virtual ~IOTraceService(); + + ::grpc::Status CloseIOTrace(::grpc::ServerContext* context, const CloseIOTraceRequest* request, CloseIOTraceResponse* response) override; + ::grpc::Status LogMessage(::grpc::ServerContext* context, const LogMessageRequest* request, LogMessageResponse* response) override; + ::grpc::Status OpenIOTrace(::grpc::ServerContext* context, const OpenIOTraceRequest* request, OpenIOTraceResponse* response) override; + ::grpc::Status StartTracing(::grpc::ServerContext* context, const StartTracingRequest* request, StartTracingResponse* response) override; + ::grpc::Status StopTracing(::grpc::ServerContext* context, const StopTracingRequest* request, StopTracingResponse* response) override; +private: + IOTraceLibraryInterface* library_; + ResourceRepositorySharedPtr session_repository_; + + IOTraceFeatureToggles feature_toggles_; +}; + +} // namespace iotrace_grpc + +#endif // IOTRACE_GRPC_SERVICE_H diff --git a/generated/iotrace/iotrace_service_registrar.cpp b/generated/iotrace/iotrace_service_registrar.cpp new file mode 100644 index 000000000..88df32e5a --- /dev/null +++ b/generated/iotrace/iotrace_service_registrar.cpp @@ -0,0 +1,52 @@ + +//--------------------------------------------------------------------- +// This file is automatically generated. All manual edits will be lost. +//--------------------------------------------------------------------- +// Service registrar implementation for the IO-TRACE Metadata +//--------------------------------------------------------------------- +#include "iotrace_library.h" + +#include + +#include "iotrace_service.h" +#include "iotrace_service_registrar.h" + +namespace iotrace_grpc { + +namespace { +struct LibraryAndService { + LibraryAndService( + const std::shared_ptr>& resource_repository, + const IOTraceFeatureToggles& feature_toggles) + : library(), + service( + &library, + resource_repository, + feature_toggles) { + } + IOTraceLibrary library; + IOTraceService service; +}; +} + +std::shared_ptr register_service( + grpc::ServerBuilder& builder, + const std::shared_ptr>& resource_repository, + const nidevice_grpc::FeatureToggles& feature_toggles) +{ + auto toggles = IOTraceFeatureToggles(feature_toggles); + + if (toggles.is_enabled) + { + auto library_and_service_ptr = std::make_shared( + resource_repository, + toggles); + auto& service = library_and_service_ptr->service; + builder.RegisterService(&service); + return library_and_service_ptr; + } + + return {}; +} + +} // iotrace_grpc diff --git a/generated/iotrace/iotrace_service_registrar.h b/generated/iotrace/iotrace_service_registrar.h new file mode 100644 index 000000000..dd60c407a --- /dev/null +++ b/generated/iotrace/iotrace_service_registrar.h @@ -0,0 +1,30 @@ + +//--------------------------------------------------------------------- +// This file is automatically generated. All manual edits will be lost. +//--------------------------------------------------------------------- +// Service registrar header for the IO-TRACE Metadata +//--------------------------------------------------------------------- +#ifndef IOTRACE_GRPC_SERVICE_REGISTRAR_H +#define IOTRACE_GRPC_SERVICE_REGISTRAR_H +#include +#include + +#include + +#include // for int32_t + +namespace grpc { +class ServerBuilder; +} + +namespace iotrace_grpc { +using CodeReadiness = nidevice_grpc::FeatureToggles::CodeReadiness; + +std::shared_ptr register_service( + grpc::ServerBuilder& server_builder, + const std::shared_ptr>& resource_repository, + const nidevice_grpc::FeatureToggles& feature_toggles); + +} // iotrace_grpc + +#endif // IOTRACE_GRPC_SERVICE_REGISTRAR_H diff --git a/generated/register_all_services.cpp b/generated/register_all_services.cpp index b6f61bc52..5753719ba 100644 --- a/generated/register_all_services.cpp +++ b/generated/register_all_services.cpp @@ -11,6 +11,7 @@ #include #include +#include "iotrace/iotrace_service_registrar.h" #include "nidaqmx/nidaqmx_service_registrar.h" #include "nidcpower/nidcpower_service_registrar.h" #include "nidigitalpattern/nidigitalpattern_service_registrar.h" @@ -57,6 +58,7 @@ std::shared_ptr register_all_services( service_vector->end(), {session_repository, core_service}); + auto int32_t_repository = std::make_shared>(session_repository.get()); auto task_handle_repository = std::make_shared>(session_repository.get()); auto vi_session_repository = std::make_shared>(session_repository.get()); #if defined(_MSC_VER) @@ -67,6 +69,11 @@ std::shared_ptr register_all_services( auto nx_socket_repository = std::make_shared>(session_repository.get()); auto nx_ip_stack_ref_t_repository = std::make_shared>(session_repository.get()); + service_vector->push_back( + iotrace_grpc::register_service( + server_builder, + int32_t_repository, + feature_toggles)); service_vector->push_back( nidaqmx_grpc::register_service( server_builder, diff --git a/imports/include/NiSpyAPI.h b/imports/include/NiSpyAPI.h new file mode 100644 index 000000000..e3081a8e7 --- /dev/null +++ b/imports/include/NiSpyAPI.h @@ -0,0 +1,118 @@ +/* NiSpyAPI.h */ +/* */ +/* This file is the main include file for using the NI Spy Programmatic API. */ +/* */ +/* Contained within this header are the functions to be called by a client */ +/* application as well as enumerations detailing parameters and error return */ +/* values. */ + +#if !defined(_NI_SPY_API_H_) +#define _NI_SPY_API_H_ + +#include + +#if defined(WIN32) || defined(_WIN32) || defined(_WIN64) + #define NISPY_API_APITYPE __stdcall + #if defined(NISPY_EXPORT_CWRAPPER) + #define NISPY_API_CWRAPPER __declspec(dllexport) + #else + #define NISPY_API_CWRAPPER __declspec(dllimport) + #endif + #define NISPY_API_CWRAPPER_END +#else + #define NISPY_API_APITYPE + #define NISPY_API_CWRAPPER + #if defined(__MACH__) + #define NISPY_API_CWRAPPER_END __attribute__((weak_import)) + #else + #define NISPY_API_CWRAPPER_END + #endif +#endif + +/* This enum corresponds to the logFileSetting specified in nispy_StartSpying */ +enum eNiSpyLogFileSetting +{ + klogFileSettingNoFile = -1, + kLogFileSettingSpy, + kLogFileSettingPlainText, + kLogFileSettingCommaSeparated, + kLogFileSettingXml +}; + +#if !defined(__cplusplus) && !defined(__cplusplus__) +typedef enum eNiSpyLogFileSetting eNiSpyLogFileSetting; +#endif + +/* This enum represents the error codes returned by the Spy API calls */ +enum eNiSpyAPICommandStatus +{ + kCommandSuccess = 0, + kCommandFailedNoExecute = -303200, + kCommandFailedIncompatibleState = -303201, + kCommandFailedUnableToOpenLogFile = -303202, + kCommandFailedSpyGUIClosed = -303203, + kCommandFailedInvalidSettings = -303204, + kCommandFailedBadParameter = -303205, + kCommandFailedInternalFailure = -303206, + kCommandFailedInvalidFileExtension = -303207, + kCommandFailedBufferTooSmall = -303208, + kCommandFailedFileAlreadyExists = -303209 +}; + +#if !defined(__cplusplus) && !defined(__cplusplus__) +typedef enum eNiSpyAPICommandStatus eNiSpyAPICommandStatus; +#endif + +/* This enum represents the valid write modes for the nispy_StartSpying call */ +enum eNiSpyAPIFileWriteMode +{ + kCreateOnly = 0, + kCreateOrAppend, + kCreateOrOverwrite +}; + +#if !defined(__cplusplus) && !defined(__cplusplus__) +typedef enum eNiSpyAPIFileWriteMode eNiSpyAPIFileWriteMode; +#endif + +/* Functions supported by the NI Spy Programmatic API begin here */ +#if defined(__cplusplus) || defined(__cplusplus__) +extern "C" { +#endif + +/* nispy_GetApplicationPath returns the path to the application for launch. + It is the user's responsibility to actually launch the application. */ +NISPY_API_CWRAPPER eNiSpyAPICommandStatus NISPY_API_APITYPE + nispy_GetApplicationPath(char * pathString, size_t pathStringSize) +NISPY_API_CWRAPPER_END; + +/* nispy_StartSpying initiates spying on calls. This command will only succeed + if NI Spy has been launched. logFileSetting specifies how the log should be + stored to file and filePathString is a path to the desired log file. + For overwriteFile, zero is false (append) and any nonzero value is true. */ +NISPY_API_CWRAPPER eNiSpyAPICommandStatus NISPY_API_APITYPE + nispy_StartSpying(eNiSpyLogFileSetting logFileSetting, const char * filePathString, eNiSpyAPIFileWriteMode fileWriteMode ) +NISPY_API_CWRAPPER_END; + +/* nispy_StopSpying halts spying on calls. */ +NISPY_API_CWRAPPER eNiSpyAPICommandStatus NISPY_API_APITYPE + nispy_StopSpying(void) +NISPY_API_CWRAPPER_END; + +/* nispy_WriteTextEntry places a debug message into the current Spy log. + This message also gets exported to the log file if logging to a file. */ +NISPY_API_CWRAPPER eNiSpyAPICommandStatus NISPY_API_APITYPE + nispy_WriteTextEntry(const char * message) +NISPY_API_CWRAPPER_END; + +/* nispy_CloseSpy closes the NI Spy application. This implies that logging is halted + and the program will need to be relaunched if more calls need to be logged. */ +NISPY_API_CWRAPPER eNiSpyAPICommandStatus NISPY_API_APITYPE + nispy_CloseSpy(void) +NISPY_API_CWRAPPER_END; + +#if defined(__cplusplus) || defined(__cplusplus__) +} /* extern "C" closing brace */ +#endif + +#endif diff --git a/imports/lib/win64/NiSpyLog.lib b/imports/lib/win64/NiSpyLog.lib new file mode 100644 index 000000000..b22294961 Binary files /dev/null and b/imports/lib/win64/NiSpyLog.lib differ diff --git a/source/codegen/client_helpers.py b/source/codegen/client_helpers.py index fcb41532b..8cfe2e5d5 100644 --- a/source/codegen/client_helpers.py +++ b/source/codegen/client_helpers.py @@ -124,6 +124,10 @@ def _const_ref_t(t: str) -> str: return f"const {t}&" +def _is_enum(param: dict) -> bool: + return param.get("actual_enum", False) + + def _get_param_mechanism(param: dict) -> ParamMechanism: if _is_grpc_array(param): return ParamMechanism.ARRAY @@ -133,7 +137,7 @@ def _get_param_mechanism(param: dict) -> ParamMechanism: return ParamMechanism.ENUM if "mapped-enum" in param: return ParamMechanism.MAPPED_ENUM - if _is_basic_type(param["grpc_type"]): + if _is_basic_type(param["grpc_type"]) or _is_enum(param): return ParamMechanism.BASIC return ParamMechanism.COPY diff --git a/source/codegen/common_helpers.py b/source/codegen/common_helpers.py index b371bdf68..820b07ccf 100644 --- a/source/codegen/common_helpers.py +++ b/source/codegen/common_helpers.py @@ -435,7 +435,7 @@ def pascal_to_snake(pascal_string): def filter_proto_rpc_functions(functions): """Return function metadata only for functions to include for generating proto rpc methods.""" - functions_for_proto = {"public", "CustomCode", "CustomCodeCustomProtoMessage"} + functions_for_proto = {"public", "CustomCode", "CustomCodeCustomProtoMessage", "CustomGrpcOnly"} return [ name for name, function in functions.items() @@ -445,7 +445,7 @@ def filter_proto_rpc_functions(functions): def filter_proto_rpc_functions_for_message(functions): """Return function metadata only for functions to include for generating proto rpc messages.""" - functions_for_proto = {"public", "CustomCode"} + functions_for_proto = {"public", "CustomCode", "CustomGrpcOnly"} return [ name for name, function in functions.items() diff --git a/source/codegen/metadata/iotrace/CHANGES.md b/source/codegen/metadata/iotrace/CHANGES.md new file mode 100644 index 000000000..e69de29bb diff --git a/source/codegen/metadata/iotrace/__init__.py b/source/codegen/metadata/iotrace/__init__.py new file mode 100644 index 000000000..479c7c96a --- /dev/null +++ b/source/codegen/metadata/iotrace/__init__.py @@ -0,0 +1,11 @@ +from .functions import functions +from .attributes import attributes +from .enums import enums +from .config import config + +metadata = { + "functions" : functions, + "attributes" : attributes, + "enums" : enums, + "config" : config +} \ No newline at end of file diff --git a/source/codegen/metadata/iotrace/attributes.py b/source/codegen/metadata/iotrace/attributes.py new file mode 100644 index 000000000..4395c908a --- /dev/null +++ b/source/codegen/metadata/iotrace/attributes.py @@ -0,0 +1,2 @@ +# -*- coding: utf-8 -*- +attributes = {} diff --git a/source/codegen/metadata/iotrace/attributes_addon.py b/source/codegen/metadata/iotrace/attributes_addon.py new file mode 100644 index 000000000..65af90534 --- /dev/null +++ b/source/codegen/metadata/iotrace/attributes_addon.py @@ -0,0 +1,6 @@ +# These dictionaries are applied to the generated attributes dictionary at build time +# Any changes to the API should be made here. attributes.py is code generated + +attributes_override_metadata = { +} + diff --git a/source/codegen/metadata/iotrace/config.py b/source/codegen/metadata/iotrace/config.py new file mode 100644 index 000000000..67e0ced0f --- /dev/null +++ b/source/codegen/metadata/iotrace/config.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +# This file is generated from IO Trace API metadata version 18.2 +config = { + "api_version": "18.2", + "c_header": "NiSpyAPI.h", + "c_function_prefix": "nispy_", + "service_class_prefix": "IOTrace", + "java_package": "com.ni.grpc.iotrace", + "csharp_namespace": "NationalInstruments.Grpc.IOTrace", + "namespace_component": "iotrace", + "close_function": "Doesn't matter, there isn't one", + "resource_handle_type": "int32_t", # Note: This api shouldn't have "sessions", this is just to appease the build temporarily. + "custom_types": [], + "type_to_grpc_type": { + "char[]": "string", + "int32_t": "int32", + "int": "int32", + "eNiSpyLogFileSetting": "LogFileSetting", + "eNiSpyAPIFileWriteMode": "FileWriteMode", + "eNiSpyAPICommandStatus": "CommandStatus", + }, + "driver_name": "IO-TRACE", + "status_ok": "status >= 0", + "library_info": { + "Linux": {"64bit": {"name": "libNiSpyLog", "type": "cdll"}}, + "Windows": { + "32bit": {"name": "NiSpyLog.dll", "type": "windll"}, + "64bit": {"name": "NiSpyLog.dll", "type": "cdll"}, + }, + }, + "metadata_version": "2.0", + "module_name": "iotrace", + "code_readiness": "NextRelease", +} diff --git a/source/codegen/metadata/iotrace/config_addon.py b/source/codegen/metadata/iotrace/config_addon.py new file mode 100644 index 000000000..e5928fb3f --- /dev/null +++ b/source/codegen/metadata/iotrace/config_addon.py @@ -0,0 +1,2 @@ +# We need to maintain the version here since it needs to be updated by the build process on GitHub +config_additional_config = {} diff --git a/source/codegen/metadata/iotrace/enums.py b/source/codegen/metadata/iotrace/enums.py new file mode 100644 index 000000000..e742b14c0 --- /dev/null +++ b/source/codegen/metadata/iotrace/enums.py @@ -0,0 +1,95 @@ +# -*- coding: utf-8 -*- +# This file is generated from NI-SCOPE API metadata version 20.5.0d7 +enums = { + 'FileWriteMode': { + 'force-include': True, + 'values': [ + { + 'name': 'CreateOnly', + 'value': 0 + }, + { + 'name': 'CreateOrAppend', + 'value': 1 + }, + { + 'name': 'CreateOrOverwrite', + 'value': 2 + }, + ] + }, + 'LogFileSetting': { + 'force-include': True, + 'values': [ + { + 'name': 'NoFile', + 'value': -1 + }, + { + 'name': 'Spy', + 'value': 0 + }, + { + 'name': 'PlainText', + 'value': 1 + }, + { + 'name': 'CommaSeparated', + 'value': 2 + }, + { + 'name': 'SettingXml', + 'value': 3 + }, + ] + }, + 'CommandStatus': { + 'force-include': True, + 'values': [ + { + 'name': 'Success', + 'value': 0 + }, + { + 'name': 'NoExecute', + 'value': -303200 + }, + { + 'name': 'IncompatibleState', + 'value': -303201 + }, + { + 'name': 'UnableToOpenLogFile', + 'value': -303202 + }, + { + 'name': 'IOTraceClosed', + 'value': -303203 + }, + { + 'name': 'InvalidSettings', + 'value': -303204 + }, + { + 'name': 'BadParameter', + 'value': -303205 + }, + { + 'name': 'InternalFailure', + 'value': -303206 + }, + { + 'name': 'InvalidFileExtension', + 'value': -303207 + }, + { + 'name': 'BufferTooSmall', + 'value': -303208 + }, + { + 'name': 'FileAlreadyExists', + 'value': -303209 + }, + ] + }, +} diff --git a/source/codegen/metadata/iotrace/enums_addon.py b/source/codegen/metadata/iotrace/enums_addon.py new file mode 100644 index 000000000..2ecd181b7 --- /dev/null +++ b/source/codegen/metadata/iotrace/enums_addon.py @@ -0,0 +1,6 @@ +# These dictionaries are applied to the generated enums dictionary at build time +# Any changes to the API should be made here. enums.py is code generated + +enums_override_metadata = { +} + diff --git a/source/codegen/metadata/iotrace/functions.py b/source/codegen/metadata/iotrace/functions.py new file mode 100644 index 000000000..3e5b9dba5 --- /dev/null +++ b/source/codegen/metadata/iotrace/functions.py @@ -0,0 +1,80 @@ +# -*- coding: utf-8 -*- +# This file is generated from NI-SCOPE API metadata version 20.5.0d7 +functions = { + 'CloseIOTrace': { + 'cname': 'nispy_CloseSpy', + 'parameters': [ + ], + 'returns': 'eNiSpyAPICommandStatus' + }, + 'GetIOTracePath': { + 'cname': 'nispy_GetApplicationPath', + 'codegen_method': 'private', + 'parameters': [ + { + 'direction': 'out', + 'name': 'pathString', + 'type': 'char[]', + 'size': { + 'mechanism': 'fixed', + 'value': '256' + } + }, + { + 'direction': 'in', + 'name': 'pathStringSize', + 'type': 'int32_t', + 'include_in_proto': False, + 'hardcoded_value': '256' + } + ], + 'returns': 'eNiSpyAPICommandStatus' + }, + 'LogMessage': { + 'cname': 'nispy_WriteTextEntry', + 'parameters': [ + { + 'direction': 'in', + 'name': 'message', + 'type': 'const char[]' + } + ], + 'returns': 'eNiSpyAPICommandStatus' + }, + 'OpenIOTrace': { + 'codegen_method': 'CustomGrpcOnly', + 'parameters': [], + 'returns': 'eNiSpyAPICommandStatus' + }, + 'StartTracing': { + 'cname': 'nispy_StartSpying', + 'parameters': [ + { + 'direction': 'in', + 'name': 'logFileSetting', + 'type': 'eNiSpyLogFileSetting', + 'supports_standard_copy_convert': True, + 'actual_enum': True + }, + { + 'direction': 'in', + 'name': 'filePathString', + 'type': 'const char[]' + }, + { + 'direction': 'in', + 'name': 'fileWriteMode', + 'type': 'eNiSpyAPIFileWriteMode', + 'supports_standard_copy_convert': True, + 'actual_enum': True + }, + ], + 'returns': 'eNiSpyAPICommandStatus' + }, + 'StopTracing': { + 'cname': 'nispy_StopSpying', + 'parameters': [ + ], + 'returns': 'eNiSpyAPICommandStatus' + }, +} diff --git a/source/codegen/metadata/iotrace/functions_addon.py b/source/codegen/metadata/iotrace/functions_addon.py new file mode 100644 index 000000000..cca5b4cec --- /dev/null +++ b/source/codegen/metadata/iotrace/functions_addon.py @@ -0,0 +1,5 @@ +# These dictionaries are merged with the extracted function metadata at build time. +# Changes to the metadata should be made here, because functions.py is generated thus any changes get overwritten. + +functions_override_metadata = { +} diff --git a/source/codegen/metadata_validation.py b/source/codegen/metadata_validation.py index 06bcb9dfc..94f4eba98 100644 --- a/source/codegen/metadata_validation.py +++ b/source/codegen/metadata_validation.py @@ -85,6 +85,7 @@ class RULES: Optional("additional_arguments_to_output_allocation"): [str], Optional("proto_only"): bool, Optional("input_passed_by_ptr"): bool, + Optional("actual_enum"): bool, } ) @@ -103,6 +104,7 @@ class RULES: "no", "python-only", "CustomCodeCustomProtoMessage", + "CustomGrpcOnly", ), ), Optional("init_method"): bool, diff --git a/source/codegen/service_helpers.py b/source/codegen/service_helpers.py index 054fdee7e..92917efe7 100644 --- a/source/codegen/service_helpers.py +++ b/source/codegen/service_helpers.py @@ -316,7 +316,7 @@ def filter_api_functions(functions, only_mockable_functions=True): """Filter function metadata to only include those to be generated into the API library.""" def filter_function(function): - if function.get("codegen_method", "") == "no": + if function.get("codegen_method", "") in ["no", "CustomGrpcOnly"]: return False if only_mockable_functions and not common_helpers.can_mock_function(function["parameters"]): return False diff --git a/source/custom/iotrace_service.custom.cpp b/source/custom/iotrace_service.custom.cpp new file mode 100644 index 000000000..d30d1c186 --- /dev/null +++ b/source/custom/iotrace_service.custom.cpp @@ -0,0 +1,33 @@ +#include +#include + +#include +#include +using namespace std::string_literals; + +namespace iotrace_grpc { +//--------------------------------------------------------------------- +//--------------------------------------------------------------------- +// TODO: This is hacky. Make this proper (support Linux, validate correct way to launch IO Trace, etc.) +::grpc::Status IOTraceService::OpenIOTrace(::grpc::ServerContext* context, const OpenIOTraceRequest* request, OpenIOTraceResponse* response) +{ + if (context->IsCancelled()) { + return ::grpc::Status::CANCELLED; + } + try { + auto path_string_size = 256; + std::string path_string(path_string_size - 1, '\0'); + auto status = library_->GetIOTracePath((char*)path_string.data(), path_string_size); + response->set_status(status); + if (status >= 0) { + ShellExecute(NULL, "open", path_string.c_str(), NULL, NULL, SW_SHOWDEFAULT); + } + + return ::grpc::Status::OK; + } + catch (nidevice_grpc::LibraryLoadException& ex) { + return ::grpc::Status(::grpc::NOT_FOUND, ex.what()); + } +} + +} // namespace iotrace_grpc diff --git a/source/tests/system/io_trace_api_tests.cpp b/source/tests/system/io_trace_api_tests.cpp new file mode 100644 index 000000000..b5a00f43e --- /dev/null +++ b/source/tests/system/io_trace_api_tests.cpp @@ -0,0 +1,116 @@ +#include +#include +#include + +#include +#include +#include +#include + +#include "device_server.h" +#include "enumerate_devices.h" + +using namespace iotrace_grpc; +namespace client = iotrace_grpc::experimental::client; +namespace pb = google::protobuf; +using namespace ::testing; +using nlohmann::json; +using namespace std::string_literals; // for trailing ""s string literal syntax; + +namespace ni { +namespace tests { +namespace system { +namespace { + +class IOTraceApiTests : public ::testing::Test { + protected: + IOTraceApiTests() + : device_server_(DeviceServerInterface::Singleton()), + stub_(IOTrace::NewStub(device_server_->InProcessChannel())) + { + device_server_->ResetServer(); + } + virtual ~IOTraceApiTests() {} + + void TearDown() override + { + device_server_->ResetServer(); + } + + template + std::unique_ptr create_stub() + { + return TService::NewStub(device_server_->InProcessChannel()); + } + + std::unique_ptr& stub() + { + return stub_; + } + + private: + DeviceServerInterface* device_server_; + std::unique_ptr stub_; +}; + +TEST_F(IOTraceApiTests, IOTraceIsStarted_StartTracing_Success) +{ + // Pre-requisite for test, IO Trace application is running + + std::string path = "C:\\dev\\reckenro\\Docs\\text-output.nitrace"; + auto start_tracing_response = client::start_tracing(stub(), LOG_FILE_SETTING_Spy, path, FILE_WRITE_MODE_CreateOrOverwrite); + + EXPECT_EQ(0, start_tracing_response.status()); +} + +TEST_F(IOTraceApiTests, IOTraceIsStarted_StartTracingWithInvalidArgs_InvalidArgsResponse) +{ + // Pre-requisite for test, IO Trace application is running + + std::string path = ""; // Empty string not allowed for file path. + auto start_tracing_response = client::start_tracing(stub(), LOG_FILE_SETTING_Spy, path, FILE_WRITE_MODE_CreateOrAppend); + + EXPECT_EQ(COMMAND_STATUS_InvalidSettings, start_tracing_response.status()); +} + +TEST_F(IOTraceApiTests, IOTraceIsStarted_StopTracing_Success) +{ + // Pre-requisite for test, IO Trace application is running + + auto stop_tracing_response = client::stop_tracing(stub()); + + EXPECT_EQ(0, stop_tracing_response.status()); +} + +TEST_F(IOTraceApiTests, IOTraceIsStarted_CloseIOTrace_Success) +{ + // Pre-requisite for test, IO Trace application is running + + auto close_trace_response = client::close_io_trace(stub()); + + EXPECT_EQ(0, close_trace_response.status()); +} + +TEST_F(IOTraceApiTests, IOTraceIsInstalled_OpenIOTrace_SuccessAndOpensIOTrace) +{ + // Pre-requisite for test, IO Trace application is installed + + auto open_io_trace = client::open_io_trace(stub()); + + EXPECT_EQ(0, open_io_trace.status()); +} + +TEST_F(IOTraceApiTests, IOTraceIsStarted_LogMessage_Success) +{ + // Pre-requisite for test, IO Trace application is running + + std::string message = "Hello World!"; + auto log_message_response = client::log_message(stub(), message); + + EXPECT_EQ(0, log_message_response.status()); +} + +} // namespace +} // namespace system +} // namespace tests +} // namespace ni