From cdb9c3648c65b65348501fe773ec11b6b55a226c Mon Sep 17 00:00:00 2001 From: Alex Dubois Date: Mon, 14 Sep 2026 10:48:21 -0500 Subject: [PATCH 01/10] Add documentation for creating nitlsconfig gRPC channels to existing gRPC documentation --- docs/conf.py | 2 ++ docs/grpc_session_options.rst | 50 +++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/docs/conf.py b/docs/conf.py index d51244514..1c1b99460 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -47,6 +47,8 @@ intersphinx_mapping = { "grpc": ("https://grpc.github.io/grpc/python/", None), + # Read the Docs project slug differs from the module name. + "nitlsconfig": ("https://nitlsconfig-python.readthedocs.io/en/latest/", None), "nitypes": ("https://nitypes.readthedocs.io/en/latest/", None), "numpy": ("https://numpy.org/doc/stable/", None), "protobuf": ("https://googleapis.dev/python/protobuf/latest/", None), diff --git a/docs/grpc_session_options.rst b/docs/grpc_session_options.rst index e3fe1c435..6bcc9ec28 100644 --- a/docs/grpc_session_options.rst +++ b/docs/grpc_session_options.rst @@ -7,6 +7,56 @@ Support for using NI-DAQmx over gRPC .. py:currentmodule:: nidaqmx +Creating a gRPC channel +----------------------- + +Using NI-DAQmx over gRPC requires the ``grpc`` extra:: + + $ python -m pip install nidaqmx[grpc] + +Every NI-DAQmx gRPC object is created from a ``grpc.Channel`` that you build and pass to +:py:class:`nidaqmx.GrpcSessionOptions`. The constructors for :py:class:`nidaqmx.Task`, +:py:class:`nidaqmx.Scale`, and other classes accept a ``grpc_options`` parameter, and +:py:meth:`nidaqmx.system.System.remote` accepts one to access the remote DAQmx system. You own the +channel, not the objects created from it, so you must close the gRPC channel only after every +NI-DAQmx gRPC object using it is closed. + +The recommended way to create a gRPC channel to a remote system running NI gRPC Device Server is +:py:func:`nitlsconfig.create_grpc_device_channel() ` +from the `nitlsconfig `_ package, which the +``grpc`` extra installs for you. It reads the nitlsconfig client configuration installed with the +NI-DAQmx runtime and by default will attempt to build an encrypted gRPC channel using mTLS. + +Before ``create_grpc_device_channel`` can succeed, you must use NI Hardware Manager to perform a +certificate exchange with the remote system. +See `Managing mTLS `_ for +additional information. + +For example:: + + import nidaqmx + import nitlsconfig + + with nitlsconfig.create_grpc_device_channel('remote_grpc_device', 31763) as channel: + options = nidaqmx.GrpcSessionOptions(channel, '') + with nidaqmx.Task(grpc_options=options) as task: + ... # Calls to task over the encrypted channel + +.. note:: From NI Hardware Manager, you can disable TLS to make ``create_grpc_device_channel`` + produce an insecure channel. + +.. note:: ``create_grpc_device_channel`` also accepts an ``options`` parameter for gRPC channel + arguments such as ``grpc.ssl_target_name_override``, and a ``retry_policy`` parameter. Channel + arguments cannot be changed after the channel is built, so they must be supplied here. + +.. note:: NI gRPC Device Server must be configured to accept remote connections and to take its + TLS settings from nitlsconfig. See + `Bind Address Support `_ and + `NI TLS Config Integration `_ for details. + +You can also build the gRPC channel yourself with ``grpc.insecure_channel`` or ``grpc.secure_channel`` +if you need full control over how credentials are supplied. + .. py:class:: SessionInitializationBehavior :canonical: nidaqmx.grpc_session_options.SessionInitializationBehavior From 6c013313ba28fad321666d746b6a0e61482f76a4 Mon Sep 17 00:00:00 2001 From: Alex Dubois Date: Mon, 14 Sep 2026 14:33:03 -0500 Subject: [PATCH 02/10] Update per Brad's comments. Created a remote systems and local system sections, and created formal sections altogether. --- docs/grpc_session_options.rst | 47 +++++++++++++++++++++++++++++------ 1 file changed, 39 insertions(+), 8 deletions(-) diff --git a/docs/grpc_session_options.rst b/docs/grpc_session_options.rst index 6bcc9ec28..75ad011e5 100644 --- a/docs/grpc_session_options.rst +++ b/docs/grpc_session_options.rst @@ -21,15 +21,19 @@ Every NI-DAQmx gRPC object is created from a ``grpc.Channel`` that you build and channel, not the objects created from it, so you must close the gRPC channel only after every NI-DAQmx gRPC object using it is closed. -The recommended way to create a gRPC channel to a remote system running NI gRPC Device Server is +Which approach you use depends on where NI gRPC Device Server runs. + +Remote systems +~~~~~~~~~~~~~~ + +For a remote system, the recommended way is :py:func:`nitlsconfig.create_grpc_device_channel() ` from the `nitlsconfig `_ package, which the ``grpc`` extra installs for you. It reads the nitlsconfig client configuration installed with the -NI-DAQmx runtime and by default will attempt to build an encrypted gRPC channel using mTLS. - -Before ``create_grpc_device_channel`` can succeed, you must use NI Hardware Manager to perform a -certificate exchange with the remote system. -See `Managing mTLS `_ for +NI-DAQmx runtime and by default will attempt to build an encrypted gRPC channel using mTLS. Before +it can reach a remote system, you must use NI Hardware Manager to perform a certificate exchange +with that system. See +`Managing mTLS `_ for additional information. For example:: @@ -54,8 +58,32 @@ For example:: `Bind Address Support `_ and `NI TLS Config Integration `_ for details. -You can also build the gRPC channel yourself with ``grpc.insecure_channel`` or ``grpc.secure_channel`` -if you need full control over how credentials are supplied. +You can also build an insecure channel yourself with ``grpc.insecure_channel``, or use +``grpc.secure_channel`` to build a secure channel with full control over how credentials are supplied. + +The local system +~~~~~~~~~~~~~~~~ + +For a simple local system setup, build the channel yourself with ``grpc.insecure_channel``. + +For a more complex but secure local system setup, create the channel with +:py:func:`nitlsconfig.create_grpc_device_channel() ` +and use the Manage client certificates and Manage server certificates dialog boxes in NI Hardware +Manager to add the certificates for the local system connection. See +`Managing mTLS `_ for +additional information. You can also build the secure channel yourself with ``grpc.secure_channel``. + +If you are writing a +`measurement plug-in `_, +you do not create the channel at all. The +`session manager `_ +creates it for you and hands you a :py:class:`nidaqmx.Task`, so you do not create +:py:class:`nidaqmx.GrpcSessionOptions` yourself. For working measurements that use NI-DAQmx this +way, see the +`measurement plug-in examples `_. + +SessionInitializationBehavior +----------------------------- .. py:class:: SessionInitializationBehavior :canonical: nidaqmx.grpc_session_options.SessionInitializationBehavior @@ -84,6 +112,9 @@ if you need full control over how credentials are supplied. and leave it open. +GrpcSessionOptions +------------------ + .. py:class:: GrpcSessionOptions(self, grpc_channel, session_name, initialization_behavior=SessionInitializationBehavior.AUTO) :canonical: nidaqmx.grpc_session_options.GrpcSessionOptions From 3a82120f118f25243db5d443d42d975a4525b4e8 Mon Sep 17 00:00:00 2001 From: Alex Dubois Date: Mon, 14 Sep 2026 15:35:03 -0500 Subject: [PATCH 03/10] Make some follow-up changes to improve the flow and clarify when to use nitlsconfig in the local system case --- docs/grpc_session_options.rst | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/docs/grpc_session_options.rst b/docs/grpc_session_options.rst index 75ad011e5..b0a6ec9af 100644 --- a/docs/grpc_session_options.rst +++ b/docs/grpc_session_options.rst @@ -21,7 +21,10 @@ Every NI-DAQmx gRPC object is created from a ``grpc.Channel`` that you build and channel, not the objects created from it, so you must close the gRPC channel only after every NI-DAQmx gRPC object using it is closed. -Which approach you use depends on where NI gRPC Device Server runs. +The recommended way to create the channel depends on where NI gRPC Device Server runs. The sections +below cover a remote system and the local system. In either case you can instead build the channel +yourself, with ``grpc.insecure_channel`` for an insecure channel or ``grpc.secure_channel`` when you +need full control over how credentials are supplied. Remote systems ~~~~~~~~~~~~~~ @@ -58,9 +61,6 @@ For example:: `Bind Address Support `_ and `NI TLS Config Integration `_ for details. -You can also build an insecure channel yourself with ``grpc.insecure_channel``, or use -``grpc.secure_channel`` to build a secure channel with full control over how credentials are supplied. - The local system ~~~~~~~~~~~~~~~~ @@ -71,7 +71,12 @@ For a more complex but secure local system setup, create the channel with and use the Manage client certificates and Manage server certificates dialog boxes in NI Hardware Manager to add the certificates for the local system connection. See `Managing mTLS `_ for -additional information. You can also build the secure channel yourself with ``grpc.secure_channel``. +additional information. + +.. note:: This requires NI gRPC Device Server to be configured to take its TLS settings from + nitlsconfig. If it is not configured this way, do not use ``create_grpc_device_channel`` for + the local system. See + `NI TLS Config Integration `_ for details. If you are writing a `measurement plug-in `_, From e1c4679ce2f3a2a31886d813e5a855bd2c038737 Mon Sep 17 00:00:00 2001 From: alexdubois-ni <74616312+alexdubois-ni@users.noreply.github.com> Date: Thu, 17 Sep 2026 17:24:28 -0500 Subject: [PATCH 04/10] Update docs/grpc_session_options.rst Co-authored-by: Brad Keryan --- docs/grpc_session_options.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/grpc_session_options.rst b/docs/grpc_session_options.rst index b0a6ec9af..feb959bad 100644 --- a/docs/grpc_session_options.rst +++ b/docs/grpc_session_options.rst @@ -85,7 +85,7 @@ you do not create the channel at all. The creates it for you and hands you a :py:class:`nidaqmx.Task`, so you do not create :py:class:`nidaqmx.GrpcSessionOptions` yourself. For working measurements that use NI-DAQmx this way, see the -`measurement plug-in examples `_. +`NI-DAQmx measurement plug-in example `_. SessionInitializationBehavior ----------------------------- From 82ab45c80ac46671a64ed50cd49b4576a35a56c1 Mon Sep 17 00:00:00 2001 From: alexdubois-ni <74616312+alexdubois-ni@users.noreply.github.com> Date: Thu, 17 Sep 2026 17:25:08 -0500 Subject: [PATCH 05/10] Update docs/grpc_session_options.rst Co-authored-by: Brad Keryan --- docs/grpc_session_options.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/grpc_session_options.rst b/docs/grpc_session_options.rst index feb959bad..b65f5b5cd 100644 --- a/docs/grpc_session_options.rst +++ b/docs/grpc_session_options.rst @@ -81,8 +81,7 @@ additional information. If you are writing a `measurement plug-in `_, you do not create the channel at all. The -`session manager `_ -creates it for you and hands you a :py:class:`nidaqmx.Task`, so you do not create +`session management service `_ tracks the lifetimes of NI-DAQmx tasks on the NI gRPC Device Server and the `session management client `_ creates a :py:class:`nidaqmx.Task` for you, so you do not create :py:class:`nidaqmx.GrpcSessionOptions` yourself. For working measurements that use NI-DAQmx this way, see the `NI-DAQmx measurement plug-in example `_. From b9c1759d269d34f1ea4d18b1ddf7a7f7c4d71420 Mon Sep 17 00:00:00 2001 From: alexdubois-ni <74616312+alexdubois-ni@users.noreply.github.com> Date: Thu, 17 Sep 2026 17:25:30 -0500 Subject: [PATCH 06/10] Update docs/grpc_session_options.rst Co-authored-by: Brad Keryan --- docs/grpc_session_options.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/grpc_session_options.rst b/docs/grpc_session_options.rst index b65f5b5cd..1373da943 100644 --- a/docs/grpc_session_options.rst +++ b/docs/grpc_session_options.rst @@ -64,7 +64,7 @@ For example:: The local system ~~~~~~~~~~~~~~~~ -For a simple local system setup, build the channel yourself with ``grpc.insecure_channel``. +For a simple local system setup, build the channel yourself with :py:func:`grpc.insecure_channel`. For a more complex but secure local system setup, create the channel with :py:func:`nitlsconfig.create_grpc_device_channel() ` From 4a1886d28337accd9185022272c6d2e15367e31f Mon Sep 17 00:00:00 2001 From: alexdubois-ni <74616312+alexdubois-ni@users.noreply.github.com> Date: Thu, 17 Sep 2026 17:25:45 -0500 Subject: [PATCH 07/10] Update docs/grpc_session_options.rst Co-authored-by: Brad Keryan --- docs/grpc_session_options.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/grpc_session_options.rst b/docs/grpc_session_options.rst index 1373da943..8f4d1da75 100644 --- a/docs/grpc_session_options.rst +++ b/docs/grpc_session_options.rst @@ -14,7 +14,7 @@ Using NI-DAQmx over gRPC requires the ``grpc`` extra:: $ python -m pip install nidaqmx[grpc] -Every NI-DAQmx gRPC object is created from a ``grpc.Channel`` that you build and pass to +Every NI-DAQmx gRPC object is created from a :py:class:`grpc.Channel` that you build and pass to :py:class:`nidaqmx.GrpcSessionOptions`. The constructors for :py:class:`nidaqmx.Task`, :py:class:`nidaqmx.Scale`, and other classes accept a ``grpc_options`` parameter, and :py:meth:`nidaqmx.system.System.remote` accepts one to access the remote DAQmx system. You own the From 6673d9694f5331c69e615b47341eb18828fce891 Mon Sep 17 00:00:00 2001 From: alexdubois-ni <74616312+alexdubois-ni@users.noreply.github.com> Date: Thu, 17 Sep 2026 17:25:57 -0500 Subject: [PATCH 08/10] Update docs/grpc_session_options.rst Co-authored-by: Brad Keryan --- docs/grpc_session_options.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/grpc_session_options.rst b/docs/grpc_session_options.rst index 8f4d1da75..47504a460 100644 --- a/docs/grpc_session_options.rst +++ b/docs/grpc_session_options.rst @@ -23,7 +23,7 @@ NI-DAQmx gRPC object using it is closed. The recommended way to create the channel depends on where NI gRPC Device Server runs. The sections below cover a remote system and the local system. In either case you can instead build the channel -yourself, with ``grpc.insecure_channel`` for an insecure channel or ``grpc.secure_channel`` when you +yourself, with :py:class:`grpc.insecure_channel` for an insecure channel or :py:class:`grpc.secure_channel` when you need full control over how credentials are supplied. Remote systems From ffb28515cf40033836e8ea03cc34caad33a33a46 Mon Sep 17 00:00:00 2001 From: Alex Dubois Date: Thu, 17 Sep 2026 18:07:00 -0500 Subject: [PATCH 09/10] Address more link issues in the documentation --- docs/grpc_session_options.rst | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/docs/grpc_session_options.rst b/docs/grpc_session_options.rst index 47504a460..01498dcea 100644 --- a/docs/grpc_session_options.rst +++ b/docs/grpc_session_options.rst @@ -15,16 +15,16 @@ Using NI-DAQmx over gRPC requires the ``grpc`` extra:: $ python -m pip install nidaqmx[grpc] Every NI-DAQmx gRPC object is created from a :py:class:`grpc.Channel` that you build and pass to -:py:class:`nidaqmx.GrpcSessionOptions`. The constructors for :py:class:`nidaqmx.Task`, -:py:class:`nidaqmx.Scale`, and other classes accept a ``grpc_options`` parameter, and +:py:class:`nidaqmx.GrpcSessionOptions`. The constructors for :py:class:`nidaqmx.Task `, +:py:class:`nidaqmx.Scale `, and other classes accept a ``grpc_options`` parameter, and :py:meth:`nidaqmx.system.System.remote` accepts one to access the remote DAQmx system. You own the channel, not the objects created from it, so you must close the gRPC channel only after every NI-DAQmx gRPC object using it is closed. The recommended way to create the channel depends on where NI gRPC Device Server runs. The sections below cover a remote system and the local system. In either case you can instead build the channel -yourself, with :py:class:`grpc.insecure_channel` for an insecure channel or :py:class:`grpc.secure_channel` when you -need full control over how credentials are supplied. +yourself, with :py:func:`grpc.insecure_channel` for an insecure channel or +:py:func:`grpc.secure_channel` when you need full control over how credentials are supplied. Remote systems ~~~~~~~~~~~~~~ @@ -81,7 +81,10 @@ additional information. If you are writing a `measurement plug-in `_, you do not create the channel at all. The -`session management service `_ tracks the lifetimes of NI-DAQmx tasks on the NI gRPC Device Server and the `session management client `_ creates a :py:class:`nidaqmx.Task` for you, so you do not create +`session management service `_ +tracks the lifetimes of NI-DAQmx tasks on the NI gRPC Device Server, and the +`session management client `_ +creates a :py:class:`nidaqmx.Task ` for you, so you do not create :py:class:`nidaqmx.GrpcSessionOptions` yourself. For working measurements that use NI-DAQmx this way, see the `NI-DAQmx measurement plug-in example `_. From 30c88be265621095c11a9b38ee791ea6ea3a7f65 Mon Sep 17 00:00:00 2001 From: Alex Dubois Date: Fri, 18 Sep 2026 08:47:02 -0500 Subject: [PATCH 10/10] Fix msvc path for nidaqmxconfig since we updated compilers --- .github/workflows/run_system_tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run_system_tests.yml b/.github/workflows/run_system_tests.yml index d95442668..8c51622e0 100644 --- a/.github/workflows/run_system_tests.yml +++ b/.github/workflows/run_system_tests.yml @@ -27,7 +27,7 @@ jobs: with: persist-credentials: false - name: Import DAQmx config - run: C:\nidaqmxconfig\targets\win64U\x64\msvc-14.0\release\nidaqmxconfig.exe --eraseconfig --import tests\max_config\nidaqmxMaxConfig.ini + run: C:\nidaqmxconfig\targets\win64U\x64\msvc2022\release\nidaqmxconfig.exe --eraseconfig --import tests\max_config\nidaqmxMaxConfig.ini - name: Set up Python uses: ni/python-actions/setup-python@dee640bba235ae28fdc6b7337c643bd06358ae90 # v0.9.0 - name: Set up Poetry