├── .env.sample ├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── feature_request.md │ ├── tech_debt.md │ └── user_story.md ├── PULL_REQUEST_TEMPLATE.md ├── renovate.json └── workflows │ ├── CI.yml │ ├── PR.yml │ ├── build.yml │ ├── generate_docs.yml │ ├── publish.yml │ ├── report_test_results.yml │ ├── run_system_tests.yml │ ├── run_unit_tests.yml │ └── sync_github_issues_to_azdo.yml ├── .gitignore ├── .readthedocs.yml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.rst ├── SECURITY.md ├── docs ├── Makefile ├── collections.rst ├── conf.py ├── constants.rst ├── device.rst ├── device_collection.rst ├── errors.rst ├── expiration_state.rst ├── expiration_states_collection.rst ├── grpc_session_options.rst ├── img │ ├── hwcu_device_name.png │ ├── max_device_name.png │ └── waveform.png ├── index.rst ├── make.bat ├── persisted_channel.rst ├── persisted_channel_collection.rst ├── persisted_scale.rst ├── persisted_scale_collection.rst ├── persisted_task.rst ├── persisted_task_collection.rst ├── physical_channel.rst ├── physical_channel_collection.rst ├── scale.rst ├── storage.rst ├── stream_readers.rst ├── stream_writers.rst ├── system.rst ├── task.rst ├── task_channels.rst ├── task_collections.rst ├── task_triggering.rst ├── types.rst ├── utils.rst └── watchdog.rst ├── examples ├── analog_in │ ├── ai_multi_task_pxie_ref_clk.py │ ├── ai_raw.py │ ├── cont_thrmcpl_samples_int_clk.py │ ├── cont_voltage_acq_int_clk.py │ ├── cont_voltage_acq_int_clk_dig_start.py │ ├── cont_voltage_acq_int_clk_dig_start_retrig.py │ ├── cont_voltage_acq_int_clk_every_n_samples_event.py │ ├── thrmcpl_sample.py │ ├── voltage_acq_int_clk.py │ ├── voltage_acq_int_clk_dig_ref.py │ ├── voltage_acq_int_clk_dig_start_ref.py │ ├── voltage_acq_int_clk_plot_data.py │ ├── voltage_acq_int_clk_tdms_logging.py │ └── voltage_sample.py ├── analog_out │ ├── cont_gen_voltage_wfm_int_clk.py │ ├── cont_gen_voltage_wfm_int_clk_every_n_samples_event.py │ ├── cont_gen_voltage_wfm_int_clk_non_regen.py │ ├── gen_voltage_wfm_int_clk.py │ └── voltage_update.py ├── channel_properties.py ├── counter_in │ ├── cnt_dig_event.py │ ├── cont_cnt_buff_event_ext_clk.py │ ├── cont_read_buff_freq.py │ ├── read_freq.py │ └── read_pulse_freq.py ├── counter_out │ ├── cont_gen_dig_pulse_train.py │ ├── cont_gen_dig_pulse_train_buff_ext_clk.py │ ├── cont_gen_dig_pulse_train_buff_implicit.py │ └── write_single_dig_pulse.py ├── digital_in │ ├── acq_dig_port_int_clk.py │ ├── cont_acq_dig_lines_int_clk.py │ ├── read_dig_lines.py │ └── read_dig_port.py ├── digital_out │ ├── cont_gen_dig_port_int_clk.py │ ├── gen_dig_line_int_clk.py │ ├── write_dig_lines.py │ └── write_dig_port.py ├── every_n_samples_event.py ├── nidaqmx_warnings.py ├── playrec.py ├── pwr_hw_timed.py ├── pwr_hw_timed_stream.py ├── pwr_sw_timed.py ├── synchronization │ └── multi_function │ │ ├── ai_ao_sync.py │ │ └── cont_ai_di_acq.py └── system_properties.py ├── generated └── nidaqmx │ ├── __init__.py │ ├── __main__.py │ ├── _base_interpreter.py │ ├── _bitfield_utils.py │ ├── _grpc_interpreter.py │ ├── _grpc_time.py │ ├── _install_daqmx.py │ ├── _installer_metadata.json │ ├── _lib.py │ ├── _lib_time.py │ ├── _library_interpreter.py │ ├── _linux_installation_commands.py │ ├── _stubs │ ├── __init__.py │ ├── data_moniker_pb2.py │ ├── data_moniker_pb2.pyi │ ├── data_moniker_pb2_grpc.py │ ├── data_moniker_pb2_grpc.pyi │ ├── nidaqmx_pb2.py │ ├── nidaqmx_pb2.pyi │ ├── nidaqmx_pb2_grpc.py │ ├── nidaqmx_pb2_grpc.pyi │ ├── session_pb2.py │ ├── session_pb2.pyi │ ├── session_pb2_grpc.py │ └── session_pb2_grpc.pyi │ ├── _time.py │ ├── constants.py │ ├── error_codes.py │ ├── errors.py │ ├── grpc_session_options.py │ ├── scale.py │ ├── stream_readers.py │ ├── stream_writers.py │ ├── system │ ├── __init__.py │ ├── _collections │ │ ├── __init__.py │ │ ├── device_collection.py │ │ ├── persisted_channel_collection.py │ │ ├── persisted_scale_collection.py │ │ ├── persisted_task_collection.py │ │ └── physical_channel_collection.py │ ├── _watchdog_modules │ │ ├── __init__.py │ │ ├── expiration_state.py │ │ └── expiration_states_collection.py │ ├── device.py │ ├── physical_channel.py │ ├── storage │ │ ├── __init__.py │ │ ├── persisted_channel.py │ │ ├── persisted_scale.py │ │ └── persisted_task.py │ ├── system.py │ └── watchdog.py │ ├── task │ ├── __init__.py │ ├── _export_signals.py │ ├── _in_stream.py │ ├── _out_stream.py │ ├── _task.py │ ├── _timing.py │ ├── channels │ │ ├── __init__.py │ │ ├── _ai_channel.py │ │ ├── _ao_channel.py │ │ ├── _channel.py │ │ ├── _ci_channel.py │ │ ├── _co_channel.py │ │ ├── _di_channel.py │ │ └── _do_channel.py │ ├── collections │ │ ├── __init__.py │ │ ├── _ai_channel_collection.py │ │ ├── _ao_channel_collection.py │ │ ├── _channel_collection.py │ │ ├── _ci_channel_collection.py │ │ ├── _co_channel_collection.py │ │ ├── _di_channel_collection.py │ │ └── _do_channel_collection.py │ └── triggering │ │ ├── __init__.py │ │ ├── _arm_start_trigger.py │ │ ├── _handshake_trigger.py │ │ ├── _pause_trigger.py │ │ ├── _reference_trigger.py │ │ ├── _start_trigger.py │ │ └── _triggers.py │ ├── types.py │ └── utils.py ├── poetry.lock ├── poetry.toml ├── pyproject.toml ├── src ├── codegen │ ├── __init__.py │ ├── __main__.py │ ├── functions │ │ ├── adaptor_parameter.py │ │ ├── function.py │ │ └── parameter.py │ ├── generator.py │ ├── metadata │ │ ├── __init__.py │ │ ├── attributes.py │ │ ├── enums.py │ │ ├── functions.py │ │ ├── functions_addon.py │ │ └── script_info.py │ ├── properties │ │ ├── attribute.py │ │ └── parameter.py │ ├── protos │ │ ├── data_moniker.proto │ │ ├── nidaqmx.proto │ │ └── session.proto │ ├── stub_generator.py │ ├── templates │ │ ├── _base_interpreter.py.mako │ │ ├── _grpc_interpreter.py.mako │ │ ├── _library_interpreter.py.mako │ │ ├── constants.mako │ │ ├── error_codes.mako │ │ ├── function_template.py.mako │ │ ├── grpc_interpreter │ │ │ ├── default_grpc_function_call.py.mako │ │ │ └── event_function_call.py.mako │ │ ├── library_interpreter │ │ │ ├── default_c_function_call.py.mako │ │ │ ├── double_c_function_call.py.mako │ │ │ ├── event_function_call.py.mako │ │ │ └── exec_cdecl_c_function_call.py.mako │ │ ├── property_deleter_template.py.mako │ │ ├── property_deprecated_template.py.mako │ │ ├── property_empty_getter_template.py.mako │ │ ├── property_getter_template.py.mako │ │ ├── property_legacy_deleter_template.py.mako │ │ ├── property_legacy_setter_template.py.mako │ │ ├── property_setter_template.py.mako │ │ ├── property_template.py.mako │ │ ├── scale.py.mako │ │ ├── system │ │ │ ├── _watchdog_modules │ │ │ │ └── expiration_state.py.mako │ │ │ ├── device.py.mako │ │ │ ├── physical_channel.py.mako │ │ │ ├── system.py.mako │ │ │ └── watchdog.py.mako │ │ └── task │ │ │ ├── _export_signals.py.mako │ │ │ ├── _in_stream.py.mako │ │ │ ├── _out_stream.py.mako │ │ │ ├── _timing.py.mako │ │ │ ├── channels │ │ │ ├── _ai_channel.py.mako │ │ │ ├── _ao_channel.py.mako │ │ │ ├── _channel.py.mako │ │ │ ├── _ci_channel.py.mako │ │ │ ├── _co_channel.py.mako │ │ │ ├── _di_channel.py.mako │ │ │ └── _do_channel.py.mako │ │ │ ├── collections │ │ │ ├── _ai_channel_collection.py.mako │ │ │ ├── _ao_channel_collection.py.mako │ │ │ ├── _ci_channel_collection.py.mako │ │ │ ├── _co_channel_collection.py.mako │ │ │ ├── _di_channel_collection.py.mako │ │ │ └── _do_channel_collection.py.mako │ │ │ └── triggering │ │ │ ├── _arm_start_trigger.py.mako │ │ │ ├── _handshake_trigger.py.mako │ │ │ ├── _pause_trigger.py.mako │ │ │ ├── _reference_trigger.py.mako │ │ │ ├── _start_trigger.py.mako │ │ │ └── _triggers.py.mako │ └── utilities │ │ ├── __init__.py │ │ ├── attribute_helpers.py │ │ ├── enum_helpers.py │ │ ├── function_helpers.py │ │ ├── helpers.py │ │ ├── interpreter_helpers.py │ │ └── text_wrappers.py └── handwritten │ ├── __init__.py │ ├── __main__.py │ ├── _bitfield_utils.py │ ├── _grpc_time.py │ ├── _install_daqmx.py │ ├── _installer_metadata.json │ ├── _lib.py │ ├── _lib_time.py │ ├── _linux_installation_commands.py │ ├── _time.py │ ├── errors.py │ ├── grpc_session_options.py │ ├── stream_readers.py │ ├── stream_writers.py │ ├── system │ ├── __init__.py │ ├── _collections │ │ ├── __init__.py │ │ ├── device_collection.py │ │ ├── persisted_channel_collection.py │ │ ├── persisted_scale_collection.py │ │ ├── persisted_task_collection.py │ │ └── physical_channel_collection.py │ ├── _watchdog_modules │ │ ├── __init__.py │ │ └── expiration_states_collection.py │ └── storage │ │ ├── __init__.py │ │ ├── persisted_channel.py │ │ ├── persisted_scale.py │ │ └── persisted_task.py │ ├── task │ ├── __init__.py │ ├── _task.py │ ├── channels │ │ └── __init__.py │ ├── collections │ │ ├── __init__.py │ │ └── _channel_collection.py │ └── triggering │ │ └── __init__.py │ ├── types.py │ └── utils.py ├── tests ├── __init__.py ├── _event_utils.py ├── _grpc_utils.py ├── acceptance │ ├── __init__.py │ ├── test_examples.py │ ├── test_internationalization.py │ └── test_multi_threading.py ├── component │ ├── __init__.py │ ├── system │ │ ├── __init__.py │ │ ├── _collections │ │ │ ├── __init__.py │ │ │ ├── test_device_collection.py │ │ │ ├── test_persisted_channel_collection.py │ │ │ ├── test_persisted_scale_collection.py │ │ │ ├── test_persisted_task_collection.py │ │ │ └── test_physical_channel_collection.py │ │ ├── storage │ │ │ ├── __init__.py │ │ │ ├── test_persisted_channel.py │ │ │ ├── test_persisted_channel_properties.py │ │ │ ├── test_persisted_scale.py │ │ │ ├── test_persisted_scale_properties.py │ │ │ ├── test_persisted_task.py │ │ │ └── test_persisted_task_properties.py │ │ ├── test_device.py │ │ ├── test_device_properties.py │ │ ├── test_physical_channel.py │ │ ├── test_physical_channel_properties.py │ │ ├── test_system.py │ │ └── test_watchdog_properties.py │ ├── task │ │ ├── __init__.py │ │ ├── channels │ │ │ ├── __init__.py │ │ │ ├── test_ai_channel.py │ │ │ ├── test_channel_properties.py │ │ │ ├── test_ci_channel.py │ │ │ ├── test_co_channel.py │ │ │ ├── test_di_channel.py │ │ │ └── test_do_channel.py │ │ ├── test_export_signals_properties.py │ │ ├── test_in_stream.py │ │ ├── test_in_stream_buffer_properties.py │ │ ├── test_in_stream_read_properties.py │ │ ├── test_out_stream.py │ │ ├── test_out_stream_buffer_properties.py │ │ ├── test_out_stream_write_properties.py │ │ ├── test_timing.py │ │ ├── test_timing_properties.py │ │ ├── test_triggers.py │ │ └── test_triggers_properties.py │ ├── test_export_signals.py │ ├── test_interpreter.py │ ├── test_scale.py │ ├── test_scale_properties.py │ ├── test_stream_readers_ai.py │ ├── test_stream_readers_ci.py │ ├── test_stream_readers_di.py │ ├── test_stream_writers.py │ ├── test_stream_writers_ao.py │ ├── test_stream_writers_co.py │ ├── test_stream_writers_do.py │ ├── test_task.py │ ├── test_task_events.py │ ├── test_task_properties.py │ ├── test_task_read_ai.py │ ├── test_task_resource_warnings.py │ └── test_watchdog.py ├── conftest.py ├── helpers.py ├── legacy │ ├── __init__.py │ ├── test_channel_creation.py │ ├── test_channels.py │ ├── test_container_operations.py │ ├── test_enum_bitfields.py │ ├── test_events.py │ ├── test_export_signals.py │ ├── test_invalid_reads.py │ ├── test_invalid_writes.py │ ├── test_multi_threading.py │ ├── test_power_up_states.py │ ├── test_properties.py │ ├── test_read_exceptions.py │ ├── test_read_write.py │ ├── test_resource_warnings.py │ ├── test_stream_analog_readers_writers.py │ ├── test_stream_counter_readers_writers.py │ ├── test_stream_digital_readers_writers.py │ ├── test_stream_power_readers.py │ ├── test_system_collections.py │ ├── test_teds.py │ ├── test_triggers.py │ ├── test_utils.py │ ├── test_watchdog.py │ └── test_write_exceptions.py ├── max_config │ ├── examplesMaxConfig.ini │ ├── linux │ │ └── nidaqmxMaxConfig.ini │ └── nidaqmxMaxConfig.ini ├── test_assets │ └── teds │ │ ├── Accelerometer.ted │ │ ├── Current.ted │ │ ├── ForceSensor.ted │ │ ├── LVDT.ted │ │ ├── Microphone.ted │ │ ├── RVDT.ted │ │ ├── Resistance.ted │ │ ├── StrainGage.ted │ │ ├── TempRTD.ted │ │ ├── TempTC.ted │ │ ├── ThermistorIex.ted │ │ ├── ThermistorVex.ted │ │ ├── Voltage.ted │ │ ├── forcebridge.ted │ │ ├── pressurebridge.ted │ │ └── torquebridge.ted └── unit │ ├── __init__.py │ ├── _grpc_utils.py │ ├── _task_utils.py │ ├── _time_utils.py │ ├── conftest.py │ ├── system │ ├── __init__.py │ └── storage │ │ ├── __init__.py │ │ └── test_persisted_task.py │ ├── test_grpc_time.py │ ├── test_lib_time.py │ ├── test_task.py │ └── test_task_events.py └── tox.ini /.env.sample: -------------------------------------------------------------------------------- 1 | # This is a sample nidaqmx-python configuration file. 2 | 3 | # To use it: 4 | # - Copy this file to your application's directory or one of its parent directories 5 | # (such as the root of your Git repository). 6 | # - Rename it to `.env`. 7 | # - Uncomment and edit the options you want to change. 8 | # - Restart any affected applications or services. 9 | 10 | # By default, nidaqmx-python on Windows uses nicai_utf8, the UTF-8 version of the NI-DAQmx C library. 11 | # If that is not available, it falls back to nicaiu, the MBCS (multibyte character set) version. You can override 12 | # this behavior by uncommenting the following option: 13 | 14 | # NIDAQMX_C_LIBRARY=nicaiu -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @zhindes @maxxboehme @bkeryan -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: 'bug,triage' 6 | --- 7 | 8 | 13 | 14 | # Bug Report 15 | 16 | 17 | 18 | ## Repro or Code Sample 19 | 20 | 21 | 22 | ## Expected Behavior 23 | 24 | 25 | 26 | ## Current Behavior 27 | 28 | 29 | 30 | 31 | 32 | ## Possible Solution 33 | 34 | 35 | 36 | 37 | ## Context 38 | 39 | 40 | 41 | 42 | ## Your Environment 43 | 44 | 45 | 46 | * Operating system and version: [e.g. Windows 11 24H2, Ubuntu Linux 24.04] 47 | * NI-DAQmx version: [e.g. 2024 Q4] 48 | * `nidaqmx-python` version: [e.g. 1.0.1] 49 | * Python version [e.g. 3.9] -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: 'enhancement,triage' 6 | --- 7 | 8 | 13 | 14 | ## Problem to Solve 15 | 16 | 17 | 18 | ## Proposed Solution 19 | 20 | 21 | 22 | 26 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/tech_debt.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Tech debt 3 | about: (DEV TEAM ONLY) Non-user-visible improvement to code or development process 4 | title: '' 5 | labels: 'tech debt,triage' 6 | --- 7 | ## Tech Debt 8 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/user_story.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: User story 3 | about: (DEV TEAM ONLY) A small chunk of work to be done 4 | title: '(Fully descriptive title)' 5 | labels: 'user story,triage' 6 | --- 7 | 8 | 9 | 10 | ## User Story 11 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | - [ ] This contribution adheres to [CONTRIBUTING.md](https://github.com/ni/nidaqmx-python/blob/master/CONTRIBUTING.md). 2 | 3 | TODO: Check the above box with an 'x' indicating you've read and followed [CONTRIBUTING.md](https://github.com/ni/nidaqmx-python/blob/master/CONTRIBUTING.md). 4 | 5 | - [ ] I've updated [CHANGELOG.md](https://github.com/ni/nidaqmx-python/blob/master/CHANGELOG.md) if applicable. 6 | 7 | TODO: Check the above box with an 'x' if considered if there were any and then documented client facing changes in [CHANGELOG.md](https://github.com/ni/nidaqmx-python/blob/master/CHANGELOG.md). Strike through if this is not a relevant client facing change. 8 | 9 | - [ ] I've added tests applicable for this pull request 10 | 11 | TODO: Check the above box with an 'x' indicating you have considered and added any applicable system or unit tests. Strike through if you considered and decided additional tests were not warranted. 12 | 13 | ### What does this Pull Request accomplish? 14 | 15 | TODO: Include high-level description of the changes in this pull request. 16 | 17 | ### Why should this Pull Request be merged? 18 | 19 | TODO: Justify why this contribution should be part of the project. 20 | 21 | ### What testing has been done? 22 | 23 | TODO: Detail what testing has been done to ensure this submission meets requirements. 24 | -------------------------------------------------------------------------------- /.github/renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "branchPrefix": "users/renovate/", 4 | "extends": [ 5 | "config:recommended", 6 | ":maintainLockFilesWeekly", 7 | ":automergePatch", 8 | "schedule:automergeDaily", 9 | "helpers:pinGitHubActionDigestsToSemver" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /.github/workflows/CI.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | - 'releases/**' 8 | workflow_call: 9 | workflow_dispatch: 10 | jobs: 11 | build: 12 | name: Build 13 | uses: ./.github/workflows/build.yml 14 | run_unit_tests: 15 | name: Run unit tests 16 | uses: ./.github/workflows/run_unit_tests.yml 17 | needs: [build] 18 | generate_docs: 19 | name: Generate docs 20 | uses: ./.github/workflows/generate_docs.yml 21 | needs: [build] 22 | run_system_tests: 23 | name: Run system tests 24 | uses: ./.github/workflows/run_system_tests.yml 25 | needs: [build, run_unit_tests] 26 | report_test_results: 27 | name: Report test results 28 | uses: ./.github/workflows/report_test_results.yml 29 | needs: [run_unit_tests, run_system_tests] 30 | if: always() 31 | permissions: 32 | checks: write 33 | pull-requests: write 34 | -------------------------------------------------------------------------------- /.github/workflows/PR.yml: -------------------------------------------------------------------------------- 1 | name: PR 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - master 7 | - 'releases/**' 8 | workflow_call: 9 | workflow_dispatch: 10 | 11 | concurrency: 12 | group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} 13 | cancel-in-progress: true 14 | 15 | jobs: 16 | run_ci: 17 | name: Run CI 18 | uses: ./.github/workflows/CI.yml 19 | permissions: 20 | checks: write 21 | pull-requests: write 22 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | workflow_call: 5 | workflow_dispatch: 6 | jobs: 7 | build: 8 | name: Build 9 | runs-on: 10 | - ubuntu-latest 11 | steps: 12 | - name: Check out repo 13 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 14 | - name: Set up Python 15 | uses: ni/python-actions/setup-python@2c946e7237558ed6d59565787f1edb2150df50ba # v0.2.0 16 | id: setup-python 17 | with: 18 | # The codegen scripts require Python 3.9 or later. 19 | python-version: "3.9" 20 | - name: Set up Poetry 21 | uses: ni/python-actions/setup-poetry@2c946e7237558ed6d59565787f1edb2150df50ba # v0.2.0 22 | - name: Cache virtualenv (all extras) 23 | uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 24 | with: 25 | path: .venv 26 | key: nidaqmx-all-extras-${{ runner.os }}-py${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('poetry.lock') }} 27 | - name: Install dependencies 28 | run: | 29 | python -m pip install --upgrade pip 30 | poetry install --all-extras 31 | - name: Run linters 32 | run: poetry run ni-python-styleguide lint 33 | - name: Run mypy (Linux) 34 | run: poetry run mypy 35 | - name: Run mypy (Windows) 36 | run: poetry run mypy --platform win32 37 | - name: Run Bandit security checks 38 | run: poetry run bandit -c pyproject.toml -r generated/nidaqmx 39 | - name: Generate ni-daqmx files 40 | run: | 41 | rm -fr generated/nidaqmx 42 | poetry run python src/codegen --dest generated/nidaqmx 43 | - name: Check for files dirtied by codegen 44 | run: git diff --exit-code 45 | -------------------------------------------------------------------------------- /.github/workflows/generate_docs.yml: -------------------------------------------------------------------------------- 1 | name: Generate docs 2 | 3 | on: 4 | workflow_call: 5 | workflow_dispatch: 6 | jobs: 7 | generate_docs: 8 | name: Generate docs 9 | runs-on: 10 | - ubuntu-latest 11 | steps: 12 | - name: Check out repo 13 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 14 | - name: Set up Python 15 | uses: ni/python-actions/setup-python@2c946e7237558ed6d59565787f1edb2150df50ba # v0.2.0 16 | id: setup-python 17 | - name: Set up Poetry 18 | uses: ni/python-actions/setup-poetry@2c946e7237558ed6d59565787f1edb2150df50ba # v0.2.0 19 | - name: Cache virtualenvs 20 | uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 21 | with: 22 | path: | 23 | .venv 24 | .tox 25 | key: generate-docs-${{ runner.os }}-py${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('poetry.lock') }} 26 | - name: Install dependencies 27 | run: | 28 | python -m pip install --upgrade pip 29 | poetry install 30 | - name: Generate docs 31 | run: poetry run tox -e docs -------------------------------------------------------------------------------- /.github/workflows/report_test_results.yml: -------------------------------------------------------------------------------- 1 | name: Report test results 2 | 3 | on: 4 | workflow_call: 5 | workflow_dispatch: 6 | 7 | jobs: 8 | report_test_results: 9 | name: Report test results 10 | runs-on: ubuntu-latest 11 | permissions: 12 | checks: write 13 | pull-requests: write 14 | steps: 15 | - name: Check out repo 16 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 17 | - name: Download test results 18 | uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 19 | with: 20 | path: test_results 21 | pattern: test_results_* 22 | merge-multiple: true 23 | - name: List downloaded files 24 | run: ls -lR 25 | - name: Publish test results 26 | uses: EnricoMi/publish-unit-test-result-action@3a74b2957438d0b6e2e61d67b05318aa25c9e6c6 # v2.20.0 27 | with: 28 | files: "test_results/*.xml" 29 | -------------------------------------------------------------------------------- /.github/workflows/run_system_tests.yml: -------------------------------------------------------------------------------- 1 | name: Run system tests 2 | 3 | on: 4 | workflow_call: 5 | workflow_dispatch: 6 | jobs: 7 | run_system_tests: 8 | name: Run system tests 9 | runs-on: 10 | - self-hosted 11 | - windows 12 | - x64 13 | - rdss-nidaqmxbot-${{ matrix.configuration }} 14 | strategy: 15 | matrix: 16 | configuration: ["win-10-py32", "win-10-py64"] 17 | # Fail-fast skews the pass/fail ratio and seems to make pytest produce 18 | # incomplete JUnit XML results. 19 | fail-fast: false 20 | timeout-minutes: 90 21 | steps: 22 | - name: Check out repo 23 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 24 | - name: Import DAQmx config 25 | run: C:\nidaqmxconfig\targets\win64U\x64\msvc-14.0\release\nidaqmxconfig.exe --eraseconfig --import tests\max_config\nidaqmxMaxConfig.ini 26 | - name: Cache virtualenvs 27 | uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 28 | with: 29 | path: | 30 | .venv 31 | .tox 32 | key: run-system-tests-${{ runner.os }}-${{ matrix.configuration }}-${{ hashFiles('poetry.lock') }} 33 | - name: Install dependencies 34 | run: poetry install 35 | - name: Run system tests 36 | run: poetry run tox 37 | - name: Rename test results 38 | # TODO: Add Git Bash to the path on self-hosted Windows runners 39 | run: Get-ChildItem test_results/*.xml | Rename-Item -NewName { $_.Name -replace '.xml','-${{ matrix.configuration }}.xml' } 40 | if: always() && runner.os == 'Windows' 41 | - name: Rename test results 42 | run: | 43 | for result in test_results/*.xml; do 44 | mv $result ${result%.xml}-${{ matrix.configuration }}.xml 45 | done 46 | if: always() && runner.os != 'Windows' 47 | - name: Upload test results 48 | uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 49 | with: 50 | name: test_results_system_${{ matrix.configuration }} 51 | path: test_results/*.xml 52 | if: always() 53 | -------------------------------------------------------------------------------- /.github/workflows/sync_github_issues_to_azdo.yml: -------------------------------------------------------------------------------- 1 | name: Sync issue to Azure DevOps work item 2 | 3 | on: 4 | issues: 5 | # Omit "labeled" and "unlabeled" to work around https://github.com/danhellem/github-actions-issue-to-work-item/issues/70 6 | types: 7 | [opened, edited, deleted, closed, reopened, assigned] 8 | issue_comment: 9 | types: [created, edited, deleted] 10 | 11 | jobs: 12 | alert: 13 | if: ${{ !github.event.issue.pull_request && github.event.issue.title != 'Dependency Dashboard' }} 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Choose work item type 17 | id: choose_work_item_type 18 | run: | 19 | if [ "${{ contains(github.event.issue.labels.*.name, 'enhancement') || contains(github.event.issue.labels.*.name, 'user story') }}" == "true" ]; then 20 | echo "work_item_type=User Story" >> $GITHUB_OUTPUT 21 | elif [ "${{ contains(github.event.issue.labels.*.name, 'tech debt') }}" == "true" ]; then 22 | echo "work_item_type=Technical Debt" >> $GITHUB_OUTPUT 23 | else 24 | echo "work_item_type=Bug" >> $GITHUB_OUTPUT 25 | fi 26 | - uses: danhellem/github-actions-issue-to-work-item@45eb3b46e684f2acd2954f02ef70350c835ee4bb # v2.4 27 | env: 28 | ado_token: "${{ secrets.AZDO_WORK_ITEM_TOKEN }}" 29 | github_token: "${{ secrets.GH_REPO_TOKEN }}" 30 | ado_organization: "ni" 31 | ado_project: "DevCentral" 32 | ado_area_path: "DevCentral\\Product RnD\\Platform HW and SW\\Core SW and Drivers\\Platform HW and Drivers\\Drivers\\Venus" 33 | ado_wit: "${{ steps.choose_work_item_type.outputs.work_item_type }}" 34 | ado_new_state: "New" 35 | ado_active_state: "Active" 36 | ado_close_state: "Closed" 37 | ado_bypassrules: true 38 | log_level: 100 39 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | 4 | # Unit tests 5 | .tox/ 6 | test_results/ 7 | 8 | # Coverage output 9 | .coverage 10 | htmlcov/ 11 | 12 | # Environments 13 | .env 14 | .venv 15 | 16 | # Built artifacts 17 | dist/ 18 | docs/_build/ 19 | 20 | # Common editor metadata 21 | .vscode/ -------------------------------------------------------------------------------- /.readthedocs.yml: -------------------------------------------------------------------------------- 1 | # .readthedocs.yml 2 | 3 | version: 2 4 | 5 | build: 6 | os: ubuntu-24.04 7 | tools: 8 | python: "3.11" 9 | jobs: 10 | post_create_environment: 11 | - pip install poetry==1.8.2 12 | post_install: 13 | - VIRTUAL_ENV=$READTHEDOCS_VIRTUALENV_PATH poetry install --only main,docs 14 | 15 | sphinx: 16 | configuration: docs/conf.py 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is the MIT license: http://www.opensource.org/licenses/mit-license.php 2 | 3 | Copyright (c) 2022, National Instruments Corp. NI-DAQ is a trademark of National 4 | Instruments. 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of this 7 | software and associated documentation files (the "Software"), to deal in the Software 8 | without restriction, including without limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons 10 | to whom the Software is furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all copies or 13 | substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 16 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 17 | PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 18 | FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Security 4 | 5 | NI views the security of our software products as an important part of our commitment to our users. This includes source code repositories managed through the [NI](https://github.com/ni) GitHub organization. 6 | 7 | ## Reporting Security Issues 8 | 9 | We encourage you to report security vulnerabilities to us privately so we can follow the principle of [Coordinated Vulnerability Disclosure (CVD)](https://vuls.cert.org/confluence/display/CVD). This allows us time to thoroughly investigate security issues and publicly disclose them when appropriate. 10 | 11 | **Please do not report security vulnerabilities through public GitHub issues.** 12 | 13 | Instead, please report them by sending an email to [security@ni.com](mailto:security@ni.com) with sufficient details about the type of issue, the impact of the issue, and how to reproduce the issue. You may use the [NI PGP key](https://www.ni.com/en/support/security/pgp.html) to encrypt any sensitive communications you send to us. When you notify us of a potential security issue, our remediation process includes acknowledging receipt and coordinating any necessary response activities with you. 14 | 15 | ## Learn More 16 | 17 | To learn more about NI Security, please see [https://ni.com/security](https://ni.com/security) 18 | 19 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | SPHINXPROJ = NI-DAQmxPythonAPI 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) -------------------------------------------------------------------------------- /docs/collections.rst: -------------------------------------------------------------------------------- 1 | nidaqmx.system.collections 2 | ========================== 3 | 4 | .. toctree:: 5 | 6 | device_collection 7 | persisted_channel_collection 8 | persisted_scale_collection 9 | persisted_task_collection 10 | physical_channel_collection 11 | -------------------------------------------------------------------------------- /docs/constants.rst: -------------------------------------------------------------------------------- 1 | nidaqmx.constants 2 | ================= 3 | .. automodule:: nidaqmx.constants 4 | :members: 5 | :undoc-members: 6 | :show-inheritance: -------------------------------------------------------------------------------- /docs/device.rst: -------------------------------------------------------------------------------- 1 | nidaqmx.system.device 2 | ===================== 3 | 4 | .. automodule:: nidaqmx.system.device 5 | :members: 6 | :show-inheritance: 7 | :special-members: 8 | -------------------------------------------------------------------------------- /docs/device_collection.rst: -------------------------------------------------------------------------------- 1 | nidaqmx.system.device_collection 2 | ================================ 3 | 4 | .. automodule:: nidaqmx.system._collections.device_collection 5 | :members: 6 | :show-inheritance: 7 | -------------------------------------------------------------------------------- /docs/errors.rst: -------------------------------------------------------------------------------- 1 | nidaqmx.errors 2 | ============== 3 | .. automodule:: nidaqmx.errors 4 | :members: 5 | :undoc-members: 6 | :show-inheritance: -------------------------------------------------------------------------------- /docs/expiration_state.rst: -------------------------------------------------------------------------------- 1 | nidaqmx.system.expiration_state 2 | =============================== 3 | 4 | .. automodule:: nidaqmx.system._watchdog_modules.expiration_state 5 | :members: 6 | :show-inheritance: 7 | -------------------------------------------------------------------------------- /docs/expiration_states_collection.rst: -------------------------------------------------------------------------------- 1 | nidaqmx.system.expiration_states_collection 2 | =========================================== 3 | 4 | .. automodule:: nidaqmx.system._watchdog_modules.expiration_states_collection 5 | :members: 6 | :show-inheritance: 7 | -------------------------------------------------------------------------------- /docs/img/hwcu_device_name.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ni/nidaqmx-python/9515a863bba08f6acb2a0a2e65a7839372d43c85/docs/img/hwcu_device_name.png -------------------------------------------------------------------------------- /docs/img/max_device_name.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ni/nidaqmx-python/9515a863bba08f6acb2a0a2e65a7839372d43c85/docs/img/max_device_name.png -------------------------------------------------------------------------------- /docs/img/waveform.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ni/nidaqmx-python/9515a863bba08f6acb2a0a2e65a7839372d43c85/docs/img/waveform.png -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. NI-DAQmx Python API documentation master file, created by 2 | sphinx-quickstart on Thu Dec 15 09:40:36 2016. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | NI-DAQmx Python Documentation 7 | ============================= 8 | 9 | .. include:: ../README.rst 10 | 11 | .. py:module:: nidaqmx 12 | 13 | .. toctree:: 14 | :maxdepth: 3 15 | :caption: API Reference: 16 | 17 | constants 18 | errors 19 | grpc_session_options 20 | scale 21 | stream_readers 22 | stream_writers 23 | system 24 | task 25 | types 26 | utils 27 | 28 | 29 | Indices and Tables 30 | ================== 31 | 32 | * :ref:`genindex` 33 | * :ref:`modindex` 34 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=. 11 | set BUILDDIR=_build 12 | set SPHINXPROJ=NI-DAQmxPythonAPI 13 | 14 | if "%1" == "" goto help 15 | 16 | %SPHINXBUILD% >NUL 2>NUL 17 | if errorlevel 9009 ( 18 | echo. 19 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 20 | echo.installed, then set the SPHINXBUILD environment variable to point 21 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 22 | echo.may add the Sphinx directory to PATH. 23 | echo. 24 | echo.If you don't have Sphinx installed, grab it from 25 | echo.http://sphinx-doc.org/ 26 | exit /b 1 27 | ) 28 | 29 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 30 | goto end 31 | 32 | :help 33 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 34 | 35 | :end 36 | popd 37 | -------------------------------------------------------------------------------- /docs/persisted_channel.rst: -------------------------------------------------------------------------------- 1 | nidaqmx.system.persisted_channel 2 | ================================ 3 | 4 | .. automodule:: nidaqmx.system.storage.persisted_channel 5 | :members: 6 | :show-inheritance: 7 | :special-members: 8 | -------------------------------------------------------------------------------- /docs/persisted_channel_collection.rst: -------------------------------------------------------------------------------- 1 | nidaqmx.system.persisted_channel_collection 2 | =========================================== 3 | 4 | .. automodule:: nidaqmx.system._collections.persisted_channel_collection 5 | :members: 6 | :show-inheritance: 7 | -------------------------------------------------------------------------------- /docs/persisted_scale.rst: -------------------------------------------------------------------------------- 1 | nidaqmx.system.persisted_scale 2 | ============================== 3 | 4 | .. automodule:: nidaqmx.system.storage.persisted_scale 5 | :members: 6 | :show-inheritance: 7 | :special-members: 8 | -------------------------------------------------------------------------------- /docs/persisted_scale_collection.rst: -------------------------------------------------------------------------------- 1 | nidaqmx.system.persisted_scale_collection 2 | ========================================= 3 | 4 | .. automodule:: nidaqmx.system._collections.persisted_scale_collection 5 | :members: 6 | :show-inheritance: 7 | -------------------------------------------------------------------------------- /docs/persisted_task.rst: -------------------------------------------------------------------------------- 1 | nidaqmx.system.persisted_task 2 | ============================= 3 | 4 | .. automodule:: nidaqmx.system.storage.persisted_task 5 | :members: 6 | :show-inheritance: 7 | :special-members: 8 | -------------------------------------------------------------------------------- /docs/persisted_task_collection.rst: -------------------------------------------------------------------------------- 1 | nidaqmx.system.persisted_task_collection 2 | ======================================== 3 | 4 | .. automodule:: nidaqmx.system._collections.persisted_task_collection 5 | :members: 6 | :show-inheritance: 7 | -------------------------------------------------------------------------------- /docs/physical_channel.rst: -------------------------------------------------------------------------------- 1 | nidaqmx.system.physical_channel 2 | =============================== 3 | 4 | .. automodule:: nidaqmx.system.physical_channel 5 | :members: 6 | :show-inheritance: 7 | :special-members: -------------------------------------------------------------------------------- /docs/physical_channel_collection.rst: -------------------------------------------------------------------------------- 1 | nidaqmx.system.physical_channel_collection 2 | ========================================== 3 | 4 | .. automodule:: nidaqmx.system._collections.physical_channel_collection 5 | :members: 6 | :show-inheritance: 7 | -------------------------------------------------------------------------------- /docs/scale.rst: -------------------------------------------------------------------------------- 1 | nidaqmx.scale 2 | ===================== 3 | 4 | .. automodule:: nidaqmx.scale 5 | :members: 6 | :show-inheritance: 7 | :special-members: -------------------------------------------------------------------------------- /docs/storage.rst: -------------------------------------------------------------------------------- 1 | nidaqmx.system.storage 2 | ====================== 3 | 4 | .. toctree:: 5 | 6 | persisted_channel 7 | persisted_scale 8 | persisted_task 9 | -------------------------------------------------------------------------------- /docs/stream_readers.rst: -------------------------------------------------------------------------------- 1 | nidaqmx.stream_readers 2 | ====================== 3 | 4 | .. automodule:: nidaqmx.stream_readers 5 | :members: 6 | :show-inheritance: 7 | :inherited-members: -------------------------------------------------------------------------------- /docs/stream_writers.rst: -------------------------------------------------------------------------------- 1 | nidaqmx.stream_writers 2 | ====================== 3 | 4 | .. automodule:: nidaqmx.stream_writers 5 | :members: 6 | :show-inheritance: 7 | :inherited-members: -------------------------------------------------------------------------------- /docs/system.rst: -------------------------------------------------------------------------------- 1 | nidaqmx.system 2 | ============== 3 | 4 | .. automodule:: nidaqmx.system.system 5 | :members: 6 | :show-inheritance: 7 | 8 | .. toctree:: 9 | 10 | collections 11 | device 12 | physical_channel 13 | storage 14 | watchdog 15 | -------------------------------------------------------------------------------- /docs/task.rst: -------------------------------------------------------------------------------- 1 | nidaqmx.task 2 | ============ 3 | .. automodule:: nidaqmx.task 4 | :members: 5 | :show-inheritance: 6 | :special-members: 7 | 8 | .. toctree:: 9 | 10 | task_channels 11 | task_collections 12 | task_triggering -------------------------------------------------------------------------------- /docs/task_channels.rst: -------------------------------------------------------------------------------- 1 | nidaqmx.task.channels 2 | ===================== 3 | 4 | .. automodule:: nidaqmx.task.channels 5 | :members: 6 | :show-inheritance: 7 | :member-order: bysource 8 | -------------------------------------------------------------------------------- /docs/task_collections.rst: -------------------------------------------------------------------------------- 1 | nidaqmx.task.collections 2 | =============================== 3 | 4 | .. automodule:: nidaqmx.task.collections 5 | :members: 6 | :show-inheritance: 7 | :member-order: bysource 8 | -------------------------------------------------------------------------------- /docs/task_triggering.rst: -------------------------------------------------------------------------------- 1 | nidaqmx.task.triggering 2 | ======================= 3 | 4 | .. automodule:: nidaqmx.task.triggering 5 | :members: 6 | :show-inheritance: 7 | :member-order: bysource 8 | -------------------------------------------------------------------------------- /docs/types.rst: -------------------------------------------------------------------------------- 1 | nidaqmx.types 2 | ============= 3 | .. automodule:: nidaqmx.types 4 | :members: 5 | :undoc-members: 6 | :show-inheritance: -------------------------------------------------------------------------------- /docs/utils.rst: -------------------------------------------------------------------------------- 1 | nidaqmx.utils 2 | ============= 3 | 4 | .. automodule:: nidaqmx.utils 5 | :members: 6 | :show-inheritance: -------------------------------------------------------------------------------- /docs/watchdog.rst: -------------------------------------------------------------------------------- 1 | nidaqmx.system.watchdog 2 | ======================= 3 | 4 | .. automodule:: nidaqmx.system.watchdog 5 | :members: 6 | :show-inheritance: 7 | :special-members: 8 | 9 | .. toctree:: 10 | 11 | expiration_state 12 | expiration_states_collection 13 | -------------------------------------------------------------------------------- /examples/analog_in/ai_multi_task_pxie_ref_clk.py: -------------------------------------------------------------------------------- 1 | """Example of AI multitask operation.""" 2 | 3 | import pprint 4 | 5 | import nidaqmx 6 | from nidaqmx.constants import AcquisitionType, TaskMode 7 | 8 | pp = pprint.PrettyPrinter(indent=4) 9 | 10 | 11 | with nidaqmx.Task() as master_task, nidaqmx.Task() as slave_task: 12 | master_task.ai_channels.add_ai_voltage_chan("/PXI1Slot3/ai0") 13 | slave_task.ai_channels.add_ai_voltage_chan("/PXI1Slot7/ai0") 14 | 15 | master_task.timing.ref_clk_src = "PXIe_Clk100" 16 | master_task.timing.ref_clk_rate = 100000000 17 | master_task.timing.cfg_samp_clk_timing(1000, sample_mode=AcquisitionType.CONTINUOUS) 18 | master_task.triggers.sync_type.MASTER = True 19 | 20 | slave_task.timing.ref_clk_src = "PXIe_Clk100" 21 | slave_task.timing.ref_clk_rate = 100000000 22 | slave_task.timing.cfg_samp_clk_timing(1000, sample_mode=AcquisitionType.CONTINUOUS) 23 | slave_task.triggers.sync_type.SLAVE = True 24 | 25 | master_task.control(TaskMode.TASK_COMMIT) 26 | 27 | slave_task.triggers.start_trigger.cfg_dig_edge_start_trig("/PXI1Slot3/ai/StartTrigger") 28 | 29 | print("2 Channels 1 Sample Read Loop 10: ") 30 | slave_task.start() 31 | master_task.start() 32 | 33 | for _ in range(10): 34 | master_data = master_task.read(number_of_samples_per_channel=10) 35 | slave_data = slave_task.read(number_of_samples_per_channel=10) 36 | 37 | print("Master Task Data: ") 38 | pp.pprint(master_data) 39 | print("Slave Task Data: ") 40 | pp.pprint(slave_data) 41 | -------------------------------------------------------------------------------- /examples/analog_in/ai_raw.py: -------------------------------------------------------------------------------- 1 | """Example of AI raw operation.""" 2 | 3 | import pprint 4 | 5 | import nidaqmx 6 | 7 | pp = pprint.PrettyPrinter(indent=4) 8 | 9 | with nidaqmx.Task() as task: 10 | task.ai_channels.add_ai_voltage_chan("cDAQ1Mod1/ai0") 11 | in_stream = task.in_stream 12 | 13 | print("1 Channel 1 Sample Read Raw: ") 14 | data = in_stream.read(number_of_samples_per_channel=1) 15 | pp.pprint(data) 16 | 17 | print("1 Channel N Samples Read Raw: ") 18 | data = in_stream.read(number_of_samples_per_channel=8) 19 | pp.pprint(data) 20 | 21 | task.ai_channels.add_ai_voltage_chan("cDAQ1Mod1/ai1:3") 22 | 23 | print("N Channel 1 Sample Read Raw: ") 24 | data = in_stream.read(number_of_samples_per_channel=1) 25 | pp.pprint(data) 26 | 27 | print("N Channel N Samples Read Raw: ") 28 | data = in_stream.read(number_of_samples_per_channel=8) 29 | pp.pprint(data) 30 | -------------------------------------------------------------------------------- /examples/analog_in/cont_thrmcpl_samples_int_clk.py: -------------------------------------------------------------------------------- 1 | """Example of continuous temperature acquisition. 2 | 3 | This example demonstrates how to make continuous, hardware-timed 4 | temperature measurement using a thermocouple. 5 | """ 6 | 7 | import nidaqmx 8 | from nidaqmx.constants import ( 9 | AcquisitionType, 10 | CJCSource, 11 | TemperatureUnits, 12 | ThermocoupleType, 13 | ) 14 | 15 | with nidaqmx.Task() as task: 16 | task.ai_channels.add_ai_thrmcpl_chan( 17 | "Dev1/ai0", 18 | units=TemperatureUnits.DEG_C, 19 | thermocouple_type=ThermocoupleType.K, 20 | cjc_source=CJCSource.CONSTANT_USER_VALUE, 21 | cjc_val=25.0, 22 | ) 23 | task.timing.cfg_samp_clk_timing(10.0, sample_mode=AcquisitionType.CONTINUOUS) 24 | task.start() 25 | print("Acquiring samples continuously. Press Ctrl+C to stop.") 26 | 27 | try: 28 | total_read = 0 29 | while True: 30 | data = task.read(number_of_samples_per_channel=50) 31 | read = len(data) 32 | total_read += read 33 | print(f"Acquired data: {read} samples. Total {total_read}.", end="\r") 34 | except KeyboardInterrupt: 35 | pass 36 | finally: 37 | task.stop() 38 | print(f"\nAcquired {total_read} total samples.") 39 | -------------------------------------------------------------------------------- /examples/analog_in/cont_voltage_acq_int_clk.py: -------------------------------------------------------------------------------- 1 | """Example of analog input voltage acquisition. 2 | 3 | This example demonstrates how to acquire a continuous amount of data 4 | using the DAQ device's internal clock. 5 | """ 6 | 7 | import nidaqmx 8 | from nidaqmx.constants import AcquisitionType 9 | 10 | with nidaqmx.Task() as task: 11 | task.ai_channels.add_ai_voltage_chan("Dev1/ai0") 12 | task.timing.cfg_samp_clk_timing(1000.0, sample_mode=AcquisitionType.CONTINUOUS) 13 | task.start() 14 | print("Running task. Press Ctrl+C to stop.") 15 | 16 | try: 17 | total_read = 0 18 | while True: 19 | data = task.read(number_of_samples_per_channel=1000) 20 | read = len(data) 21 | total_read += read 22 | print(f"Acquired data: {read} samples. Total {total_read}.", end="\r") 23 | except KeyboardInterrupt: 24 | pass 25 | finally: 26 | task.stop() 27 | print(f"\nAcquired {total_read} total samples.") 28 | -------------------------------------------------------------------------------- /examples/analog_in/cont_voltage_acq_int_clk_dig_start.py: -------------------------------------------------------------------------------- 1 | """Example of analog input voltage acquisition with a digital start trigger. 2 | 3 | This example demonstrates how to acquire a continuous amount of 4 | data using an external sample clock, started by a digital edge. 5 | """ 6 | 7 | import nidaqmx 8 | from nidaqmx.constants import AcquisitionType 9 | 10 | 11 | def main(): 12 | """Continuously acquires data started by a digital edge.""" 13 | total_read = 0 14 | with nidaqmx.Task() as task: 15 | 16 | def callback(task_handle, every_n_samples_event_type, number_of_samples, callback_data): 17 | """Callback function for reading signals.""" 18 | nonlocal total_read 19 | read = len(task.read(number_of_samples_per_channel=number_of_samples)) 20 | total_read += read 21 | print(f"Acquired data: {read} samples. Total {total_read}.", end="\r") 22 | 23 | return 0 24 | 25 | task.ai_channels.add_ai_voltage_chan("Dev1/ai0") 26 | task.timing.cfg_samp_clk_timing(1000.0, sample_mode=AcquisitionType.CONTINUOUS) 27 | task.triggers.start_trigger.cfg_dig_edge_start_trig("/Dev1/PFI0") 28 | 29 | task.register_every_n_samples_acquired_into_buffer_event(1000, callback) 30 | task.start() 31 | 32 | input("Acquiring samples continuously. Press Enter to stop.\n") 33 | 34 | task.stop() 35 | print(f"\nAcquired {total_read} total samples.") 36 | 37 | 38 | if __name__ == "__main__": 39 | main() 40 | -------------------------------------------------------------------------------- /examples/analog_in/cont_voltage_acq_int_clk_dig_start_retrig.py: -------------------------------------------------------------------------------- 1 | """Example of analog input voltage acquisition with retriggering. 2 | 3 | This example demonstrates how to acquire finite amounts of data 4 | on each digital trigger. 5 | """ 6 | 7 | import nidaqmx 8 | from nidaqmx.constants import AcquisitionType 9 | 10 | 11 | def main(): 12 | """Acquires data on each digital trigger.""" 13 | total_read = 0 14 | with nidaqmx.Task() as task: 15 | 16 | def callback(task_handle, every_n_samples_event_type, number_of_samples, callback_data): 17 | """Callback function for reading signals.""" 18 | nonlocal total_read 19 | read = len(task.read(number_of_samples_per_channel=number_of_samples)) 20 | total_read += read 21 | print(f"Acquired data: {read} samples. Total {total_read}.", end="\r") 22 | 23 | return 0 24 | 25 | task.ai_channels.add_ai_voltage_chan("Dev1/ai0") 26 | task.timing.cfg_samp_clk_timing( 27 | 1000.0, sample_mode=AcquisitionType.FINITE, samps_per_chan=1000 28 | ) 29 | task.triggers.start_trigger.cfg_dig_edge_start_trig("/Dev1/PFI0") 30 | task.triggers.start_trigger.retriggerable = True 31 | 32 | task.register_every_n_samples_acquired_into_buffer_event(1000, callback) 33 | task.start() 34 | 35 | input("Acquiring samples continuously. Press Enter to stop.\n") 36 | 37 | task.stop() 38 | print(f"\nAcquired {total_read} total samples.") 39 | 40 | 41 | if __name__ == "__main__": 42 | main() 43 | -------------------------------------------------------------------------------- /examples/analog_in/cont_voltage_acq_int_clk_every_n_samples_event.py: -------------------------------------------------------------------------------- 1 | """Example of analog input voltage acquisition with events. 2 | 3 | This example demonstrates how to use Every N Samples events to 4 | acquire a continuous amount of data using the DAQ device's 5 | internal clock. The Every N Samples events indicate when data is 6 | available from DAQmx. 7 | """ 8 | 9 | import nidaqmx 10 | from nidaqmx.constants import AcquisitionType 11 | 12 | 13 | def main(): 14 | """Continuously acquires data using an Every N Samples event.""" 15 | total_read = 0 16 | with nidaqmx.Task() as task: 17 | 18 | def callback(task_handle, every_n_samples_event_type, number_of_samples, callback_data): 19 | """Callback function for reading signals.""" 20 | nonlocal total_read 21 | read = task.read(number_of_samples_per_channel=number_of_samples) 22 | total_read += len(read) 23 | print(f"Acquired data: {len(read)} samples. Total {total_read}.", end="\r") 24 | 25 | return 0 26 | 27 | task.ai_channels.add_ai_voltage_chan("Dev1/ai0") 28 | task.timing.cfg_samp_clk_timing(1000.0, sample_mode=AcquisitionType.CONTINUOUS) 29 | task.register_every_n_samples_acquired_into_buffer_event(1000, callback) 30 | task.start() 31 | 32 | input("Running task. Press Enter to stop.\n") 33 | 34 | task.stop() 35 | print(f"\nAcquired {total_read} total samples.") 36 | 37 | 38 | if __name__ == "__main__": 39 | main() 40 | -------------------------------------------------------------------------------- /examples/analog_in/thrmcpl_sample.py: -------------------------------------------------------------------------------- 1 | """Example of analog input temperature acquisition. 2 | 3 | This example demonstrates how to acquire thermocouple measurement using 4 | software timing. 5 | """ 6 | 7 | import nidaqmx 8 | from nidaqmx.constants import ThermocoupleType, TemperatureUnits 9 | 10 | with nidaqmx.Task() as task: 11 | task.ai_channels.add_ai_thrmcpl_chan( 12 | "Dev1/ai0", units=TemperatureUnits.DEG_C, thermocouple_type=ThermocoupleType.K 13 | ) 14 | 15 | data = task.read() 16 | print(f"Acquired data: {data:f}") 17 | -------------------------------------------------------------------------------- /examples/analog_in/voltage_acq_int_clk.py: -------------------------------------------------------------------------------- 1 | """Example of analog input voltage acquisition. 2 | 3 | This example demonstrates how to acquire a finite amount 4 | of data using the DAQ device's internal clock. 5 | """ 6 | 7 | import nidaqmx 8 | from nidaqmx.constants import AcquisitionType, READ_ALL_AVAILABLE 9 | 10 | with nidaqmx.Task() as task: 11 | task.ai_channels.add_ai_voltage_chan("Dev1/ai0") 12 | task.timing.cfg_samp_clk_timing(1000.0, sample_mode=AcquisitionType.FINITE, samps_per_chan=50) 13 | 14 | data = task.read(READ_ALL_AVAILABLE) 15 | print("Acquired data: [" + ", ".join(f"{value:f}" for value in data) + "]") 16 | -------------------------------------------------------------------------------- /examples/analog_in/voltage_acq_int_clk_dig_ref.py: -------------------------------------------------------------------------------- 1 | """Example of analog input voltage acquisition with reference trigger. 2 | 3 | This example demonstrates how to acquire a finite amount of data 4 | using a digital reference trigger. 5 | """ 6 | 7 | import nidaqmx 8 | from nidaqmx.constants import READ_ALL_AVAILABLE, AcquisitionType 9 | 10 | with nidaqmx.Task() as task: 11 | task.ai_channels.add_ai_voltage_chan("Dev1/ai0") 12 | task.timing.cfg_samp_clk_timing(1000.0, sample_mode=AcquisitionType.FINITE, samps_per_chan=100) 13 | task.triggers.reference_trigger.cfg_dig_edge_ref_trig("/Dev1/PFI8", pretrigger_samples=50) 14 | 15 | task.start() 16 | data = task.read(READ_ALL_AVAILABLE) 17 | print("Acquired data: [" + ", ".join(f"{value:f}" for value in data) + "]") 18 | task.stop() 19 | -------------------------------------------------------------------------------- /examples/analog_in/voltage_acq_int_clk_dig_start_ref.py: -------------------------------------------------------------------------------- 1 | """Example of analog input voltage acquisition with digital start and reference trigger. 2 | 3 | This example demonstrates how to acquire a finite amount of data 4 | using an internal clock and a digital start and reference 5 | trigger. 6 | """ 7 | 8 | import nidaqmx 9 | from nidaqmx.constants import READ_ALL_AVAILABLE, AcquisitionType 10 | 11 | with nidaqmx.Task() as task: 12 | task.ai_channels.add_ai_voltage_chan("Dev1/ai0") 13 | task.timing.cfg_samp_clk_timing(1000.0, sample_mode=AcquisitionType.FINITE, samps_per_chan=100) 14 | task.triggers.start_trigger.cfg_dig_edge_start_trig("/Dev1/PFI0") 15 | task.triggers.reference_trigger.cfg_dig_edge_ref_trig("/Dev1/PFI8", pretrigger_samples=50) 16 | 17 | task.start() 18 | data = task.read(READ_ALL_AVAILABLE) 19 | print("Acquired data: [" + ", ".join(f"{value:f}" for value in data) + "]") 20 | task.stop() 21 | -------------------------------------------------------------------------------- /examples/analog_in/voltage_acq_int_clk_plot_data.py: -------------------------------------------------------------------------------- 1 | """Example of generating visualizations for acquired data. 2 | 3 | This example demonstrates how to plot the acquired data. 4 | This example requires the matplotlib module. 5 | Run 'pip install matplotlib' to install the matplotlib module. 6 | """ 7 | 8 | import matplotlib.pyplot as plot 9 | 10 | import nidaqmx 11 | from nidaqmx.constants import READ_ALL_AVAILABLE, AcquisitionType 12 | 13 | with nidaqmx.Task() as task: 14 | task.ai_channels.add_ai_voltage_chan("Dev1/ai0") 15 | task.timing.cfg_samp_clk_timing(1000.0, sample_mode=AcquisitionType.FINITE, samps_per_chan=50) 16 | 17 | data = task.read(READ_ALL_AVAILABLE) 18 | 19 | plot.plot(data) 20 | plot.ylabel("Amplitude") 21 | plot.title("Waveform") 22 | plot.show() 23 | -------------------------------------------------------------------------------- /examples/analog_in/voltage_acq_int_clk_tdms_logging.py: -------------------------------------------------------------------------------- 1 | """Example of logging acquired data to TDMS file and read back. 2 | 3 | This example demonstrates how to log the acquired data to a TDMS file 4 | and then read the data from the file. 5 | This example requires the nptdms module. 6 | Run 'pip install nptdms' to install the nptdms module. 7 | """ 8 | 9 | import os 10 | 11 | from nptdms import TdmsFile 12 | 13 | import nidaqmx 14 | from nidaqmx.constants import ( 15 | READ_ALL_AVAILABLE, 16 | AcquisitionType, 17 | LoggingMode, 18 | LoggingOperation, 19 | ) 20 | 21 | with nidaqmx.Task() as task: 22 | task.ai_channels.add_ai_voltage_chan("Dev1/ai0") 23 | task.timing.cfg_samp_clk_timing(1000.0, sample_mode=AcquisitionType.FINITE, samps_per_chan=10) 24 | task.in_stream.configure_logging( 25 | "TestData.tdms", LoggingMode.LOG_AND_READ, operation=LoggingOperation.CREATE_OR_REPLACE 26 | ) 27 | 28 | task.read(READ_ALL_AVAILABLE) 29 | 30 | with TdmsFile.open("TestData.tdms") as tdms_file: 31 | for group in tdms_file.groups(): 32 | for channel in group.channels(): 33 | data = channel[:] 34 | print("Read data from TDMS file: [" + ", ".join(f"{value:f}" for value in data) + "]") 35 | 36 | if os.path.exists("TestData.tdms"): 37 | os.remove("TestData.tdms") 38 | 39 | if os.path.exists("TestData.tdms_index"): 40 | os.remove("TestData.tdms_index") 41 | -------------------------------------------------------------------------------- /examples/analog_in/voltage_sample.py: -------------------------------------------------------------------------------- 1 | """Example of analog input voltage acquisition. 2 | 3 | This example demonstrates how to acquire a voltage measurement using software timing. 4 | """ 5 | 6 | import nidaqmx 7 | 8 | with nidaqmx.Task() as task: 9 | task.ai_channels.add_ai_voltage_chan("Dev1/ai0") 10 | 11 | data = task.read() 12 | print(f"Acquired data: {data:f}") 13 | -------------------------------------------------------------------------------- /examples/analog_out/gen_voltage_wfm_int_clk.py: -------------------------------------------------------------------------------- 1 | """Example of analog output voltage generation. 2 | 3 | This example demonstrates how to output a finite number of 4 | voltage samples to an Analog Output Channel using an internal 5 | sample clock. 6 | """ 7 | 8 | import nidaqmx 9 | from nidaqmx.constants import AcquisitionType 10 | 11 | with nidaqmx.Task() as task: 12 | data = [] 13 | total_samples = 1000 14 | task.ao_channels.add_ao_voltage_chan("Dev1/ao0") 15 | task.timing.cfg_samp_clk_timing( 16 | 1000.0, sample_mode=AcquisitionType.FINITE, samps_per_chan=total_samples 17 | ) 18 | 19 | data = [5.0 * i / total_samples for i in range(total_samples)] 20 | number_of_samples_written = task.write(data, auto_start=True) 21 | print(f"Generating {number_of_samples_written} voltage samples.") 22 | task.wait_until_done() 23 | task.stop() 24 | -------------------------------------------------------------------------------- /examples/analog_out/voltage_update.py: -------------------------------------------------------------------------------- 1 | """Example of analog output voltage generation. 2 | 3 | This example demonstrates how to output a single Voltage Update 4 | (Sample) to an Analog Output Channel. 5 | """ 6 | 7 | import nidaqmx 8 | 9 | with nidaqmx.Task() as task: 10 | task.ao_channels.add_ao_voltage_chan("Dev1/ao0") 11 | 12 | number_of_samples_written = task.write(1.1) 13 | print(f"Generated {number_of_samples_written} voltage sample.") 14 | -------------------------------------------------------------------------------- /examples/channel_properties.py: -------------------------------------------------------------------------------- 1 | """Example for using channel properties.""" 2 | 3 | import pprint 4 | 5 | import nidaqmx 6 | 7 | pp = pprint.PrettyPrinter(indent=4) 8 | 9 | 10 | with nidaqmx.Task() as task: 11 | ai_channel = task.ai_channels.add_ai_voltage_chan("Dev1/ai0", max_val=0.1, min_val=-0.1) 12 | 13 | print(ai_channel.ai_max) 14 | print(ai_channel.ai_min) 15 | 16 | ai_channel.ai_max = 5 17 | 18 | print(ai_channel.ai_max) 19 | print(ai_channel.ai_min) 20 | 21 | ai_channels_1_to_3 = task.ai_channels.add_ai_voltage_chan("Dev1/ai1:3", max_val=10, min_val=-10) 22 | 23 | print(ai_channels_1_to_3.ai_max) 24 | print(ai_channels_1_to_3.ai_min) 25 | 26 | print(ai_channel.ai_max) 27 | print(ai_channel.ai_min) 28 | 29 | print(task.ai_channels["Dev1/ai2"].physical_channel.name) 30 | -------------------------------------------------------------------------------- /examples/counter_in/cnt_dig_event.py: -------------------------------------------------------------------------------- 1 | """Example of counter input edge count operation. 2 | 3 | This example demonstrates how to count digital events on a 4 | Counter Input Channel. The Initial Count, Count Direction, and 5 | Edge are all configurable. 6 | """ 7 | 8 | import nidaqmx 9 | from nidaqmx.constants import CountDirection, Edge 10 | 11 | with nidaqmx.Task() as task: 12 | channel = task.ci_channels.add_ci_count_edges_chan( 13 | "Dev1/ctr0", 14 | edge=Edge.RISING, 15 | initial_count=0, 16 | count_direction=CountDirection.COUNT_UP, 17 | ) 18 | channel.ci_count_edges_term = "/Dev1/PFI8" 19 | 20 | print("Continuously polling. Press Ctrl+C to stop.") 21 | task.start() 22 | 23 | try: 24 | edge_counts = 0 25 | while True: 26 | edge_counts = task.read() 27 | print(f"Acquired count: {edge_counts:n}", end="\r") 28 | except KeyboardInterrupt: 29 | pass 30 | finally: 31 | task.stop() 32 | print(f"\nAcquired {edge_counts:n} total counts.") 33 | -------------------------------------------------------------------------------- /examples/counter_in/cont_cnt_buff_event_ext_clk.py: -------------------------------------------------------------------------------- 1 | """Example of counter input edge count operation. 2 | 3 | This example demonstrates how to count buffered digital events 4 | on a Counter Input Channel. The Initial Count, Count Direction, 5 | Edge, and Sample Clock Source are all configurable. 6 | """ 7 | 8 | import nidaqmx 9 | from nidaqmx.constants import AcquisitionType, CountDirection, Edge 10 | 11 | with nidaqmx.Task() as task: 12 | channel = task.ci_channels.add_ci_count_edges_chan( 13 | "Dev1/ctr0", 14 | edge=Edge.RISING, 15 | initial_count=0, 16 | count_direction=CountDirection.COUNT_UP, 17 | ) 18 | task.timing.cfg_samp_clk_timing( 19 | 1000, source="/Dev1/PFI9", sample_mode=AcquisitionType.CONTINUOUS 20 | ) 21 | channel.ci_count_edges_term = "/Dev1/PFI8" 22 | 23 | print("Continuously polling. Press Ctrl+C to stop.") 24 | task.start() 25 | 26 | try: 27 | total_read = 0 28 | while True: 29 | edge_counts = task.read(number_of_samples_per_channel=1000) 30 | total_read += len(edge_counts) 31 | print(f"Acquired data: {len(edge_counts)} samples. Total {total_read}.", end="\r") 32 | except KeyboardInterrupt: 33 | pass 34 | finally: 35 | task.stop() 36 | print(f"\nAcquired {total_read} total samples.") 37 | -------------------------------------------------------------------------------- /examples/counter_in/cont_read_buff_freq.py: -------------------------------------------------------------------------------- 1 | """Example of counter input frequency acquisition. 2 | 3 | This example demonstrates how to continuously measure buffered frequency 4 | using one counter on a Counter Input Channel. The Edge, Minimum Value 5 | and Maximum Value are all configurable. 6 | """ 7 | 8 | import nidaqmx 9 | from nidaqmx.constants import AcquisitionType, Edge, FrequencyUnits 10 | 11 | 12 | with nidaqmx.Task() as task: 13 | channel = task.ci_channels.add_ci_freq_chan( 14 | "Dev1/ctr0", 15 | min_val=2.0, 16 | max_val=100000.0, 17 | units=FrequencyUnits.HZ, 18 | edge=Edge.RISING, 19 | ) 20 | channel.ci_freq_term = "/Dev1/PFI8" 21 | task.timing.cfg_implicit_timing(sample_mode=AcquisitionType.CONTINUOUS) 22 | 23 | print("Continuously polling. Press Ctrl+C to stop.") 24 | task.start() 25 | 26 | try: 27 | total_read = 0 28 | while True: 29 | frequencies = task.read(number_of_samples_per_channel=1000) 30 | total_read += len(frequencies) 31 | print(f"Acquired data: {len(frequencies)} samples. Total {total_read}.", end="\r") 32 | except KeyboardInterrupt: 33 | pass 34 | finally: 35 | task.stop() 36 | print(f"\nAcquired {total_read} total samples.") 37 | -------------------------------------------------------------------------------- /examples/counter_in/read_freq.py: -------------------------------------------------------------------------------- 1 | """Example of counter input frequency acquisition. 2 | 3 | This example demonstrates how to measure frequency using one 4 | counter on a Counter Input Channel. The Edge, Minimum Value and 5 | Maximum Value are all configurable. 6 | """ 7 | 8 | import nidaqmx 9 | from nidaqmx.constants import Edge, FrequencyUnits 10 | 11 | 12 | with nidaqmx.Task() as task: 13 | channel = task.ci_channels.add_ci_freq_chan( 14 | "Dev1/ctr0", 15 | min_val=2.0, 16 | max_val=100000.0, 17 | units=FrequencyUnits.HZ, 18 | edge=Edge.RISING, 19 | ) 20 | channel.ci_freq_term = "/Dev1/PFI8" 21 | 22 | task.start() 23 | 24 | data = task.read() 25 | print(f"Acquired frequency: {data:.2f} Hz") 26 | 27 | task.stop() 28 | -------------------------------------------------------------------------------- /examples/counter_in/read_pulse_freq.py: -------------------------------------------------------------------------------- 1 | """Example of CI pulse frequency operation. 2 | 3 | This example demonstrates how to configure a pulse measurement to acquire frequency and duty cycle. 4 | """ 5 | 6 | import nidaqmx 7 | from nidaqmx.constants import FrequencyUnits 8 | 9 | with nidaqmx.Task() as task: 10 | channel = task.ci_channels.add_ci_pulse_chan_freq( 11 | "Dev1/ctr0", "", min_val=2.0, max_val=100000.0, units=FrequencyUnits.HZ 12 | ) 13 | channel.ci_pulse_freq_term = "/Dev1/PFI8" 14 | 15 | task.start() 16 | 17 | data = task.read() 18 | print(f"Acquired data:") 19 | print(f"Frequency: {data.freq:.2f} Hz") 20 | print(f"Duty cycle: {(data.duty_cycle * 100):.2f}%") 21 | 22 | task.stop() 23 | -------------------------------------------------------------------------------- /examples/counter_out/cont_gen_dig_pulse_train.py: -------------------------------------------------------------------------------- 1 | """Example for continuously generating digital pulse train. 2 | 3 | This example demonstrates how to generate a continuous digital 4 | pulse train from a Counter Output Channel. The Frequency, Duty 5 | Cycle, and Idle State are all configurable. 6 | """ 7 | 8 | import nidaqmx 9 | from nidaqmx.constants import AcquisitionType, Level 10 | 11 | with nidaqmx.Task() as task: 12 | channel = task.co_channels.add_co_pulse_chan_freq( 13 | "Dev1/ctr0", idle_state=Level.LOW, initial_delay=0.0, freq=1.0, duty_cycle=0.5 14 | ) 15 | channel.co_pulse_term = "/Dev1/PFI12" 16 | task.timing.cfg_implicit_timing(sample_mode=AcquisitionType.CONTINUOUS) 17 | task.start() 18 | 19 | input("Generating pulse train. Press Enter to stop.\n") 20 | 21 | task.stop() 22 | -------------------------------------------------------------------------------- /examples/counter_out/cont_gen_dig_pulse_train_buff_ext_clk.py: -------------------------------------------------------------------------------- 1 | """Example for continuously generating digital pulse train with external timing. 2 | 3 | This example demonstrates how to generate a continuous buffered 4 | sample clocked digital pulse train from a Counter Output 5 | Channel. The Frequency, Duty Cycle, and Idle State are all 6 | configurable. The default data generated is a pulse train with a 7 | fixed frequency but a duty cycle that varies based on the Duty 8 | Cycle Max/Min and the signal type. The duty cycle will update 9 | with each sample clock edge. 10 | """ 11 | 12 | import nidaqmx 13 | from nidaqmx.constants import AcquisitionType, Level 14 | from nidaqmx.types import CtrFreq 15 | 16 | with nidaqmx.Task() as task: 17 | duty_min = 0.5 18 | duty_max = 0.8 19 | duty_step = (duty_max - duty_min) / 1000 20 | ctr_freq_data = [CtrFreq(1000, (duty_min + duty_step * i)) for i in range(1000)] 21 | 22 | channel = task.co_channels.add_co_pulse_chan_freq( 23 | "Dev1/ctr0", idle_state=Level.LOW, initial_delay=0.0, freq=1.0, duty_cycle=0.5 24 | ) 25 | channel.co_pulse_term = "/Dev1/PFI12" 26 | task.timing.cfg_samp_clk_timing(1000.0, "/Dev1/PFI0", sample_mode=AcquisitionType.CONTINUOUS) 27 | task.out_stream.output_buf_size = 1000 28 | task.write(ctr_freq_data) 29 | task.start() 30 | 31 | input("Generating pulse train. Press Enter to stop.\n") 32 | 33 | task.stop() 34 | -------------------------------------------------------------------------------- /examples/counter_out/cont_gen_dig_pulse_train_buff_implicit.py: -------------------------------------------------------------------------------- 1 | """Example for continuously generating digital pulse train with implicit timing. 2 | 3 | This example demonstrates how to generate a continuous buffered 4 | implicit timed digital pulse train from a Counter Output 5 | Channel. The Frequency, Duty Cycle, and Idle State are all 6 | configurable. The default data generated is a pulse train with a 7 | fixed frequency but a duty cycle that varies based on the Duty 8 | Cycle Max/Min and the signal type. The duty cycle will update 9 | with each sample generated. 10 | """ 11 | 12 | import nidaqmx 13 | from nidaqmx.constants import AcquisitionType, Level 14 | from nidaqmx.types import CtrFreq 15 | 16 | with nidaqmx.Task() as task: 17 | duty_min = 0.5 18 | duty_max = 0.8 19 | duty_step = (duty_max - duty_min) / 1000 20 | ctr_freq_data = [CtrFreq(1000, (duty_min + duty_step * i)) for i in range(1000)] 21 | 22 | channel = task.co_channels.add_co_pulse_chan_freq( 23 | "Dev1/ctr0", idle_state=Level.LOW, initial_delay=0.0, freq=1.0, duty_cycle=0.5 24 | ) 25 | channel.co_pulse_term = "/Dev1/PFI12" 26 | task.timing.cfg_implicit_timing(sample_mode=AcquisitionType.CONTINUOUS) 27 | task.write(ctr_freq_data) 28 | task.start() 29 | 30 | input("Generating pulse train. Press Enter to stop.\n") 31 | 32 | task.stop() 33 | -------------------------------------------------------------------------------- /examples/counter_out/write_single_dig_pulse.py: -------------------------------------------------------------------------------- 1 | """Example for generating a single digital pulse. 2 | 3 | This example demonstrates how to generate a single digital pulse 4 | from a Counter Output Channel. The Initial Delay, High Time, Low 5 | Time, and Idle State are all configurable. 6 | """ 7 | 8 | import nidaqmx 9 | from nidaqmx.constants import Level 10 | 11 | with nidaqmx.Task() as task: 12 | channel = task.co_channels.add_co_pulse_chan_time( 13 | "Dev1/ctr0", idle_state=Level.LOW, initial_delay=0.0, low_time=0.5, high_time=1.0 14 | ) 15 | channel.co_pulse_term = "/Dev1/PFI12" 16 | 17 | task.start() 18 | task.wait_until_done(timeout=10) 19 | task.stop() 20 | -------------------------------------------------------------------------------- /examples/digital_in/acq_dig_port_int_clk.py: -------------------------------------------------------------------------------- 1 | """Example for reading digital signals. 2 | 3 | This example demonstrates how to input a finite digital pattern 4 | using the DAQ device's internal clock. 5 | """ 6 | 7 | import nidaqmx 8 | from nidaqmx.constants import AcquisitionType, LineGrouping, READ_ALL_AVAILABLE 9 | 10 | 11 | with nidaqmx.Task() as task: 12 | task.di_channels.add_di_chan("Dev1/port0", line_grouping=LineGrouping.CHAN_FOR_ALL_LINES) 13 | task.timing.cfg_samp_clk_timing(1000.0, sample_mode=AcquisitionType.FINITE, samps_per_chan=50) 14 | 15 | data = task.read(READ_ALL_AVAILABLE) 16 | print("Acquired data: [" + ", ".join(f"{value:#x}" for value in data) + "]") 17 | -------------------------------------------------------------------------------- /examples/digital_in/cont_acq_dig_lines_int_clk.py: -------------------------------------------------------------------------------- 1 | """Example for reading digital signals. 2 | 3 | This example demonstrates how to acquire a continuous digital 4 | waveform using the DAQ device's internal clock. 5 | """ 6 | 7 | import nidaqmx 8 | from nidaqmx.constants import AcquisitionType, LineGrouping 9 | 10 | 11 | with nidaqmx.Task() as task: 12 | task.di_channels.add_di_chan("Dev1/port0/line0:3", line_grouping=LineGrouping.CHAN_PER_LINE) 13 | task.timing.cfg_samp_clk_timing(1000.0, sample_mode=AcquisitionType.CONTINUOUS) 14 | task.start() 15 | print("Acquiring samples continuously. Press Ctrl+C to stop.") 16 | 17 | try: 18 | total_read = 0 19 | while True: 20 | data = task.read(number_of_samples_per_channel=1000) 21 | read = len(data) 22 | total_read += read 23 | print(f"Acquired data: {read} samples. Total {total_read}.", end="\r") 24 | except KeyboardInterrupt: 25 | pass 26 | finally: 27 | task.stop() 28 | print(f"\nAcquired {total_read} total samples.") 29 | -------------------------------------------------------------------------------- /examples/digital_in/read_dig_lines.py: -------------------------------------------------------------------------------- 1 | """Example for reading digital signals. 2 | 3 | This example demonstrates how to read values from one or more 4 | digital input channels. 5 | """ 6 | 7 | import nidaqmx 8 | from nidaqmx.constants import LineGrouping 9 | 10 | 11 | with nidaqmx.Task() as task: 12 | task.di_channels.add_di_chan("Dev1/port0/line0:3", line_grouping=LineGrouping.CHAN_PER_LINE) 13 | 14 | data = task.read() 15 | print(f"Acquired data: {data}") 16 | -------------------------------------------------------------------------------- /examples/digital_in/read_dig_port.py: -------------------------------------------------------------------------------- 1 | """Example for reading a digital signal. 2 | 3 | This example demonstrates how to read values from a digital 4 | input port. 5 | """ 6 | 7 | import nidaqmx 8 | from nidaqmx.constants import LineGrouping 9 | 10 | 11 | with nidaqmx.Task() as task: 12 | task.di_channels.add_di_chan("Dev1/port0", line_grouping=LineGrouping.CHAN_FOR_ALL_LINES) 13 | 14 | data = task.read() 15 | print(f"Acquired data: {data:#x}") 16 | -------------------------------------------------------------------------------- /examples/digital_out/cont_gen_dig_port_int_clk.py: -------------------------------------------------------------------------------- 1 | """Example for generating digital signals. 2 | 3 | This example demonstrates how to output a continuous digital 4 | pattern using the DAQ device's clock. 5 | """ 6 | 7 | import nidaqmx 8 | from nidaqmx.constants import AcquisitionType, LineGrouping 9 | 10 | with nidaqmx.Task() as task: 11 | data = [1, 2, 4, 8, 16, 32, 64, 128] 12 | 13 | task.do_channels.add_do_chan("Dev1/port0", line_grouping=LineGrouping.CHAN_FOR_ALL_LINES) 14 | task.timing.cfg_samp_clk_timing(1000.0, sample_mode=AcquisitionType.CONTINUOUS) 15 | task.write(data) 16 | task.start() 17 | 18 | input("Generating voltage continuously. Press Enter to stop.\n") 19 | 20 | task.stop() 21 | -------------------------------------------------------------------------------- /examples/digital_out/gen_dig_line_int_clk.py: -------------------------------------------------------------------------------- 1 | """Example for generating digital signals. 2 | 3 | This example demonstrates how to output a finite digital 4 | waveform using the DAQ device's internal clock. 5 | """ 6 | 7 | import nidaqmx 8 | from nidaqmx.constants import AcquisitionType, LineGrouping 9 | 10 | with nidaqmx.Task() as task: 11 | data: bool = [bool(i % 2) for i in range(1000)] 12 | 13 | task.do_channels.add_do_chan("Dev1/port0/line0", line_grouping=LineGrouping.CHAN_PER_LINE) 14 | task.timing.cfg_samp_clk_timing( 15 | 1000.0, sample_mode=AcquisitionType.FINITE, samps_per_chan=len(data) 16 | ) 17 | 18 | number_of_samples_written = task.write(data) 19 | task.start() 20 | print(f"Generating {number_of_samples_written} voltage samples.") 21 | task.wait_until_done() 22 | task.stop() 23 | -------------------------------------------------------------------------------- /examples/digital_out/write_dig_lines.py: -------------------------------------------------------------------------------- 1 | """Example for generating digital signals. 2 | 3 | This example demonstrates how to write values to a digital 4 | output channel. 5 | """ 6 | 7 | import nidaqmx 8 | from nidaqmx.constants import LineGrouping 9 | 10 | with nidaqmx.Task() as task: 11 | data = [True, False, True, False] 12 | 13 | task.do_channels.add_do_chan("Dev1/port0/line0:3", line_grouping=LineGrouping.CHAN_PER_LINE) 14 | task.start() 15 | task.write(data) 16 | task.stop() 17 | -------------------------------------------------------------------------------- /examples/digital_out/write_dig_port.py: -------------------------------------------------------------------------------- 1 | """Example for generating digital signals. 2 | 3 | This example demonstrates how to write values to a digital 4 | output port. 5 | """ 6 | 7 | import nidaqmx 8 | from nidaqmx.constants import LineGrouping 9 | 10 | with nidaqmx.Task() as task: 11 | data = 0xFFFFFFFF 12 | 13 | task.do_channels.add_do_chan("Dev1/port0", line_grouping=LineGrouping.CHAN_FOR_ALL_LINES) 14 | task.start() 15 | task.write(data) 16 | task.stop() 17 | -------------------------------------------------------------------------------- /examples/every_n_samples_event.py: -------------------------------------------------------------------------------- 1 | """Example for reading signals for every n samples.""" 2 | 3 | import pprint 4 | 5 | import nidaqmx 6 | from nidaqmx.constants import AcquisitionType 7 | 8 | pp = pprint.PrettyPrinter(indent=4) 9 | 10 | 11 | with nidaqmx.Task() as task: 12 | task.ai_channels.add_ai_voltage_chan("Dev1/ai0") 13 | 14 | task.timing.cfg_samp_clk_timing(1000, sample_mode=AcquisitionType.CONTINUOUS) 15 | 16 | samples = [] 17 | 18 | def callback(task_handle, every_n_samples_event_type, number_of_samples, callback_data): 19 | """Callback function for reading signals.""" 20 | print("Every N Samples callback invoked.") 21 | 22 | samples.extend(task.read(number_of_samples_per_channel=1000)) 23 | 24 | return 0 25 | 26 | task.register_every_n_samples_acquired_into_buffer_event(1000, callback) 27 | 28 | task.start() 29 | 30 | input("Running task. Press Enter to stop and see number of " "accumulated samples.\n") 31 | 32 | print(len(samples)) 33 | -------------------------------------------------------------------------------- /examples/nidaqmx_warnings.py: -------------------------------------------------------------------------------- 1 | """Example for catching warnings in DAQmx.""" 2 | 3 | import warnings 4 | 5 | import nidaqmx 6 | from nidaqmx.error_codes import DAQmxWarnings 7 | 8 | # By default warnings are shown. 9 | with nidaqmx.Task() as task: 10 | task.ao_channels.add_ao_voltage_chan("Dev1/ao0") 11 | task.timing.cfg_samp_clk_timing(1000) 12 | 13 | task.write([1.1, 2.2, 3.3, 4.4, 5.5], auto_start=True) 14 | task.stop() 15 | 16 | 17 | # Catching warnings using the built-in warnings context manager. 18 | with nidaqmx.Task() as task: 19 | task.ao_channels.add_ao_voltage_chan("Dev1/ao0") 20 | task.timing.cfg_samp_clk_timing(1000) 21 | 22 | with warnings.catch_warnings(record=True) as w: 23 | task.write([1.1, 2.2, 3.3, 4.4, 5.5], auto_start=True) 24 | task.stop() 25 | 26 | if not task.devices[0].is_simulated: 27 | # Verify some things 28 | assert len(w) == 1 29 | assert issubclass(w[-1].category, nidaqmx.DaqWarning) 30 | 31 | if w: 32 | print("DaqWarning caught: {}\n".format(w[-1].message)) 33 | 34 | 35 | # Raising warnings as exceptions. 36 | with nidaqmx.Task() as task: 37 | task.ao_channels.add_ao_voltage_chan("Dev1/ao0") 38 | task.timing.cfg_samp_clk_timing(1000) 39 | 40 | warnings.filterwarnings("error", category=nidaqmx.DaqWarning) 41 | 42 | try: 43 | task.write([1.1, 2.2, 3.3, 4.4, 5.5], auto_start=True) 44 | task.stop() 45 | except nidaqmx.DaqWarning as e: 46 | print("DaqWarning caught as exception: {}\n".format(e)) 47 | assert e.error_code == DAQmxWarnings.STOPPED_BEFORE_DONE 48 | 49 | 50 | # Suppressing DaqWarnings. 51 | print("Suppressing warnings.") 52 | warnings.filterwarnings("ignore", category=nidaqmx.DaqWarning) 53 | 54 | with nidaqmx.Task() as task: 55 | task.ao_channels.add_ao_voltage_chan("Dev1/ao0") 56 | task.timing.cfg_samp_clk_timing(1000) 57 | 58 | task.write([1.1, 2.2, 3.3, 4.4, 5.5], auto_start=True) 59 | task.stop() 60 | -------------------------------------------------------------------------------- /examples/pwr_hw_timed.py: -------------------------------------------------------------------------------- 1 | """Examples for hw timed power operation.""" 2 | 3 | import pprint 4 | 5 | import nidaqmx 6 | from nidaqmx.constants import AcquisitionType, PowerIdleOutputBehavior, Sense 7 | 8 | pp = pprint.PrettyPrinter(indent=4) 9 | 10 | 11 | with nidaqmx.Task() as task: 12 | # Channel Settings 13 | voltage_setpoint = 0 14 | current_setpoint = 0.03 15 | output_enable = True 16 | idle_output_behavior = PowerIdleOutputBehavior.OUTPUT_DISABLED 17 | remote_sense = Sense.LOCAL 18 | 19 | # Timing Settings 20 | sample_rate = 10.0 21 | number_of_samples_per_channel = 10 22 | 23 | chan = task.ai_channels.add_ai_power_chan( 24 | "TS1Mod1/power", voltage_setpoint, current_setpoint, output_enable 25 | ) 26 | chan.pwr_idle_output_behavior = idle_output_behavior 27 | chan.pwr_remote_sense = remote_sense 28 | 29 | task.timing.cfg_samp_clk_timing(sample_rate, sample_mode=AcquisitionType.CONTINUOUS) 30 | 31 | task.start() 32 | 33 | try: 34 | print("Press Ctrl+C to stop") 35 | while True: 36 | # Note: at runtime, you can write the following channel attributes: 37 | # * pwr_voltage_setpoint 38 | # * pwr_current_setpoint 39 | # * pwr_output_enable 40 | 41 | data = task.read(number_of_samples_per_channel=number_of_samples_per_channel) 42 | 43 | print(f"Data:") 44 | pp.pprint(data) 45 | 46 | print(f"output state: {chan.pwr_output_state}") 47 | if task.in_stream.overtemperature_chans_exist: 48 | print(f"overtemperature chans: {task.in_stream.overtemperature_chans}") 49 | except KeyboardInterrupt: 50 | pass 51 | 52 | task.stop() 53 | -------------------------------------------------------------------------------- /examples/pwr_hw_timed_stream.py: -------------------------------------------------------------------------------- 1 | """Examples for hw timed power stream operation.""" 2 | 3 | import pprint 4 | 5 | import numpy 6 | 7 | import nidaqmx 8 | from nidaqmx.constants import AcquisitionType, PowerIdleOutputBehavior, Sense 9 | from nidaqmx.stream_readers import PowerSingleChannelReader 10 | 11 | pp = pprint.PrettyPrinter(indent=4) 12 | 13 | 14 | with nidaqmx.Task() as task: 15 | # Channel Settings 16 | voltage_setpoint = 0 17 | current_setpoint = 0.03 18 | output_enable = True 19 | idle_output_behavior = PowerIdleOutputBehavior.OUTPUT_DISABLED 20 | remote_sense = Sense.LOCAL 21 | 22 | # Timing Settings 23 | sample_rate = 100.0 24 | number_of_samples_per_channel = 100 25 | 26 | chan = task.ai_channels.add_ai_power_chan( 27 | "TS1Mod1/power", voltage_setpoint, current_setpoint, output_enable 28 | ) 29 | chan.pwr_idle_output_behavior = idle_output_behavior 30 | chan.pwr_remote_sense = remote_sense 31 | 32 | task.timing.cfg_samp_clk_timing(sample_rate, sample_mode=AcquisitionType.CONTINUOUS) 33 | 34 | stream = PowerSingleChannelReader(task.in_stream) 35 | voltage_data = numpy.zeros(number_of_samples_per_channel, dtype=numpy.float64) 36 | current_data = numpy.zeros(number_of_samples_per_channel, dtype=numpy.float64) 37 | 38 | task.start() 39 | 40 | try: 41 | print("Press Ctrl+C to stop") 42 | while True: 43 | # Note: at runtime, you can write the following channel attributes: 44 | # * pwr_voltage_setpoint 45 | # * pwr_current_setpoint 46 | # * pwr_output_enable 47 | 48 | stream.read_many_sample( 49 | voltage_data, 50 | current_data, 51 | number_of_samples_per_channel=number_of_samples_per_channel, 52 | ) 53 | 54 | print(f"Voltage Data:") 55 | pp.pprint(voltage_data) 56 | 57 | print(f"Current Data:") 58 | pp.pprint(current_data) 59 | 60 | print(f"output state: {chan.pwr_output_state}") 61 | if task.in_stream.overtemperature_chans_exist: 62 | print(f"overtemperature chans: {task.in_stream.overtemperature_chans}") 63 | except KeyboardInterrupt: 64 | pass 65 | 66 | task.stop() 67 | -------------------------------------------------------------------------------- /examples/pwr_sw_timed.py: -------------------------------------------------------------------------------- 1 | """Examples for sw timed power operation.""" 2 | 3 | import pprint 4 | import time 5 | 6 | import nidaqmx 7 | from nidaqmx.constants import PowerIdleOutputBehavior, Sense 8 | 9 | pp = pprint.PrettyPrinter(indent=4) 10 | 11 | 12 | with nidaqmx.Task() as task: 13 | # Channel Settings 14 | voltage_setpoint = 0 15 | current_setpoint = 0.03 16 | output_enable = True 17 | idle_output_behavior = PowerIdleOutputBehavior.OUTPUT_DISABLED 18 | remote_sense = Sense.LOCAL 19 | 20 | chan = task.ai_channels.add_ai_power_chan( 21 | "TS1Mod1/power", voltage_setpoint, current_setpoint, output_enable 22 | ) 23 | chan.pwr_idle_output_behavior = idle_output_behavior 24 | chan.pwr_remote_sense = remote_sense 25 | 26 | task.start() 27 | 28 | try: 29 | print("Press Ctrl+C to stop") 30 | while True: 31 | # Note: at runtime, you can write the following channel attributes: 32 | # * pwr_voltage_setpoint 33 | # * pwr_current_setpoint 34 | # * pwr_output_enable 35 | 36 | data = task.read() 37 | 38 | print(f"Data:") 39 | pp.pprint(data) 40 | 41 | print(f"output state: {chan.pwr_output_state}") 42 | if task.in_stream.overtemperature_chans_exist: 43 | print(f"overtemperature chans: {task.in_stream.overtemperature_chans}") 44 | 45 | time.sleep(1) 46 | except KeyboardInterrupt: 47 | pass 48 | 49 | task.stop() 50 | -------------------------------------------------------------------------------- /examples/system_properties.py: -------------------------------------------------------------------------------- 1 | """Examples for using system properties in DAQmx.""" 2 | 3 | import nidaqmx 4 | 5 | local_system = nidaqmx.system.System.local() 6 | driver_version = local_system.driver_version 7 | 8 | print( 9 | "DAQmx {}.{}.{}".format( 10 | driver_version.major_version, 11 | driver_version.minor_version, 12 | driver_version.update_version, 13 | ) 14 | ) 15 | 16 | for device in local_system.devices: 17 | print( 18 | "Device Name: {}, Product Category: {}, Product Type: {}".format( 19 | device.name, device.product_category, device.product_type 20 | ) 21 | ) 22 | -------------------------------------------------------------------------------- /generated/nidaqmx/__init__.py: -------------------------------------------------------------------------------- 1 | from nidaqmx.errors import DaqError, DaqReadError, DaqWriteError, DaqWarning, DaqResourceWarning 2 | from nidaqmx.grpc_session_options import * 3 | from nidaqmx.scale import Scale 4 | from nidaqmx.task import Task 5 | from nidaqmx.types import CtrFreq, CtrTick, CtrTime 6 | 7 | try: 8 | from importlib.metadata import version 9 | except ImportError: 10 | from importlib_metadata import version # type: ignore[no-redef] 11 | 12 | __version__ = version(__name__) 13 | 14 | __all__ = ["errors", "scale", "stream_readers", "stream_writers", "task"] 15 | 16 | # Do not add a null logging handler. If the application has not configured logging, the 17 | # default behavior is to log warnings and errors to stderr. 18 | -------------------------------------------------------------------------------- /generated/nidaqmx/__main__.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import logging 4 | from typing import Optional 5 | 6 | import click 7 | 8 | from . import _install_daqmx 9 | 10 | 11 | @click.group("nidaqmx") 12 | @click.option( 13 | "-v", 14 | "--verbose", 15 | "verbosity", 16 | count=True, 17 | help="Enable verbose logging. Repeat to increase verbosity.", 18 | ) 19 | def main(verbosity: int) -> None: 20 | _configure_logging(verbosity) 21 | 22 | 23 | @main.command() 24 | def installdriver(): 25 | _install_daqmx.installdriver() 26 | 27 | 28 | def _configure_logging(verbosity: int) -> None: 29 | """Configure logging for this process.""" 30 | if verbosity > 1: 31 | level = logging.DEBUG 32 | elif verbosity == 1: 33 | level = logging.INFO 34 | else: 35 | level = logging.WARNING 36 | logging.basicConfig(format="%(asctime)s %(levelname)s: %(message)s", level=level) 37 | 38 | 39 | if __name__ == "__main__": 40 | main() 41 | -------------------------------------------------------------------------------- /generated/nidaqmx/_bitfield_utils.py: -------------------------------------------------------------------------------- 1 | def enum_bitfield_to_list(bitfield_value, bitfield_enum_type, 2 | actual_enum_type): 3 | """ 4 | Converts a bitfield value to a list of enums. 5 | 6 | Args: 7 | bitfield_value (int): Specifies the value of the bitfield. 8 | bitfield_enum_type (enum.Enum): Specifies the bitfield enum type 9 | from which to mask and extract the enum values. 10 | actual_enum_type (enum.Enum): Specifies the actual enum type. 11 | Returns: 12 | List[enum.Enum]: Indicates the converted list of enums. 13 | """ 14 | supported_values = [] 15 | for bitfield_mask in bitfield_enum_type: 16 | if bitfield_value & bitfield_mask.value: 17 | enum_value = next( 18 | e for e in actual_enum_type if e.name == bitfield_mask.name) 19 | supported_values.append(enum_value) 20 | 21 | return supported_values 22 | 23 | 24 | def enum_list_to_bitfield(enum_list, bitfield_enum_type): 25 | """ 26 | Converts a list of enums to a bitfield value. 27 | 28 | Args: 29 | enum_list (List[enum.Enum]): Specifies the list of enums. 30 | bitfield_enum_type (enum.Enum): Specifies the bitfield enum type 31 | from which to mask and extract the enum values. 32 | Returns: 33 | int: Indicates the value of the bitfield. 34 | """ 35 | bitfield_value = 0 36 | for enum_value in enum_list: 37 | bitfield_mask = next( 38 | b for b in bitfield_enum_type if b.name == enum_value.name) 39 | bitfield_value |= bitfield_mask.value 40 | 41 | return bitfield_value -------------------------------------------------------------------------------- /generated/nidaqmx/_grpc_time.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from datetime import timezone 4 | from datetime import datetime as std_datetime 5 | from datetime import tzinfo as dt_tzinfo 6 | from hightime import datetime as ht_datetime 7 | from hightime import timedelta as ht_timedelta 8 | from typing import Optional, Union 9 | from nidaqmx._time import _convert_to_desired_timezone 10 | 11 | from google.protobuf.timestamp_pb2 import Timestamp as GrpcTimestamp 12 | 13 | # 66 years, 17 leap days = 24107 days = 2082844800 seconds 14 | _BIAS_FROM_1970_EPOCH = 2082844800 15 | 16 | _NS_PER_S = 10**9 17 | _NS_PER_US = 10**3 18 | 19 | _YS_PER_US = 10**18 20 | _YS_PER_NS = 10**15 21 | _YS_PER_FS = 10**9 22 | 23 | _EPOCH_1970 = ht_datetime(1970, 1, 1, tzinfo=timezone.utc) 24 | 25 | def convert_time_to_timestamp(dt: std_datetime | ht_datetime, ts: GrpcTimestamp | None = None) -> GrpcTimestamp: 26 | seconds_since_1970 = 0 27 | 28 | if ts is None: 29 | ts = GrpcTimestamp() 30 | 31 | if isinstance(dt, ht_datetime): 32 | seconds_since_1970 = int((dt - _EPOCH_1970).precision_total_seconds()) 33 | total_yoctoseconds = dt.yoctosecond 34 | total_yoctoseconds += dt.femtosecond * _YS_PER_FS 35 | total_yoctoseconds += dt.microsecond * _YS_PER_US 36 | nanos, remainder_yoctoseconds = divmod(total_yoctoseconds, _YS_PER_NS) 37 | # round up, if necessary 38 | if remainder_yoctoseconds >= _YS_PER_NS / 2: 39 | nanos += 1 40 | else: 41 | seconds_since_1970 = int((dt - _EPOCH_1970).total_seconds()) 42 | nanos = dt.microsecond * _NS_PER_US 43 | 44 | ts.FromNanoseconds(seconds_since_1970 * _NS_PER_S + nanos) 45 | return ts 46 | 47 | def convert_timestamp_to_time(ts: GrpcTimestamp, tzinfo: dt_tzinfo | None = None) -> ht_datetime: 48 | total_nanos = ts.ToNanoseconds() 49 | seconds, nanos = divmod(total_nanos, _NS_PER_S) 50 | # Convert the nanoseconds to yoctoseconds. 51 | total_yoctoseconds = int(round(_YS_PER_NS * nanos)) 52 | dt = _EPOCH_1970 + ht_timedelta(seconds = seconds) + ht_timedelta(yoctoseconds=total_yoctoseconds) 53 | return _convert_to_desired_timezone(dt, tzinfo) 54 | -------------------------------------------------------------------------------- /generated/nidaqmx/_installer_metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "Windows": [ 3 | { 4 | "Version": "25.3.1", 5 | "Release": "2025Q2 Patch 1", 6 | "Location": "https://download.ni.com/support/nipkg/products/ni-d/ni-daqmx/25.3/online/ni-daqmx_25.3_online.exe", 7 | "supportedOS": [ 8 | "Windows 11", 9 | "Windows Server 2022 64-bit", 10 | "Windows 10 64-bit", 11 | "Windows Server 2016 64-bit", 12 | "Windows Server 2019 64-bit" 13 | ] 14 | } 15 | ], 16 | "Linux": [ 17 | { 18 | "Version": "25.3.0", 19 | "Release": "2025Q2", 20 | "_comment": "Location url must be of the format 'https://download.ni.com/support/softlib/MasterRepository/LinuxDrivers/NILinuxDeviceDrivers.zip'. Any change to the format will require a change in the code.", 21 | "Location": "https://download.ni.com/support/softlib/MasterRepository/LinuxDrivers2025Q2/NILinux2025Q2DeviceDrivers.zip", 22 | "supportedOS": [ 23 | "ubuntu 20.04", 24 | "ubuntu 22.04", 25 | "ubuntu 24.04", 26 | "rhel 8", 27 | "rhel 9", 28 | "opensuse 15.4", 29 | "opensuse 15.5" 30 | ] 31 | } 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /generated/nidaqmx/_stubs/__init__.py: -------------------------------------------------------------------------------- 1 | """Auto generated gRPC files.""" 2 | -------------------------------------------------------------------------------- /generated/nidaqmx/_time.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from tzlocal import get_localzone 4 | from datetime import timezone 5 | from datetime import tzinfo as dt_tzinfo 6 | from datetime import datetime as std_datetime 7 | from hightime import datetime as ht_datetime 8 | from typing import Optional, Union 9 | from zoneinfo import ZoneInfo 10 | 11 | # theoretically the same as astimezone(), but with support for dates before 1970 12 | def _convert_to_desired_timezone(expected_time_utc: std_datetime | ht_datetime, tzinfo: dt_tzinfo | None = None) -> std_datetime | ht_datetime: 13 | # if timezone matches, no need to do conversion 14 | if expected_time_utc.tzinfo is tzinfo: 15 | return expected_time_utc 16 | 17 | # if timezone is not defined, use system timezone 18 | if tzinfo is None: 19 | tzinfo = get_localzone() 20 | 21 | # use ZoneInfo here to account for daylight savings 22 | if isinstance(tzinfo, ZoneInfo): 23 | localized_time = expected_time_utc.replace(tzinfo=tzinfo) 24 | desired_expected_time = tzinfo.fromutc(localized_time) 25 | return(desired_expected_time) 26 | 27 | # if the tzinfo passed in is a timedelta function, then we don't need to consider daylight savings 28 | elif tzinfo.utcoffset(None) is not None: 29 | current_time_utc = ht_datetime.now(timezone.utc) 30 | desired_timezone_offset = current_time_utc.astimezone(tz=tzinfo).utcoffset() 31 | desired_expected_time = expected_time_utc + desired_timezone_offset 32 | new_datetime = desired_expected_time.replace(tzinfo=tzinfo) 33 | return new_datetime 34 | 35 | # if the tzinfo passed in is none of the above, fall back to original astimezone() 36 | else: 37 | return expected_time_utc.astimezone(tzinfo) 38 | -------------------------------------------------------------------------------- /generated/nidaqmx/system/__init__.py: -------------------------------------------------------------------------------- 1 | from nidaqmx.system.system import ( 2 | System, AOPowerUpState, CDAQSyncConnection, DOPowerUpState, 3 | DOResistorPowerUpState) 4 | from nidaqmx.system.device import Device 5 | from nidaqmx.system.physical_channel import PhysicalChannel 6 | from nidaqmx.system.watchdog import ( 7 | WatchdogTask, AOExpirationState, COExpirationState, DOExpirationState) 8 | 9 | __all__ = ['system', 'device', 'physical_channel', 'storage', 'watchdog'] 10 | -------------------------------------------------------------------------------- /generated/nidaqmx/system/_collections/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ni/nidaqmx-python/9515a863bba08f6acb2a0a2e65a7839372d43c85/generated/nidaqmx/system/_collections/__init__.py -------------------------------------------------------------------------------- /generated/nidaqmx/system/_watchdog_modules/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ni/nidaqmx-python/9515a863bba08f6acb2a0a2e65a7839372d43c85/generated/nidaqmx/system/_watchdog_modules/__init__.py -------------------------------------------------------------------------------- /generated/nidaqmx/system/_watchdog_modules/expiration_states_collection.py: -------------------------------------------------------------------------------- 1 | from nidaqmx.errors import DaqError 2 | from nidaqmx.system._watchdog_modules.expiration_state import ExpirationState 3 | 4 | 5 | class ExpirationStatesCollection: 6 | """ 7 | Contains the collection of expiration states for a DAQmx Watchdog Task. 8 | 9 | This class defines methods that implements a container object. 10 | """ 11 | def __init__(self, task_handle, interpreter): 12 | self._handle = task_handle 13 | self._interpreter = interpreter 14 | 15 | def __eq__(self, other): 16 | if isinstance(other, self.__class__): 17 | return self._handle == other._handle 18 | return False 19 | 20 | def __hash__(self): 21 | return self._interpreter.hash_task_handle(self._handle) 22 | 23 | def __ne__(self, other): 24 | return not self.__eq__(other) 25 | 26 | def __getitem__(self, index): 27 | """ 28 | Indexes an expiration state on this collection. 29 | 30 | Args: 31 | index (str): Name of the physical channel of which the 32 | expiration state to retrieve. 33 | Returns: 34 | nidaqmx.system._watchdog_modules.expiration_state.ExpirationState: 35 | 36 | The object representing the indexed expiration state. 37 | """ 38 | if isinstance(index, str): 39 | return ExpirationState(self._handle, index, self._interpreter) 40 | else: 41 | raise DaqError( 42 | 'Invalid index type "{}" used to access expiration states.' 43 | .format(type(index)), -1) 44 | -------------------------------------------------------------------------------- /generated/nidaqmx/system/storage/__init__.py: -------------------------------------------------------------------------------- 1 | from nidaqmx.system.storage.persisted_channel import PersistedChannel 2 | from nidaqmx.system.storage.persisted_scale import PersistedScale 3 | from nidaqmx.system.storage.persisted_task import PersistedTask 4 | 5 | __all__ = ['persisted_channel', 'persisted_scale', 'persisted_task'] 6 | -------------------------------------------------------------------------------- /generated/nidaqmx/task/__init__.py: -------------------------------------------------------------------------------- 1 | from nidaqmx.task._task import ( 2 | Task, _TaskEventType, _TaskAlternateConstructor 3 | ) 4 | from nidaqmx.task._in_stream import InStream 5 | from nidaqmx.task._out_stream import OutStream 6 | from nidaqmx.task._export_signals import ExportSignals 7 | from nidaqmx.task._timing import Timing 8 | 9 | __all__ = ['Task', 'InStream', 'OutStream', 'ExportSignals', 'Timing',] -------------------------------------------------------------------------------- /generated/nidaqmx/task/channels/__init__.py: -------------------------------------------------------------------------------- 1 | from nidaqmx.task.channels._channel import Channel 2 | from nidaqmx.task.channels._ai_channel import AIChannel 3 | from nidaqmx.task.channels._ao_channel import AOChannel 4 | from nidaqmx.task.channels._ci_channel import CIChannel 5 | from nidaqmx.task.channels._co_channel import COChannel 6 | from nidaqmx.task.channels._di_channel import DIChannel 7 | from nidaqmx.task.channels._do_channel import DOChannel 8 | 9 | __all__ = ['Channel', 'AIChannel', 'AOChannel', 'CIChannel', 'COChannel', 'DIChannel', 'DOChannel',] -------------------------------------------------------------------------------- /generated/nidaqmx/task/collections/__init__.py: -------------------------------------------------------------------------------- 1 | from nidaqmx.task.collections._channel_collection import ChannelCollection 2 | from nidaqmx.task.collections._ai_channel_collection import AIChannelCollection 3 | from nidaqmx.task.collections._ao_channel_collection import AOChannelCollection 4 | from nidaqmx.task.collections._ci_channel_collection import CIChannelCollection 5 | from nidaqmx.task.collections._co_channel_collection import COChannelCollection 6 | from nidaqmx.task.collections._di_channel_collection import DIChannelCollection 7 | from nidaqmx.task.collections._do_channel_collection import DOChannelCollection 8 | 9 | __all__ = ['ChannelCollection', 'AIChannelCollection', 'AOChannelCollection', 'CIChannelCollection', 'COChannelCollection', 'DIChannelCollection', 'DOChannelCollection',] -------------------------------------------------------------------------------- /generated/nidaqmx/task/triggering/__init__.py: -------------------------------------------------------------------------------- 1 | from nidaqmx.task.triggering._triggers import Triggers 2 | from nidaqmx.task.triggering._arm_start_trigger import ArmStartTrigger 3 | from nidaqmx.task.triggering._handshake_trigger import HandshakeTrigger 4 | from nidaqmx.task.triggering._pause_trigger import PauseTrigger 5 | from nidaqmx.task.triggering._reference_trigger import ReferenceTrigger 6 | from nidaqmx.task.triggering._start_trigger import StartTrigger 7 | 8 | __all__ = ['Triggers', 'ArmStartTrigger', 'HandshakeTrigger', 'PauseTrigger', 'ReferenceTrigger', 'StartTrigger'] -------------------------------------------------------------------------------- /generated/nidaqmx/task/triggering/_handshake_trigger.py: -------------------------------------------------------------------------------- 1 | # Do not edit this file; it was automatically generated. 2 | 3 | from nidaqmx.constants import ( 4 | Level, TriggerType) 5 | 6 | 7 | class HandshakeTrigger: 8 | """ 9 | Represents the handshake trigger configurations for a DAQmx task. 10 | """ 11 | __slots__ = ('_handle', '_interpreter') 12 | 13 | def __init__(self, task_handle, interpreter): 14 | self._handle = task_handle 15 | self._interpreter = interpreter 16 | 17 | @property 18 | def interlocked_asserted_lvl(self): 19 | """ 20 | :class:`nidaqmx.constants.Level`: Specifies the asserted level 21 | of the Handshake Trigger. 22 | """ 23 | 24 | val = self._interpreter.get_trig_attribute_int32(self._handle, 0x22b9) 25 | return Level(val) 26 | 27 | @interlocked_asserted_lvl.setter 28 | def interlocked_asserted_lvl(self, val): 29 | val = val.value 30 | self._interpreter.set_trig_attribute_int32(self._handle, 0x22b9, val) 31 | 32 | @interlocked_asserted_lvl.deleter 33 | def interlocked_asserted_lvl(self): 34 | self._interpreter.reset_trig_attribute(self._handle, 0x22b9) 35 | 36 | @property 37 | def interlocked_src(self): 38 | """ 39 | str: Specifies the source terminal of the Handshake Trigger. 40 | """ 41 | 42 | val = self._interpreter.get_trig_attribute_string(self._handle, 0x22b8) 43 | return val 44 | 45 | @interlocked_src.setter 46 | def interlocked_src(self, val): 47 | self._interpreter.set_trig_attribute_string(self._handle, 0x22b8, val) 48 | 49 | @interlocked_src.deleter 50 | def interlocked_src(self): 51 | self._interpreter.reset_trig_attribute(self._handle, 0x22b8) 52 | 53 | @property 54 | def trig_type(self): 55 | """ 56 | :class:`nidaqmx.constants.TriggerType`: Specifies the type of 57 | Handshake Trigger to use. 58 | """ 59 | 60 | val = self._interpreter.get_trig_attribute_int32(self._handle, 0x22b7) 61 | return TriggerType(val) 62 | 63 | @trig_type.setter 64 | def trig_type(self, val): 65 | val = val.value 66 | self._interpreter.set_trig_attribute_int32(self._handle, 0x22b7, val) 67 | 68 | @trig_type.deleter 69 | def trig_type(self): 70 | self._interpreter.reset_trig_attribute(self._handle, 0x22b7) 71 | 72 | -------------------------------------------------------------------------------- /generated/nidaqmx/types.py: -------------------------------------------------------------------------------- 1 | import collections 2 | import typing 3 | 4 | # region Task Counter IO namedtuples 5 | 6 | CtrFreq = collections.namedtuple( 7 | 'CtrFreq', ['freq', 'duty_cycle']) 8 | 9 | CtrTick = collections.namedtuple( 10 | 'CtrTick', ['high_tick', 'low_tick']) 11 | 12 | CtrTime = collections.namedtuple( 13 | 'CtrTime', ['high_time', 'low_time']) 14 | 15 | # endregion 16 | 17 | # region Power IO namedtuples 18 | 19 | PowerMeasurement = collections.namedtuple( 20 | 'PowerMeasurement', ['voltage', 'current']) 21 | 22 | # endregion 23 | 24 | # region Watchdog namedtuples 25 | 26 | AOExpirationState = collections.namedtuple( 27 | 'AOExpirationState', 28 | ['physical_channel', 'expiration_state', 'output_type']) 29 | 30 | COExpirationState = collections.namedtuple( 31 | 'COExpirationState', ['physical_channel', 'expiration_state']) 32 | 33 | DOExpirationState = collections.namedtuple( 34 | 'DOExpirationState', ['physical_channel', 'expiration_state']) 35 | 36 | # endregion 37 | 38 | # region Power Up States namedtuples 39 | 40 | AOPowerUpState = collections.namedtuple( 41 | 'AOPowerUpState', ['physical_channel', 'power_up_state', 'channel_type']) 42 | 43 | DOPowerUpState = collections.namedtuple( 44 | 'DOPowerUpState', ['physical_channel', 'power_up_state']) 45 | 46 | DOResistorPowerUpState = collections.namedtuple( 47 | 'DOResistorPowerUpState', ['physical_channel', 'power_up_state']) 48 | 49 | # endregion 50 | 51 | # region System namedtuples 52 | 53 | CDAQSyncConnection = collections.namedtuple( 54 | 'CDAQSyncConnection', ['output_port', 'input_port']) 55 | 56 | # endregion 57 | 58 | # region ID Pin namedtuples 59 | 60 | class IDPinContents(typing.NamedTuple): 61 | """IDPinContents represent the contents of the memory connected to the ID pin.""" 62 | 63 | data: list[int] 64 | """The binary data stored on the memory connected to the ID pin.""" 65 | 66 | format_code: int 67 | """The format code of the binary data.""" 68 | 69 | # endregion 70 | -------------------------------------------------------------------------------- /poetry.toml: -------------------------------------------------------------------------------- 1 | [virtualenvs] 2 | in-project = true -------------------------------------------------------------------------------- /src/codegen/__init__.py: -------------------------------------------------------------------------------- 1 | """NI-DAQmx code generator.""" 2 | 3 | import logging 4 | 5 | _logger = logging.getLogger(__name__) 6 | _logger.addHandler(logging.NullHandler()) 7 | -------------------------------------------------------------------------------- /src/codegen/__main__.py: -------------------------------------------------------------------------------- 1 | """DAQmx code generator.""" 2 | 3 | import logging 4 | import pathlib 5 | import sys 6 | 7 | import click 8 | 9 | try: 10 | sys.path.append(str(pathlib.Path(__file__).parent.parent)) 11 | import codegen.generator 12 | import codegen.stub_generator 13 | finally: 14 | pass 15 | 16 | _logger = logging.getLogger(__name__) 17 | 18 | 19 | def _get_logging_level(verbose, quiet): 20 | if 0 < verbose and 0 < quiet: 21 | click.exceptions.error("Mixing --verbose and --quiet is contradictory") 22 | verbosity = 2 + quiet - verbose 23 | verbosity = max(verbosity, 0) 24 | verbosity = min(verbosity, 4) 25 | logging_level = { 26 | 0: logging.DEBUG, 27 | 1: logging.INFO, 28 | 2: logging.WARNING, 29 | 3: logging.ERROR, 30 | 4: logging.CRITICAL, 31 | }[verbosity] 32 | 33 | return logging_level 34 | 35 | 36 | @click.command() 37 | @click.option("--dest", type=pathlib.Path) 38 | @click.option("--verbose", type=int, default=0, required=False) 39 | @click.option("--quiet", type=int, default=0, required=False) 40 | def main(dest, verbose, quiet): 41 | """Starts the code generator based on the out folders.""" 42 | logging_level = _get_logging_level(verbose, quiet) 43 | 44 | log_format = "[%(relativeCreated)6d] %(levelname)-5s %(funcName)s: %(message)s" 45 | logging.basicConfig(level=logging_level, format=log_format) 46 | 47 | codegen.generator.generate(dest) 48 | codegen.stub_generator.generate_stubs() 49 | 50 | 51 | if __name__ == "__main__": 52 | main() 53 | -------------------------------------------------------------------------------- /src/codegen/functions/adaptor_parameter.py: -------------------------------------------------------------------------------- 1 | """Structure for storing adaptor parameter from scrapigen.""" 2 | 3 | 4 | class AdaptorParameter: 5 | """Structure for storing adaptor parameter from scrapigen.""" 6 | 7 | def __init__(self, adaptor_parameter): 8 | """Structure for storing adaptor parameter from scrapigen.""" 9 | self._adaptor = adaptor_parameter["python_adaptor"] 10 | self._data_type = adaptor_parameter["python_data_type"] 11 | self._direction = adaptor_parameter["direction"] 12 | self._description = adaptor_parameter["description"] 13 | 14 | @property 15 | def adaptor(self): 16 | """str: Defines the string that should be used when using the parameter.""" 17 | return self._adaptor 18 | 19 | @property 20 | def data_type(self): 21 | """str: Defines the data type of the parameter.""" 22 | return self._data_type 23 | 24 | @property 25 | def direction(self): 26 | """str: Direction of the parameter.""" 27 | return self._direction 28 | 29 | @property 30 | def description(self): 31 | """str: The documentation of the parameter.""" 32 | return self._description 33 | -------------------------------------------------------------------------------- /src/codegen/metadata/__init__.py: -------------------------------------------------------------------------------- 1 | from .attributes import attributes 2 | from .functions import functions 3 | from .script_info import script_info 4 | from .enums import enums 5 | from .functions_addon import functions_override_metadata 6 | 7 | copy_functions = functions.copy() 8 | def update_functions(): 9 | for function_name, function_data in functions_override_metadata.items(): 10 | copy_functions[function_name] = {**copy_functions[function_name], **functions_override_metadata[function_name]} 11 | return copy_functions 12 | 13 | all_functions = update_functions() 14 | 15 | metadata = {"attributes": attributes, "script_info": script_info, "enums": enums, "functions": all_functions} -------------------------------------------------------------------------------- /src/codegen/properties/parameter.py: -------------------------------------------------------------------------------- 1 | """Structure for storing parameter metadata from scrapigen.""" 2 | 3 | 4 | class Parameter: 5 | """Structure for storing parameter metadata from scrapigen.""" 6 | 7 | def __init__(self, name, parameter_metadata): 8 | """Structure for storing parameter metadata from scrapigen.""" 9 | self._handle_name = name 10 | self._accessor = parameter_metadata.get( 11 | "python_accessor", parameter_metadata.get("accessor") 12 | ) 13 | assert self._accessor is not None 14 | self._ctypes_data_type = parameter_metadata["ctypes_data_type"] 15 | self._cvi_name = parameter_metadata["cvi_name"] 16 | 17 | @property 18 | def handle_name(self): 19 | """str: The key of the parameter.""" 20 | return self._handle_name 21 | 22 | @property 23 | def accessor(self): 24 | """str: Defines how to access the handle parameter. 25 | 26 | This value would be directly substituted when trying to use the handle parameter 27 | """ 28 | return self._accessor 29 | 30 | @property 31 | def ctypes_data_type(self): 32 | """str: Defines the ctypes data_type of the handle parameter. 33 | 34 | This is used when mentioning the data_type of the handle parameter. 35 | """ 36 | return self._ctypes_data_type 37 | 38 | @property 39 | def cvi_name(self): 40 | """str: The cvi name of the parameter. 41 | 42 | This is kept for the gRPC client implementation. 43 | """ 44 | return self._cvi_name 45 | -------------------------------------------------------------------------------- /src/codegen/protos/data_moniker.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option cc_enable_arenas = true; 4 | option csharp_namespace = "NationalInstruments.DataMonikers"; 5 | 6 | package ni.data_monikers; 7 | 8 | import "google/protobuf/any.proto"; 9 | 10 | service DataMoniker { 11 | rpc BeginSidebandStream(BeginMonikerSidebandStreamRequest) returns (BeginMonikerSidebandStreamResponse) {}; 12 | rpc StreamReadWrite(stream MonikerWriteRequest) returns (stream MonikerReadResponse) {}; 13 | rpc StreamRead(MonikerList) returns (stream MonikerReadResponse) {}; 14 | rpc StreamWrite(stream MonikerWriteRequest) returns (stream StreamWriteResponse) {}; 15 | } 16 | 17 | enum SidebandStrategy 18 | { 19 | UNKNOWN = 0; 20 | GRPC = 1; 21 | SHARED_MEMORY = 2; 22 | DOUBLE_BUFFERED_SHARED_MEMORY = 3; 23 | SOCKETS = 4; 24 | SOCKETS_LOW_LATENCY = 5; 25 | HYPERVISOR_SOCKETS = 6; 26 | RDMA = 7; 27 | RDMA_LOW_LATENCY = 8; 28 | } 29 | 30 | message BeginMonikerSidebandStreamRequest { 31 | SidebandStrategy strategy = 1; 32 | MonikerList monikers = 2; 33 | } 34 | 35 | message BeginMonikerSidebandStreamResponse { 36 | SidebandStrategy strategy = 1; 37 | string connection_url = 2; 38 | string sideband_identifier = 3; 39 | sint64 buffer_size = 4; 40 | } 41 | 42 | message Moniker { 43 | string service_location = 1; 44 | string data_source = 2; 45 | int64 data_instance = 3; 46 | } 47 | 48 | message MonikerWriteRequest { 49 | oneof write_data { 50 | MonikerList monikers = 1; 51 | MonikerValues data = 2; 52 | } 53 | } 54 | 55 | message MonikerReadResponse { 56 | MonikerValues data = 1; 57 | } 58 | 59 | message MonikerList { 60 | repeated Moniker read_monikers = 2; 61 | repeated Moniker write_monikers = 3; 62 | } 63 | 64 | message MonikerValues { 65 | repeated google.protobuf.Any values = 1; 66 | } 67 | 68 | message SidebandWriteRequest { 69 | bool cancel = 1; 70 | MonikerValues values = 2; 71 | } 72 | 73 | message SidebandReadResponse { 74 | bool cancel = 1; 75 | MonikerValues values = 2; 76 | } 77 | 78 | message StreamWriteResponse { 79 | } 80 | -------------------------------------------------------------------------------- /src/codegen/templates/_base_interpreter.py.mako: -------------------------------------------------------------------------------- 1 | <% 2 | from codegen.utilities.interpreter_helpers import ( 3 | get_interpreter_functions, 4 | get_interpreter_parameter_signature, 5 | get_params_for_function_signature, 6 | INCLUDE_SIZE_HINT_FUNCTIONS 7 | ) 8 | from codegen.utilities.function_helpers import order_function_parameters_by_optional 9 | from codegen.utilities.text_wrappers import wrap, docstring_wrap 10 | functions = get_interpreter_functions(data) 11 | %>\ 12 | # Do not edit this file; it was automatically generated. 13 | import abc 14 | from typing import Optional 15 | 16 | 17 | class BaseEventHandler(abc.ABC): 18 | """Interpreter-specific object that is returned from register_*_event().""" 19 | __slots__ = () 20 | 21 | @abc.abstractmethod 22 | def close(self) -> None: 23 | """Release resources used by the event handler. 24 | 25 | After releasing the resources, this method may report handler-specific errors 26 | (e.g. gRPC stream errors) by raising an exception. 27 | """ 28 | raise NotImplementedError 29 | 30 | 31 | class BaseInterpreter(abc.ABC): 32 | """ 33 | Contains signature of functions for all DAQmx APIs. 34 | """ 35 | __slots__ = () 36 | 37 | % for func in functions: 38 | <% 39 | params = get_params_for_function_signature(func) 40 | sorted_params = order_function_parameters_by_optional(params) 41 | parameter_signature = get_interpreter_parameter_signature(is_python_factory, sorted_params) 42 | if func.function_name in INCLUDE_SIZE_HINT_FUNCTIONS: 43 | parameter_signature = ", ".join([parameter_signature, "size_hint=0"]) 44 | %>\ 45 | @abc.abstractmethod 46 | %if (len(func.function_name) + len(parameter_signature)) > 68: 47 | def ${func.function_name}( 48 | ${parameter_signature + '):' | wrap(12, 12)} 49 | %else: 50 | def ${func.function_name}(${parameter_signature}): 51 | %endif 52 | raise NotImplementedError 53 | 54 | % endfor 55 | @abc.abstractmethod 56 | def hash_task_handle(self, task_handle): 57 | raise NotImplementedError -------------------------------------------------------------------------------- /src/codegen/templates/constants.mako: -------------------------------------------------------------------------------- 1 | <% 2 | import codegen.utilities.enum_helpers as enum_helpers 3 | 4 | enums = enum_helpers.get_enums(data) 5 | %>\ 6 | # Do not edit this file; it was automatically generated. 7 | 8 | from enum import Enum 9 | 10 | # Constants 11 | AUTO = -1 12 | CFG_DEFAULT = -1 13 | DEFAULT = -1 14 | READ_ALL_AVAILABLE = -1 15 | WAIT_INFINITELY = -1.0 16 | 17 | 18 | # Enums 19 | % for name, enum_metadata in enums.items(): 20 | class ${name}(Enum): 21 | % for value in enum_metadata['values']: 22 | ${value['name']} = ${value['value']}${enum_helpers.get_enum_value_docstring(value['documentation']['description'])} 23 | % endfor 24 | 25 | 26 | % endfor 27 | -------------------------------------------------------------------------------- /src/codegen/templates/error_codes.mako: -------------------------------------------------------------------------------- 1 | <% 2 | errors_metadata, warnings_metadata = data['DAQmxErrors'], data['DAQmxWarnings'] 3 | %>\ 4 | # Do not edit this file; it was automatically generated. 5 | 6 | from enum import IntEnum 7 | 8 | __all__ = ['DAQmxErrors', 'DAQmxWarnings'] 9 | 10 | 11 | class DAQmxErrors(IntEnum): 12 | %for value in errors_metadata['values']: 13 | ${value['name']} = ${value['value']} 14 | %endfor 15 | UNKNOWN = -1 16 | 17 | 18 | class DAQmxWarnings(IntEnum): 19 | UNKNOWN = -1 20 | %for value in warnings_metadata['values']: 21 | ${value['name']} = ${value['value']} 22 | %endfor -------------------------------------------------------------------------------- /src/codegen/templates/library_interpreter/default_c_function_call.py.mako: -------------------------------------------------------------------------------- 1 | <%page args="function"/>\ 2 | <% 3 | from codegen.utilities.interpreter_helpers import ( 4 | generate_interpreter_function_call_args, 5 | get_argument_types, 6 | get_samps_per_chan_read_or_write_param, 7 | ) 8 | from codegen.utilities.text_wrappers import wrap, docstring_wrap 9 | 10 | function_call_args = generate_interpreter_function_call_args(function) 11 | 12 | # samps_per_chan_param includes the keyword argument (samps_per_chan_read= 13 | # or samps_per_chan_written=) 14 | samps_per_chan_param = get_samps_per_chan_read_or_write_param(function.base_parameters) 15 | %>\ 16 | cfunc = lib_importer.${'windll' if function.calling_convention == 'StdCall' else 'cdll'}.DAQmx${function.c_function_name} 17 | if cfunc.argtypes is None: 18 | with cfunc.arglock: 19 | if cfunc.argtypes is None: 20 | cfunc.argtypes = [ 21 | ${', '.join(get_argument_types(function)) | wrap(24, 24)}] 22 | 23 | error_code = cfunc( 24 | ${', '.join(function_call_args) | wrap(12, 12)}) 25 | %if samps_per_chan_param is None: 26 | self.check_for_error(error_code) 27 | %else: 28 | self.check_for_error(error_code, ${samps_per_chan_param}.value) 29 | %endif -------------------------------------------------------------------------------- /src/codegen/templates/library_interpreter/double_c_function_call.py.mako: -------------------------------------------------------------------------------- 1 | <%page args="function"/>\ 2 | <% 3 | from codegen.utilities.interpreter_helpers import ( 4 | generate_interpreter_function_call_args, 5 | get_argument_types, 6 | get_output_param_with_ivi_dance_mechanism, 7 | get_output_params, 8 | INCLUDE_SIZE_HINT_FUNCTIONS, 9 | ) 10 | from codegen.utilities.function_helpers import instantiate_explicit_output_param 11 | from codegen.utilities.text_wrappers import wrap, docstring_wrap 12 | 13 | function_call_args = generate_interpreter_function_call_args(function) 14 | explicit_output_param = get_output_param_with_ivi_dance_mechanism(function) 15 | %>\ 16 | cfunc = lib_importer.${'windll' if function.calling_convention == 'StdCall' else 'cdll'}.DAQmx${function.c_function_name} 17 | if cfunc.argtypes is None: 18 | with cfunc.arglock: 19 | if cfunc.argtypes is None: 20 | cfunc.argtypes = [ 21 | ${', '.join(get_argument_types(function)) | wrap(24, 24)}] 22 | 23 | temp_size = ${'size_hint' if function.function_name in INCLUDE_SIZE_HINT_FUNCTIONS else '0'} 24 | while True: 25 | ${instantiate_explicit_output_param(explicit_output_param)} 26 | size_or_code = cfunc( 27 | ${', '.join(function_call_args) | wrap(16, 16)}) 28 | %if explicit_output_param.ctypes_data_type == 'ctypes.c_char_p': 29 | if is_string_buffer_too_small(size_or_code): 30 | %else: 31 | if is_array_buffer_too_small(size_or_code): 32 | %endif 33 | # Buffer size must have changed between calls; check again. 34 | temp_size = 0 35 | elif size_or_code > 0 and temp_size == 0: 36 | # Buffer size obtained, use to retrieve data. 37 | temp_size = size_or_code 38 | else: 39 | break 40 | self.check_for_error(size_or_code) 41 | -------------------------------------------------------------------------------- /src/codegen/templates/library_interpreter/event_function_call.py.mako: -------------------------------------------------------------------------------- 1 | <%page args="function"/>\ 2 | <% 3 | import re 4 | from codegen.utilities.interpreter_helpers import ( 5 | generate_interpreter_function_call_args, 6 | get_argument_types, 7 | get_callback_func_param, 8 | get_callback_param_data_types, 9 | get_event_name, 10 | is_event_function, 11 | is_event_register_function, 12 | is_event_unregister_function, 13 | ) 14 | from codegen.utilities.text_wrappers import wrap 15 | 16 | assert is_event_function(function) 17 | 18 | argument_types = get_argument_types(function) 19 | callback_func_param = get_callback_func_param(function) 20 | callback_param_types = get_callback_param_data_types(function) 21 | event_name = get_event_name(function) 22 | function_call_args = generate_interpreter_function_call_args(function) 23 | %>\ 24 | ${callback_func_param.type} = ctypes.CFUNCTYPE( 25 | ${', '.join(callback_param_types) | wrap(12)}) 26 | 27 | cfunc = lib_importer.${'windll' if function.calling_convention == 'StdCall' else 'cdll'}.DAQmx${function.c_function_name} 28 | if cfunc.argtypes is None: 29 | with cfunc.arglock: 30 | if cfunc.argtypes is None: 31 | cfunc.argtypes = [ 32 | ${', '.join(argument_types) | wrap(24)}] 33 | 34 | %if is_event_register_function(function): 35 | assert callback_function is not None 36 | callback_method_ptr = ${callback_func_param.type}(callback_function) 37 | %elif is_event_unregister_function(function): 38 | %if "every_n_samples" in function.function_name: 39 | n_samples = 0 40 | %endif 41 | options = 0 42 | callback_method_ptr = ${callback_func_param.type}() 43 | callback_data = None 44 | %endif 45 | 46 | error_code = cfunc( 47 | ${', '.join(function_call_args) | wrap(12)}) 48 | self.check_for_error(error_code) 49 | %if is_event_register_function(function): 50 | 51 | return LibraryEventHandler(callback_method_ptr) 52 | %endif 53 | -------------------------------------------------------------------------------- /src/codegen/templates/library_interpreter/exec_cdecl_c_function_call.py.mako: -------------------------------------------------------------------------------- 1 | <%page args="function"/>\ 2 | <% 3 | from codegen.utilities.interpreter_helpers import ( 4 | generate_interpreter_function_call_args, 5 | get_argument_definition_lines_for_varargs, 6 | get_argument_types, 7 | get_instantiation_lines_for_varargs, 8 | get_varargs_parameters, 9 | ) 10 | from codegen.utilities.text_wrappers import wrap, docstring_wrap 11 | 12 | varargs_parameters = get_varargs_parameters(function) 13 | 14 | ## This is under the assumption that the varargs passes are all arrays of the same size. 15 | varargs_array_length = f"len({varargs_parameters[0].parameter_name})" 16 | instantiation_lines = get_instantiation_lines_for_varargs(function) 17 | argument_definition_lines = get_argument_definition_lines_for_varargs(varargs_parameters) 18 | %>\ 19 | args = [device_name] 20 | argtypes: List[type] = [ctypes_byte_str] 21 | 22 | for index in range(${varargs_array_length}): 23 | %for instantiation_line in instantiation_lines: 24 | ${instantiation_line} 25 | %endfor 26 | 27 | %for argument_definition_line in argument_definition_lines: 28 | ${argument_definition_line} 29 | %endfor 30 | args.append(None) 31 | argtypes.append(ctypes.c_void_p) 32 | 33 | cfunc = lib_importer.${'windll' if function.calling_convention == 'StdCall' else 'cdll'}.DAQmx${function.c_function_name} 34 | with cfunc.arglock: 35 | cfunc.argtypes = argtypes 36 | error_code = cfunc(*args) 37 | self.check_for_error(error_code) 38 | -------------------------------------------------------------------------------- /src/codegen/templates/property_deleter_template.py.mako: -------------------------------------------------------------------------------- 1 | <%def name="script_property_deleter(attribute)">\ 2 | <% 3 | from codegen.utilities.attribute_helpers import get_generic_attribute_function_name 4 | %>\ 5 | @${attribute.name}.deleter 6 | def ${attribute.name}(self): 7 | \ 8 | ## Script interpreter call. 9 | <% 10 | generic_attribute_func = get_generic_attribute_function_name(attribute) 11 | function_call_args = [] 12 | for handle_parameter in attribute.handle_parameters: 13 | function_call_args.append(handle_parameter.accessor) 14 | if attribute.python_class_name == "Watchdog": 15 | function_call_args.append("\"\"") 16 | function_call_args.append(hex(attribute.id)) 17 | %>\ 18 | self._interpreter.reset_${generic_attribute_func}(${', '.join(function_call_args)}) 19 | -------------------------------------------------------------------------------- /src/codegen/templates/property_deprecated_template.py.mako: -------------------------------------------------------------------------------- 1 | <%def name="script_deprecated_property(attributes)">\ 2 | <% 3 | from codegen.utilities.attribute_helpers import get_deprecated_attributes 4 | deprecated_attributes = get_deprecated_attributes(attributes) 5 | %>\ 6 | %for old_property, attribute in deprecated_attributes.items(): 7 | @property 8 | @deprecation.deprecated(deprecated_in="${attribute["deprecated_in"]}", details="Use ${attribute["new_name"]} instead.") 9 | def ${old_property}(self): 10 | return self.${attribute["new_name"]} 11 | 12 | %if attribute["access"] != "read": 13 | @${old_property}.setter 14 | @deprecation.deprecated(deprecated_in="${attribute["deprecated_in"]}", details="Use ${attribute["new_name"]} instead.") 15 | def ${old_property}(self, val): 16 | self.${attribute["new_name"]} = val 17 | 18 | %endif 19 | %if attribute["resettable"]: 20 | @${old_property}.deleter 21 | @deprecation.deprecated(deprecated_in="${attribute["deprecated_in"]}", details="Use ${attribute["new_name"]} instead.") 22 | def ${old_property}(self): 23 | del self.${attribute["new_name"]} 24 | 25 | %endif 26 | %endfor 27 | -------------------------------------------------------------------------------- /src/codegen/templates/property_empty_getter_template.py.mako: -------------------------------------------------------------------------------- 1 | <%def name="script_empty_property_getter(attribute)">\ 2 | <% 3 | from codegen.utilities.text_wrappers import docstring_wrap 4 | %>\ 5 | @property 6 | def ${attribute.name}(self): 7 | """ 8 | ${attribute.get_return_type() + ": " + attribute.python_description | docstring_wrap(initial_indent=8, subsequent_indent=12)} 9 | """ 10 | raise NotImplementedError( 11 | 'Reading this NI-DAQmx property is not supported.') 12 | -------------------------------------------------------------------------------- /src/codegen/templates/property_legacy_deleter_template.py.mako: -------------------------------------------------------------------------------- 1 | <%def name="script_property_deleter(attribute)">\ 2 | <% 3 | from codegen.utilities.text_wrappers import wrap 4 | %>\ 5 | @${attribute.name}.deleter 6 | def ${attribute.name}(self): 7 | from nidaqmx._library_interpreter import LibraryInterpreter 8 | from nidaqmx._lib import lib_importer, ctypes_byte_str, c_bool32 9 | if not isinstance(self._interpreter, LibraryInterpreter): 10 | raise NotImplementedError 11 | ## When the length of the function name is too long, it will be wrapped to the next line 12 | %if len(attribute.c_function_name) < 33: 13 | cfunc = lib_importer.${attribute.get_lib_importer_type()}.DAQmxReset${attribute.c_function_name} 14 | %else: 15 | cfunc = (lib_importer.${attribute.get_lib_importer_type()}. 16 | DAQmxReset${attribute.c_function_name}) 17 | %endif 18 | if cfunc.argtypes is None: 19 | with cfunc.arglock: 20 | if cfunc.argtypes is None: 21 | cfunc.argtypes = [ 22 | ${', '.join(attribute.get_handle_parameter_arguments()) | wrap(initial_indent=24)}] 23 | \ 24 | ## Script function call. 25 | <% 26 | function_call_args = [] 27 | for handle_parameter in attribute.handle_parameters: 28 | function_call_args.append(handle_parameter.accessor) 29 | %>\ 30 | error_code = cfunc( 31 | ${', '.join(function_call_args) | wrap(initial_indent=12)}) 32 | self._interpreter.check_for_error(error_code) 33 | -------------------------------------------------------------------------------- /src/codegen/templates/property_setter_template.py.mako: -------------------------------------------------------------------------------- 1 | <%def name="script_property_setter(attribute)">\ 2 | <% 3 | from codegen.utilities.attribute_helpers import get_generic_attribute_function_name, get_generic_attribute_function_type, ATTRIBUTE_WITH_FILE_PATH_TYPE 4 | %>\ 5 | @${attribute.name}.setter 6 | %if attribute.name in ATTRIBUTE_WITH_FILE_PATH_TYPE: 7 | def ${attribute.name}(self, val: Optional[Union[str, pathlib.PurePath]]): 8 | %else: 9 | def ${attribute.name}(self, val): 10 | %endif 11 | \ 12 | %if attribute.bitfield_enum is not None: 13 | val = enum_list_to_bitfield( 14 | val, ${attribute.bitfield_enum}) 15 | %elif attribute.is_enum and not attribute.is_list: 16 | val = val.value 17 | %elif attribute.is_enum and attribute.is_list: 18 | val = numpy.array([e.value for e in val], dtype=${attribute.ctypes_data_type}) 19 | %elif attribute.is_object and not attribute.is_list: 20 | val = val.name 21 | %elif attribute.is_object and attribute.is_list: 22 | val = flatten_channel_string[o.name for o in val] 23 | %elif attribute.is_list and attribute.ctypes_data_type == 'ctypes.c_char_p': 24 | val = flatten_channel_string(val) 25 | %elif attribute.is_list: 26 | val = numpy.array(val, dtype=${attribute.ctypes_data_type}) 27 | %elif attribute.name in ATTRIBUTE_WITH_FILE_PATH_TYPE: 28 | if val is None: 29 | val = "" 30 | val = str(val) 31 | %endif 32 | \ 33 | ## Script interpreter call. 34 | <% 35 | mapped_func_type = get_generic_attribute_function_type(attribute) 36 | generic_attribute_func = get_generic_attribute_function_name(attribute) + "_" + mapped_func_type 37 | function_call_args = [] 38 | for handle_parameter in attribute.handle_parameters: 39 | function_call_args.append(handle_parameter.accessor) 40 | if attribute.python_class_name == "Watchdog": 41 | function_call_args.append("\"\"") 42 | function_call_args.append(hex(attribute.id)) 43 | function_call_args.append('val') 44 | %>\ 45 | self._interpreter.set_${generic_attribute_func}(${', '.join(function_call_args)}) 46 | -------------------------------------------------------------------------------- /src/codegen/templates/property_template.py.mako: -------------------------------------------------------------------------------- 1 | <%def name="script_property(attribute)">\ 2 | <%namespace name="property_getter_template" file="/property_getter_template.py.mako"/>\ 3 | <%namespace name="property_empty_getter_template" file="/property_empty_getter_template.py.mako"/>\ 4 | <%namespace name="property_setter_template" file="/property_setter_template.py.mako"/>\ 5 | <%namespace name="property_deleter_template" file="/property_deleter_template.py.mako"/>\ 6 | <%namespace name="property_legacy_setter_template" file="/property_legacy_setter_template.py.mako"/>\ 7 | <%namespace name="property_legacy_deleter_template" file="/property_legacy_deleter_template.py.mako"/>\ 8 | %if attribute.access == "read": 9 | ${property_getter_template.script_property_getter(attribute)} 10 | %elif attribute.access == "write": 11 | ${property_empty_getter_template.script_empty_property_getter(attribute)} 12 | ${property_setter_template.script_property_setter(attribute)} 13 | %elif attribute.access == "read-write" and attribute.python_class_name == "PhysicalChannel": 14 | ${property_getter_template.script_property_getter(attribute)} 15 | ${property_legacy_setter_template.script_property_setter(attribute)} 16 | %elif attribute.access == "read-write": 17 | ${property_getter_template.script_property_getter(attribute)} 18 | ${property_setter_template.script_property_setter(attribute)} 19 | %endif 20 | \ 21 | %if attribute.resettable and attribute.python_class_name == "PhysicalChannel": 22 | ${property_legacy_deleter_template.script_property_deleter(attribute)} 23 | %elif attribute.resettable: 24 | ${property_deleter_template.script_property_deleter(attribute)} 25 | %endif 26 | -------------------------------------------------------------------------------- /src/codegen/templates/system/_watchdog_modules/expiration_state.py.mako: -------------------------------------------------------------------------------- 1 | <% 2 | from codegen.utilities.attribute_helpers import get_attributes, get_enums_used 3 | from codegen.utilities.text_wrappers import wrap 4 | attributes = get_attributes(data, "ExpirationState") 5 | enums_used = get_enums_used(attributes) 6 | %>\ 7 | # Do not edit this file; it was automatically generated. 8 | 9 | import deprecation 10 | 11 | from nidaqmx.constants import ( 12 | ${', '.join([c for c in enums_used]) | wrap(4, 4)}) 13 | 14 | 15 | class ExpirationState: 16 | """ 17 | Represents a DAQmx Watchdog expiration state. 18 | """ 19 | __slots__ = ('_handle', '_physical_channel', '_interpreter') 20 | 21 | def __init__(self, task_handle, physical_channel, interpreter): 22 | self._handle = task_handle 23 | self._physical_channel = physical_channel 24 | self._interpreter = interpreter 25 | 26 | def __eq__(self, other): 27 | if isinstance(other, self.__class__): 28 | return (self._handle == other._handle and 29 | self._physical_channel == other._physical_channel) 30 | return False 31 | 32 | def __hash__(self): 33 | return self._interpreter.hash_task_handle(self._handle) ^ hash(self._physical_channel) 34 | 35 | def __ne__(self, other): 36 | return not self.__eq__(other) 37 | 38 | <%namespace name="property_template" file="/property_template.py.mako"/>\ 39 | %for attribute in attributes: 40 | ${property_template.script_property(attribute)}\ 41 | %endfor 42 | <%namespace name="deprecated_template" file="/property_deprecated_template.py.mako"/>\ 43 | ${deprecated_template.script_deprecated_property(attributes)}\ 44 | -------------------------------------------------------------------------------- /src/codegen/templates/task/_export_signals.py.mako: -------------------------------------------------------------------------------- 1 | <% 2 | from codegen.utilities.attribute_helpers import get_attributes 3 | from codegen.utilities.attribute_helpers import get_enums_used as get_enums_used_in_attributes 4 | from codegen.utilities.function_helpers import get_functions 5 | from codegen.utilities.function_helpers import get_enums_used as get_enums_used_in_functions 6 | from codegen.utilities.helpers import get_enums_to_import 7 | from codegen.utilities.text_wrappers import wrap 8 | attributes = get_attributes(data,"ExportSignals") 9 | functions = get_functions(data,"ExportSignals") 10 | enums_in_attributes = get_enums_used_in_attributes(attributes) 11 | enums_in_functions = get_enums_used_in_functions(functions) 12 | enums_used = get_enums_to_import(enums_in_attributes, enums_in_functions) 13 | %>\ 14 | # Do not edit this file; it was automatically generated. 15 | 16 | from nidaqmx.constants import ( 17 | ${', '.join([c for c in enums_used]) | wrap(4, 4)}) 18 | 19 | 20 | class ExportSignals: 21 | """ 22 | Represents the exported signal configurations for a DAQmx task. 23 | """ 24 | __slots__ = ('_handle', '_interpreter') 25 | 26 | def __init__(self, task_handle, interpreter): 27 | self._handle = task_handle 28 | self._interpreter = interpreter 29 | 30 | <%namespace name="property_template" file="/property_template.py.mako"/>\ 31 | %for attribute in attributes: 32 | ${property_template.script_property(attribute)}\ 33 | %endfor 34 | \ 35 | <%namespace name="function_template" file="/function_template.py.mako"/>\ 36 | %for function_object in functions: 37 | ${function_template.script_function(function_object)} 38 | %endfor 39 | -------------------------------------------------------------------------------- /src/codegen/templates/task/_timing.py.mako: -------------------------------------------------------------------------------- 1 | <% 2 | from codegen.utilities.text_wrappers import wrap 3 | from codegen.utilities.function_helpers import get_functions 4 | from codegen.utilities.attribute_helpers import get_attributes 5 | from codegen.utilities.function_helpers import get_enums_used as get_enums_used_in_functions 6 | from codegen.utilities.attribute_helpers import get_enums_used as get_enums_used_in_attributes 7 | from codegen.utilities.helpers import get_enums_to_import 8 | attributes = get_attributes(data,"Timing") 9 | functions = get_functions(data,"Timing") 10 | enums_in_attributes = get_enums_used_in_attributes(attributes) 11 | enums_in_functions = get_enums_used_in_functions(functions) 12 | enums_used = get_enums_to_import(enums_in_attributes, enums_in_functions) 13 | %>\ 14 | # Do not edit this file; it was automatically generated. 15 | 16 | from nidaqmx.system.physical_channel import _PhysicalChannelAlternateConstructor 17 | %if enums_used: 18 | from nidaqmx.constants import ( 19 | ${', '.join([c for c in enums_used]) | wrap(4, 4)}) 20 | %endif 21 | 22 | 23 | class Timing: 24 | """ 25 | Represents the timing configurations for a DAQmx task. 26 | """ 27 | __slots__ = ('_handle', '_interpreter') 28 | 29 | def __init__(self, task_handle, interpreter): 30 | self._handle = task_handle 31 | self._interpreter = interpreter 32 | 33 | <%namespace name="property_template" file="/property_template.py.mako"/>\ 34 | %for attribute in attributes: 35 | ${property_template.script_property(attribute)}\ 36 | %endfor 37 | <%namespace name="deprecated_template" file="/property_deprecated_template.py.mako"/>\ 38 | ${deprecated_template.script_deprecated_property(attributes)}\ 39 | <%namespace name="function_template" file="/function_template.py.mako"/>\ 40 | %for function_object in functions: 41 | ${function_template.script_function(function_object)} 42 | %endfor -------------------------------------------------------------------------------- /src/codegen/templates/task/channels/_ai_channel.py.mako: -------------------------------------------------------------------------------- 1 | <% 2 | from codegen.utilities.attribute_helpers import get_attributes, get_enums_used 3 | from codegen.utilities.text_wrappers import wrap 4 | attributes = get_attributes(data, "AIChannel") 5 | enums_used = get_enums_used(attributes) 6 | %>\ 7 | # Do not edit this file; it was automatically generated. 8 | 9 | import numpy 10 | import deprecation 11 | 12 | from nidaqmx.scale import _ScaleAlternateConstructor 13 | from nidaqmx.task.channels._channel import Channel 14 | from nidaqmx.utils import unflatten_channel_string 15 | from nidaqmx.constants import ( 16 | ${', '.join([c for c in enums_used]) | wrap(4, 4)}) 17 | 18 | 19 | class AIChannel(Channel): 20 | """ 21 | Represents one or more analog input virtual channels and their properties. 22 | """ 23 | __slots__ = () 24 | 25 | def __repr__(self): 26 | return f'AIChannel(name={self._name})' 27 | 28 | <%namespace name="property_template" file="/property_template.py.mako"/>\ 29 | %for attribute in attributes: 30 | ${property_template.script_property(attribute)}\ 31 | %endfor 32 | <%namespace name="deprecated_template" file="/property_deprecated_template.py.mako"/>\ 33 | ${deprecated_template.script_deprecated_property(attributes)}\ 34 | -------------------------------------------------------------------------------- /src/codegen/templates/task/channels/_ao_channel.py.mako: -------------------------------------------------------------------------------- 1 | <% 2 | from codegen.utilities.attribute_helpers import get_attributes, get_enums_used 3 | from codegen.utilities.text_wrappers import wrap 4 | attributes = get_attributes(data, "AOChannel") 5 | enums_used = get_enums_used(attributes) 6 | %>\ 7 | # Do not edit this file; it was automatically generated. 8 | 9 | from nidaqmx.scale import _ScaleAlternateConstructor 10 | from nidaqmx.task.channels._channel import Channel 11 | from nidaqmx.constants import ( 12 | ${', '.join([c for c in enums_used]) | wrap(4, 4)}) 13 | 14 | 15 | class AOChannel(Channel): 16 | """ 17 | Represents one or more analog output virtual channels and their properties. 18 | """ 19 | __slots__ = () 20 | 21 | def __repr__(self): 22 | return f'AOChannel(name={self._name})' 23 | 24 | <%namespace name="property_template" file="/property_template.py.mako"/>\ 25 | %for attribute in attributes: 26 | ${property_template.script_property(attribute)}\ 27 | %endfor 28 | -------------------------------------------------------------------------------- /src/codegen/templates/task/channels/_ci_channel.py.mako: -------------------------------------------------------------------------------- 1 | <% 2 | from codegen.utilities.attribute_helpers import get_attributes, get_enums_used 3 | from codegen.utilities.text_wrappers import wrap 4 | attributes = get_attributes(data, "CIChannel") 5 | enums_used = get_enums_used(attributes) 6 | %>\ 7 | # Do not edit this file; it was automatically generated. 8 | 9 | from nidaqmx.scale import _ScaleAlternateConstructor 10 | from nidaqmx.task.channels._channel import Channel 11 | from nidaqmx.constants import ( 12 | ${', '.join([c for c in enums_used]) | wrap(4, 4)}) 13 | 14 | 15 | class CIChannel(Channel): 16 | """ 17 | Represents one or more counter input virtual channels and their properties. 18 | """ 19 | __slots__ = () 20 | 21 | def __repr__(self): 22 | return f'CIChannel(name={self._name})' 23 | 24 | <%namespace name="property_template" file="/property_template.py.mako"/>\ 25 | %for attribute in attributes: 26 | ${property_template.script_property(attribute)}\ 27 | %endfor -------------------------------------------------------------------------------- /src/codegen/templates/task/channels/_co_channel.py.mako: -------------------------------------------------------------------------------- 1 | <% 2 | from codegen.utilities.attribute_helpers import get_attributes, get_enums_used 3 | from codegen.utilities.text_wrappers import wrap 4 | attributes = get_attributes(data, "COChannel") 5 | enums_used = get_enums_used(attributes) 6 | %>\ 7 | # Do not edit this file; it was automatically generated. 8 | 9 | from nidaqmx.task.channels._channel import Channel 10 | from nidaqmx.constants import ( 11 | ${', '.join([c for c in enums_used]) | wrap(4, 4)}) 12 | 13 | 14 | class COChannel(Channel): 15 | """ 16 | Represents one or more counter output virtual channels and their properties. 17 | """ 18 | __slots__ = () 19 | 20 | def __repr__(self): 21 | return f'COChannel(name={self._name})' 22 | 23 | <%namespace name="property_template" file="/property_template.py.mako"/>\ 24 | %for attribute in attributes: 25 | ${property_template.script_property(attribute)}\ 26 | %endfor 27 | -------------------------------------------------------------------------------- /src/codegen/templates/task/channels/_di_channel.py.mako: -------------------------------------------------------------------------------- 1 | <% 2 | from codegen.utilities.attribute_helpers import get_attributes, get_enums_used 3 | from codegen.utilities.text_wrappers import wrap 4 | attributes = get_attributes(data, "DIChannel") 5 | enums_used = get_enums_used(attributes) 6 | %>\ 7 | # Do not edit this file; it was automatically generated. 8 | 9 | from nidaqmx.task.channels._channel import Channel 10 | from nidaqmx.constants import ( 11 | ${', '.join([c for c in enums_used]) | wrap(4, 4)}) 12 | 13 | 14 | class DIChannel(Channel): 15 | """ 16 | Represents one or more digital input virtual channels and their properties. 17 | """ 18 | __slots__ = () 19 | 20 | def __repr__(self): 21 | return f'DIChannel(name={self._name})' 22 | 23 | <%namespace name="property_template" file="/property_template.py.mako"/>\ 24 | %for attribute in attributes: 25 | ${property_template.script_property(attribute)}\ 26 | %endfor 27 | -------------------------------------------------------------------------------- /src/codegen/templates/task/channels/_do_channel.py.mako: -------------------------------------------------------------------------------- 1 | <% 2 | from codegen.utilities.attribute_helpers import get_attributes, get_enums_used 3 | from codegen.utilities.text_wrappers import wrap 4 | attributes = get_attributes(data, "DOChannel") 5 | enums_used = get_enums_used(attributes) 6 | %>\ 7 | # Do not edit this file; it was automatically generated. 8 | 9 | from nidaqmx.task.channels._channel import Channel 10 | from nidaqmx.constants import ( 11 | ${', '.join([c for c in enums_used]) | wrap(4, 4)}) 12 | 13 | 14 | class DOChannel(Channel): 15 | """ 16 | Represents one or more digital output virtual channels and their properties. 17 | """ 18 | __slots__ = () 19 | 20 | def __repr__(self): 21 | return f'DOChannel(name={self._name})' 22 | 23 | <%namespace name="property_template" file="/property_template.py.mako"/>\ 24 | %for attribute in attributes: 25 | ${property_template.script_property(attribute)}\ 26 | %endfor 27 | -------------------------------------------------------------------------------- /src/codegen/templates/task/triggering/_arm_start_trigger.py.mako: -------------------------------------------------------------------------------- 1 | <% 2 | from codegen.utilities.text_wrappers import wrap 3 | from codegen.utilities.function_helpers import get_functions, get_enums_used as functions_enums 4 | from codegen.utilities.attribute_helpers import get_attributes, get_enums_used as attribute_enums 5 | from codegen.utilities.helpers import get_enums_to_import 6 | from codegen.utilities.text_wrappers import wrap 7 | attributes = get_attributes(data, "ArmStartTrigger") 8 | functions = get_functions(data,"ArmStartTrigger") 9 | attr_enums = attribute_enums(attributes) 10 | fuct_enums = functions_enums(functions) 11 | enums_used = get_enums_to_import(attr_enums, fuct_enums) 12 | %>\ 13 | # Do not edit this file; it was automatically generated. 14 | 15 | %if enums_used: 16 | from nidaqmx.constants import ( 17 | ${', '.join([c for c in enums_used]) | wrap(4, 4)}) 18 | %endif 19 | 20 | 21 | class ArmStartTrigger: 22 | """ 23 | Represents the arm start trigger configurations for a DAQmx task. 24 | """ 25 | __slots__ = ('_handle', '_interpreter') 26 | 27 | def __init__(self, task_handle, interpreter): 28 | self._handle = task_handle 29 | self._interpreter = interpreter 30 | 31 | <%namespace name="property_template" file="/property_template.py.mako"/>\ 32 | %for attribute in attributes: 33 | ${property_template.script_property(attribute)}\ 34 | %endfor 35 | \ 36 | <%namespace name="function_template" file="/function_template.py.mako"/>\ 37 | %for function_object in functions: 38 | ${function_template.script_function(function_object)} 39 | %endfor -------------------------------------------------------------------------------- /src/codegen/templates/task/triggering/_handshake_trigger.py.mako: -------------------------------------------------------------------------------- 1 | <% 2 | from codegen.utilities.text_wrappers import wrap 3 | from codegen.utilities.function_helpers import get_functions, get_enums_used as functions_enums 4 | from codegen.utilities.attribute_helpers import get_attributes, get_enums_used as attribute_enums 5 | from codegen.utilities.helpers import get_enums_to_import 6 | attributes = get_attributes(data, "HandshakeTrigger") 7 | functions = get_functions(data,"HandshakeTrigger") 8 | attr_enums = attribute_enums(attributes) 9 | fuct_enums = functions_enums(functions) 10 | enums_used = get_enums_to_import(attr_enums, fuct_enums) 11 | %>\ 12 | # Do not edit this file; it was automatically generated. 13 | 14 | %if enums_used: 15 | from nidaqmx.constants import ( 16 | ${', '.join([c for c in enums_used]) | wrap(4, 4)}) 17 | %endif 18 | 19 | 20 | class HandshakeTrigger: 21 | """ 22 | Represents the handshake trigger configurations for a DAQmx task. 23 | """ 24 | __slots__ = ('_handle', '_interpreter') 25 | 26 | def __init__(self, task_handle, interpreter): 27 | self._handle = task_handle 28 | self._interpreter = interpreter 29 | 30 | <%namespace name="property_template" file="/property_template.py.mako"/>\ 31 | %for attribute in attributes: 32 | ${property_template.script_property(attribute)}\ 33 | %endfor 34 | \ 35 | <%namespace name="function_template" file="/function_template.py.mako"/>\ 36 | %for function_object in functions: 37 | ${function_template.script_function(function_object)} 38 | %endfor -------------------------------------------------------------------------------- /src/codegen/templates/task/triggering/_pause_trigger.py.mako: -------------------------------------------------------------------------------- 1 | <% 2 | from codegen.utilities.text_wrappers import wrap 3 | from codegen.utilities.function_helpers import get_functions, get_enums_used as functions_enums 4 | from codegen.utilities.attribute_helpers import get_attributes, get_enums_used as attribute_enums 5 | from codegen.utilities.helpers import get_enums_to_import 6 | attributes = get_attributes(data, "PauseTrigger") 7 | functions = get_functions(data,"PauseTrigger") 8 | attr_enums = attribute_enums(attributes) 9 | fuct_enums = functions_enums(functions) 10 | enums_used = get_enums_to_import(attr_enums, fuct_enums) 11 | %>\ 12 | # Do not edit this file; it was automatically generated. 13 | 14 | from nidaqmx.system.physical_channel import _PhysicalChannelAlternateConstructor 15 | %if enums_used: 16 | from nidaqmx.constants import ( 17 | ${', '.join([c for c in enums_used]) | wrap(4, 4)}) 18 | %endif 19 | 20 | 21 | class PauseTrigger: 22 | """ 23 | Represents the pause trigger configurations for a DAQmx task. 24 | """ 25 | __slots__ = ('_handle', '_interpreter') 26 | 27 | def __init__(self, task_handle, interpreter): 28 | self._handle = task_handle 29 | self._interpreter = interpreter 30 | 31 | <%namespace name="property_template" file="/property_template.py.mako"/>\ 32 | %for attribute in attributes: 33 | ${property_template.script_property(attribute)}\ 34 | %endfor 35 | \ 36 | <%namespace name="function_template" file="/function_template.py.mako"/>\ 37 | %for function_object in functions: 38 | ${function_template.script_function(function_object)} 39 | %endfor 40 | -------------------------------------------------------------------------------- /src/codegen/templates/task/triggering/_reference_trigger.py.mako: -------------------------------------------------------------------------------- 1 | <% 2 | from codegen.utilities.text_wrappers import wrap 3 | from codegen.utilities.function_helpers import get_functions, get_enums_used as functions_enums 4 | from codegen.utilities.attribute_helpers import get_attributes, get_enums_used as attribute_enums 5 | from codegen.utilities.helpers import get_enums_to_import 6 | attributes = get_attributes(data, "ReferenceTrigger") 7 | functions = get_functions(data,"ReferenceTrigger") 8 | attr_enums = attribute_enums(attributes) 9 | fuct_enums = functions_enums(functions) 10 | enums_used = get_enums_to_import(attr_enums, fuct_enums) 11 | %>\ 12 | # Do not edit this file; it was automatically generated. 13 | 14 | import numpy 15 | 16 | from nidaqmx.system.physical_channel import _PhysicalChannelAlternateConstructor 17 | %if enums_used: 18 | from nidaqmx.constants import ( 19 | ${', '.join([c for c in enums_used]) | wrap(4, 4)}) 20 | %endif 21 | 22 | 23 | class ReferenceTrigger: 24 | """ 25 | Represents the reference trigger configurations for a DAQmx task. 26 | """ 27 | __slots__ = ('_handle', '_interpreter') 28 | 29 | def __init__(self, task_handle, interpreter): 30 | self._handle = task_handle 31 | self._interpreter = interpreter 32 | 33 | <%namespace name="property_template" file="/property_template.py.mako"/>\ 34 | %for attribute in attributes: 35 | ${property_template.script_property(attribute)}\ 36 | %endfor 37 | \ 38 | <%namespace name="function_template" file="/function_template.py.mako"/>\ 39 | %for function_object in functions: 40 | ${function_template.script_function(function_object)} 41 | %endfor 42 | -------------------------------------------------------------------------------- /src/codegen/templates/task/triggering/_start_trigger.py.mako: -------------------------------------------------------------------------------- 1 | <% 2 | from codegen.utilities.text_wrappers import wrap 3 | from codegen.utilities.function_helpers import get_functions, get_enums_used as functions_enums 4 | from codegen.utilities.attribute_helpers import get_attributes, get_enums_used as attribute_enums 5 | from codegen.utilities.helpers import get_enums_to_import 6 | attributes = get_attributes(data, "StartTrigger") 7 | functions = get_functions(data,"StartTrigger") 8 | attr_enums = attribute_enums(attributes) 9 | fuct_enums = functions_enums(functions) 10 | enums_used = get_enums_to_import(attr_enums, fuct_enums) 11 | %>\ 12 | # Do not edit this file; it was automatically generated. 13 | 14 | import numpy 15 | 16 | from nidaqmx.system.physical_channel import _PhysicalChannelAlternateConstructor 17 | %if enums_used: 18 | from nidaqmx.constants import ( 19 | ${', '.join([c for c in enums_used]) | wrap(4, 4)}) 20 | %endif 21 | from datetime import datetime 22 | 23 | 24 | class StartTrigger: 25 | """ 26 | Represents the start trigger configurations for a DAQmx task. 27 | """ 28 | __slots__ = ('_handle', '_interpreter') 29 | 30 | def __init__(self, task_handle, interpreter): 31 | self._handle = task_handle 32 | self._interpreter = interpreter 33 | 34 | <%namespace name="property_template" file="/property_template.py.mako"/>\ 35 | %for attribute in attributes: 36 | ${property_template.script_property(attribute)}\ 37 | %endfor 38 | \ 39 | <%namespace name="function_template" file="/function_template.py.mako"/>\ 40 | %for function_object in functions: 41 | ${function_template.script_function(function_object)} 42 | %endfor 43 | -------------------------------------------------------------------------------- /src/codegen/utilities/__init__.py: -------------------------------------------------------------------------------- 1 | """This module contains the helpers for code generator.""" 2 | -------------------------------------------------------------------------------- /src/codegen/utilities/text_wrappers.py: -------------------------------------------------------------------------------- 1 | """This contains the helpers methods for wrapping texts.""" 2 | 3 | import textwrap 4 | 5 | """ 6 | Use global text wrapper objects and reassign properties each time instead 7 | of instantiating a new text wrapper object each time, as it is about 2 orders 8 | of magnitude faster. 9 | 10 | An extra character is subtracted from the line width of the code text wrapper 11 | to allow for closing brackets, since the Mako template will be prettier 12 | without lines like [${variable + ']' | wrap(4. 8)} 13 | """ 14 | wrapper = textwrap.TextWrapper(width=78, break_long_words=False) 15 | docstring_wrapper = textwrap.TextWrapper(width=72, break_long_words=False) 16 | 17 | 18 | def wrap(initial_indent, subsequent_indent=None): 19 | """Returns custom Mako filter function that wraps code text. 20 | 21 | Returning another function from within this function is a trick used to 22 | enable Mako filter functions to accept arguments. 23 | """ 24 | 25 | def text_wrap(text): 26 | wrapper.initial_indent = " " * initial_indent 27 | if subsequent_indent is None: 28 | wrapper.subsequent_indent = " " * initial_indent 29 | else: 30 | wrapper.subsequent_indent = " " * subsequent_indent 31 | return wrapper.fill(text).lstrip() 32 | 33 | return text_wrap 34 | 35 | 36 | def docstring_wrap(initial_indent, subsequent_indent=None): 37 | """Returns custom Mako filter function that wraps docstring text. 38 | 39 | Returning another function from within this function is a trick used to 40 | enable Mako filter functions to accept arguments. 41 | """ 42 | 43 | def doc_string_wrap(text): 44 | docstring_wrapper.initial_indent = " " * initial_indent 45 | if subsequent_indent is None: 46 | docstring_wrapper.subsequent_indent = " " * initial_indent 47 | else: 48 | docstring_wrapper.subsequent_indent = " " * subsequent_indent 49 | return docstring_wrapper.fill(text).lstrip() 50 | 51 | return doc_string_wrap 52 | -------------------------------------------------------------------------------- /src/handwritten/__init__.py: -------------------------------------------------------------------------------- 1 | from nidaqmx.errors import DaqError, DaqReadError, DaqWriteError, DaqWarning, DaqResourceWarning 2 | from nidaqmx.grpc_session_options import * 3 | from nidaqmx.scale import Scale 4 | from nidaqmx.task import Task 5 | from nidaqmx.types import CtrFreq, CtrTick, CtrTime 6 | 7 | try: 8 | from importlib.metadata import version 9 | except ImportError: 10 | from importlib_metadata import version # type: ignore[no-redef] 11 | 12 | __version__ = version(__name__) 13 | 14 | __all__ = ["errors", "scale", "stream_readers", "stream_writers", "task"] 15 | 16 | # Do not add a null logging handler. If the application has not configured logging, the 17 | # default behavior is to log warnings and errors to stderr. 18 | -------------------------------------------------------------------------------- /src/handwritten/__main__.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import logging 4 | from typing import Optional 5 | 6 | import click 7 | 8 | from . import _install_daqmx 9 | 10 | 11 | @click.group("nidaqmx") 12 | @click.option( 13 | "-v", 14 | "--verbose", 15 | "verbosity", 16 | count=True, 17 | help="Enable verbose logging. Repeat to increase verbosity.", 18 | ) 19 | def main(verbosity: int) -> None: 20 | _configure_logging(verbosity) 21 | 22 | 23 | @main.command() 24 | def installdriver(): 25 | _install_daqmx.installdriver() 26 | 27 | 28 | def _configure_logging(verbosity: int) -> None: 29 | """Configure logging for this process.""" 30 | if verbosity > 1: 31 | level = logging.DEBUG 32 | elif verbosity == 1: 33 | level = logging.INFO 34 | else: 35 | level = logging.WARNING 36 | logging.basicConfig(format="%(asctime)s %(levelname)s: %(message)s", level=level) 37 | 38 | 39 | if __name__ == "__main__": 40 | main() 41 | -------------------------------------------------------------------------------- /src/handwritten/_bitfield_utils.py: -------------------------------------------------------------------------------- 1 | def enum_bitfield_to_list(bitfield_value, bitfield_enum_type, 2 | actual_enum_type): 3 | """ 4 | Converts a bitfield value to a list of enums. 5 | 6 | Args: 7 | bitfield_value (int): Specifies the value of the bitfield. 8 | bitfield_enum_type (enum.Enum): Specifies the bitfield enum type 9 | from which to mask and extract the enum values. 10 | actual_enum_type (enum.Enum): Specifies the actual enum type. 11 | Returns: 12 | List[enum.Enum]: Indicates the converted list of enums. 13 | """ 14 | supported_values = [] 15 | for bitfield_mask in bitfield_enum_type: 16 | if bitfield_value & bitfield_mask.value: 17 | enum_value = next( 18 | e for e in actual_enum_type if e.name == bitfield_mask.name) 19 | supported_values.append(enum_value) 20 | 21 | return supported_values 22 | 23 | 24 | def enum_list_to_bitfield(enum_list, bitfield_enum_type): 25 | """ 26 | Converts a list of enums to a bitfield value. 27 | 28 | Args: 29 | enum_list (List[enum.Enum]): Specifies the list of enums. 30 | bitfield_enum_type (enum.Enum): Specifies the bitfield enum type 31 | from which to mask and extract the enum values. 32 | Returns: 33 | int: Indicates the value of the bitfield. 34 | """ 35 | bitfield_value = 0 36 | for enum_value in enum_list: 37 | bitfield_mask = next( 38 | b for b in bitfield_enum_type if b.name == enum_value.name) 39 | bitfield_value |= bitfield_mask.value 40 | 41 | return bitfield_value -------------------------------------------------------------------------------- /src/handwritten/_grpc_time.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from datetime import timezone 4 | from datetime import datetime as std_datetime 5 | from datetime import tzinfo as dt_tzinfo 6 | from hightime import datetime as ht_datetime 7 | from hightime import timedelta as ht_timedelta 8 | from typing import Optional, Union 9 | from nidaqmx._time import _convert_to_desired_timezone 10 | 11 | from google.protobuf.timestamp_pb2 import Timestamp as GrpcTimestamp 12 | 13 | # 66 years, 17 leap days = 24107 days = 2082844800 seconds 14 | _BIAS_FROM_1970_EPOCH = 2082844800 15 | 16 | _NS_PER_S = 10**9 17 | _NS_PER_US = 10**3 18 | 19 | _YS_PER_US = 10**18 20 | _YS_PER_NS = 10**15 21 | _YS_PER_FS = 10**9 22 | 23 | _EPOCH_1970 = ht_datetime(1970, 1, 1, tzinfo=timezone.utc) 24 | 25 | def convert_time_to_timestamp(dt: std_datetime | ht_datetime, ts: GrpcTimestamp | None = None) -> GrpcTimestamp: 26 | seconds_since_1970 = 0 27 | 28 | if ts is None: 29 | ts = GrpcTimestamp() 30 | 31 | if isinstance(dt, ht_datetime): 32 | seconds_since_1970 = int((dt - _EPOCH_1970).precision_total_seconds()) 33 | total_yoctoseconds = dt.yoctosecond 34 | total_yoctoseconds += dt.femtosecond * _YS_PER_FS 35 | total_yoctoseconds += dt.microsecond * _YS_PER_US 36 | nanos, remainder_yoctoseconds = divmod(total_yoctoseconds, _YS_PER_NS) 37 | # round up, if necessary 38 | if remainder_yoctoseconds >= _YS_PER_NS / 2: 39 | nanos += 1 40 | else: 41 | seconds_since_1970 = int((dt - _EPOCH_1970).total_seconds()) 42 | nanos = dt.microsecond * _NS_PER_US 43 | 44 | ts.FromNanoseconds(seconds_since_1970 * _NS_PER_S + nanos) 45 | return ts 46 | 47 | def convert_timestamp_to_time(ts: GrpcTimestamp, tzinfo: dt_tzinfo | None = None) -> ht_datetime: 48 | total_nanos = ts.ToNanoseconds() 49 | seconds, nanos = divmod(total_nanos, _NS_PER_S) 50 | # Convert the nanoseconds to yoctoseconds. 51 | total_yoctoseconds = int(round(_YS_PER_NS * nanos)) 52 | dt = _EPOCH_1970 + ht_timedelta(seconds = seconds) + ht_timedelta(yoctoseconds=total_yoctoseconds) 53 | return _convert_to_desired_timezone(dt, tzinfo) 54 | -------------------------------------------------------------------------------- /src/handwritten/_installer_metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "Windows": [ 3 | { 4 | "Version": "25.3.1", 5 | "Release": "2025Q2 Patch 1", 6 | "Location": "https://download.ni.com/support/nipkg/products/ni-d/ni-daqmx/25.3/online/ni-daqmx_25.3_online.exe", 7 | "supportedOS": [ 8 | "Windows 11", 9 | "Windows Server 2022 64-bit", 10 | "Windows 10 64-bit", 11 | "Windows Server 2016 64-bit", 12 | "Windows Server 2019 64-bit" 13 | ] 14 | } 15 | ], 16 | "Linux": [ 17 | { 18 | "Version": "25.3.0", 19 | "Release": "2025Q2", 20 | "_comment": "Location url must be of the format 'https://download.ni.com/support/softlib/MasterRepository/LinuxDrivers/NILinuxDeviceDrivers.zip'. Any change to the format will require a change in the code.", 21 | "Location": "https://download.ni.com/support/softlib/MasterRepository/LinuxDrivers2025Q2/NILinux2025Q2DeviceDrivers.zip", 22 | "supportedOS": [ 23 | "ubuntu 20.04", 24 | "ubuntu 22.04", 25 | "ubuntu 24.04", 26 | "rhel 8", 27 | "rhel 9", 28 | "opensuse 15.4", 29 | "opensuse 15.5" 30 | ] 31 | } 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /src/handwritten/_time.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from tzlocal import get_localzone 4 | from datetime import timezone 5 | from datetime import tzinfo as dt_tzinfo 6 | from datetime import datetime as std_datetime 7 | from hightime import datetime as ht_datetime 8 | from typing import Optional, Union 9 | from zoneinfo import ZoneInfo 10 | 11 | # theoretically the same as astimezone(), but with support for dates before 1970 12 | def _convert_to_desired_timezone(expected_time_utc: std_datetime | ht_datetime, tzinfo: dt_tzinfo | None = None) -> std_datetime | ht_datetime: 13 | # if timezone matches, no need to do conversion 14 | if expected_time_utc.tzinfo is tzinfo: 15 | return expected_time_utc 16 | 17 | # if timezone is not defined, use system timezone 18 | if tzinfo is None: 19 | tzinfo = get_localzone() 20 | 21 | # use ZoneInfo here to account for daylight savings 22 | if isinstance(tzinfo, ZoneInfo): 23 | localized_time = expected_time_utc.replace(tzinfo=tzinfo) 24 | desired_expected_time = tzinfo.fromutc(localized_time) 25 | return(desired_expected_time) 26 | 27 | # if the tzinfo passed in is a timedelta function, then we don't need to consider daylight savings 28 | elif tzinfo.utcoffset(None) is not None: 29 | current_time_utc = ht_datetime.now(timezone.utc) 30 | desired_timezone_offset = current_time_utc.astimezone(tz=tzinfo).utcoffset() 31 | desired_expected_time = expected_time_utc + desired_timezone_offset 32 | new_datetime = desired_expected_time.replace(tzinfo=tzinfo) 33 | return new_datetime 34 | 35 | # if the tzinfo passed in is none of the above, fall back to original astimezone() 36 | else: 37 | return expected_time_utc.astimezone(tzinfo) 38 | -------------------------------------------------------------------------------- /src/handwritten/system/__init__.py: -------------------------------------------------------------------------------- 1 | from nidaqmx.system.system import ( 2 | System, AOPowerUpState, CDAQSyncConnection, DOPowerUpState, 3 | DOResistorPowerUpState) 4 | from nidaqmx.system.device import Device 5 | from nidaqmx.system.physical_channel import PhysicalChannel 6 | from nidaqmx.system.watchdog import ( 7 | WatchdogTask, AOExpirationState, COExpirationState, DOExpirationState) 8 | 9 | __all__ = ['system', 'device', 'physical_channel', 'storage', 'watchdog'] 10 | -------------------------------------------------------------------------------- /src/handwritten/system/_collections/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ni/nidaqmx-python/9515a863bba08f6acb2a0a2e65a7839372d43c85/src/handwritten/system/_collections/__init__.py -------------------------------------------------------------------------------- /src/handwritten/system/_watchdog_modules/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ni/nidaqmx-python/9515a863bba08f6acb2a0a2e65a7839372d43c85/src/handwritten/system/_watchdog_modules/__init__.py -------------------------------------------------------------------------------- /src/handwritten/system/_watchdog_modules/expiration_states_collection.py: -------------------------------------------------------------------------------- 1 | from nidaqmx.errors import DaqError 2 | from nidaqmx.system._watchdog_modules.expiration_state import ExpirationState 3 | 4 | 5 | class ExpirationStatesCollection: 6 | """ 7 | Contains the collection of expiration states for a DAQmx Watchdog Task. 8 | 9 | This class defines methods that implements a container object. 10 | """ 11 | def __init__(self, task_handle, interpreter): 12 | self._handle = task_handle 13 | self._interpreter = interpreter 14 | 15 | def __eq__(self, other): 16 | if isinstance(other, self.__class__): 17 | return self._handle == other._handle 18 | return False 19 | 20 | def __hash__(self): 21 | return self._interpreter.hash_task_handle(self._handle) 22 | 23 | def __ne__(self, other): 24 | return not self.__eq__(other) 25 | 26 | def __getitem__(self, index): 27 | """ 28 | Indexes an expiration state on this collection. 29 | 30 | Args: 31 | index (str): Name of the physical channel of which the 32 | expiration state to retrieve. 33 | Returns: 34 | nidaqmx.system._watchdog_modules.expiration_state.ExpirationState: 35 | 36 | The object representing the indexed expiration state. 37 | """ 38 | if isinstance(index, str): 39 | return ExpirationState(self._handle, index, self._interpreter) 40 | else: 41 | raise DaqError( 42 | 'Invalid index type "{}" used to access expiration states.' 43 | .format(type(index)), -1) 44 | -------------------------------------------------------------------------------- /src/handwritten/system/storage/__init__.py: -------------------------------------------------------------------------------- 1 | from nidaqmx.system.storage.persisted_channel import PersistedChannel 2 | from nidaqmx.system.storage.persisted_scale import PersistedScale 3 | from nidaqmx.system.storage.persisted_task import PersistedTask 4 | 5 | __all__ = ['persisted_channel', 'persisted_scale', 'persisted_task'] 6 | -------------------------------------------------------------------------------- /src/handwritten/task/__init__.py: -------------------------------------------------------------------------------- 1 | from nidaqmx.task._task import ( 2 | Task, _TaskEventType, _TaskAlternateConstructor 3 | ) 4 | from nidaqmx.task._in_stream import InStream 5 | from nidaqmx.task._out_stream import OutStream 6 | from nidaqmx.task._export_signals import ExportSignals 7 | from nidaqmx.task._timing import Timing 8 | 9 | __all__ = ['Task', 'InStream', 'OutStream', 'ExportSignals', 'Timing',] -------------------------------------------------------------------------------- /src/handwritten/task/channels/__init__.py: -------------------------------------------------------------------------------- 1 | from nidaqmx.task.channels._channel import Channel 2 | from nidaqmx.task.channels._ai_channel import AIChannel 3 | from nidaqmx.task.channels._ao_channel import AOChannel 4 | from nidaqmx.task.channels._ci_channel import CIChannel 5 | from nidaqmx.task.channels._co_channel import COChannel 6 | from nidaqmx.task.channels._di_channel import DIChannel 7 | from nidaqmx.task.channels._do_channel import DOChannel 8 | 9 | __all__ = ['Channel', 'AIChannel', 'AOChannel', 'CIChannel', 'COChannel', 'DIChannel', 'DOChannel',] -------------------------------------------------------------------------------- /src/handwritten/task/collections/__init__.py: -------------------------------------------------------------------------------- 1 | from nidaqmx.task.collections._channel_collection import ChannelCollection 2 | from nidaqmx.task.collections._ai_channel_collection import AIChannelCollection 3 | from nidaqmx.task.collections._ao_channel_collection import AOChannelCollection 4 | from nidaqmx.task.collections._ci_channel_collection import CIChannelCollection 5 | from nidaqmx.task.collections._co_channel_collection import COChannelCollection 6 | from nidaqmx.task.collections._di_channel_collection import DIChannelCollection 7 | from nidaqmx.task.collections._do_channel_collection import DOChannelCollection 8 | 9 | __all__ = ['ChannelCollection', 'AIChannelCollection', 'AOChannelCollection', 'CIChannelCollection', 'COChannelCollection', 'DIChannelCollection', 'DOChannelCollection',] -------------------------------------------------------------------------------- /src/handwritten/task/triggering/__init__.py: -------------------------------------------------------------------------------- 1 | from nidaqmx.task.triggering._triggers import Triggers 2 | from nidaqmx.task.triggering._arm_start_trigger import ArmStartTrigger 3 | from nidaqmx.task.triggering._handshake_trigger import HandshakeTrigger 4 | from nidaqmx.task.triggering._pause_trigger import PauseTrigger 5 | from nidaqmx.task.triggering._reference_trigger import ReferenceTrigger 6 | from nidaqmx.task.triggering._start_trigger import StartTrigger 7 | 8 | __all__ = ['Triggers', 'ArmStartTrigger', 'HandshakeTrigger', 'PauseTrigger', 'ReferenceTrigger', 'StartTrigger'] -------------------------------------------------------------------------------- /src/handwritten/types.py: -------------------------------------------------------------------------------- 1 | import collections 2 | import typing 3 | 4 | # region Task Counter IO namedtuples 5 | 6 | CtrFreq = collections.namedtuple( 7 | 'CtrFreq', ['freq', 'duty_cycle']) 8 | 9 | CtrTick = collections.namedtuple( 10 | 'CtrTick', ['high_tick', 'low_tick']) 11 | 12 | CtrTime = collections.namedtuple( 13 | 'CtrTime', ['high_time', 'low_time']) 14 | 15 | # endregion 16 | 17 | # region Power IO namedtuples 18 | 19 | PowerMeasurement = collections.namedtuple( 20 | 'PowerMeasurement', ['voltage', 'current']) 21 | 22 | # endregion 23 | 24 | # region Watchdog namedtuples 25 | 26 | AOExpirationState = collections.namedtuple( 27 | 'AOExpirationState', 28 | ['physical_channel', 'expiration_state', 'output_type']) 29 | 30 | COExpirationState = collections.namedtuple( 31 | 'COExpirationState', ['physical_channel', 'expiration_state']) 32 | 33 | DOExpirationState = collections.namedtuple( 34 | 'DOExpirationState', ['physical_channel', 'expiration_state']) 35 | 36 | # endregion 37 | 38 | # region Power Up States namedtuples 39 | 40 | AOPowerUpState = collections.namedtuple( 41 | 'AOPowerUpState', ['physical_channel', 'power_up_state', 'channel_type']) 42 | 43 | DOPowerUpState = collections.namedtuple( 44 | 'DOPowerUpState', ['physical_channel', 'power_up_state']) 45 | 46 | DOResistorPowerUpState = collections.namedtuple( 47 | 'DOResistorPowerUpState', ['physical_channel', 'power_up_state']) 48 | 49 | # endregion 50 | 51 | # region System namedtuples 52 | 53 | CDAQSyncConnection = collections.namedtuple( 54 | 'CDAQSyncConnection', ['output_port', 'input_port']) 55 | 56 | # endregion 57 | 58 | # region ID Pin namedtuples 59 | 60 | class IDPinContents(typing.NamedTuple): 61 | """IDPinContents represent the contents of the memory connected to the ID pin.""" 62 | 63 | data: list[int] 64 | """The binary data stored on the memory connected to the ID pin.""" 65 | 66 | format_code: int 67 | """The format code of the binary data.""" 68 | 69 | # endregion 70 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | """Tests for the nidaqmx package.""" 2 | -------------------------------------------------------------------------------- /tests/acceptance/__init__.py: -------------------------------------------------------------------------------- 1 | """Acceptance tests for the nidaqmx package.""" 2 | -------------------------------------------------------------------------------- /tests/component/__init__.py: -------------------------------------------------------------------------------- 1 | """Component tests for the nidaqmx package.""" 2 | -------------------------------------------------------------------------------- /tests/component/system/__init__.py: -------------------------------------------------------------------------------- 1 | """Component tests for nidaqmx.system.*.""" 2 | -------------------------------------------------------------------------------- /tests/component/system/_collections/__init__.py: -------------------------------------------------------------------------------- 1 | """Component tests for nidaqmx.system.storage._collecitons.*.""" 2 | -------------------------------------------------------------------------------- /tests/component/system/_collections/test_device_collection.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from nidaqmx.system import System 4 | 5 | 6 | def test___devices___getitem_int___forward_order(system: System): 7 | devices = [system.devices[i] for i in range(len(system.devices))] 8 | 9 | assert [dev.name for dev in devices] == system.devices.device_names 10 | 11 | 12 | def test___devices___getitem_int___shared_interpreter(system: System): 13 | devices = [system.devices[i] for i in range(len(system.devices))] 14 | 15 | assert all(dev._interpreter is system._interpreter for dev in devices) 16 | 17 | 18 | def test___devices___getitem_slice___forward_order(system: System): 19 | devices = system.devices[:] 20 | 21 | assert [dev.name for dev in devices] == system.devices.device_names 22 | 23 | 24 | def test___devices___getitem_slice___shared_interpreter(system: System): 25 | devices = system.devices[:] 26 | 27 | assert all(dev._interpreter is system._interpreter for dev in devices) 28 | 29 | 30 | def test___devices___getitem_str___shared_interpreter(system: System): 31 | devices = [system.devices[name] for name in system.devices.device_names] 32 | 33 | assert all(dev._interpreter is system._interpreter for dev in devices) 34 | 35 | 36 | def test___devices___getitem_str_list___shared_interpreter(system: System): 37 | if len(system.devices) < 2: 38 | pytest.skip("This test requires two or more devices.") 39 | 40 | devices = system.devices[",".join(system.devices.device_names)] 41 | 42 | assert all(dev._interpreter is system._interpreter for dev in devices) 43 | 44 | 45 | def test___devices___iter___forward_order(system: System): 46 | devices = iter(system.devices) 47 | 48 | assert [dev.name for dev in devices] == system.devices.device_names 49 | 50 | 51 | def test___devices___iter___shared_interpreter(system: System): 52 | devices = iter(system.devices) 53 | 54 | assert all(dev._interpreter is system._interpreter for dev in devices) 55 | 56 | 57 | def test___devices___reversed___reverse_order(system: System): 58 | devices = reversed(system.devices) 59 | 60 | assert [dev.name for dev in devices] == list(reversed(system.devices.device_names)) 61 | 62 | 63 | def test___devices___reversed___shared_interpreter(system: System): 64 | devices = reversed(system.devices) 65 | 66 | assert all(dev._interpreter is system._interpreter for dev in devices) 67 | -------------------------------------------------------------------------------- /tests/component/system/_collections/test_persisted_scale_collection.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from nidaqmx.system import System 4 | 5 | 6 | def test___scales___getitem_int___forward_order(system: System): 7 | scales = [system.scales[i] for i in range(len(system.scales))] 8 | 9 | assert [scale.name for scale in scales] == system.scales.scale_names 10 | 11 | 12 | def test___scales___getitem_int___shared_interpreter(system: System): 13 | scales = [system.scales[i] for i in range(len(system.scales))] 14 | 15 | assert all(scale._interpreter is system._interpreter for scale in scales) 16 | 17 | 18 | def test___scales___getitem_slice___forward_order(system: System): 19 | scales = system.scales[:] 20 | 21 | assert [scale.name for scale in scales] == system.scales.scale_names 22 | 23 | 24 | def test___scales___getitem_slice___shared_interpreter(system: System): 25 | scales = system.scales[:] 26 | 27 | assert all(scale._interpreter is system._interpreter for scale in scales) 28 | 29 | 30 | def test___scales___getitem_str___shared_interpreter(system: System): 31 | scales = [system.scales[name] for name in system.scales.scale_names] 32 | 33 | assert all(scale._interpreter is system._interpreter for scale in scales) 34 | 35 | 36 | def test___scales___getitem_str_list___shared_interpreter(system: System): 37 | if len(system.scales) < 2: 38 | pytest.skip("This test requires two or more persisted scales.") 39 | 40 | scales = system.scales[",".join(system.scales.scale_names)] 41 | 42 | assert all(scale._interpreter is system._interpreter for scale in scales) 43 | 44 | 45 | def test___scales___iter___forward_order(system: System): 46 | scales = iter(system.scales) 47 | 48 | assert [scale.name for scale in scales] == system.scales.scale_names 49 | 50 | 51 | def test___scales___iter___shared_interpreter(system: System): 52 | scales = iter(system.scales) 53 | 54 | assert all(scale._interpreter is system._interpreter for scale in scales) 55 | 56 | 57 | def test___scales___reversed___reverse_order(system: System): 58 | scales = reversed(system.scales) 59 | 60 | assert [scale.name for scale in scales] == list(reversed(system.scales.scale_names)) 61 | 62 | 63 | def test___scales___reversed___shared_interpreter(system: System): 64 | scales = reversed(system.scales) 65 | 66 | assert all(scale._interpreter is system._interpreter for scale in scales) 67 | -------------------------------------------------------------------------------- /tests/component/system/_collections/test_persisted_task_collection.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from nidaqmx.system import System 4 | 5 | 6 | def test___tasks___getitem_int___forward_order(system: System): 7 | tasks = [system.tasks[i] for i in range(len(system.tasks))] 8 | 9 | assert [task.name for task in tasks] == system.tasks.task_names 10 | 11 | 12 | def test___tasks___getitem_int___shared_interpreter(system: System): 13 | tasks = [system.tasks[i] for i in range(len(system.tasks))] 14 | 15 | assert all(task._interpreter is system._interpreter for task in tasks) 16 | 17 | 18 | def test___tasks___getitem_slice___forward_order(system: System): 19 | tasks = system.tasks[:] 20 | 21 | assert [task.name for task in tasks] == system.tasks.task_names 22 | 23 | 24 | def test___tasks___getitem_slice___shared_interpreter(system: System): 25 | tasks = system.tasks[:] 26 | 27 | assert all(task._interpreter is system._interpreter for task in tasks) 28 | 29 | 30 | def test___tasks___getitem_str___shared_interpreter(system: System): 31 | tasks = [system.tasks[name] for name in system.tasks.task_names] 32 | 33 | assert all(task._interpreter is system._interpreter for task in tasks) 34 | 35 | 36 | def test___tasks___getitem_str_list___shared_interpreter(system: System): 37 | if len(system.tasks) < 2: 38 | pytest.skip("This test requires two or more persisted tasks.") 39 | 40 | tasks = system.tasks[",".join(system.tasks.task_names)] 41 | 42 | assert all(task._interpreter is system._interpreter for task in tasks) 43 | 44 | 45 | def test___tasks___iter___forward_order(system: System): 46 | tasks = iter(system.tasks) 47 | 48 | assert [task.name for task in tasks] == system.tasks.task_names 49 | 50 | 51 | def test___tasks___iter___shared_interpreter(system: System): 52 | tasks = iter(system.tasks) 53 | 54 | assert all(task._interpreter is system._interpreter for task in tasks) 55 | 56 | 57 | def test___tasks___reversed___reverse_order(system: System): 58 | tasks = reversed(system.tasks) 59 | 60 | assert [task.name for task in tasks] == list(reversed(system.tasks.task_names)) 61 | 62 | 63 | def test___tasks___reversed___shared_interpreter(system: System): 64 | tasks = reversed(system.tasks) 65 | 66 | assert all(task._interpreter is system._interpreter for task in tasks) 67 | -------------------------------------------------------------------------------- /tests/component/system/storage/__init__.py: -------------------------------------------------------------------------------- 1 | """Component tests for nidaqmx.system.storage.*.""" 2 | -------------------------------------------------------------------------------- /tests/component/system/storage/test_persisted_channel_properties.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from nidaqmx import DaqError 4 | from nidaqmx.error_codes import DAQmxErrors 5 | from nidaqmx.system.storage import PersistedChannel 6 | 7 | 8 | def test___constructed_persisted_channel___get_property___returns_persisted_value(init_kwargs): 9 | persisted_channel = PersistedChannel("VoltageTesterChannel", **init_kwargs) 10 | 11 | assert persisted_channel.author == "Test Author" 12 | 13 | 14 | def test___nonexistent_persisted_channel___get_property___raises_invalid_global_chan(init_kwargs): 15 | persisted_channel = PersistedChannel("NonexistentChannel", **init_kwargs) 16 | 17 | with pytest.raises(DaqError) as exc_info: 18 | _ = persisted_channel.author 19 | 20 | assert exc_info.value.error_code == DAQmxErrors.INVALID_GLOBAL_CHAN 21 | 22 | 23 | @pytest.mark.channel_name("VoltageTesterChannel") 24 | def test___persisted_channel___get_bool_property___returns_persisted_value(persisted_channel): 25 | assert persisted_channel.allow_interactive_editing 26 | 27 | 28 | @pytest.mark.channel_name("VoltageTesterChannel") 29 | def test___persisted_channel___get_string_property___returns_persisted_value(persisted_channel): 30 | assert persisted_channel.author == "Test Author" 31 | -------------------------------------------------------------------------------- /tests/component/system/storage/test_persisted_scale.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from nidaqmx.system.storage import PersistedScale 4 | 5 | 6 | def test___persisted_scales_with_same_name___compare___equal(init_kwargs): 7 | persisted_scale1 = PersistedScale("Scale1", **init_kwargs) 8 | persisted_scale2 = PersistedScale("Scale1", **init_kwargs) 9 | 10 | assert persisted_scale1 is not persisted_scale2 11 | assert persisted_scale1 == persisted_scale2 12 | 13 | 14 | def test___persisted_scales_with_different_names___compare___not_equal(init_kwargs): 15 | persisted_scale1 = PersistedScale("Scale1", **init_kwargs) 16 | persisted_scale2 = PersistedScale("Scale2", **init_kwargs) 17 | 18 | assert persisted_scale1 != persisted_scale2 19 | 20 | 21 | def test___persisted_scales_with_same_name___hash___equal(init_kwargs): 22 | persisted_scale1 = PersistedScale("Scale1", **init_kwargs) 23 | persisted_scale2 = PersistedScale("Scale1", **init_kwargs) 24 | 25 | assert persisted_scale1 is not persisted_scale2 26 | assert hash(persisted_scale1) == hash(persisted_scale2) 27 | 28 | 29 | def test___persisted_scales_with_different_names___hash___not_equal(init_kwargs): 30 | persisted_scale1 = PersistedScale("Scale1", **init_kwargs) 31 | persisted_scale2 = PersistedScale("Scale2", **init_kwargs) 32 | 33 | assert hash(persisted_scale1) != hash(persisted_scale2) 34 | 35 | 36 | @pytest.mark.scale_name("double_gain_scale") 37 | def test___persisted_scale___load___shared_interpreter(persisted_scale: PersistedScale): 38 | scale = persisted_scale.load() 39 | 40 | assert scale._interpreter is persisted_scale._interpreter 41 | -------------------------------------------------------------------------------- /tests/component/system/storage/test_persisted_scale_properties.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from nidaqmx import DaqError 4 | from nidaqmx.error_codes import DAQmxErrors 5 | from nidaqmx.system.storage import PersistedScale 6 | 7 | 8 | def test___constructed_persisted_scale___get_property___returns_persisted_value(init_kwargs): 9 | persisted_scale = PersistedScale("double_gain_scale", **init_kwargs) 10 | 11 | assert persisted_scale.author == "Test Author" 12 | 13 | 14 | def test___nonexistent_persisted_scale___get_property___raises_custom_scale_does_not_exist( 15 | init_kwargs, 16 | ): 17 | persisted_scale = PersistedScale("NonexistentScale", **init_kwargs) 18 | 19 | with pytest.raises(DaqError) as exc_info: 20 | _ = persisted_scale.author 21 | 22 | assert exc_info.value.error_code == DAQmxErrors.CUSTOM_SCALE_DOES_NOT_EXIST 23 | 24 | 25 | @pytest.mark.scale_name("double_gain_scale") 26 | def test___persisted_scale___get_bool_property___returns_persisted_value(persisted_scale): 27 | assert persisted_scale.allow_interactive_editing 28 | 29 | 30 | @pytest.mark.scale_name("double_gain_scale") 31 | def test___persisted_scale___get_string_property___returns_persisted_value(persisted_scale): 32 | assert persisted_scale.author == "Test Author" 33 | 34 | 35 | @pytest.mark.scale_name("double_gain_scale") 36 | def test___persisted_scale___load_and_get_float64_property___returns_persisted_value( 37 | persisted_scale, 38 | ): 39 | assert persisted_scale.load().lin_slope == 2.0 40 | 41 | 42 | @pytest.mark.scale_name("polynomial_scale") 43 | def test___persisted_scale___load_and_get_float64_list_property___returns_persisted_value( 44 | persisted_scale, 45 | ): 46 | assert persisted_scale.load().poly_forward_coeff == [0.0, 1.0] 47 | 48 | 49 | @pytest.mark.scale_name("double_gain_scale") 50 | def test___persisted_scale___load_and_get_string_property___returns_persisted_value( 51 | persisted_scale, 52 | ): 53 | assert persisted_scale.load().description == "Twice the gain" 54 | -------------------------------------------------------------------------------- /tests/component/system/test_device.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | import pytest 4 | 5 | from nidaqmx import DaqError 6 | from nidaqmx.error_codes import DAQmxErrors 7 | from nidaqmx.system import Device 8 | 9 | 10 | def test___devices_with_same_name___compare___equal(init_kwargs): 11 | device1 = Device("bridgeTester", **init_kwargs) 12 | device2 = Device("bridgeTester", **init_kwargs) 13 | 14 | assert device1 is not device2 15 | assert device1 == device2 16 | 17 | 18 | def test___devices_with_different_names___compare___not_equal(init_kwargs): 19 | device1 = Device("bridgeTester", **init_kwargs) 20 | device2 = Device("tsVoltageTester1", **init_kwargs) 21 | 22 | assert device1 != device2 23 | 24 | 25 | def test___devices_with_same_name___hash___equal(init_kwargs): 26 | device1 = Device("bridgeTester", **init_kwargs) 27 | device2 = Device("bridgeTester", **init_kwargs) 28 | 29 | assert device1 is not device2 30 | assert hash(device1) == hash(device2) 31 | 32 | 33 | def test___devices_with_different_names___hash___not_equal(init_kwargs): 34 | device1 = Device("bridgeTester", **init_kwargs) 35 | device2 = Device("tsVoltageTester1", **init_kwargs) 36 | 37 | assert hash(device1) != hash(device2) 38 | 39 | 40 | def test___self_test_device___no_errors(sim_6363_device: Device) -> None: 41 | sim_6363_device.self_test_device() 42 | 43 | 44 | def test___restore_last_ext_cal_const___no_errors(sim_6363_device: Device) -> None: 45 | sim_6363_device.restore_last_ext_cal_const() 46 | 47 | 48 | def test___self_cal___no_errors(sim_6363_device: Device) -> None: 49 | sim_6363_device.self_cal() 50 | 51 | 52 | @pytest.mark.skipif(not sys.platform.startswith("win"), reason="mioDAQ support Windows-only") 53 | def test___read_id_pin_memory___error(sim_6423_device: Device) -> None: 54 | with pytest.raises(DaqError) as exc_info: 55 | sim_6423_device.read_id_pin_memory("id0") 56 | 57 | assert exc_info.value.error_code == DAQmxErrors.ID_PIN_NO_EEPROM 58 | 59 | 60 | @pytest.mark.skipif(not sys.platform.startswith("win"), reason="mioDAQ support Windows-only") 61 | def test___write_id_pin_memory___error(sim_6423_device: Device) -> None: 62 | with pytest.raises(DaqError) as exc_info: 63 | sim_6423_device.write_id_pin_memory("id0", [0], 1) 64 | 65 | assert exc_info.value.error_code == DAQmxErrors.ID_PIN_NO_EEPROM 66 | -------------------------------------------------------------------------------- /tests/component/task/__init__.py: -------------------------------------------------------------------------------- 1 | """Component tests for nidaqmx.task.*.""" 2 | -------------------------------------------------------------------------------- /tests/component/task/channels/__init__.py: -------------------------------------------------------------------------------- 1 | """Component tests for nidaqmx.task.channels.*.""" 2 | -------------------------------------------------------------------------------- /tests/component/task/channels/test_do_channel.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from nidaqmx import Task 4 | from nidaqmx.constants import ChannelType, LineGrouping 5 | from nidaqmx.system import Device, System 6 | from nidaqmx.task.channels import DOChannel 7 | from nidaqmx.utils import flatten_channel_string, unflatten_channel_string 8 | 9 | 10 | @pytest.mark.parametrize( 11 | "num_lines", 12 | [1, 2, 8], 13 | ) 14 | def test___task___add_do_chan_chan_for_all_lines___sets_channel_attributes( 15 | task: Task, 16 | sim_6363_device: Device, 17 | num_lines: int, 18 | ) -> None: 19 | chan: DOChannel = task.do_channels.add_do_chan( 20 | flatten_channel_string(sim_6363_device.do_lines.channel_names[:num_lines]), 21 | line_grouping=LineGrouping.CHAN_FOR_ALL_LINES, 22 | ) 23 | 24 | assert len(chan) == 1 25 | assert chan.chan_type == ChannelType.DIGITAL_OUTPUT 26 | assert chan.do_num_lines == num_lines 27 | 28 | 29 | @pytest.mark.parametrize( 30 | "num_lines", 31 | [1, 2, 8], 32 | ) 33 | def test___task___add_do_chan_chan_per_line___sets_channel_attributes( 34 | task: Task, 35 | sim_6363_device: Device, 36 | num_lines: int, 37 | ) -> None: 38 | chans: DOChannel = task.do_channels.add_do_chan( 39 | flatten_channel_string(sim_6363_device.do_lines.channel_names[:num_lines]), 40 | line_grouping=LineGrouping.CHAN_PER_LINE, 41 | ) 42 | 43 | assert len(chans) == num_lines 44 | for chan in chans: 45 | assert chan.chan_type == ChannelType.DIGITAL_OUTPUT 46 | assert chan.do_num_lines == 1 47 | 48 | 49 | # For more extensive virtual channel name testing, refer to test_di_channel.py 50 | @pytest.mark.skipif( 51 | System.local().driver_version < (24, 5, 0), 52 | reason="The fix for this test requires DAQmx 24.5.0 and later", 53 | ) 54 | @pytest.mark.library_only( 55 | reason="The fix for this test isn't supported on gRPC", 56 | ) 57 | def test___task___add_do_chans_with_name___sets_channel_name( 58 | task: Task, 59 | sim_6363_device: Device, 60 | ) -> None: 61 | chan: DOChannel = task.do_channels.add_do_chan( 62 | f"{sim_6363_device.name}/port0/line0:7", 63 | line_grouping=LineGrouping.CHAN_PER_LINE, 64 | name_to_assign_to_lines="myChan0", 65 | ) 66 | 67 | assert unflatten_channel_string(chan.name) == unflatten_channel_string("myChan0:7") 68 | -------------------------------------------------------------------------------- /tests/component/task/test_in_stream_buffer_properties.py: -------------------------------------------------------------------------------- 1 | from nidaqmx.constants import SampleTimingType 2 | 3 | 4 | def test___ai_task___set_int32_property___value_is_set(task, sim_6363_device): 5 | task.ai_channels.add_ai_voltage_chan(sim_6363_device.ai_physical_chans[0].name) 6 | task.timing.samp_timing_type = SampleTimingType.SAMPLE_CLOCK 7 | 8 | # Setting a valid input buffer size of type int32 9 | task.in_stream.input_buf_size = 2000000000 10 | 11 | assert task.in_stream.input_buf_size == 2000000000 12 | 13 | 14 | def test___ai_task___reset_int32_property___value_is_set_to_default(task, sim_6363_device): 15 | task.ai_channels.add_ai_voltage_chan(sim_6363_device.ai_physical_chans[0].name) 16 | task.timing.samp_timing_type = SampleTimingType.SAMPLE_CLOCK 17 | default_buffer_size = task.in_stream.input_buf_size 18 | task.in_stream.input_buf_size = 2000000000 19 | 20 | # Resetting input buffer size 21 | del task.in_stream.input_buf_size 22 | 23 | assert task.in_stream.input_buf_size == default_buffer_size 24 | -------------------------------------------------------------------------------- /tests/component/task/test_out_stream.py: -------------------------------------------------------------------------------- 1 | import numpy 2 | import pytest 3 | 4 | import nidaqmx 5 | import nidaqmx.system 6 | 7 | 8 | @pytest.fixture(params=[1, 2]) 9 | def ao_task( 10 | task: nidaqmx.Task, sim_6363_device: nidaqmx.system.Device, request: pytest.FixtureRequest 11 | ) -> nidaqmx.Task: 12 | """Returns an analog output task with a varying number of channels.""" 13 | _create_ao_channels(task, sim_6363_device, number_of_channels=request.param) 14 | return task 15 | 16 | 17 | def _create_ao_channels( 18 | task: nidaqmx.Task, device: nidaqmx.system.Device, number_of_channels: int 19 | ) -> None: 20 | """Creates the specified number of analog output channels.""" 21 | for i in range(number_of_channels): 22 | task.ao_channels.add_ao_voltage_chan(device.ao_physical_chans[i].name) 23 | 24 | # This test assumes the data format is int16. 25 | assert task.ao_channels.all.ao_resolution == 16 26 | 27 | 28 | @pytest.mark.parametrize("samples_to_write", [1, 10]) 29 | def test___valid_array___write___returns_samples_written( 30 | ao_task: nidaqmx.Task, samples_to_write: int 31 | ) -> None: 32 | ao_task.out_stream.auto_start = True 33 | data = numpy.full(ao_task.number_of_channels * samples_to_write, 0x1234, dtype=numpy.int16) 34 | 35 | samples_written = ao_task.out_stream.write(data) 36 | 37 | assert samples_written == samples_to_write 38 | 39 | 40 | def test___odd_sized_array___write___returns_whole_samples( 41 | task: nidaqmx.Task, sim_6363_device: nidaqmx.system.Device 42 | ) -> None: 43 | _create_ao_channels(task, sim_6363_device, number_of_channels=2) 44 | task.out_stream.auto_start = True 45 | data = numpy.full(19, 0x1234, dtype=numpy.int16) 46 | 47 | samples_written = task.out_stream.write(data) 48 | 49 | assert samples_written == 9 50 | 51 | 52 | def test___out_stream___set_nonexistent_property___raises_exception(task: nidaqmx.Task): 53 | with pytest.raises(AttributeError): 54 | task.out_stream.nonexistent_property = "foo" # type: ignore[attr-defined] 55 | -------------------------------------------------------------------------------- /tests/component/task/test_out_stream_buffer_properties.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from nidaqmx.constants import SampleTimingType 4 | from nidaqmx.error_codes import DAQmxErrors 5 | from nidaqmx.errors import DaqError 6 | 7 | 8 | def test___ai_task___set_valid_value_to_unsupported_property___unsupported_error_raised( 9 | task, 10 | sim_6363_device, 11 | ): 12 | task.ai_channels.add_ai_voltage_chan(sim_6363_device.ai_physical_chans[0].name) 13 | task.timing.samp_timing_type = SampleTimingType.SAMPLE_CLOCK 14 | 15 | with pytest.raises(DaqError) as exc_info: 16 | task.out_stream.output_buf_size = 2000 17 | 18 | assert exc_info.value.error_code == DAQmxErrors.ATTRIBUTE_NOT_SUPPORTED_IN_TASK_CONTEXT 19 | -------------------------------------------------------------------------------- /tests/component/test_export_signals.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | import nidaqmx 4 | 5 | 6 | def test___export_signals___set_nonexistent_property___raises_exception(task: nidaqmx.Task): 7 | with pytest.raises(AttributeError): 8 | task.export_signals.nonexistent_property = "foo" # type: ignore[attr-defined] 9 | -------------------------------------------------------------------------------- /tests/component/test_interpreter.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from nidaqmx._base_interpreter import BaseInterpreter 4 | from nidaqmx.error_codes import DAQmxErrors 5 | 6 | try: 7 | from nidaqmx._grpc_interpreter import _ERROR_MESSAGES 8 | except ImportError: 9 | _ERROR_MESSAGES = {} 10 | 11 | 12 | def test___known_error_code___get_error_string___returns_known_error_message( 13 | interpreter: BaseInterpreter, 14 | ) -> None: 15 | error_message = interpreter.get_error_string(DAQmxErrors.INVALID_ATTRIBUTE_VALUE) 16 | 17 | assert error_message.startswith("Requested value is not a supported value for this property.") 18 | 19 | 20 | def test___unknown_error_code___get_error_string___returns_unable_to_locate_error_resources( 21 | interpreter: BaseInterpreter, 22 | ) -> None: 23 | error_message = interpreter.get_error_string(-12345) 24 | 25 | assert error_message.startswith("Error code could not be found.") 26 | 27 | 28 | @pytest.mark.grpc_only(reason="Tests gRPC-specific error case") 29 | @pytest.mark.temporary_grpc_channel(options=[("grpc.max_send_message_length", 1)]) 30 | def test___grpc_channel_with_errors___get_error_string___returns_failed_to_retrieve_error_description( 31 | interpreter: BaseInterpreter, 32 | ) -> None: 33 | error_message = interpreter.get_error_string(DAQmxErrors.INVALID_ATTRIBUTE_VALUE) 34 | 35 | assert error_message.startswith("Failed to retrieve error description.") 36 | 37 | 38 | @pytest.mark.grpc_only(reason="Tests gRPC-specific error message lookup") 39 | @pytest.mark.parametrize("error_code", list(_ERROR_MESSAGES)) 40 | def test___error_code_with_hardcoded_error_message___get_error_string___returns_hardcoded_error_message( 41 | interpreter: BaseInterpreter, error_code: DAQmxErrors 42 | ) -> None: 43 | error_message = interpreter.get_error_string(error_code) 44 | 45 | assert error_message == _ERROR_MESSAGES[error_code] 46 | -------------------------------------------------------------------------------- /tests/component/test_scale.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | import nidaqmx 4 | 5 | 6 | def test___polynomial___calculate_reverse_poly_coeff___returns_reverse_coeff(init_kwargs): 7 | # The given values represents the polynomial y = 2x + 1 8 | forward_coeff = [1.0, 2.0] 9 | min_val_x = -10.0 10 | max_val_x = 10.0 11 | num_of_points_to_compute = 1000 12 | reverse_polynomial_order = 1 13 | 14 | reverse_coeff = nidaqmx.Scale.calculate_reverse_poly_coeff( 15 | forward_coeff, 16 | min_val_x, 17 | max_val_x, 18 | num_of_points_to_compute, 19 | reverse_polynomial_order, 20 | **init_kwargs 21 | ) 22 | 23 | # The expected inverted polynomial is 0.5y - 0.5 = x 24 | assert reverse_coeff == pytest.approx([-0.5, 0.5]) 25 | -------------------------------------------------------------------------------- /tests/component/test_task_properties.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from nidaqmx import Task 4 | from nidaqmx.system import Device 5 | 6 | 7 | @pytest.fixture 8 | def ai_task(task: Task, sim_6363_device: Device) -> Task: 9 | """Gets an AI task.""" 10 | task.ai_channels.add_ai_voltage_chan( 11 | f"{sim_6363_device.name}/ai0:3", name_to_assign_to_channel="MyChannel" 12 | ) 13 | return task 14 | 15 | 16 | def test___get_channels___returns_channels(ai_task: Task): 17 | channel = ai_task.channels 18 | 19 | assert channel.name == "MyChannel0:3" 20 | 21 | 22 | def test___get_channels___shared_interpreter(ai_task: Task): 23 | channel = ai_task.channels 24 | 25 | assert channel._interpreter is ai_task._interpreter 26 | 27 | 28 | def test___get_devices___returns_devices(ai_task: Task, sim_6363_device: Device): 29 | devices = ai_task.devices 30 | 31 | assert [dev.name for dev in devices] == [sim_6363_device.name] 32 | 33 | 34 | def test___get_devices___shared_interpreter(ai_task: Task): 35 | devices = ai_task.devices 36 | 37 | assert all(dev._interpreter is ai_task._interpreter for dev in devices) 38 | 39 | 40 | def test___task___set_nonexistent_property___raises_exception(task: Task): 41 | with pytest.raises(AttributeError): 42 | task.nonexistent_property = "foo" # type: ignore[attr-defined] 43 | -------------------------------------------------------------------------------- /tests/component/test_task_resource_warnings.py: -------------------------------------------------------------------------------- 1 | import warnings 2 | 3 | import pytest 4 | 5 | import nidaqmx 6 | from nidaqmx.errors import DaqResourceWarning 7 | 8 | 9 | @pytest.mark.library_only(reason="gRPC task allows detaching from task on server") 10 | def test___unclosed_task___leak_task___resource_warning_raised(task): 11 | # Since __del__ is not guaranteed to be called, for the purposes of 12 | # consistent test results call __del__ manually. 13 | with pytest.warns(DaqResourceWarning): 14 | task.__del__() 15 | 16 | 17 | @pytest.mark.grpc_only(reason="gRPC task allows detaching from task on server") 18 | def test___grpc_task___leak_task___resource_warning_not_raised(task): 19 | with warnings.catch_warnings(record=True) as warnings_raised: 20 | task.__del__() 21 | 22 | assert len(warnings_raised) == 0 23 | 24 | 25 | def test___closed_task___del_task___resource_warning_not_raised(init_kwargs): 26 | task = nidaqmx.Task(**init_kwargs) 27 | task.close() 28 | 29 | with warnings.catch_warnings(record=True) as warnings_raised: 30 | task.__del__() 31 | 32 | assert len(warnings_raised) == 0 33 | -------------------------------------------------------------------------------- /tests/helpers.py: -------------------------------------------------------------------------------- 1 | """This contains the helpers methods used in the DAQmx tests.""" 2 | 3 | from __future__ import annotations 4 | 5 | import contextlib 6 | import pathlib 7 | from typing import Generator 8 | 9 | from nidaqmx.system.physical_channel import PhysicalChannel 10 | 11 | # Power uses fixed-point scaling, so we have a pretty wide epsilon. 12 | POWER_ABS_EPSILON = 1e-3 13 | 14 | 15 | def generate_random_seed(): 16 | """Creates a random integer.""" 17 | # Randomizing the random seed makes the GitHub test reporting action 18 | # (EnricoMi/publish-unit-test-result-action) report many added/removed 19 | # tests, so use the same random seed every time. 20 | return 42 21 | 22 | 23 | @contextlib.contextmanager 24 | def configure_teds( 25 | phys_chan: PhysicalChannel, teds_file_path: str | pathlib.PurePath | None = None 26 | ) -> Generator[PhysicalChannel]: 27 | """Yields a physical channel with TEDS configured and then clears it after the test is done.""" 28 | phys_chan.configure_teds(teds_file_path) 29 | try: 30 | yield phys_chan 31 | finally: 32 | phys_chan.clear_teds() 33 | -------------------------------------------------------------------------------- /tests/legacy/__init__.py: -------------------------------------------------------------------------------- 1 | """Legacy tests that do not follow NI's Python testing conventions.""" 2 | -------------------------------------------------------------------------------- /tests/legacy/test_channels.py: -------------------------------------------------------------------------------- 1 | """Tests for validating the channel objects.""" 2 | 3 | import numpy 4 | 5 | from nidaqmx.constants import TaskMode 6 | 7 | 8 | class TestChannels: 9 | """Contains a collection of pytest tests. 10 | 11 | This validate the channel objects in the NI-DAQmx Python API. 12 | """ 13 | 14 | def test_ai_channel(self, task, sim_6363_device): 15 | """Tests for creating ai channel.""" 16 | ai_channel = task.ai_channels.add_ai_voltage_chan( 17 | sim_6363_device.ai_physical_chans[0].name, max_val=10 18 | ) 19 | 20 | # Test property default value. 21 | assert ai_channel.ai_max == 10 22 | 23 | def test_ao_channel(self, task, sim_6363_device): 24 | """Tests for creating ao channel.""" 25 | ao_channel = task.ao_channels.add_ao_voltage_chan( 26 | sim_6363_device.ao_physical_chans[0].name, max_val=5 27 | ) 28 | 29 | # Test property default value. 30 | assert ao_channel.ao_max == 5 31 | 32 | def test_ci_channel(self, task, real_x_series_device): 33 | """Tests for creating ci channel.""" 34 | ci_channel = task.ci_channels.add_ci_count_edges_chan( 35 | real_x_series_device.ci_physical_chans[0].name, initial_count=10 36 | ) 37 | 38 | task.control(TaskMode.TASK_COMMIT) 39 | 40 | assert ci_channel.ci_count == 10 41 | 42 | def test_co_channel(self, task, sim_6363_device): 43 | """Tests for creating co channel.""" 44 | co_channel = task.co_channels.add_co_pulse_chan_freq( 45 | sim_6363_device.co_physical_chans[0].name, freq=5000 46 | ) 47 | 48 | task.control(TaskMode.TASK_COMMIT) 49 | 50 | numpy.testing.assert_allclose([co_channel.co_pulse_freq], [5000], rtol=0.05) 51 | 52 | def test_di_channel(self, task, sim_6363_device): 53 | """Tests for creating di channel.""" 54 | di_channel = task.di_channels.add_di_chan(sim_6363_device.di_lines[0].name) 55 | 56 | assert di_channel.di_num_lines == 1 57 | 58 | def test_do_channel(self, task, sim_6363_device): 59 | """Tests for creating do channel.""" 60 | do_channel = task.do_channels.add_do_chan(sim_6363_device.do_lines[0].name) 61 | 62 | assert do_channel.do_num_lines == 1 63 | -------------------------------------------------------------------------------- /tests/legacy/test_export_signals.py: -------------------------------------------------------------------------------- 1 | """Tests for validating export signals functionality.""" 2 | 3 | import random 4 | 5 | import pytest 6 | 7 | from nidaqmx.constants import Signal 8 | from tests.helpers import generate_random_seed 9 | from tests.legacy.test_read_write import TestDAQmxIOBase 10 | 11 | 12 | class TestExportSignals(TestDAQmxIOBase): 13 | """Contains a collection of pytest tests. 14 | 15 | These validate the export signals functionality in the NI-DAQmx Python API. 16 | These tests use only a single X Series device by utilizing the internal 17 | loopback routes on the device. 18 | """ 19 | 20 | @pytest.mark.parametrize("seed", [generate_random_seed()]) 21 | def test_export_signals(self, task, sim_6363_device, seed): 22 | """Test for validating export signals.""" 23 | # Reset the pseudorandom number generator with seed. 24 | random.seed(seed) 25 | 26 | ai_chan = random.choice(sim_6363_device.ai_physical_chans) 27 | pfi_line = random.choice(self._get_device_pfi_lines(sim_6363_device)) 28 | 29 | task.ai_channels.add_ai_voltage_chan(ai_chan.name) 30 | task.timing.cfg_samp_clk_timing(1000) 31 | 32 | task.export_signals.export_signal(Signal.SAMPLE_CLOCK, pfi_line) 33 | 34 | assert task.export_signals.samp_clk_output_term == pfi_line 35 | -------------------------------------------------------------------------------- /tests/legacy/test_power_up_states.py: -------------------------------------------------------------------------------- 1 | """Tests for validating power up states functions.""" 2 | 3 | from nidaqmx.constants import PowerUpStates 4 | from nidaqmx.system.system import DOPowerUpState 5 | 6 | 7 | class TestPowerUpStates: 8 | """Contains a collection of pytest tests. 9 | 10 | This validate the power up states functions in the Python NI-DAQmx API. 11 | """ 12 | 13 | def test_digital_power_up_states(self, real_x_series_device, system): 14 | """Test for validating digital power up states.""" 15 | # The power up state for digital lines for an X Series device has to 16 | # be set for the whole port. 17 | do_port = real_x_series_device.do_ports[0].name 18 | 19 | # Set power up state for whole port. 20 | state_to_set = DOPowerUpState(physical_channel=do_port, power_up_state=PowerUpStates.LOW) 21 | system.set_digital_power_up_states(real_x_series_device.name, [state_to_set]) 22 | 23 | # Getting power up states returns state for all channels on device. 24 | all_states = system.get_digital_power_up_states(real_x_series_device.name) 25 | 26 | # Find power states for all the digital lines that belong to the port. 27 | port_states = [p for p in all_states if do_port in p.physical_channel] 28 | 29 | # Validate power up states were set. 30 | for state in port_states: 31 | assert state.power_up_state == PowerUpStates.LOW 32 | 33 | # Reset power up states to tristate. 34 | state_to_set = DOPowerUpState( 35 | physical_channel=do_port, power_up_state=PowerUpStates.TRISTATE 36 | ) 37 | system.set_digital_power_up_states(real_x_series_device.name, [state_to_set]) 38 | 39 | all_states = system.get_digital_power_up_states(real_x_series_device.name) 40 | port_states = [p for p in all_states if do_port in p.physical_channel] 41 | 42 | for state in port_states: 43 | assert state.power_up_state == PowerUpStates.TRISTATE 44 | -------------------------------------------------------------------------------- /tests/legacy/test_resource_warnings.py: -------------------------------------------------------------------------------- 1 | """Tests for validating resource warning behavior.""" 2 | 3 | import pytest 4 | 5 | import nidaqmx 6 | from nidaqmx import DaqResourceWarning 7 | 8 | 9 | class TestResourceWarnings: 10 | """Contains a collection of pytest tests. 11 | 12 | These validate the ResourceWarning behavior of the Python NI-DAQmx API. 13 | """ 14 | 15 | def test_task_double_close(self, init_kwargs): 16 | """Test to validate double closure of tasks.""" 17 | with pytest.warns(DaqResourceWarning): 18 | with nidaqmx.Task(**init_kwargs) as task: 19 | task.close() 20 | -------------------------------------------------------------------------------- /tests/legacy/test_teds.py: -------------------------------------------------------------------------------- 1 | """Tests for validating TEDS functionality.""" 2 | 3 | import random 4 | 5 | import pytest 6 | 7 | from nidaqmx.constants import TEDSUnits, TerminalConfiguration 8 | from tests.helpers import configure_teds, generate_random_seed 9 | 10 | 11 | class TestTEDS: 12 | """Contains a collection of pytest tests. 13 | 14 | These validate the TEDS functionality in the NI-DAQmx Python API. 15 | """ 16 | 17 | @pytest.mark.parametrize("seed", [generate_random_seed()]) 18 | def test_create_teds_ai_voltage_chan(self, task, sim_6363_device, seed, voltage_teds_file_path): 19 | """Test to validate TEDS functionality.""" 20 | # Reset the pseudorandom number generator with seed. 21 | random.seed(seed) 22 | 23 | with configure_teds( 24 | random.choice(sim_6363_device.ai_physical_chans), voltage_teds_file_path 25 | ) as ai_phys_chan: 26 | assert ai_phys_chan.teds_mfg_id == 17 27 | assert ai_phys_chan.teds_model_num == 1 28 | assert ai_phys_chan.teds_version_letter == "A" 29 | assert ai_phys_chan.teds_version_num == 1 30 | assert ai_phys_chan.teds_template_ids == [30] 31 | 32 | ai_channel = task.ai_channels.add_teds_ai_voltage_chan( 33 | ai_phys_chan.name, 34 | name_to_assign_to_channel="TEDSVoltageChannel", 35 | terminal_config=TerminalConfiguration.DEFAULT, 36 | min_val=-300.0, 37 | max_val=100.0, 38 | units=TEDSUnits.FROM_TEDS, 39 | ) 40 | 41 | assert ai_channel.ai_teds_is_teds 42 | assert ai_channel.ai_teds_units == "Kelvin" 43 | assert ai_channel.ai_min == -300.0 44 | assert ai_channel.ai_max == 100.0 45 | -------------------------------------------------------------------------------- /tests/legacy/test_utils.py: -------------------------------------------------------------------------------- 1 | """Tests for validating utilities functionality.""" 2 | 3 | from __future__ import annotations 4 | 5 | from nidaqmx.utils import flatten_channel_string, unflatten_channel_string 6 | 7 | 8 | class TestUtils: 9 | """Contains a collection of pytest tests. 10 | 11 | These validate the utilities functionality in the NI-DAQmx Python API. 12 | """ 13 | 14 | def test_basic_flatten_flatten_and_unflatten(self): 15 | """Test to validate flatten and unflatten channel string function.""" 16 | unflattened_channels = ["Dev1/ai0", "Dev1/ai1", "Dev1/ai2", "Dev1/ai4", "Dev2/ai0"] 17 | flattened_channels = "Dev1/ai0:2,Dev1/ai4,Dev2/ai0" 18 | assert flatten_channel_string(unflattened_channels) == flattened_channels 19 | assert unflatten_channel_string(flattened_channels) == unflattened_channels 20 | 21 | def test_backwards_flatten_flatten_and_unflatten(self): 22 | """Test to validate unflatten and flatten channel string function.""" 23 | unflattened_channels = ["Dev1/ai2", "Dev1/ai1", "Dev1/ai0", "Dev1/ai4", "Dev2/ai0"] 24 | flattened_channels = "Dev1/ai2:0,Dev1/ai4,Dev2/ai0" 25 | assert flatten_channel_string(unflattened_channels) == flattened_channels 26 | assert unflatten_channel_string(flattened_channels) == unflattened_channels 27 | 28 | def test_empty_flatten_flatten_and_unflatten(self): 29 | """Test to validate flatten and unflatten empty channel.""" 30 | unflattened_channels: list[str] = [] 31 | flattened_channels = "" 32 | assert flatten_channel_string(unflattened_channels) == flattened_channels 33 | assert unflatten_channel_string(flattened_channels) == unflattened_channels 34 | 35 | def test_leading_zeros_flatten_and_unflatten(self): 36 | """Test to validate leading zeros in flatten and unflatten string function.""" 37 | unflattened_channels = ["EV01", "EV02"] 38 | flattened_channels = "EV01:02" 39 | assert flatten_channel_string(unflattened_channels) == flattened_channels 40 | assert unflatten_channel_string(flattened_channels) == unflattened_channels 41 | -------------------------------------------------------------------------------- /tests/max_config/examplesMaxConfig.ini: -------------------------------------------------------------------------------- 1 | [DAQmx] 2 | MajorVersion = 23 3 | MinorVersion = 0 4 | 5 | [DAQmxCDAQChassis cDAQ1] 6 | ProductType = cDAQ-9178 7 | DevSerialNum = 0x0 8 | DevIsSimulated = 1 9 | 10 | [DAQmxCDAQModule cDAQ1Mod1] 11 | ProductType = NI 9215 12 | DevSerialNum = 0x0 13 | DevIsSimulated = 1 14 | CompactDAQ.ChassisDevName = cDAQ1 15 | CompactDAQ.SlotNum = 1 16 | 17 | [DAQmxCDAQModule cDAQ1Mod2] 18 | ProductType = NI 9214 19 | DevSerialNum = 0x0 20 | DevIsSimulated = 1 21 | CompactDAQ.ChassisDevName = cDAQ1 22 | CompactDAQ.SlotNum = 2 23 | 24 | [DAQmxDevice Dev1] 25 | ProductType = PCIe-6363 26 | DevSerialNum = 0x0 27 | DevIsSimulated = 1 28 | ProductNum = 0x7435C4C4 29 | BusType = PCIe 30 | PCI.BusNum = 0x0 31 | PCI.DevNum = 0x0 32 | 33 | [DAQmxDevice PXI1Slot3] 34 | ProductType = PXIe-6368 35 | DevSerialNum = 0x0 36 | DevIsSimulated = 1 37 | ProductNum = 0x7439C4C4 38 | BusType = PXIe 39 | PXI.ChassisNum = 1 40 | PXI.SlotNum = 3 41 | 42 | [DAQmxDevice PXI1Slot7] 43 | ProductType = PXIe-6368 44 | DevSerialNum = 0x0 45 | DevIsSimulated = 1 46 | ProductNum = 0x7439C4C4 47 | BusType = PXIe 48 | PXI.ChassisNum = 1 49 | PXI.SlotNum = 7 50 | 51 | [DAQmxCDAQChassis TS1] 52 | ProductType = TS-15000 53 | DevSerialNum = 0x0 54 | DevIsSimulated = 1 55 | 56 | [DAQmxCDAQModule TS1Mod1] 57 | ProductType = TS-15200 58 | DevSerialNum = 0x0 59 | DevIsSimulated = 1 60 | CompactDAQ.ChassisDevName = TS1 61 | CompactDAQ.SlotNum = 1 62 | 63 | -------------------------------------------------------------------------------- /tests/test_assets/teds/Accelerometer.ted: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /tests/test_assets/teds/Current.ted: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /tests/test_assets/teds/ForceSensor.ted: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /tests/test_assets/teds/LVDT.ted: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /tests/test_assets/teds/Microphone.ted: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /tests/test_assets/teds/RVDT.ted: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /tests/test_assets/teds/Resistance.ted: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /tests/test_assets/teds/StrainGage.ted: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /tests/test_assets/teds/TempRTD.ted: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /tests/test_assets/teds/TempTC.ted: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /tests/test_assets/teds/ThermistorIex.ted: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /tests/test_assets/teds/ThermistorVex.ted: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /tests/test_assets/teds/Voltage.ted: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /tests/test_assets/teds/forcebridge.ted: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /tests/test_assets/teds/pressurebridge.ted: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /tests/test_assets/teds/torquebridge.ted: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /tests/unit/__init__.py: -------------------------------------------------------------------------------- 1 | """Unit tests for the nidaqmx package.""" 2 | -------------------------------------------------------------------------------- /tests/unit/_grpc_utils.py: -------------------------------------------------------------------------------- 1 | """gRPC helper functions.""" 2 | 3 | import pytest 4 | from pytest_mock import MockerFixture 5 | 6 | import nidaqmx 7 | 8 | try: 9 | import grpc 10 | except ImportError: 11 | grpc = None # type: ignore 12 | 13 | 14 | def create_grpc_options(mocker: MockerFixture, session_name="") -> nidaqmx.GrpcSessionOptions: 15 | """Create a GrpcSessionOptions object.""" 16 | if grpc is None: 17 | pytest.skip("The grpc module is not available.") 18 | grpc_channel = mocker.create_autospec(grpc.Channel) 19 | grpc_options = nidaqmx.GrpcSessionOptions(grpc_channel, session_name) 20 | return grpc_options 21 | -------------------------------------------------------------------------------- /tests/unit/_task_utils.py: -------------------------------------------------------------------------------- 1 | """Task helper functions.""" 2 | 3 | from __future__ import annotations 4 | 5 | from typing import Iterable 6 | from unittest.mock import Mock 7 | 8 | from pytest_mock import MockerFixture 9 | 10 | from nidaqmx import Task 11 | from nidaqmx._base_interpreter import BaseEventHandler 12 | from nidaqmx.task import _TaskEventType 13 | 14 | 15 | def expect_create_task( 16 | interpreter: Mock, 17 | task_handle="SomeTaskHandle", 18 | new_session_initialized=True, 19 | ): 20 | """Expect a call to interpreter.create_task.""" 21 | interpreter.create_task.return_value = (task_handle, new_session_initialized) 22 | 23 | 24 | def expect_load_task( 25 | interpreter: Mock, 26 | task_handle="SomeTaskHandle", 27 | new_session_initialized=True, 28 | ): 29 | """Expect a call to interpreter.load_task.""" 30 | interpreter.load_task.return_value = (task_handle, new_session_initialized) 31 | 32 | 33 | def expect_get_task_name(interpreter: Mock, name: str): 34 | """Expect a call to get the task name.""" 35 | # Assume there are no other calls to get_task_attribute_string. 36 | interpreter.get_task_attribute_string.return_value = name 37 | 38 | 39 | def register_event_handler(mocker: MockerFixture, task: Task, event_type: _TaskEventType) -> Mock: 40 | """Register a mock event handler.""" 41 | event_handler = mocker.create_autospec(BaseEventHandler) 42 | task._event_handlers[event_type] = event_handler 43 | return event_handler 44 | 45 | 46 | def register_event_handlers( 47 | mocker: MockerFixture, task: Task, event_types: Iterable[_TaskEventType] 48 | ) -> dict[_TaskEventType, Mock]: 49 | """Register mock event handlers and return a dictionary mapping event name -> handler.""" 50 | return { 51 | event_type: register_event_handler(mocker, task, event_type) for event_type in event_types 52 | } 53 | -------------------------------------------------------------------------------- /tests/unit/_time_utils.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime as std_datetime 2 | from datetime import timezone 3 | 4 | from hightime import datetime as ht_datetime 5 | 6 | # Jan 1, 2002 = 32 years + 8 leapdays = 11688 days = 1009843200 seconds 7 | JAN_01_2002_TIMESTAMP_1970_EPOCH = 0x3C30FC00 8 | JAN_01_1850_TIMESTAMP_1970_EPOCH = -0xE1B65F80 9 | # Jan 1, 2002 = 98 years + 25 leapdays = 35795 days = 3092688000 seconds 10 | JAN_01_2002_TIMESTAMP_1904_EPOCH = 0xB856AC80 11 | JAN_01_1904_TIMESTAMP_1904_EPOCH = 0 12 | JAN_01_1850_TIMESTAMP_1904_EPOCH = -0x6590AF00 13 | 14 | JAN_01_2002_DATETIME = std_datetime(2002, 1, 1, tzinfo=timezone.utc) 15 | JAN_01_2002_HIGHTIME = ht_datetime(2002, 1, 1, tzinfo=timezone.utc) 16 | JAN_01_1904_DATETIME = std_datetime(1904, 1, 1, tzinfo=timezone.utc) 17 | JAN_01_1904_HIGHTIME = ht_datetime(1904, 1, 1, tzinfo=timezone.utc) 18 | JAN_01_1850_DATETIME = std_datetime(1850, 1, 1, tzinfo=timezone.utc) 19 | JAN_01_1850_HIGHTIME = ht_datetime(1850, 1, 1, tzinfo=timezone.utc) 20 | -------------------------------------------------------------------------------- /tests/unit/conftest.py: -------------------------------------------------------------------------------- 1 | """Fixtures used in the DAQmx unit tests.""" 2 | 3 | from __future__ import annotations 4 | 5 | from typing import Generator 6 | from unittest.mock import Mock 7 | 8 | import pytest 9 | from pytest_mock import MockerFixture 10 | 11 | from nidaqmx import Task 12 | from nidaqmx._base_interpreter import BaseInterpreter 13 | from tests.unit._task_utils import expect_create_task, expect_get_task_name 14 | 15 | 16 | @pytest.fixture 17 | def interpreter(mocker: MockerFixture) -> Mock: 18 | """Create a mock interpreter.""" 19 | mock_interpreter = mocker.create_autospec(BaseInterpreter) 20 | mock_select_interpreter = mocker.patch("nidaqmx.utils._select_interpreter", autospec=True) 21 | mock_select_interpreter.return_value = mock_interpreter 22 | return mock_interpreter 23 | 24 | 25 | @pytest.fixture 26 | def task(interpreter: Mock) -> Generator[Task]: 27 | """Create a DAQmx task. 28 | 29 | This fixture owns the task. Do not use it for test cases that destroy the task, or else you 30 | may get double-close warnings. 31 | """ 32 | expect_create_task(interpreter) 33 | expect_get_task_name(interpreter, "MyTask") 34 | 35 | with Task("MyTask") as task: 36 | yield task 37 | -------------------------------------------------------------------------------- /tests/unit/system/__init__.py: -------------------------------------------------------------------------------- 1 | """Unit tests for nidaqmx.system.*.""" 2 | -------------------------------------------------------------------------------- /tests/unit/system/storage/__init__.py: -------------------------------------------------------------------------------- 1 | """Unit tests for nidaqmx.system.storage.*.""" 2 | -------------------------------------------------------------------------------- /tests/unit/system/storage/test_persisted_task.py: -------------------------------------------------------------------------------- 1 | from unittest.mock import Mock 2 | 3 | import pytest 4 | 5 | from nidaqmx.system.storage import PersistedTask 6 | from tests.unit._task_utils import expect_get_task_name, expect_load_task 7 | 8 | 9 | def test___persisted_task___load___specified_name_saved(interpreter: Mock): 10 | expect_load_task(interpreter) 11 | expect_get_task_name(interpreter, "SpecifiedName") 12 | persisted_task = PersistedTask("SpecifiedName") 13 | 14 | task = persisted_task.load() 15 | 16 | with task: 17 | assert task._saved_name == "SpecifiedName" 18 | 19 | 20 | @pytest.mark.parametrize("new_session_initialized", [False, True]) 21 | def test___persisted_task___load___load_task_called( 22 | interpreter: Mock, new_session_initialized: bool 23 | ): 24 | expect_load_task(interpreter, "MyTaskHandle", new_session_initialized) 25 | expect_get_task_name(interpreter, "SpecifiedName") 26 | persisted_task = PersistedTask("SpecifiedName") 27 | 28 | task = persisted_task.load() 29 | 30 | with task: 31 | interpreter.load_task.assert_called_with("SpecifiedName") 32 | assert task._handle == "MyTaskHandle" 33 | assert task._close_on_exit == new_session_initialized 34 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | # Tox (http://tox.testrun.org/) is a tool for running tests 2 | # in multiple virtualenvs. This configuration file will run the 3 | # test suite on all supported python versions. To use it, "pip install tox" 4 | # and then run "tox" from this directory. 5 | 6 | [tox] 7 | isolated_build = true 8 | envlist = clean, py{39,310,311,312,313}-base, py{39,310,311,312,313}-grpc, py39-base-nicaiu, py39-base-nicai_utf8, report, docs 9 | 10 | [testenv] 11 | skip_install = true 12 | allowlist_externals = poetry 13 | setenv = 14 | base: INSTALL_OPTS=--only main,test 15 | grpc: INSTALL_OPTS=--only main,test --extras grpc 16 | base: PYTEST_OPTS=-k "not grpc" 17 | grpc: PYTEST_OPTS= 18 | nicaiu: NIDAQMX_C_LIBRARY=nicaiu 19 | nicai_utf8: NIDAQMX_C_LIBRARY=nicai_utf8 20 | platform = 21 | nicaiu: win32 22 | nicai_utf8: win32 23 | commands = 24 | poetry run python --version 25 | poetry install -v {env:INSTALL_OPTS} 26 | poetry run python -c "from nidaqmx._lib import lib_importer; print(f'Library: {lib_importer.windll._library._name}\nLibrary encoding: {lib_importer.encoding}')" 27 | poetry run pytest --quiet --cov=generated/nidaqmx --cov-append --cov-report= --junitxml=test_results/system-{envname}.xml {env:PYTEST_OPTS} {posargs} 28 | 29 | [testenv:clean] 30 | commands = poetry run coverage erase 31 | 32 | [testenv:report] 33 | commands = 34 | poetry run coverage html 35 | poetry run coverage report 36 | 37 | [testenv:docs] 38 | # base_python should match the version specified in .readthedocs.yml and the PR workflow. 39 | base_python = python3.11 40 | commands = 41 | poetry run python --version 42 | poetry install -v --only main,docs 43 | # Use -W to treat warnings as errors. 44 | poetry run sphinx-build -b html -W docs docs/_build 45 | --------------------------------------------------------------------------------