├── .coveragerc ├── .github ├── PULL_REQUEST_TEMPLATE.md ├── actions │ └── build │ │ └── action.yml └── workflows │ ├── lint.yml │ ├── publish-pypi.yml │ ├── publish-testpypi.yml │ └── test-build-install.yml ├── .gitignore ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── STATUS.md ├── azure-pipelines.yml ├── pyproject.toml ├── requirements.txt ├── setup.cfg ├── src └── nitsm │ ├── __init__.py │ ├── _pinmapinterfaces.py │ ├── codemoduleapi.py │ ├── debug.py │ ├── enums.py │ ├── pinquerycontexts.py │ └── tsmcontext.py ├── systemtests ├── FrontEndCallbacks.seq ├── SystemTestFixture.seq ├── __init__.py ├── conftest.py ├── custom_instruments.pinmap ├── custom_instruments.seq ├── custom_instruments_codemodules.py ├── nidaqmx.offlinecfg ├── nidaqmx.pinmap ├── nidaqmx.seq ├── nidaqmx_codemodules.py ├── nidcpower.pinmap ├── nidcpower.seq ├── nidcpower_codemodules.py ├── nidigital.digicapture ├── nidigital.digilevels ├── nidigital.digipat ├── nidigital.digiproj ├── nidigital.digitiming ├── nidigital.pinmap ├── nidigital.seq ├── nidigital.specs ├── nidigital.tdms ├── nidigital_codemodules.py ├── nidmm.pinmap ├── nidmm.seq ├── nidmm_codemodules.py ├── nifgen.pinmap ├── nifgen.seq ├── nifgen_codemodules.py ├── nirelaydriver.pinmap ├── nirelaydriver.seq ├── nirelaydriver_codemodules.py ├── niscope.pinmap ├── niscope.seq ├── niscope_codemodules.py ├── sessionutils.py ├── site_and_global_data.pinmap ├── site_and_global_data.seq ├── site_and_global_data_codemodules.py ├── specifications.pinmap ├── specifications.seq ├── specifications.specs ├── specifications_codemodules.py ├── switch.pinmap ├── switch.seq ├── switch_codemodules.py └── test_system.py ├── tests ├── __init__.py ├── codemoduleapi.pinmap ├── conftest.py ├── custom_instruments.pinmap ├── general_and_advanced.pinmap ├── nidaqmx.pinmap ├── nidcpower.pinmap ├── nidigital.pinmap ├── nidmm.pinmap ├── nifgen.pinmap ├── nirelaydriver.pinmap ├── niscope.pinmap ├── pinquerycontext.pinmap ├── publish.pinmap ├── publish_single_site.pinmap ├── site_and_global_data.pinmap ├── switch.pinmap ├── test_codemoduleapi.py ├── test_custom_instruments.py ├── test_general_and_advanced.py ├── test_nidaqmx.py ├── test_nidcpower.py ├── test_nidigital.py ├── test_nidmm.py ├── test_nifgen.py ├── test_nirelaydriver.py ├── test_niscope.py ├── test_pinquerycontext.py ├── test_publish.py ├── test_site_and_global_data.py └── test_switch.py └── tools └── makepy_pinmapinterfaces.py /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | source_pkgs = nitsm 3 | omit = 4 | */nitsm/_pinmapinterfaces.py 5 | */nitsm/__init__.py 6 | */nitsm/debug.py 7 | 8 | [paths] 9 | tox = 10 | src/nitsm/ 11 | */nitsm/ 12 | 13 | [report] 14 | exclude_lines = 15 | pragma: no cover 16 | if typing.TYPE_CHECKING: 17 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | - [ ] This contribution adheres to [CONTRIBUTING.md](https://github.com/ni/nitsm-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/tsm-python/blob/master/CONTRIBUTING.md). 4 | 5 | ### What does this Pull Request accomplish? 6 | 7 | TODO: Include high-level description of the changes in this pull request. 8 | 9 | ### Why should this Pull Request be merged? 10 | 11 | TODO: Justify why this contribution should be part of the project. 12 | 13 | ### What testing has been done? 14 | 15 | TODO: Detail what testing has been done to ensure this submission meets requirements. 16 | 17 | - [ ] I have run the automated tests (required if there are code changes) -------------------------------------------------------------------------------- /.github/actions/build/action.yml: -------------------------------------------------------------------------------- 1 | name: Build Package 2 | 3 | runs: 4 | using: composite 5 | 6 | steps: 7 | - name: Install dependencies 8 | run: | 9 | python -m pip install --upgrade pip 10 | pip install build 11 | shell: bash 12 | 13 | - name: Build package 14 | run: python -m build 15 | shell: bash 16 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | # Checks pull request for proper code formatting and style 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions 3 | 4 | name: Lint 5 | 6 | on: 7 | pull_request: 8 | branches: 9 | - main 10 | 11 | jobs: 12 | lint: 13 | 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - uses: actions/checkout@v2 18 | 19 | - name: Set up Python 20 | uses: actions/setup-python@v2 21 | with: 22 | python-version: '3.10' 23 | 24 | - name: Upgrade pip 25 | run: | 26 | python -m pip install --upgrade pip 27 | 28 | - name: Check code formatting with black 29 | run: | 30 | python -m pip install --upgrade black 31 | python -m black --check . 32 | 33 | - name: Lint with NI style guide 34 | run: | 35 | python -m pip install --upgrade ni-python-styleguide 36 | python -m ni_python_styleguide lint --extend-ignore=D203,D204,D205,D213,D215,D400,D401,D404,D406,D407,D408,D409,D413,D415 src 37 | -------------------------------------------------------------------------------- /.github/workflows/publish-pypi.yml: -------------------------------------------------------------------------------- 1 | name: Publish Package to PyPI 2 | 3 | on: workflow_dispatch 4 | 5 | jobs: 6 | deploy: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v2 12 | 13 | - name: Set up Python 14 | uses: actions/setup-python@v2 15 | with: 16 | python-version: 3.6 17 | 18 | - name: Build package 19 | uses: ./.github/actions/build 20 | 21 | - name: Publish package 22 | uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29 23 | with: 24 | user: __token__ 25 | password: ${{ secrets.PYPI_API_TOKEN }} 26 | -------------------------------------------------------------------------------- /.github/workflows/publish-testpypi.yml: -------------------------------------------------------------------------------- 1 | name: Publish Package to TestPyPI 2 | 3 | on: workflow_dispatch 4 | 5 | jobs: 6 | deploy: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v2 12 | 13 | - name: Set up Python 14 | uses: actions/setup-python@v2 15 | with: 16 | python-version: 3.6 17 | 18 | - name: Build package 19 | uses: ./.github/actions/build 20 | 21 | - name: Publish package 22 | uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29 23 | with: 24 | user: __token__ 25 | password: ${{ secrets.TEST_PYPI_API_TOKEN }} 26 | repository_url: https://test.pypi.org/legacy/ 27 | -------------------------------------------------------------------------------- /.github/workflows/test-build-install.yml: -------------------------------------------------------------------------------- 1 | name: Test Build and Install Package 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | build-install: 10 | 11 | runs-on: windows-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v2 15 | 16 | - name: Set up Python 17 | uses: actions/setup-python@v2 18 | with: 19 | python-version: 3.6 20 | 21 | - name: Build package 22 | uses: ./.github/actions/build 23 | 24 | - name: Install package 25 | run: | 26 | pip install pywin32 # install from pypi 27 | pip install nitsm --no-index -f dist/ # install local distribution 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # PyCharm 2 | .idea/ 3 | 4 | # Python 5 | __pycache__/ 6 | *.pyc 7 | .venv/ 8 | venv/ 9 | 10 | # pytest 11 | .pytest_cache/ 12 | *-results.xml 13 | 14 | # build 15 | dist/ 16 | *.egg-info 17 | 18 | # tox 19 | .tox/ 20 | 21 | # coverage 22 | *.coverage* 23 | coverage.xml 24 | 25 | # Digital Pattern Editor 26 | *.digiprojcache 27 | 28 | # TestStand Reports 29 | systemtests/*BatchReport*.xml 30 | systemtests/*.html 31 | systemtests/*.xml 32 | 33 | # TSM Reports. Example: File_10Sep2020_1030_Site1.txt 34 | systemtests/*_[0-9][0-9]???202[0-9]_*.csv 35 | systemtests/*_[0-9][0-9]???202[0-9]_*.std 36 | systemtests/*_[0-9][0-9]???202[0-9]_*.txt 37 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Contributing to nitsm-python 2 | =========================== 3 | 4 | Contributions to [nitsm-python](https://github.com/ni/nitsm-python) are welcome from all! 5 | 6 | nitsm-python is managed via [Git](https://git-scm.com), with the canonical upstream repository hosted on 7 | [GitHub](https://github.com/ni/nitsm-python). 8 | 9 | nitsm-python follows a pull request model for development. If you wish to contribute, you will need to create a GitHub 10 | account, fork this project, push a branch with your changes to your project, and then submit a pull request. 11 | 12 | See [GitHub's official documentation](https://help.github.com/articles/using-pull-requests/) for more details. 13 | 14 | # Getting Started 15 | 16 | ## Environment Setup 17 | Before beginning development, it is recommended to install the following dependencies: 18 | * [TestStand 20.0+](https://www.ni.com/en-us/support/downloads/software-products/download.teststand.html) 19 | * [TestStand Semiconductor Module 20.0+](https://www.ni.com/en-us/support/downloads/software-products/download.teststand-semiconductor-module.html) 20 | * NI instrument drivers: 21 | - [NI-DCPower 20.6+](https://www.ni.com/en-us/support/downloads/drivers/download.ni-dcpower.html) 22 | - [NI-DMM](https://www.ni.com/en-us/support/downloads/drivers/download.ni-dmm.html) 23 | - [NI-SCOPE](https://www.ni.com/en-us/support/downloads/drivers/download.ni-scope.html) 24 | - [NI-Digital](https://www.ni.com/en-us/support/downloads/drivers/download.ni-digital-pattern-driver.html) 25 | - [NI-SWITCH](https://www.ni.com/en-us/support/downloads/drivers/download.ni-switch.html) 26 | - [NI-DAQmx](https://www.ni.com/en-us/support/downloads/drivers/download.ni-daqmx.html) 27 | - [NI-FGEN](https://www.ni.com/en-us/support/downloads/drivers/download.ni-fgen.html) 28 | * Python packages: 29 | ``` 30 | pip install -r requirements.txt 31 | ``` 32 | 33 | ## Code Formatting 34 | nitsm-python uses [black](https://black.readthedocs.io/en/stable/index.html) for formatting. To format the entire 35 | project, run: 36 | ``` 37 | black . 38 | ``` 39 | 40 | ## Linting 41 | nitsm-python uses [ni-python-styleguide](https://github.com/ni/python-styleguide) for linting. To lint the entire 42 | project, run: 43 | ``` 44 | ni-python-styleguide lint 45 | ``` 46 | 47 | ## Testing 48 | Executing nitsm-python tests in the [tests/](https://github.com/ni/nitsm-python/tree/main/tests) directory requires the 49 | **TSM Standalone Semiconductor Module Context**. If you are an NI employee, contact one of the repository owners to 50 | determine how to obtain a copy of this non-public component. If you are not an NI employee, hang tight! We are currently 51 | working on a process to enable external contributors to use this tool. Note that this does not apply to tests in the 52 | [systemtests/](https://github.com/ni/nitsm-python/tree/main/systemtests) directory. 53 | 54 | nitsm uses [pytest](https://docs.pytest.org/) to run tests. First, install nitsm in edit mode: 55 | ``` 56 | pip install -e . 57 | ``` 58 | Then, run pytest: 59 | ``` 60 | pytest 61 | ``` 62 | 63 | Running pytest without arguments will run all tests in the [tests/](https://github.com/ni/nitsm-python/tree/main/tests) 64 | directory. To include system tests, include the [systemtests/](https://github.com/ni/nitsm-python/tree/main/systemtests) 65 | directory. 66 | ``` 67 | pytest tests systemtests 68 | ``` 69 | 70 | [tox](https://tox.readthedocs.io/en/latest/) is used to run tests against multiple versions of python. To test against 71 | all environments, run: 72 | ``` 73 | tox 74 | ``` 75 | 76 | The tox configuration in [pyproject.toml](https://github.com/ni/nitsm-python/blob/main/pyproject.toml) creates 77 | environments for running pytest in [tests/](https://github.com/ni/nitsm-python/tree/main/tests) and 78 | [systemtests/](https://github.com/ni/nitsm-python/tree/main/systemtests). It also defines two additional environments, 79 | `clean` and `report` for cleaning and creating report files respectively. To specify a subset of tox environments, run 80 | tox with the `-e` flag followed by a comma separated list of environments: 81 | ``` 82 | tox -e clean,py36-tests,py36-sysytemtests,report 83 | ``` 84 | 85 | ## Building 86 | To build nitsm-python, you will first need to install [build](https://pypi.org/project/build/): 87 | ``` 88 | pip install build 89 | ``` 90 | Next, build the project: 91 | ``` 92 | python -m build 93 | ``` 94 | If the build succeeds, artifacts will be placed in `dist/`. 95 | 96 | # Developer Certificate of Origin (DCO) 97 | 98 | Developer's Certificate of Origin 1.1 99 | 100 | By making a contribution to this project, I certify that: 101 | 102 | (a) The contribution was created in whole or in part by me and I 103 | have the right to submit it under the open source license 104 | indicated in the file; or 105 | 106 | (b) The contribution is based upon previous work that, to the best 107 | of my knowledge, is covered under an appropriate open source 108 | license and I have the right under that license to submit that 109 | work with modifications, whether created in whole or in part 110 | by me, under the same open source license (unless I am 111 | permitted to submit under a different license), as indicated 112 | in the file; or 113 | 114 | (c) The contribution was provided directly to me by some other 115 | person who certified (a), (b) or (c) and I have not modified 116 | it. 117 | 118 | (d) I understand and agree that this project and the contribution 119 | are public and that a record of the contribution (including all 120 | personal information I submit with it, including my sign-off) is 121 | maintained indefinitely and may be redistributed consistent with 122 | this project or the open source license(s) involved. 123 | 124 | (taken from [developercertificate.org](https://developercertificate.org/)) 125 | 126 | See [LICENSE](https://github.com/ni/nitsm-python/blob/master/LICENSE) for details about how 127 | [nitsm-python](https://github.com/ni/nitsm-python) is licensed. 128 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 NI 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://ni.visualstudio.com/DevCentral/_apis/build/status/TSM/nitsm-python-tests?branchName=main)](https://ni.visualstudio.com/DevCentral/_build/latest?definitionId=5945&branchName=main) 2 | # nitsm-python 3 | Write code modules with the TestStand Semiconductor Module in python. 4 | 5 | ## Note to End Users 6 | This project is intended for use in automated device validation. Our primary focus is to provide a pythonic approach to 7 | automated testing with TestStand and TSM. More emphasis has been placed on simplicity and usability than execution time. 8 | 9 | ## Python Version Support 10 | nitsm supports python versions 3.6, 3.7, and 3.8. Newer versions of python might work, but it is not guaranteed. Python 11 | 2.7 is not supported. 12 | 13 | ## Installation 14 | ``` 15 | pip install nitsm 16 | ``` 17 | 18 | nitsm requires [NI TestStand](https://www.ni.com/en-us/support/downloads/software-products/download.teststand.html) 19 | 20.0 or higher and 20 | [NI TestStand Semiconductor Module](https://www.ni.com/en-us/support/downloads/software-products/download.teststand-semiconductor-module.html) 21 | 20.0 or higher. 22 | 23 | To use nitsm in conjunction with [nimi-python](https://github.com/ni/nimi-python), you must also install the appropriate 24 | NI instrument driver for each device you plan to use: 25 | - [NI-DCPower 20.6+](https://www.ni.com/en-us/support/downloads/drivers/download.ni-dcpower.html) 26 | - [NI-DMM](https://www.ni.com/en-us/support/downloads/drivers/download.ni-dmm.html) 27 | - [NI-SCOPE](https://www.ni.com/en-us/support/downloads/drivers/download.ni-scope.html) 28 | - [NI-Digital](https://www.ni.com/en-us/support/downloads/drivers/download.ni-digital-pattern-driver.html) 29 | - [NI-SWITCH](https://www.ni.com/en-us/support/downloads/drivers/download.ni-switch.html) 30 | - [NI-DAQmx](https://www.ni.com/en-us/support/downloads/drivers/download.ni-daqmx.html) 31 | - [NI-FGEN](https://www.ni.com/en-us/support/downloads/drivers/download.ni-fgen.html) 32 | 33 | Visit the [nimi-python](https://github.com/ni/nimi-python) project for information on which python packages to install 34 | alongside each instrument driver. 35 | 36 | ## Usage 37 | Define code modules with the `code_module` decorator in the `nitsm.codemoduleapi` module. When called from TestStand, 38 | the decorator will convert the [pywin32](https://pypi.org/project/pywin32/) COM object into an 39 | `nitsm.codemoduleapi.SemiconductorModuleContext` object. 40 | 41 | ```python 42 | import nidcpower 43 | import nitsm.codemoduleapi 44 | 45 | @nitsm.codemoduleapi.code_module 46 | def source_current(tsm_context, pins, current_level): 47 | pin_query_context, sessions, channel_strings = tsm_context.pins_to_nidcpower_sessions(pins) 48 | for session, channel_string in zip(sessions, channel_strings): 49 | session.channels[channel_string].output_function = nidcpower.OutputFunction.DC_CURRENT 50 | session.channels[channel_string].current_level = current_level 51 | session.channels[channel_string].initiate() 52 | ``` 53 | 54 | ## Known Limitations 55 | * Instrument alarms are currently not supported 56 | * The Set Relays TestStand step is not supported when creating relay sessions in python 57 | * See [STATUS.md](https://github.com/ni/nitsm-python/blob/main/STATUS.md) for additional information about the current 58 | state of the API and system tests 59 | -------------------------------------------------------------------------------- /STATUS.md: -------------------------------------------------------------------------------- 1 | # Status 2 | ## Instrument Based Session API 3 | (Excluding HSDIO) 4 | 5 | | Instrument Type | .NET Method | Python | Python System Tests | 6 | |-----------------|-------------------------------------------------------------------------|-------------------------------------------------|---------------------------| 7 | | NI-DAQmx | GetNIDAQmxTaskNames | get\_all\_nidaqmx\_task\_names | test\_nidaqmx | 8 | | NI-DAQmx | SetNIDAQmxTask | set\_nidaqmx\_task | test\_nidaqmx | 9 | | NI-DAQmx | GetNIDAQmxTask(s) | pins\_to\_nidaqmx\_task(s) | test\_nidaqmx | 10 | | NI-DAQmx | GetAllNIDAQmxTasks | get\_all\_nidaqmx\_tasks | test\_nidaqmx | 11 | | NI-DCPower | GetNIDCPowerInstrumentNames | *omitted on purpose | | 12 | | NI-DCPower | GetNIDCPowerResourceStrings | get\_all\_nidcpower\_resource\_strings | test\_nidcpower | 13 | | NI-DCPower | GetNIDCPowerSession(s) | pins\_to\_nidcpower\_session(s) | test\_nidcpower | 14 | | NI-DCPower | SetNIDCPowerSession (name/channel) | *omitted on purpose | | 15 | | NI-DCPower | SetNIDCPowerSession (resourceString) | set\_nidcpower\_session | test\_nidcpower | 16 | | NI-DCPower | GetAllNIDCPowerSessions | get\_all\_nidcpower\_sessions | test\_nidcpower | 17 | | NI-Digital | GetNIDigitalPatternInstrumentNames | get\_all\_nidigital\_instrument\_names | test\_nidigital | 18 | | NI-Digital | GetNIDigitalPatternSession(s) | \*omitted on purpose | | 19 | | NI-Digital | GetNIDigitalPatternSession(s)ForPattern | get\_nidigital\_session\_for\_pattern | test\_nidigital | 20 | | NI-Digital | GetNIDigitalPatternSession(s)ForPpmu | get\_nidigital\_session\_for\_ppmu | test\_nidigital | 21 | | NI-Digital | SetNIDigitalPatternSession | set\_nidigital\_session | test\_nidigital | 22 | | NI-Digital | GetAllNIDigitalPatternSessions | get\_all\_nidigital\_sessions | test\_nidigital | 23 | | NI-Digital | FetchMultisiteHistoryRamInformation\*
\*NI-Digital driver extension | | | 24 | | NI-Digital | NIDigitalHistoryRamCycleInformation\*
\*class to support HRAM data | | | 25 | | NI-DMM | GetNIDmmInstrumentNames | get\_all\_nidmm\_instrument\_names | test\_nidmm | 26 | | NI-DMM | GetNIDmmSession(s) | pin\_to\_nidmm\_session(s) | test\_nidmm | 27 | | NI-DMM | SetNIDmmSession | set\_nidmm\_session | test\_nidmm | 28 | | NI-DMM | GetAllNIDmmSessions | get\_all\_nidmm\_sessions | test\_nidmm | 29 | | NI-FGEN | GetNIFGenInstrumentNames | get\_all\_nifgen\_instrument\_names | test\_nifgen | 30 | | NI-FGEN | GetNIFgenSession(s) | pins\_to\_nifgen\_session(s) | test\_nifgen | 31 | | NI-FGEN | SetNIFGenSession | set\_nifgen\_session | test\_nifgen | 32 | | NI-FGEN | GetAllNIFGenSessions | get\_all\_nifgen\_sessions | test\_nifgen | 33 | | NI-RFmx | GetNIRfmxInstrumentNames | | | 34 | | NI-RFmx | GetNIRfmxMultipleDeembeddingData | | | 35 | | NI-RFmx | GetNIRfmxSingleDeembeddingData | | | 36 | | NI-RFmx | GetNIRfmxSession(s) | | | 37 | | NI-RFmx | SetNIRfmxSession | | | 38 | | NI-RFmx | GetAllNIRFmxSessions | | | 39 | | NI-RFPM | GetNIRfpmInstrumentNames | | | 40 | | NI-RFPM | GetNIRfpmSessions | | | 41 | | NI-RFPM | SetNIRfpmSession | | | 42 | | NI-RFPM | GetAllNIRfpmSessions | | | 43 | | NI-RFSA | GetNIRfsaInstrumentNames | | | 44 | | NI-RFSA | GetNIRfsaMultipleDeembeddingData | | | 45 | | NI-RFSA | GetNIRfsaSingleDeembeddingData | | | 46 | | NI-RFSA | GetNIRfsaSession(s) | | | 47 | | NI-RFSA | SetNIRfsaSession | | | 48 | | NI-RFSA | GetAllNIRfsaSessions | | | 49 | | NI-RFSG | GetNIRfsgInstrumentNames | | | 50 | | NI-RFSG | GetNIRfsgMultipleDeembeddingData | | | 51 | | NI-RFSG | GetNIRfsgSingleDeembeddingData | | | 52 | | NI-RFSG | GetNIRfsgSession(s) | | | 53 | | NI-RFSG | SetNIRfsgSession | | | 54 | | NI-RFSG | GetAllNIRfsgSessions | | | 55 | | NI-Scope | GetNIScopeInstrumentNames | get\_all\_niscope\_instrument\_names | test\_niscope | 56 | | NI-Scope | GetNIScopeSession(s) | pins\_to\_niscope\_session(s) | test\_niscope | 57 | | NI-Scope | SetNIScopeSession | set\_niscope\_session | test\_niscope | 58 | | NI-Scope | GetAllNIScopeSessions | get\_all\_niscope\_sessions | test\_niscope | 59 | | FPGA | GetFpgaInstrumentNames | | | 60 | | FPGA | SetFpgaVIReference | | | 61 | | FPGA | GetFpgaVIReference(s) | | | 62 | | FPGA | GetAllFpgaVIReferences | | | 63 | | Relay Driver | GetRelayDriverModuleNames | get\_relay\_driver\_module\_names | test\_nirelaydriver | 64 | | Relay Driver | GetRelayDriverNISwitchSession(s) | relays\_to\_relay\_driver\_niswitch\_session(s) | test\_nirelaydriver | 65 | | Relay Driver | SetRelayDriverNISwitchSession | set\_relay\_driver\_niswitch\_session | test\_nirelaydriver | 66 | | Relay Driver | GetAllRelayDriverNISwitchSessions | get\_all\_relay\_driver\_niswitch\_sessions | test\_nirelaydriver | 67 | | Relay Driver | ControlRelay | control_relays | unit test suffices | 68 | | Relay Driver | ApplyRelayConfiguration | apply_relay_configuration | unit test suffices | 69 | | Custom | GetCustomInstrumentNames | get\_custom\_instrument\_names | test\_custom\_instruments | 70 | | Custom | SetCustomSession | set\_custom\_session | test\_custom\_instruments | 71 | | Custom | GetCustomSession(s) | pins\_to\_custom\_session(s) | test\_custom\_instruments | 72 | | Custom | GetAllCustomSessions | get\_all\_custom\_sessions | test\_custom\_instruments | 73 | | Multiplexer | GetSwitchNames | get\_all\_switch\_names | test\_switch | 74 | | Multiplexer | SetSwitchSession | set\_switch\_session | test\_switch | 75 | | Multiplexer | GetSwitchSession(s) | pin\_to\_switch\_sessions | test\_switch | 76 | | Multiplexer | GetAllSwitchSessions | get\_all\_switch\_sessions | test\_switch | 77 | 78 | ## Pin Query API 79 | | Class | .NET Method | Python | Python System Tests | 80 | |---------------------------------|------------------------------------------------|----------------------------------------------------------------------------------------------------|---------------------| 81 | | SemiconductorModuleContext | PublishPerSite | publish\_per\_site | | 82 | | SemiconductorModuleContext | PublishToTestStandVariablePerSite | | | 83 | | PinQueryContext | ExtractPinData | | | 84 | | PinQueryContext | PerInstrumentToPerSiteData | | | 85 | | PinQueryContext | GetSessionAndChannelIndex | get\_session\_and\_channel\_index | unit test suffices | 86 | | MultiplePinQueryContext | PerInstrumentToPerSiteData | | | 87 | | \_\_SingleSessionQueryContext | Publish | publish\*
\*supports all variations of data types (float/bool) and number of pins and sessions | various | 88 | | \_\_MultipleSessionQueryContext | Publish | publish | various | 89 | | NIDigitalPatternPinQueryContext | PublishPatternResults | publish\_pattern\_results | test\_nidigital | 90 | | NIDigitalPatternPinQueryContext | PerInstrumentToPerSitePatternResults | | | 91 | | NIDigitalPatternPinQueryContext | PerInstrumentToPerSiteWaveforms | | | 92 | | NIDigitalPatternPinQueryContext | PerSiteToPerInstrumentWaveforms | | | 93 | | NIDAQmxPinQueryContext | CreateMultisiteDataForDAQmxAnalogOutput | | | 94 | | NIDAQmxPinQueryContext | CreatePerSiteMultisiteDataForDAQmxAnalogOutput | | | 95 | 96 | ## Other 97 | | Category/Palette | .NET Method | Python | Python System Tests | 98 | |----------------------|-----------------------------------------------|----------------------------------------------------|----------------------| 99 | | Specifications | GetSpecificationsValue | get\_specifications\_value | test_specifications | 100 | | Specifications | GetSpecificationsValues | get\_specifications\_values | test_specifications | 101 | | Site and Global Data | GetGlobalData | get\_global\_data | test_site_and_global | 102 | | Site and Global Data | GetSiteData | get\_site\_data | test_site_and_global | 103 | | Site and Global Data | GlobalDataExists | global\_data\_exists | test_site_and_global | 104 | | Site and Global Data | SetGlobalData | set\_global\_data | test_site_and_global | 105 | | Site and Global Data | SetSiteData | set\_site\_data | test_site_and_global | 106 | | Site and Global Data | SiteDataExists | site\_data\_exists | test_site_and_global | 107 | | Advanced | GetSemiconductorModuleContextWithSites | | | 108 | | Advanced | GetSiteSemiconductorModuleContext | | | 109 | | Misc | FilterPinsByInstrumentType | filter\_pins\_by\_instrument\_type | unit test suffices | 110 | | Misc | GetPins | get\_pin\_names | unit test suffices | 111 | | Misc | GetPinsInPinGroup(s) | get\_pins\_in\_pin\_groups | unit test suffices | 112 | | Misc | GetRelays | get\_relay\_names | unit test suffices | 113 | | Misc | GetRelaysInRelayGroups | | | 114 | | Misc | IsSemiconductorModuleInOfflineMode | | | 115 | | Misc | PinMapFilePath | pin\_map\_file\_path | unit test suffices | 116 | | Misc | PinMapUsesNIDCPowerChannelGroups | | | 117 | | Misc | SiteNumbers | site\_numbers | unit test suffices | 118 | | Misc | InstrumentTypeIdConstants | InstrumentTypeIdConstants | n/a | 119 | | Digital Project | DigitalPatternProjectCaptureWaveformFilePaths | nidigital\_project\_capture\_waveform\_file\_paths | test\_nidigital | 120 | | Digital Project | DigitalPatternProjectLevelsFilePaths | nidigital\_project\_levels\_file\_paths | test\_nidigital | 121 | | Digital Project | DigitalPatternProjectPatternFilePaths | nidigital\_project\_pattern\_file\_paths | test\_nidigital | 122 | | Digital Project | DigitalPatternProjectSourceWaveformFilePaths | nidigital\_project\_source\_waveform\_file\_paths | test\_nidigital | 123 | | Digital Project | DigitalPatternProjectSpecificationsFilePaths | nidigital\_project\_specifications\_file\_paths | test\_nidigital | 124 | | Digital Project | DigitalPatternProjectTimingFilePaths | nidigital\_project\_timing\_file\_paths | test\_nidigital | 125 | | Input Data | GetInputDataAsBooleans | | | 126 | | Input Data | GetInputDataAsDouble | | | 127 | | Input Data | GetInputDataAsStrings | | | 128 | 129 | ## Model-Based Instruments 130 | | Class/Interface | Method/Property | Python | 131 | |-----------------------------------|--------------------------|--------| 132 | | SemiconductorModuleContext | GetModelBasedInstruments | | 133 | | IModelBasedInstrument | Category | | 134 | | IModelBasedInstrument | ModelName | | 135 | | IModelBasedInstrument | Name | | 136 | | IModelBasedInstrument | Subcategory | | 137 | | IModelBasedInstrument | TryGetResource | | 138 | | IModelBasedResource | ModelResourceName | | 139 | | IModelBasedPropertyItemContainer | TryGetPropertyValue | | 140 | | ModelBasedInstrumentSearchOptions | Category | | 141 | | ModelBasedInstrumentSearchOptions | Subcategory | | 142 | -------------------------------------------------------------------------------- /azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | 2 | trigger: 3 | - main 4 | 5 | pr: 6 | branches: 7 | include: 8 | - main 9 | 10 | jobs: 11 | - job: CI 12 | workspace: 13 | clean: all 14 | 15 | pool: 16 | name: nitsm-python-test 17 | 18 | steps: 19 | - script: | 20 | python -m pip install --upgrade pip 21 | displayName: 'Upgrade pip' 22 | 23 | - script: | 24 | python -m pip install --upgrade tox 25 | displayName: 'Install or upgrade tox' 26 | 27 | - task: PowerShell@2 28 | displayName: 'Switch to old TestStand/TSM' 29 | inputs: 30 | targetType: filePath 31 | filePath: 'C:\Activate_TSVersion.ps1' 32 | arguments: '-VersionToActivate old' 33 | 34 | - script: | 35 | tox -- older 36 | displayName: 'Run python unit tests with older version of TestStand/TSM' 37 | 38 | - task: PowerShell@2 39 | displayName: 'Switch to new TestStand/TSM' 40 | inputs: 41 | targetType: filePath 42 | filePath: 'C:\Activate_TSVersion.ps1' 43 | arguments: '-VersionToActivate new' 44 | 45 | - script: | 46 | tox -- newer 47 | displayName: 'Run python unit tests with newer version of TestStand/TSM' 48 | 49 | - task: PublishTestResults@2 50 | condition: succeededOrFailed() 51 | inputs: 52 | testResultsFiles: '**/*-results.xml' 53 | testRunTitle: 'Publish test results' 54 | displayName: 'Display test results' 55 | 56 | - task: PublishCodeCoverageResults@1 57 | inputs: 58 | codeCoverageTool: Cobertura 59 | summaryFileLocation: '$(System.DefaultWorkingDirectory)/**/coverage.xml' 60 | displayName: 'Display code coverage results' 61 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.black] 2 | line-length = 100 3 | target-version = ["py36", "py37", "py38"] 4 | extend-exclude = "^/src/nitsm/_pinmapinterfaces.py" 5 | 6 | [tool.ni-python-styleguide] 7 | extend_exclude = "src/nitsm/_pinmapinterfaces.py" 8 | # ignore codes not relevant to the Google docstring conventions (http://www.pydocstyle.org/en/stable/error_codes.html#default-conventions) 9 | # also ignore D415 which requires docstrings to start with a one line summary 10 | extend_ignore = "D203,D204,D205,D213,D215,D400,D401,D404,D406,D407,D408,D409,D413,D415" # must also be passed via the command line until https://github.com/ni/python-styleguide/issues/76 is resolved 11 | 12 | [tool.pytest.ini_options] 13 | testpaths = [ 14 | "tests" 15 | ] 16 | markers = [ 17 | "pin_map", 18 | "sequence_file", 19 | "offline_mode" 20 | ] 21 | 22 | [tool.tox] 23 | legacy_tox_ini = """ 24 | [tox] 25 | isolated_build = True 26 | envlist = clean, py3{6,7,8}-tests, py3{6,7,8}-systemtests, report 27 | 28 | [testenv] 29 | deps = 30 | pytest 31 | pytest-cov 32 | nidcpower 33 | nidmm 34 | niscope 35 | nidigital 36 | niswitch 37 | nidaqmx 38 | nifgen 39 | commands = 40 | tests: pytest tests --junitxml={envname}-{posargs:any}-tsm-version-results.xml --cov --cov-report=term 41 | systemtests: pytest systemtests --junitxml={envname}-{posargs:any}-tsm-version-results.xml --cov --cov-report=term 42 | passenv = 43 | systemtests: TestStandPublic64 ProgramFiles(x86) 44 | setenv = 45 | tests,systemtests: COVERAGE_FILE = .coverage.{envname}-{posargs:any}-tsm-version 46 | 47 | [testenv:clean] 48 | deps = coverage 49 | skip_install = true 50 | commands = coverage erase 51 | 52 | [testenv:report] 53 | deps = coverage 54 | skip_install = true 55 | commands = 56 | coverage combine 57 | coverage report -m 58 | coverage xml 59 | """ 60 | 61 | [build-system] 62 | requires = ["setuptools", "wheel"] 63 | build-backend = "setuptools.build_meta" 64 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pywin32 2 | nidcpower 3 | nidmm 4 | niscope 5 | nidigital 6 | niswitch 7 | nidaqmx 8 | nifgen 9 | tox 10 | pytest 11 | pytest-cov 12 | black 13 | ni-python-styleguide 14 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | name = nitsm 3 | version = attr: nitsm.__version__ 4 | url = https://github.com/ni/nitsm-python 5 | author = NI 6 | author_email = opensource@ni.com 7 | classifiers = 8 | License :: OSI Approved :: MIT License 9 | Operating System :: Microsoft :: Windows 10 | description = NI TestStand Semiconductor Module Python API 11 | long_description = file: README.md 12 | long_description_content_type = text/markdown 13 | 14 | [options] 15 | install_requires = 16 | pywin32>=228;platform_system=="Windows" 17 | python_requires = >=3.6 18 | package_dir = 19 | =src 20 | packages = find: 21 | 22 | [options.packages.find] 23 | where = src 24 | include = nitsm 25 | -------------------------------------------------------------------------------- /src/nitsm/__init__.py: -------------------------------------------------------------------------------- 1 | """NI TestStand Semiconductor Module Python API""" 2 | 3 | __version__ = "0.2.0" 4 | -------------------------------------------------------------------------------- /src/nitsm/codemoduleapi.py: -------------------------------------------------------------------------------- 1 | """Code Module API""" 2 | 3 | import functools 4 | import inspect 5 | 6 | from .enums import Capability, InstrumentTypeIdConstants 7 | from .tsmcontext import SemiconductorModuleContext 8 | 9 | __all__ = ["SemiconductorModuleContext", "Capability", "InstrumentTypeIdConstants", "code_module"] 10 | 11 | 12 | # noinspection PyPep8Naming 13 | class code_module: # noqa: N801 14 | """This function decorator wraps the ISemiconductorModuleContext 15 | win32com.client.dynamic.CDispatch object passed from the Semiconductor Multi Test step into a 16 | nitsm.codemoduleapi.SemiconductorModuleContext object prior to calling the decorated function. 17 | """ 18 | 19 | def __init__(self, func): 20 | """Converts a function into a TSM code module. 21 | 22 | The Semiconductor Multi Test step must pass the Step.SemiconductorModuleContext property to 23 | the code module as the first positional argument. 24 | """ 25 | self._func = func 26 | self._signature = inspect.signature(func) 27 | functools.update_wrapper(self, func) 28 | 29 | def __get__(self, instance, owner): 30 | """Binds the code module to an object or class.""" 31 | func = self._func.__get__(instance, owner) 32 | return type(self)(func) 33 | 34 | def __call__(self, *args, **kwargs): 35 | """Calls the code module.""" 36 | bound_arguments = self._signature.bind(*args, **kwargs) 37 | arguments_iter = iter(bound_arguments.arguments.items()) 38 | 39 | # find potential argument that could be the tsm context 40 | try: 41 | argument = next(arguments_iter) # get first argument 42 | if inspect.isclass(argument[1]): # class method check 43 | argument = next(arguments_iter) # move to second argument 44 | except StopIteration: 45 | raise TypeError( 46 | ( 47 | "The number of arguments to the code module is less than expected. It must " 48 | "accept as it's first argument the Semiconductor Module context passed from " 49 | "TestStand or another code module.", 50 | ) 51 | ) 52 | 53 | # attempt to wrap argument in a SemiconductorModuleContext object 54 | argument_name, argument_value = argument 55 | if not isinstance(argument_value, SemiconductorModuleContext): 56 | try: 57 | argument_value = SemiconductorModuleContext(argument_value) 58 | except Exception: 59 | class_name = type(argument_value).__name__ 60 | raise ValueError( 61 | f"Failed to convert Semiconductor Module context from class '{class_name}'.", 62 | ) 63 | bound_arguments.arguments[argument_name] = argument_value 64 | 65 | return self._func(*bound_arguments.args, **bound_arguments.kwargs) 66 | -------------------------------------------------------------------------------- /src/nitsm/debug.py: -------------------------------------------------------------------------------- 1 | """Code Module Debugging Utilities""" 2 | 3 | import os 4 | import tkinter.messagebox 5 | 6 | 7 | def prompt_attach_debugger() -> None: 8 | """Pauses the Python interpreter and displays the process ID (PID). The PID can be used by an 9 | IDE such as PyCharm to attach to the process for debugging. This is useful for stepping into 10 | nitsm code modules from TestStand. 11 | 12 | Instructions for use with PyCharm: 13 | 1. Call this function from the code module you want to debug. Placing it at the beginning 14 | of the code module is recommended. 15 | 2. Add a breakpoint at the location where you want to start debugging. Make sure this 16 | breakpoint will be reached after this function is called. 17 | 3. In TestStand, execute a sequence that calls into the code module. 18 | 4. A dialog box will appear displaying the PID of the current process. Before clicking 19 | "Okay" on the dialog, select Run -> Attach To Process... from the PyCharm menu. 20 | 5. PyCharm will display a window of discovered processes. Click the process with the 21 | matching PID. 22 | 6. PyCharm will open a debug terminal and attach to the process. Wait for PyCharm to 23 | indicate it has successfully attached. 24 | 6. Once PyCharm is attached, click "Okay" on the dialog to continue execution. If these 25 | steps were performed correctly, PyCharm will break at the first breakpoint it reaches in 26 | the code. 27 | """ 28 | tkinter.Tk().withdraw() # hide root window 29 | tkinter.messagebox.showinfo( 30 | "Attach debugger", "Process name: niPythonHost.exe and Process ID: " + str(os.getpid()) 31 | ) 32 | return 33 | 34 | 35 | if __name__ == "__main__": 36 | prompt_attach_debugger() 37 | -------------------------------------------------------------------------------- /src/nitsm/enums.py: -------------------------------------------------------------------------------- 1 | """Enumerations""" 2 | 3 | import enum 4 | 5 | __all__ = ["Capability", "InstrumentTypeIdConstants"] 6 | 7 | 8 | class Capability(enum.Enum): 9 | """Differentiates between pins in the same instrument with different capabilities, such as 10 | NI-HSDIO Dynamic DIO channels and PFI lines. 11 | """ 12 | 13 | ALL = "" 14 | NI_HSDIO_DYNAMIC_DIO = "NIHSDIODynamicDIOCapable" 15 | 16 | 17 | class InstrumentTypeIdConstants(enum.Enum): 18 | """The type IDs for non-custom instruments in the pin map file.""" 19 | 20 | ANY = "" 21 | NI_DAQMX = "niDAQmx" 22 | NI_DCPOWER = "niDCPower" 23 | NI_DIGITAL_PATTERN = "niDigitalPattern" 24 | NI_DMM = "niDMM" 25 | NI_FGEN = "niFGen" 26 | NI_GENERIC_MULTIPLEXER = "NIGenericMultiplexer" 27 | NI_HSDIO = "niHSDIO" 28 | NI_MODEL_BASED_INSTRUMENT = "niModelBasedInstrument" 29 | NI_RELAY_DRIVER = "niRelayDriver" 30 | NI_RFPM = "niRFPM" 31 | NI_RFSA = "niRFSA" 32 | NI_RFSG = "niRFSG" 33 | NI_SCOPE = "niScope" 34 | -------------------------------------------------------------------------------- /src/nitsm/pinquerycontexts.py: -------------------------------------------------------------------------------- 1 | """Pin Query Contexts""" 2 | 3 | import re 4 | import typing 5 | 6 | __all__ = ["PinQueryContext", "DigitalPatternPinQueryContext"] 7 | 8 | if typing.TYPE_CHECKING: 9 | import nitsm._pinmapinterfaces 10 | 11 | _PublishDataScalar = typing.Union[bool, int, float] 12 | _PublishDataSequence = typing.Sequence[_PublishDataScalar] 13 | _PublishDataJaggedSequence = typing.Sequence[_PublishDataSequence] 14 | _PublishDataArg = typing.Union[ 15 | _PublishDataScalar, _PublishDataSequence, _PublishDataJaggedSequence 16 | ] 17 | _PublishPatternArg = typing.Union[ 18 | typing.Dict[int, bool], typing.Sequence[typing.Dict[int, bool]] 19 | ] 20 | 21 | 22 | def _pad_jagged_sequence(seq): 23 | """Pads a 2D jagged sequence with the default value of the element type to make it rectangular. 24 | 25 | The type of each sequence (tuple, list, etc) is maintained. 26 | """ 27 | columns = max(map(len, seq)) # gets length of the longest row 28 | return type(seq)( 29 | ( 30 | sub_seq + type(sub_seq)(type(sub_seq[0])() for _ in range(columns - len(sub_seq))) 31 | for sub_seq in seq 32 | ) 33 | ) 34 | 35 | 36 | class PinQueryContext: 37 | """Provides the base class for a pin query context, which is an object a pin query method 38 | returns to track the sessions and channels associated with the pins for one or more sites. 39 | """ 40 | 41 | def __init__(self, tsm_context, pins): 42 | """Not for public use.""" 43 | self._tsm_context: nitsm._pinmapinterfaces.ISemiconductorModuleContext = tsm_context 44 | self._pins: typing.Union[str, typing.Sequence[str]] = pins 45 | 46 | def get_session_and_channel_index(self, site_number: int, pin: str): 47 | """Returns the index of the session and channel that corresponds to a pin query. Use this 48 | method to access an individual pin on a specific site when you take a measurement across 49 | multiple instruments. When you call a pin query method, such as 50 | pins_to_nidigital_sessions_for_ppmu, the method returns a tuple of sessions and a tuple of 51 | channel lists. Use this method to identify which session and which channel refers to the pin 52 | from the pin query and the site number you specify. 53 | 54 | Args: 55 | site_number: The site number of the pin to obtain the session and channel index in a 56 | previous pin query. For a system pin, pass any valid site number. 57 | pin: The name of the pin to obtain the session and channel index in a previous pin 58 | query. 59 | 60 | Returns: 61 | session_index: Returns the index of the session for a measurement taken on the pin and 62 | site number you specify. 63 | channel_index: Returns the index of the channel within the channel list for a 64 | measurement taken on the pin and site number you specify. 65 | """ 66 | pins = [self._pins] if isinstance(self._pins, str) else self._pins 67 | return self._tsm_context.GetChannelGroupAndChannelIndex_2(pins, pin, site_number, 0, 0) 68 | 69 | def publish(self, data: "_PublishDataArg", published_data_id=""): 70 | """Publishes the measurement data for one or more pins to the Semiconductor Multi Test step 71 | for all sites in the PinQueryContext. 72 | 73 | Args: 74 | data: The measurement data from one or more pins connected to one or more instruments. 75 | The values can be bools, ints, or floats, and each value represents a measurement 76 | made for a single instrument channel. Pass a single value if the pin query refers 77 | to a single channel on a single instrument. Pass a sequence of values if the pin 78 | query refers to multiple channels on a single instrument or multiple instruments 79 | with a single channel. Pass a two dimensional sequence of values if the pin query 80 | refers to multiple channels on multiple instruments. 81 | published_data_id: The unique ID for distinguishing the measurement when you publish 82 | multiple measurements for the same pins within the same code module. This ID must 83 | match one of the values in the Published Data Id column on the Tests tab of the 84 | Semiconductor Multi Test step. 85 | """ 86 | if isinstance(data, bool): 87 | return self._publish_bool_scalar(data, published_data_id) 88 | elif isinstance(data, (float, int)): 89 | return self._publish_float_scalar(data, published_data_id) 90 | else: 91 | return self._publish_sequence(data, published_data_id) 92 | 93 | def _publish_float_scalar(self, data, published_data_id): 94 | return self._publish_float_1d([data], published_data_id) 95 | 96 | def _publish_bool_scalar(self, data, published_data_id): 97 | return self._publish_bool_1d([data], published_data_id) 98 | 99 | def _publish_sequence(self, data, published_data_id): 100 | if isinstance(data[0], bool): 101 | return self._publish_bool_1d(data, published_data_id) 102 | elif isinstance(data[0], (float, int)): 103 | return self._publish_float_1d(data, published_data_id) 104 | else: 105 | return self._publish_sequence_2d(data, published_data_id) 106 | 107 | def _publish_float_1d(self, data, published_data_id): 108 | if isinstance(self._pins, str): 109 | return self._tsm_context.Publish(self._pins, published_data_id, data) 110 | else: 111 | return self._tsm_context.Publish_2(self._pins, published_data_id, data) 112 | 113 | def _publish_bool_1d(self, data, published_data_id): 114 | if isinstance(self._pins, str): 115 | return self._tsm_context.Publish_5(self._pins, published_data_id, data) 116 | else: 117 | return self._tsm_context.Publish_6(self._pins, published_data_id, data) 118 | 119 | def _publish_sequence_2d(self, data, published_data_id): 120 | data = _pad_jagged_sequence(data) # make 2d sequence rectangular 121 | if isinstance(data[0][0], bool): 122 | return self._publish_bool_2d(data, published_data_id) 123 | else: 124 | return self._publish_float_2d(data, published_data_id) 125 | 126 | def _publish_float_2d(self, data, published_data_id): 127 | if isinstance(self._pins, str): 128 | return self._tsm_context.Publish_3(self._pins, published_data_id, data) 129 | else: 130 | return self._tsm_context.Publish_4(self._pins, published_data_id, data) 131 | 132 | def _publish_bool_2d(self, data, published_data_id): 133 | if isinstance(self._pins, str): 134 | return self._tsm_context.Publish_7(self._pins, published_data_id, data) 135 | else: 136 | return self._tsm_context.Publish_8(self._pins, published_data_id, data) 137 | 138 | 139 | class DigitalPatternPinQueryContext(PinQueryContext): 140 | """An object the pins_to_nidigital_session_for_pattern and 141 | pins_to_nidigital_sessions_for_pattern methods return to track the sessions and channels 142 | associated with the pins for one or more sites. Use this object to publish measurements to the 143 | Semiconductor Multi Test step and to extract data from a set of measurements. 144 | """ 145 | 146 | def __init__(self, tsm_context, pins, site_lists): 147 | """Not for public use.""" 148 | # convert pins to a list of pins if it isn't already 149 | if isinstance(pins, str): 150 | pins = [pins] 151 | 152 | super().__init__(tsm_context, pins) 153 | 154 | # convert site_lists to a list if it isn't already 155 | if isinstance(site_lists, str): 156 | self._site_lists = [site_lists] 157 | else: 158 | self._site_lists = site_lists 159 | 160 | def publish_pattern_results( 161 | self, instrument_site_pattern_results: "_PublishPatternArg", published_data_id="" 162 | ): 163 | """Publishes results from NI-Digital pattern burst to the Semiconductor Multi Test step for 164 | all sites in the Semiconductor Module context. Leave the Pin column blank for the test on 165 | the Semiconductor Multi Test step when publishing pattern results with this method. 166 | 167 | Args: 168 | instrument_site_pattern_results: The pattern result data from multiple pins connected to 169 | one or more NI-Digital Pattern instruments. Provide a dictionary that maps sites to 170 | result data to publish pattern results from a single NI-Digital Pattern instrument 171 | session. Provide a sequence of dictionaries that map sites to result data to publish 172 | pattern results from multiple NI-Digital Pattern instrument sessions. Each element 173 | in the sequence contains pattern results for the sites of a single instrument 174 | session. Furthermore, the size of the sequence must be the same size as the session 175 | data output from the pin query method. 176 | published_data_id: The unique ID for identifying the results. This ID must match one of 177 | the values in the Published Data Id column on the Tests tab of the Semiconductor 178 | Multi Test step. 179 | """ 180 | # convert instrument_site_pattern_results to a list if it isn't already 181 | if isinstance(instrument_site_pattern_results, dict): 182 | instrument_site_pattern_results = [instrument_site_pattern_results] 183 | 184 | # convert pattern results dictionaries to pattern results lists then publish 185 | re_pattern = re.compile(r"\s*site(\d+)") 186 | instrument_site_pattern_results = [ 187 | [ 188 | pattern_results[int(match[1])] 189 | for match in map(re_pattern.match, site_list.split(",")) 190 | ] 191 | for site_list, pattern_results in zip(self._site_lists, instrument_site_pattern_results) 192 | ] 193 | instrument_site_pattern_results = _pad_jagged_sequence(instrument_site_pattern_results) 194 | return self._tsm_context.PublishPatternResults( 195 | self._pins, published_data_id, instrument_site_pattern_results 196 | ) 197 | -------------------------------------------------------------------------------- /systemtests/SystemTestFixture.seq: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ni/nitsm-python/7231c7bba36e6d3b92e218f6dd804387b1f8e2ec/systemtests/SystemTestFixture.seq -------------------------------------------------------------------------------- /systemtests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ni/nitsm-python/7231c7bba36e6d3b92e218f6dd804387b1f8e2ec/systemtests/__init__.py -------------------------------------------------------------------------------- /systemtests/conftest.py: -------------------------------------------------------------------------------- 1 | import os 2 | import os.path 3 | import sys 4 | import shutil 5 | import subprocess 6 | import winreg 7 | import pytest 8 | 9 | _python_version = ".".join(map(str, sys.version_info[:2])) 10 | try: 11 | _python_environment_path = os.environ["VIRTUAL_ENV"] 12 | except KeyError: 13 | _python_environment_path = "" # global interpreter 14 | 15 | 16 | def get_env_variable_from_registry(variable_name): 17 | key = winreg.CreateKey( 18 | winreg.HKEY_LOCAL_MACHINE, r"System\CurrentControlSet\Control\Session Manager\Environment" 19 | ) 20 | return winreg.QueryValueEx(key, variable_name)[0] 21 | 22 | 23 | _teststand_public_path = get_env_variable_from_registry("TestStandPublic64") 24 | 25 | 26 | class SystemTestRunner: 27 | # subprocess.run with check=True will throw an exception if the return code is non-zero 28 | # with stdout set to subprocess.PIPE, exit code and stdout will be included in the exception 29 | _SUBPROCESS_RUN_OPTIONS = {"stdout": subprocess.PIPE, "timeout": 180, "check": True} 30 | 31 | # we need to get the TestStand variables from the registry since the pipeline process 32 | # does not refresh its environment after running the TestStand version selector 33 | 34 | _csharp_oi_path = os.path.join( 35 | _teststand_public_path, 36 | "UserInterfaces", 37 | "Simple", 38 | "CSharp", 39 | "Source Code", 40 | "bin", 41 | "x64", 42 | "release", 43 | "TestExec.exe", 44 | ) 45 | 46 | _test_fixture_path = os.path.join(os.path.dirname(__file__), "SystemTestFixture.seq") 47 | 48 | _offline_mode_tool_path = os.path.join( 49 | os.environ["ProgramFiles(x86)"], 50 | "National Instruments", 51 | "Shared", 52 | "OfflineMode", 53 | "NationalInstruments.Semiconductor.OfflineModeAPITool.exe", 54 | ) 55 | 56 | def __init__(self, sequence_file_path, offline_mode_cfg_path=""): 57 | self._sequence_file_path = sequence_file_path 58 | self._offline_mode_cfg_path = offline_mode_cfg_path 59 | 60 | def __enter__(self): 61 | if self._offline_mode_cfg_path: 62 | subprocess.run( 63 | [self._offline_mode_tool_path, "/enter", self._offline_mode_cfg_path], 64 | **self._SUBPROCESS_RUN_OPTIONS, 65 | ) 66 | return self 67 | 68 | def __exit__(self, exc_type, exc_val, exc_tb): 69 | if self._offline_mode_cfg_path: 70 | subprocess.run([self._offline_mode_tool_path, "/leave"], **self._SUBPROCESS_RUN_OPTIONS) 71 | 72 | def run(self): 73 | subprocess.run( 74 | [ 75 | self._csharp_oi_path, 76 | "/outputtostdio", 77 | "/run", 78 | "MainSequence", 79 | self._test_fixture_path, 80 | "/pyrunentrypoint", 81 | "Test UUTs", 82 | self._sequence_file_path, 83 | "/pyversion", 84 | _python_version, 85 | "/pyenvironment", 86 | _python_environment_path, 87 | "/quit", 88 | ], 89 | **self._SUBPROCESS_RUN_OPTIONS, 90 | ) 91 | return True 92 | 93 | 94 | @pytest.fixture(scope="session", autouse=True) 95 | def teststand_login_override(): 96 | system_tests_front_end_callbacks_path = os.path.join( 97 | os.path.dirname(__file__), "FrontEndCallbacks.seq" 98 | ) 99 | teststand_front_end_callbacks_path = os.path.join( 100 | _teststand_public_path, "Components", "Callbacks", "FrontEnd", "FrontEndCallbacks.seq" 101 | ) 102 | teststand_backup_front_end_callbacks_path = os.path.join( 103 | _teststand_public_path, "Components", "Callbacks", "FrontEnd", "FrontEndCallbacks.seq.bak" 104 | ) 105 | os.replace(teststand_front_end_callbacks_path, teststand_backup_front_end_callbacks_path) 106 | shutil.copy(system_tests_front_end_callbacks_path, teststand_front_end_callbacks_path) 107 | yield None 108 | os.replace(teststand_backup_front_end_callbacks_path, teststand_front_end_callbacks_path) 109 | 110 | 111 | @pytest.fixture 112 | def system_test_runner(request): 113 | # get absolute path of the test program file which is assumed to be relative to the test module 114 | module_directory = os.path.dirname(request.module.__file__) 115 | 116 | sequence_file_name = request.node.get_closest_marker("sequence_file").args[0] 117 | sequence_file_path = os.path.join(module_directory, sequence_file_name) 118 | 119 | offline_mode_marker = request.node.get_closest_marker("offline_mode") 120 | if offline_mode_marker: 121 | offline_mode_cfg_name = offline_mode_marker.args[0] 122 | offline_mode_cfg_path = os.path.join(module_directory, offline_mode_cfg_name) 123 | else: 124 | offline_mode_cfg_path = "" 125 | 126 | with SystemTestRunner(sequence_file_path, offline_mode_cfg_path) as test_runner: 127 | # the context manager will enter and exit offline mode if the marker was supplied 128 | yield test_runner 129 | -------------------------------------------------------------------------------- /systemtests/custom_instruments.pinmap: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /systemtests/custom_instruments.seq: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ni/nitsm-python/7231c7bba36e6d3b92e218f6dd804387b1f8e2ec/systemtests/custom_instruments.seq -------------------------------------------------------------------------------- /systemtests/custom_instruments_codemodules.py: -------------------------------------------------------------------------------- 1 | import nitsm.codemoduleapi 2 | from nitsm.codemoduleapi import SemiconductorModuleContext 3 | 4 | 5 | class CustomSession: 6 | def __init__(self, instrument_type_id, instrument_name, channel_group_id, channel_list): 7 | self._instrument_type_id = instrument_type_id 8 | self._instrument_name = instrument_name 9 | self._channel_group_id = channel_group_id 10 | self._channel_list = channel_list 11 | 12 | @property 13 | def instrument_type_id(self): 14 | return self._instrument_type_id 15 | 16 | @property 17 | def instrument_name(self): 18 | return self._instrument_name 19 | 20 | @property 21 | def channel_group_id(self): 22 | return self._channel_group_id 23 | 24 | @property 25 | def channel_list(self): 26 | return self._channel_list 27 | 28 | 29 | @nitsm.codemoduleapi.code_module 30 | def open_sessions(tsm_context: SemiconductorModuleContext, instrument_type_id): 31 | custom_instrument_info = tsm_context.get_custom_instrument_names(instrument_type_id) 32 | for instrument_name, channel_group_id, channel_list in zip(*custom_instrument_info): 33 | session = CustomSession(instrument_type_id, instrument_name, channel_group_id, channel_list) 34 | tsm_context.set_custom_session( 35 | instrument_type_id, instrument_name, channel_group_id, session 36 | ) 37 | 38 | 39 | @nitsm.codemoduleapi.code_module 40 | def measure( 41 | tsm_context: SemiconductorModuleContext, 42 | instrument_type_id, 43 | pins, 44 | expected_instrument_names, 45 | expected_channel_group_ids, 46 | expected_channel_lists, 47 | ): 48 | pin_query, *session_info = tsm_context.pins_to_custom_sessions(instrument_type_id, pins) 49 | expected_instrument_channels = set( 50 | zip(expected_instrument_names, expected_channel_group_ids, expected_channel_lists) 51 | ) 52 | valid_channels = [] 53 | 54 | for session, channel_group_id, channel_list in zip(*session_info): 55 | assert isinstance(session, CustomSession) 56 | assert session.instrument_type_id == instrument_type_id 57 | assert session.channel_group_id == channel_group_id 58 | 59 | # check instrument channels we received is in the set of instrument channels we expected 60 | actual_instrument_channels = ( 61 | session.instrument_name, 62 | channel_group_id, 63 | channel_list, 64 | ) 65 | channel_count = len(channel_list.split(",")) 66 | valid_channels.append( 67 | [actual_instrument_channels in expected_instrument_channels] * channel_count 68 | ) 69 | expected_instrument_channels -= {actual_instrument_channels} 70 | 71 | pin_query.publish(valid_channels) 72 | num_missing_channels = [ 73 | [len(expected_instrument_channels)] * len(row) for row in valid_channels 74 | ] 75 | pin_query.publish(num_missing_channels, "NumMissing") 76 | 77 | 78 | @nitsm.codemoduleapi.code_module 79 | def close_sessions(tsm_context: SemiconductorModuleContext, instrument_type_id): 80 | session_info = tsm_context.get_all_custom_sessions(instrument_type_id) 81 | for session, channel_group_id, channel_list in zip(*session_info): 82 | assert isinstance(session, CustomSession) 83 | assert session.instrument_type_id == instrument_type_id 84 | assert session.channel_group_id == channel_group_id 85 | assert session.channel_list == channel_list 86 | -------------------------------------------------------------------------------- /systemtests/nidaqmx.offlinecfg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /systemtests/nidaqmx.pinmap: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /systemtests/nidaqmx.seq: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ni/nitsm-python/7231c7bba36e6d3b92e218f6dd804387b1f8e2ec/systemtests/nidaqmx.seq -------------------------------------------------------------------------------- /systemtests/nidaqmx_codemodules.py: -------------------------------------------------------------------------------- 1 | import nidaqmx 2 | import nitsm.codemoduleapi 3 | from nitsm.codemoduleapi import SemiconductorModuleContext 4 | from sessionutils import get_task_name_from_task 5 | 6 | 7 | @nitsm.codemoduleapi.code_module 8 | def open_sessions(tsm_context: SemiconductorModuleContext): 9 | # get task names and channel lists 10 | ai_task_names, ai_channel_lists = tsm_context.get_all_nidaqmx_task_names("ai") 11 | ao_task_names, ao_channel_lists = tsm_context.get_all_nidaqmx_task_names("ao") 12 | 13 | # create and set ai tasks 14 | for task_name, channel_list in zip(ai_task_names, ai_channel_lists): 15 | task = nidaqmx.Task(task_name) 16 | task.ai_channels.add_ai_voltage_chan(channel_list) 17 | tsm_context.set_nidaqmx_task(task_name, task) 18 | 19 | # create and set ao tasks 20 | for task_name, channel_list in zip(ao_task_names, ao_channel_lists): 21 | task = nidaqmx.Task(task_name) 22 | task.ao_channels.add_ao_voltage_chan(channel_list) 23 | tsm_context.set_nidaqmx_task(task_name, task) 24 | 25 | 26 | @nitsm.codemoduleapi.code_module 27 | def measure( 28 | tsm_context: SemiconductorModuleContext, 29 | pins, 30 | expected_task_names, 31 | expected_channel_lists, 32 | ): 33 | pin_query, tasks, channel_lists = tsm_context.pins_to_nidaqmx_tasks(pins) 34 | expected_instrument_channels = set(zip(expected_task_names, expected_channel_lists)) 35 | valid_channels = [] 36 | 37 | for task, channel_list in zip(tasks, channel_lists): 38 | # call some methods on the session to ensure no errors 39 | task.timing.cfg_samp_clk_timing(1e3, "OnboardClock", samps_per_chan=10) 40 | task.in_stream.channels_to_read = task.ai_channels[channel_list] 41 | task.start() 42 | task.read() 43 | task.stop() 44 | 45 | # check instrument channel we received is in the set of instrument channels we expected 46 | task_name = get_task_name_from_task(task) 47 | actual_instrument_channel = (task_name, channel_list) 48 | valid_channel = actual_instrument_channel in expected_instrument_channels 49 | valid_channels.append([valid_channel] * len(channel_list.split(", "))) 50 | expected_instrument_channels -= {actual_instrument_channel} 51 | 52 | pin_query.publish(valid_channels) 53 | num_missing_channels = [ 54 | [len(expected_instrument_channels)] * len(row) for row in valid_channels 55 | ] 56 | pin_query.publish(num_missing_channels, "NumMissing") 57 | 58 | 59 | @nitsm.codemoduleapi.code_module 60 | def close_sessions(tsm_context: SemiconductorModuleContext): 61 | tasks = tsm_context.get_all_nidaqmx_tasks("") 62 | for task in tasks: 63 | task.close() 64 | -------------------------------------------------------------------------------- /systemtests/nidcpower.pinmap: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /systemtests/nidcpower.seq: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ni/nitsm-python/7231c7bba36e6d3b92e218f6dd804387b1f8e2ec/systemtests/nidcpower.seq -------------------------------------------------------------------------------- /systemtests/nidcpower_codemodules.py: -------------------------------------------------------------------------------- 1 | import nidcpower 2 | import nitsm.codemoduleapi 3 | from nitsm.codemoduleapi import SemiconductorModuleContext 4 | 5 | OPTIONS = {"Simulate": True, "DriverSetup": {"Model": "4162", "BoardType": "PXIe"}} 6 | 7 | 8 | @nitsm.codemoduleapi.code_module 9 | def open_sessions(tsm_context: SemiconductorModuleContext): 10 | resource_strings = tsm_context.get_all_nidcpower_resource_strings() 11 | for resource_string in resource_strings: 12 | session = nidcpower.Session(resource_string, options=OPTIONS) 13 | tsm_context.set_nidcpower_session(resource_string, session) 14 | 15 | 16 | @nitsm.codemoduleapi.code_module 17 | def measure( 18 | tsm_context: SemiconductorModuleContext, 19 | pins, 20 | expected_channel_strings, 21 | ): 22 | pin_query, sessions, channel_strings = tsm_context.pins_to_nidcpower_sessions(pins) 23 | expected_instrument_channels = set( 24 | expected_channel_string.replace(" ", "") # remove spaces 25 | for expected_channel_string in expected_channel_strings 26 | ) 27 | valid_channels = [] 28 | 29 | for session, channel_string in zip(sessions, channel_strings): 30 | # call some methods on the session to ensure no errors 31 | channel_session = session.channels[channel_string] 32 | channel_session.abort() 33 | channel_session.output_function = nidcpower.OutputFunction.DC_CURRENT 34 | channel_session.current_level = 1e-3 35 | channel_session.output_enabled = True 36 | channel_session.source_delay = 250e-6 37 | channel_session.initiate() 38 | channel_session.wait_for_event(nidcpower.Event.SOURCE_COMPLETE) 39 | channel_session.measure_multiple() 40 | 41 | # check instrument channels we received is in the set of instrument channels we expected 42 | actual_instrument_channels = session.io_resource_descriptor.replace(" ", "") 43 | channel_count = len(channel_string.split(",")) 44 | valid_channels.append( 45 | [actual_instrument_channels in expected_instrument_channels] * channel_count 46 | ) 47 | expected_instrument_channels -= {actual_instrument_channels} 48 | 49 | pin_query.publish(valid_channels) 50 | num_missing_channels = [ 51 | [len(expected_instrument_channels)] * len(row) for row in valid_channels 52 | ] 53 | pin_query.publish(num_missing_channels, "NumMissing") 54 | 55 | 56 | @nitsm.codemoduleapi.code_module 57 | def close_sessions(tsm_context: SemiconductorModuleContext): 58 | sessions = tsm_context.get_all_nidcpower_sessions() 59 | for session in sessions: 60 | session.close() 61 | -------------------------------------------------------------------------------- /systemtests/nidigital.digicapture: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ni/nitsm-python/7231c7bba36e6d3b92e218f6dd804387b1f8e2ec/systemtests/nidigital.digicapture -------------------------------------------------------------------------------- /systemtests/nidigital.digilevels: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | DC.Vl 7 | DC.Vh 8 | DC.Vl 9 | DC.Vh 10 | HighZ 11 | 12 | 13 | DC.Vl 14 | DC.Vh 15 | DC.Vl 16 | DC.Vh 17 | HighZ 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /systemtests/nidigital.digipat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ni/nitsm-python/7231c7bba36e6d3b92e218f6dd804387b1f8e2ec/systemtests/nidigital.digipat -------------------------------------------------------------------------------- /systemtests/nidigital.digiproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /systemtests/nidigital.digitiming: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | AC.Period 7 | 8 | 9 | 10 | 0 11 | 0 12 | AC.Period 13 | 14 | 15 | AC.Period / 2 16 | 17 | Pattern 18 | 19 | 20 | 21 | 0 22 | 0 23 | AC.Period 24 | 25 | 26 | AC.Period / 2 27 | 28 | Pattern 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /systemtests/nidigital.pinmap: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /systemtests/nidigital.seq: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ni/nitsm-python/7231c7bba36e6d3b92e218f6dd804387b1f8e2ec/systemtests/nidigital.seq -------------------------------------------------------------------------------- /systemtests/nidigital.specs: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 3.3 V 6 | 7 | 8 | 0 V 9 | 10 |
11 |
12 | 13 | 20 ns 14 | 15 |
16 |
-------------------------------------------------------------------------------- /systemtests/nidigital.tdms: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ni/nitsm-python/7231c7bba36e6d3b92e218f6dd804387b1f8e2ec/systemtests/nidigital.tdms -------------------------------------------------------------------------------- /systemtests/nidigital_codemodules.py: -------------------------------------------------------------------------------- 1 | import re 2 | import nidigital 3 | import nitsm.codemoduleapi 4 | from nitsm.codemoduleapi import SemiconductorModuleContext 5 | 6 | OPTIONS = {"Simulate": True, "driver_setup": {"Model": "6570"}} 7 | 8 | 9 | @nitsm.codemoduleapi.code_module 10 | def open_sessions(tsm_context: SemiconductorModuleContext): 11 | instrument_names = tsm_context.get_all_nidigital_instrument_names() 12 | for instrument_name in instrument_names: 13 | session = nidigital.Session(instrument_name, options=OPTIONS) 14 | session.load_pin_map(tsm_context.pin_map_file_path) 15 | session.load_specifications_levels_and_timing( 16 | tsm_context.nidigital_project_specifications_file_paths, 17 | tsm_context.nidigital_project_levels_file_paths, 18 | tsm_context.nidigital_project_timing_file_paths, 19 | ) 20 | session.apply_levels_and_timing("nidigital", "nidigital") 21 | for pattern_file_path in tsm_context.nidigital_project_pattern_file_paths: 22 | session.load_pattern(pattern_file_path) 23 | tsm_context.set_nidigital_session(instrument_name, session) 24 | 25 | 26 | @nitsm.codemoduleapi.code_module 27 | def measure_ppmu( 28 | tsm_context: SemiconductorModuleContext, 29 | pins, 30 | expected_instrument_names, 31 | expected_pin_set_strings, 32 | ): 33 | pin_query, sessions, pin_set_strings = tsm_context.pins_to_nidigital_sessions_for_ppmu(pins) 34 | expected_instrument_pin_sets = set(zip(expected_instrument_names, expected_pin_set_strings)) 35 | valid_pin_sets = [] 36 | 37 | for session, pin_set_string in zip(sessions, pin_set_strings): 38 | # call some methods on the session to ensure no errors 39 | session.pins[pin_set_string].ppmu_aperture_time = 4e-6 40 | session.pins[ 41 | pin_set_string 42 | ].ppmu_aperture_time_units = nidigital.PPMUApertureTimeUnits.SECONDS 43 | session.pins[pin_set_string].ppmu_output_function = nidigital.PPMUOutputFunction.CURRENT 44 | session.pins[pin_set_string].ppmu_current_level_range = 2e-6 45 | session.pins[pin_set_string].ppmu_current_level = 2e-6 46 | session.pins[pin_set_string].ppmu_voltage_limit_high = 3.3 47 | session.pins[pin_set_string].ppmu_voltage_limit_low = 0 48 | session.pins[pin_set_string].ppmu_source() 49 | session.pins[pin_set_string].ppmu_measure(nidigital.PPMUMeasurementType.CURRENT) 50 | session.abort() 51 | 52 | # check instrument pin set we received is in the set of instrument pin sets we expected 53 | actual_instrument_pin_set = (session.io_resource_descriptor, pin_set_string) 54 | num_pins_for_session = len(pin_set_string.split(",")) 55 | valid_pin_sets.extend( 56 | [actual_instrument_pin_set in expected_instrument_pin_sets] * num_pins_for_session 57 | ) 58 | expected_instrument_pin_sets -= {actual_instrument_pin_set} 59 | 60 | pin_query.publish(valid_pin_sets, "ValidPinSetStrings") 61 | num_missing_pin_sets = [len(expected_instrument_pin_sets)] * len(valid_pin_sets) 62 | pin_query.publish(num_missing_pin_sets, "NumMissingPinSetStrings") 63 | 64 | 65 | @nitsm.codemoduleapi.code_module 66 | def measure_pattern( 67 | tsm_context: SemiconductorModuleContext, pins, expected_instrument_names, expected_site_lists 68 | ): 69 | pin_query, sessions, site_lists = tsm_context.pins_to_nidigital_sessions_for_pattern(pins) 70 | expected_instrument_site_lists = set(zip(expected_instrument_names, expected_site_lists)) 71 | valid_site_lists = [] 72 | re_pattern = re.compile(r"\s*site(\d+)") 73 | 74 | for session, site_list in zip(sessions, site_lists): 75 | # call some methods on the session to ensure no errors 76 | session.sites[site_list].burst_pattern("start_label") 77 | 78 | # check instrument site we received is in the set of instrument sites we expected 79 | actual_instrument_site_list = (session.io_resource_descriptor, site_list) 80 | actual_in_expected = actual_instrument_site_list in expected_instrument_site_lists 81 | site_numbers = (int(re_pattern.match(site)[1]) for site in site_list.split(",")) 82 | valid_site_lists.append({site: actual_in_expected for site in site_numbers}) 83 | expected_instrument_site_lists -= {actual_instrument_site_list} 84 | 85 | pin_query.publish_pattern_results(valid_site_lists, "ValidSiteLists") 86 | num_missing_site_lists = [len(expected_instrument_site_lists)] * len(tsm_context.site_numbers) 87 | tsm_context.publish_per_site(num_missing_site_lists, "NumMissingSiteLists") 88 | 89 | 90 | @nitsm.codemoduleapi.code_module 91 | def check_project_paths( 92 | tsm_context: SemiconductorModuleContext, 93 | specifications_paths, 94 | levels_paths, 95 | timing_paths, 96 | pattern_paths, 97 | source_waveform_paths, 98 | capture_waveform_paths, 99 | ): 100 | site_count = len(tsm_context.site_numbers) 101 | valid_project_paths = [ 102 | tsm_context.nidigital_project_specifications_file_paths == tuple(specifications_paths) 103 | ] * site_count 104 | valid_levels_paths = [ 105 | tsm_context.nidigital_project_levels_file_paths == tuple(levels_paths) 106 | ] * site_count 107 | valid_timing_paths = [ 108 | tsm_context.nidigital_project_timing_file_paths == tuple(timing_paths) 109 | ] * site_count 110 | valid_pattern_paths = [ 111 | tsm_context.nidigital_project_pattern_file_paths == tuple(pattern_paths) 112 | ] * site_count 113 | valid_source_waveform_paths = [ 114 | tsm_context.nidigital_project_source_waveform_file_paths == tuple(source_waveform_paths) 115 | ] * site_count 116 | valid_capture_waveform_paths = [ 117 | tsm_context.nidigital_project_capture_waveform_file_paths == tuple(capture_waveform_paths) 118 | ] * site_count 119 | 120 | tsm_context.publish_per_site(valid_project_paths, "ValidSpecificationsPaths") 121 | tsm_context.publish_per_site(valid_levels_paths, "ValidLevelsPaths") 122 | tsm_context.publish_per_site(valid_timing_paths, "ValidTimingPaths") 123 | tsm_context.publish_per_site(valid_pattern_paths, "ValidPatternPaths") 124 | tsm_context.publish_per_site(valid_source_waveform_paths, "ValidSourceWaveformPaths") 125 | tsm_context.publish_per_site(valid_capture_waveform_paths, "ValidCaptureWaveformPaths") 126 | 127 | 128 | @nitsm.codemoduleapi.code_module 129 | def close_sessions(tsm_context: SemiconductorModuleContext): 130 | sessions = tsm_context.get_all_nidigital_sessions() 131 | for session in sessions: 132 | session.close() 133 | -------------------------------------------------------------------------------- /systemtests/nidmm.pinmap: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /systemtests/nidmm.seq: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ni/nitsm-python/7231c7bba36e6d3b92e218f6dd804387b1f8e2ec/systemtests/nidmm.seq -------------------------------------------------------------------------------- /systemtests/nidmm_codemodules.py: -------------------------------------------------------------------------------- 1 | import nidmm 2 | import nitsm.codemoduleapi 3 | from nitsm.codemoduleapi import SemiconductorModuleContext 4 | 5 | OPTIONS = {"Simulate": True, "DriverSetup": {"Model": "4071", "BoardType": "PXI"}} 6 | 7 | 8 | @nitsm.codemoduleapi.code_module 9 | def open_sessions(tsm_context: SemiconductorModuleContext): 10 | instrument_names = tsm_context.get_all_nidmm_instrument_names() 11 | for instrument_name in instrument_names: 12 | session = nidmm.Session(instrument_name, options=OPTIONS) 13 | tsm_context.set_nidmm_session(instrument_name, session) 14 | 15 | 16 | @nitsm.codemoduleapi.code_module 17 | def measure( 18 | tsm_context: SemiconductorModuleContext, 19 | pins, 20 | expected_instrument_names, 21 | ): 22 | pin_query, sessions = tsm_context.pins_to_nidmm_sessions(pins) 23 | expected_instrument_names = set(expected_instrument_names) 24 | valid_instruments = [] 25 | 26 | for session in sessions: 27 | # call some methods on the session to ensure no errors 28 | session.configure_measurement_digits(nidmm.Function.DC_VOLTS, 10, 5.5) 29 | session.read() 30 | session.abort() 31 | 32 | # check instrument name we received is in the set of instrument names we expected 33 | actual_instrument_name = session.io_resource_descriptor 34 | valid_instruments.append(actual_instrument_name in expected_instrument_names) 35 | expected_instrument_names -= {actual_instrument_name} 36 | 37 | pin_query.publish(valid_instruments) 38 | num_missing_instruments = [len(expected_instrument_names)] * len(sessions) 39 | pin_query.publish(num_missing_instruments, "NumMissing") 40 | 41 | 42 | @nitsm.codemoduleapi.code_module 43 | def close_sessions(tsm_context: SemiconductorModuleContext): 44 | sessions = tsm_context.get_all_nidmm_sessions() 45 | for session in sessions: 46 | session.close() 47 | -------------------------------------------------------------------------------- /systemtests/nifgen.pinmap: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /systemtests/nifgen.seq: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ni/nitsm-python/7231c7bba36e6d3b92e218f6dd804387b1f8e2ec/systemtests/nifgen.seq -------------------------------------------------------------------------------- /systemtests/nifgen_codemodules.py: -------------------------------------------------------------------------------- 1 | import nifgen 2 | import nitsm.codemoduleapi 3 | from nitsm.codemoduleapi import SemiconductorModuleContext 4 | from sessionutils import get_resource_name_from_session 5 | 6 | 7 | @nitsm.codemoduleapi.code_module 8 | def open_sessions(tsm_context: SemiconductorModuleContext): 9 | instrument_names = tsm_context.get_all_nifgen_instrument_names() 10 | for instrument_name in instrument_names: 11 | name, model = instrument_name.split("_") 12 | session = nifgen.Session(name, options={"Simulate": True, "DriverSetup": {"Model": model}}) 13 | tsm_context.set_nifgen_session(instrument_name, session) 14 | 15 | 16 | @nitsm.codemoduleapi.code_module 17 | def measure( 18 | tsm_context: SemiconductorModuleContext, 19 | pins, 20 | expected_instrument_names, 21 | expected_channel_lists, 22 | ): 23 | pin_query, sessions, channel_lists = tsm_context.pins_to_nifgen_sessions(pins) 24 | expected_instrument_channels = set(zip(expected_instrument_names, expected_channel_lists)) 25 | valid_channels = [] 26 | 27 | for session, channel_list in zip(sessions, channel_lists): 28 | # call some methods on the session to ensure no errors 29 | session.output_mode = nifgen.OutputMode.FUNC 30 | session.channels[channel_list].configure_standard_waveform(nifgen.Waveform.DC, 0, 0, 1) 31 | session.channels[channel_list].output_enabled = True 32 | session.initiate() 33 | session.abort() 34 | 35 | # check instrument channel we received is in the set of instrument channels we expected 36 | resource_name = get_resource_name_from_session(session) 37 | actual_instrument_channel = (resource_name, channel_list) 38 | valid_channel = actual_instrument_channel in expected_instrument_channels 39 | valid_channels.append([valid_channel] * len(channel_list.split(", "))) 40 | expected_instrument_channels -= {actual_instrument_channel} 41 | 42 | pin_query.publish(valid_channels) 43 | num_missing_channels = [ 44 | [len(expected_instrument_channels)] * len(row) for row in valid_channels 45 | ] 46 | pin_query.publish(num_missing_channels, "NumMissing") 47 | 48 | 49 | @nitsm.codemoduleapi.code_module 50 | def close_sessions(tsm_context: SemiconductorModuleContext): 51 | sessions = tsm_context.get_all_nifgen_sessions() 52 | for session in sessions: 53 | session.close() 54 | -------------------------------------------------------------------------------- /systemtests/nirelaydriver.pinmap: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /systemtests/nirelaydriver.seq: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ni/nitsm-python/7231c7bba36e6d3b92e218f6dd804387b1f8e2ec/systemtests/nirelaydriver.seq -------------------------------------------------------------------------------- /systemtests/nirelaydriver_codemodules.py: -------------------------------------------------------------------------------- 1 | import niswitch 2 | import nitsm.codemoduleapi 3 | from nitsm.codemoduleapi import SemiconductorModuleContext 4 | 5 | 6 | class MockSwitchSession(niswitch.Session): 7 | def __init__(self, resource_name, *args, **kwargs): 8 | # resource name must be empty string to simulate an niswitch session 9 | self.__resource_name = resource_name 10 | super().__init__("", *args, **kwargs) 11 | 12 | @property 13 | def io_resource_descriptor(self): 14 | return self.__resource_name 15 | 16 | 17 | @nitsm.codemoduleapi.code_module 18 | def open_sessions(tsm_context: SemiconductorModuleContext): 19 | module_names = tsm_context.get_relay_driver_module_names() 20 | for module_name in module_names: 21 | session = MockSwitchSession(module_name, topology="2567/Independent", simulate=True) 22 | tsm_context.set_relay_driver_niswitch_session(module_name, session) 23 | 24 | 25 | @nitsm.codemoduleapi.code_module 26 | def measure( 27 | tsm_context: SemiconductorModuleContext, 28 | relays, 29 | expected_instrument_names, 30 | expected_relay_names, 31 | ): 32 | sessions, relay_names = tsm_context.relays_to_relay_driver_niswitch_sessions(relays) 33 | expected_instrument_relays = set(zip(expected_instrument_names, expected_relay_names)) 34 | valid_channels = [] 35 | 36 | for session, relay_name in zip(sessions, relay_names): 37 | # call some methods on the session to ensure no errors 38 | session.relay_control(relay_name, niswitch.RelayAction.OPEN) 39 | session.relay_control(relay_name, niswitch.RelayAction.CLOSE) 40 | session.wait_for_debounce() 41 | 42 | # check instrument channels we received is in the set of instrument channels we expected 43 | actual_instrument_relays = (session.io_resource_descriptor, relay_name) 44 | valid_channels.append(actual_instrument_relays in expected_instrument_relays) 45 | missing_instrument_relays = expected_instrument_relays - {actual_instrument_relays} 46 | 47 | site_count = len(tsm_context.site_numbers) 48 | tsm_context.publish_per_site([all(valid_channels)] * site_count, "AllChannelsAreValid") 49 | num_missing_channels = [len(missing_instrument_relays)] * site_count 50 | tsm_context.publish_per_site(num_missing_channels, "NumMissing") 51 | 52 | 53 | @nitsm.codemoduleapi.code_module 54 | def close_sessions(tsm_context: SemiconductorModuleContext): 55 | sessions = tsm_context.get_all_relay_driver_niswitch_sessions() 56 | for session in sessions: 57 | session.close() 58 | -------------------------------------------------------------------------------- /systemtests/niscope.pinmap: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /systemtests/niscope.seq: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ni/nitsm-python/7231c7bba36e6d3b92e218f6dd804387b1f8e2ec/systemtests/niscope.seq -------------------------------------------------------------------------------- /systemtests/niscope_codemodules.py: -------------------------------------------------------------------------------- 1 | import niscope 2 | import nitsm.codemoduleapi 3 | 4 | 5 | @nitsm.codemoduleapi.code_module 6 | def open_sessions(tsm_context: nitsm.codemoduleapi.SemiconductorModuleContext): 7 | instrument_names = tsm_context.get_all_niscope_instrument_names() 8 | for instrument_name in instrument_names: 9 | session = niscope.Session(instrument_name, options={"Simulate": True}) 10 | tsm_context.set_niscope_session(instrument_name, session) 11 | 12 | 13 | @nitsm.codemoduleapi.code_module 14 | def measure( 15 | tsm_context: nitsm.codemoduleapi.SemiconductorModuleContext, 16 | pins, 17 | expected_instrument_names, 18 | expected_channel_lists, 19 | ): 20 | pin_query, sessions, channel_lists = tsm_context.pins_to_niscope_sessions(pins) 21 | expected_instrument_channels = set(zip(expected_instrument_names, expected_channel_lists)) 22 | valid_channels = [] 23 | 24 | for session, channel_list in zip(sessions, channel_lists): 25 | # call some methods on the session to ensure no errors 26 | pin_session = session.channels[channel_list] 27 | session.abort() 28 | pin_session.configure_vertical(range=10.0, offset=5.0, coupling=niscope.VerticalCoupling.DC) 29 | session.initiate() 30 | pin_session.fetch_measurement_stats(niscope.ScalarMeasurement.VOLTAGE_MAX) 31 | 32 | # check instrument channel we received is in the set of instrument channels we expected 33 | actual_instrument_channel = (session.io_resource_descriptor, channel_list) 34 | valid_channels.append(actual_instrument_channel in expected_instrument_channels) 35 | expected_instrument_channels -= {actual_instrument_channel} 36 | 37 | pin_query.publish(valid_channels) 38 | num_missing_channels = [len(expected_instrument_channels)] * len(sessions) 39 | pin_query.publish(num_missing_channels, "NumMissing") 40 | 41 | 42 | @nitsm.codemoduleapi.code_module 43 | def close_sessions(tsm_context: nitsm.codemoduleapi.SemiconductorModuleContext): 44 | sessions = tsm_context.get_all_niscope_sessions() 45 | for session in sessions: 46 | session.close() 47 | -------------------------------------------------------------------------------- /systemtests/sessionutils.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | 4 | def get_resource_name_from_session(session) -> str: 5 | """ 6 | session.io_resource_descriptor isn't 100% reliable for simulated sessions. This method uses a 7 | regular expression to get the resource name from the object's repr. 8 | """ 9 | 10 | return re.search(r"resource_name='(\w*)'", repr(session)).group(1) 11 | 12 | 13 | def get_task_name_from_task(task) -> str: 14 | """Uses a regular expression on the task's repr to return the task's name.""" 15 | return re.search(r"Task\(name=(\w*)\)", repr(task)).group(1) 16 | -------------------------------------------------------------------------------- /systemtests/site_and_global_data.pinmap: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /systemtests/site_and_global_data.seq: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ni/nitsm-python/7231c7bba36e6d3b92e218f6dd804387b1f8e2ec/systemtests/site_and_global_data.seq -------------------------------------------------------------------------------- /systemtests/site_and_global_data_codemodules.py: -------------------------------------------------------------------------------- 1 | import nitsm.codemoduleapi 2 | from nitsm.codemoduleapi import SemiconductorModuleContext 3 | 4 | 5 | @nitsm.codemoduleapi.code_module 6 | def set_global_data(tsm_context: SemiconductorModuleContext, data_id, data): 7 | tsm_context.set_global_data(data_id, data) 8 | 9 | 10 | @nitsm.codemoduleapi.code_module 11 | def check_global_data(tsm_context: SemiconductorModuleContext, data_id, data): 12 | site_count = len(tsm_context.site_numbers) 13 | global_data_exists = [tsm_context.global_data_exists(data_id)] * site_count 14 | tsm_context.publish_per_site(global_data_exists, "GlobalDataExists") 15 | 16 | valid_global_data = [tsm_context.get_global_data(data_id) == data] * site_count 17 | tsm_context.publish_per_site(valid_global_data, "ValidGlobalData") 18 | 19 | 20 | @nitsm.codemoduleapi.code_module 21 | def set_site_data(tsm_context: SemiconductorModuleContext, data_id, data): 22 | tsm_context.set_site_data(data_id, data) 23 | 24 | 25 | @nitsm.codemoduleapi.code_module 26 | def check_site_data(tsm_context: SemiconductorModuleContext, data_id, data): 27 | site_count = len(tsm_context.site_numbers) 28 | site_data_exists = [tsm_context.site_data_exists(data_id)] * site_count 29 | tsm_context.publish_per_site(site_data_exists, "SiteDataExists") 30 | 31 | valid_site_data = [tsm_context.get_site_data(data_id) == tuple(data)] * site_count 32 | tsm_context.publish_per_site(valid_site_data, "ValidSiteData") 33 | 34 | 35 | @nitsm.codemoduleapi.code_module 36 | def clear_site_data(tsm_context: SemiconductorModuleContext, data_id): 37 | tsm_context.set_site_data(data_id, []) 38 | 39 | 40 | @nitsm.codemoduleapi.code_module 41 | def check_site_data_cleared(tsm_context: SemiconductorModuleContext, data_id): 42 | site_count = len(tsm_context.site_numbers) 43 | site_data_does_not_exist = [not tsm_context.site_data_exists(data_id)] * site_count 44 | tsm_context.publish_per_site(site_data_does_not_exist, "SiteDataDoesNotExist") 45 | -------------------------------------------------------------------------------- /systemtests/specifications.pinmap: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /systemtests/specifications.seq: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ni/nitsm-python/7231c7bba36e6d3b92e218f6dd804387b1f8e2ec/systemtests/specifications.seq -------------------------------------------------------------------------------- /systemtests/specifications.specs: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 0.0 6 | 7 | 8 | 5.0 9 | 10 | 11 | 3.3 12 | 13 |
14 |
-------------------------------------------------------------------------------- /systemtests/specifications_codemodules.py: -------------------------------------------------------------------------------- 1 | import nitsm.codemoduleapi 2 | 3 | 4 | @nitsm.codemoduleapi.code_module 5 | def measure(tsm_context, namespaced_symbols, expected_values): 6 | tsm_context: nitsm.codemoduleapi.SemiconductorModuleContext 7 | if isinstance(namespaced_symbols, str): 8 | actual_values = tsm_context.get_specifications_value(namespaced_symbols) 9 | else: 10 | actual_values = tsm_context.get_specifications_values(namespaced_symbols) 11 | tsm_context.publish_per_site(expected_values == actual_values) 12 | -------------------------------------------------------------------------------- /systemtests/switch.pinmap: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /systemtests/switch.seq: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ni/nitsm-python/7231c7bba36e6d3b92e218f6dd804387b1f8e2ec/systemtests/switch.seq -------------------------------------------------------------------------------- /systemtests/switch_codemodules.py: -------------------------------------------------------------------------------- 1 | import nitsm.codemoduleapi as tsm 2 | 3 | MULTIPLEXER_TYPE_ID = "SimulatedMultiplexer" 4 | 5 | 6 | @tsm.code_module 7 | def open_sessions(tsm_context: tsm.SemiconductorModuleContext): 8 | switch_names = tsm_context.get_all_switch_names(MULTIPLEXER_TYPE_ID) 9 | for switch_name in switch_names: 10 | tsm_context.set_switch_session(switch_name, switch_name, MULTIPLEXER_TYPE_ID) 11 | # nidigital sessions are required to satisfy the pin map but won't be used 12 | instrument_names = tsm_context.get_all_nidigital_instrument_names() 13 | for instrument_name in instrument_names: 14 | tsm_context.set_nidigital_session(instrument_name, ...) 15 | 16 | 17 | @tsm.code_module 18 | def measure( 19 | tsm_context: tsm.SemiconductorModuleContext, pin, expected_switch_names, expected_switch_routes 20 | ): 21 | site_contexts, switch_names, switch_routes = tsm_context.pin_to_switch_sessions( 22 | pin, MULTIPLEXER_TYPE_ID 23 | ) 24 | expected_names_and_routes = set(zip(expected_switch_names, expected_switch_routes)) 25 | valid_names_and_routes = [] 26 | 27 | for site_context, switch_name, switch_route in zip(site_contexts, switch_names, switch_routes): 28 | # check switch route we received is in the set of switch routes we expected 29 | actual_name_and_route = (switch_name, switch_route) 30 | valid_name_and_route = actual_name_and_route in expected_names_and_routes 31 | valid_names_and_routes.append(valid_name_and_route) 32 | expected_names_and_routes.discard(actual_name_and_route) 33 | # get a pin query context and publish result 34 | pin_query = site_context.pins_to_nidigital_session_for_ppmu(pin)[0] 35 | pin_query.publish(valid_name_and_route) 36 | 37 | # publish missing switch routes for all sites 38 | pin_query = tsm_context.pins_to_nidigital_session_for_ppmu(pin)[0] 39 | num_missing_routes = [len(expected_names_and_routes)] * len(site_contexts) 40 | pin_query.publish(num_missing_routes, "NumMissing") 41 | 42 | 43 | @tsm.code_module 44 | def close_sessions(tsm_context: tsm.SemiconductorModuleContext): 45 | tsm_context.get_all_switch_sessions(MULTIPLEXER_TYPE_ID) 46 | -------------------------------------------------------------------------------- /systemtests/test_system.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | 4 | @pytest.mark.sequence_file("nidmm.seq") 5 | def test_nidmm(system_test_runner): 6 | assert system_test_runner.run() 7 | 8 | 9 | @pytest.mark.sequence_file("nidcpower.seq") 10 | def test_nidcpower(system_test_runner): 11 | assert system_test_runner.run() 12 | 13 | 14 | @pytest.mark.sequence_file("nifgen.seq") 15 | def test_nifgen(system_test_runner): 16 | assert system_test_runner.run() 17 | 18 | 19 | @pytest.mark.sequence_file("nidaqmx.seq") 20 | @pytest.mark.offline_mode("nidaqmx.offlinecfg") 21 | def test_nidaqmx(system_test_runner): 22 | assert system_test_runner.run() 23 | 24 | 25 | @pytest.mark.sequence_file("niscope.seq") 26 | def test_niscope(system_test_runner): 27 | assert system_test_runner.run() 28 | 29 | 30 | @pytest.mark.sequence_file("nidigital.seq") 31 | def test_nidigital(system_test_runner): 32 | assert system_test_runner.run() 33 | 34 | 35 | @pytest.mark.sequence_file("nirelaydriver.seq") 36 | def test_nirelaydriver(system_test_runner): 37 | assert system_test_runner.run() 38 | 39 | 40 | @pytest.mark.sequence_file("custom_instruments.seq") 41 | def test_custom_instruments(system_test_runner): 42 | assert system_test_runner.run() 43 | 44 | 45 | @pytest.mark.sequence_file("site_and_global_data.seq") 46 | def test_site_and_global_data(system_test_runner): 47 | assert system_test_runner.run() 48 | 49 | 50 | @pytest.mark.sequence_file("specifications.seq") 51 | def test_specifications(system_test_runner): 52 | assert system_test_runner.run() 53 | 54 | 55 | @pytest.mark.sequence_file("switch.seq") 56 | def test_switch(system_test_runner): 57 | assert system_test_runner.run() 58 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ni/nitsm-python/7231c7bba36e6d3b92e218f6dd804387b1f8e2ec/tests/__init__.py -------------------------------------------------------------------------------- /tests/codemoduleapi.pinmap: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /tests/conftest.py: -------------------------------------------------------------------------------- 1 | import enum 2 | import os.path 3 | import pytest 4 | import win32com.client 5 | import win32com.client.selecttlb 6 | import pythoncom 7 | import nitsm.codemoduleapi 8 | 9 | try: 10 | _standalone_tsm_context_tlb = win32com.client.selecttlb.FindTlbsWithDescription( 11 | "NI TestStand Semiconductor Module Standalone Semiconductor Module Context" 12 | )[0] 13 | except IndexError: 14 | raise RuntimeError( 15 | "The TSM Standalone Semiconductor Module Context component is not installed. " 16 | "Contact one of the repository owners to determine how to obtain this " 17 | "non-public component." 18 | ) 19 | 20 | 21 | @pytest.fixture 22 | def _published_data_reader_factory(request): 23 | # get absolute path of the pin map file which is assumed to be relative to the test module 24 | pin_map_path = request.node.get_closest_marker("pin_map").args[0] 25 | module_directory = os.path.dirname(request.module.__file__) 26 | pin_map_path = os.path.join(module_directory, pin_map_path) 27 | 28 | published_data_reader_factory = win32com.client.Dispatch( 29 | "NationalInstruments.TestStand.SemiconductorModule.Restricted.PublishedDataReaderFactory" 30 | ) 31 | return published_data_reader_factory.NewSemiconductorModuleContext(pin_map_path) 32 | 33 | 34 | @pytest.fixture 35 | def standalone_tsm_context_com_object(_published_data_reader_factory): 36 | return _published_data_reader_factory[0] 37 | 38 | 39 | @pytest.fixture 40 | def standalone_tsm_context(standalone_tsm_context_com_object): 41 | return nitsm.codemoduleapi.SemiconductorModuleContext(standalone_tsm_context_com_object) 42 | 43 | 44 | class PublishedDataType(enum.Enum): 45 | Double = 0 46 | Boolean = 1 47 | String = 2 48 | 49 | 50 | class PublishedData: 51 | def __init__(self, published_data_com_obj): 52 | self._published_data = win32com.client.CastTo( 53 | published_data_com_obj, "IPublishedData", _standalone_tsm_context_tlb 54 | ) 55 | self._published_data._oleobj_ = self._published_data._oleobj_.QueryInterface( 56 | self._published_data.CLSID, pythoncom.IID_IDispatch 57 | ) 58 | 59 | @property 60 | def boolean_value(self) -> bool: 61 | return self._published_data.BooleanValue 62 | 63 | @property 64 | def double_value(self) -> float: 65 | return self._published_data.DoubleValue 66 | 67 | @property 68 | def pin(self) -> str: 69 | return self._published_data.Pin 70 | 71 | @property 72 | def published_data_id(self) -> str: 73 | return self._published_data.PublishedDataId 74 | 75 | @property 76 | def site_number(self) -> int: 77 | return self._published_data.SiteNumber 78 | 79 | @property 80 | def string_value(self) -> str: 81 | return self._published_data.StringValue 82 | 83 | @property 84 | def type(self) -> PublishedDataType: 85 | return PublishedDataType(self._published_data.Type) 86 | 87 | 88 | class PublishedDataReader: 89 | def __init__(self, published_data_reader_com_obj): 90 | self._published_data_reader = win32com.client.CastTo( 91 | published_data_reader_com_obj, "IPublishedDataReader", _standalone_tsm_context_tlb 92 | ) 93 | 94 | def get_and_clear_published_data(self): 95 | published_data = self._published_data_reader.GetAndClearPublishedData() 96 | return [PublishedData(published_data_point) for published_data_point in published_data] 97 | 98 | 99 | @pytest.fixture 100 | def published_data_reader(_published_data_reader_factory): 101 | return PublishedDataReader(_published_data_reader_factory[1]) 102 | -------------------------------------------------------------------------------- /tests/custom_instruments.pinmap: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /tests/general_and_advanced.pinmap: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /tests/nidaqmx.pinmap: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /tests/nidcpower.pinmap: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /tests/nidigital.pinmap: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /tests/nidmm.pinmap: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /tests/nifgen.pinmap: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /tests/nirelaydriver.pinmap: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /tests/niscope.pinmap: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /tests/pinquerycontext.pinmap: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /tests/publish.pinmap: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /tests/publish_single_site.pinmap: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /tests/site_and_global_data.pinmap: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /tests/switch.pinmap: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /tests/test_codemoduleapi.py: -------------------------------------------------------------------------------- 1 | import re 2 | import pytest 3 | from nitsm.codemoduleapi import code_module, SemiconductorModuleContext 4 | 5 | 6 | @pytest.mark.pin_map("codemoduleapi.pinmap") 7 | class TestCodeModuleApi: 8 | @staticmethod 9 | @code_module 10 | def test_static_method_converts(standalone_tsm_context_com_object): 11 | assert isinstance(standalone_tsm_context_com_object, SemiconductorModuleContext) 12 | 13 | @staticmethod 14 | @code_module 15 | def test_static_method_does_not_convert(standalone_tsm_context): 16 | assert isinstance(standalone_tsm_context, SemiconductorModuleContext) 17 | 18 | @code_module 19 | def test_instance_method_converts(self, standalone_tsm_context_com_object): 20 | assert isinstance(standalone_tsm_context_com_object, SemiconductorModuleContext) 21 | 22 | @code_module 23 | def test_instance_method_does_not_convert(self, standalone_tsm_context): 24 | assert isinstance(standalone_tsm_context, SemiconductorModuleContext) 25 | 26 | @classmethod 27 | @code_module 28 | def class_method_converts_core(cls, standalone_tsm_context_com_object): 29 | assert issubclass(cls, TestCodeModuleApi) 30 | assert isinstance(standalone_tsm_context_com_object, SemiconductorModuleContext) 31 | 32 | # pytest does not collect class methods so we need a static method that calls the class method 33 | @staticmethod 34 | def test_class_method_converts(standalone_tsm_context_com_object): 35 | TestCodeModuleApi.class_method_converts_core(standalone_tsm_context_com_object) 36 | 37 | @classmethod 38 | @code_module 39 | def class_method_does_not_convert_core(cls, standalone_tsm_context): 40 | assert issubclass(cls, TestCodeModuleApi) 41 | assert isinstance(standalone_tsm_context, SemiconductorModuleContext) 42 | 43 | # pytest does not collect class methods so we need a static method that calls the class method 44 | @staticmethod 45 | def test_class_method_does_not_convert(standalone_tsm_context): 46 | TestCodeModuleApi.class_method_converts_core(standalone_tsm_context) 47 | 48 | @code_module 49 | def _invalid_number_of_positional_arguments(self): 50 | """Does not contain a positional argument for the TSM context.""" 51 | assert False # should not reach this point 52 | 53 | def test_invalid_number_of_positional_arguments(self): 54 | with pytest.raises(TypeError) as e: 55 | self._invalid_number_of_positional_arguments() 56 | assert e.value.args[0] == ( 57 | "The number of arguments to the code module is less than expected. It must " 58 | "accept as it's first argument the Semiconductor Module context passed from " 59 | "TestStand or another code module.", 60 | ) 61 | 62 | # noinspection PyUnusedLocal 63 | @code_module 64 | def _tsm_context_not_first_positional_argument(self, first_argument, tsm_context): 65 | assert False # should not reach this point 66 | 67 | @pytest.mark.parametrize("first_argument", (None, int(), str())) 68 | def test_tsm_context_not_first_positional_argument( 69 | self, first_argument, standalone_tsm_context_com_object 70 | ): 71 | with pytest.raises(Exception) as e: 72 | self._tsm_context_not_first_positional_argument( 73 | first_argument, standalone_tsm_context_com_object 74 | ) 75 | assert re.match( 76 | r"Failed to convert Semiconductor Module context from class '.*'\.", 77 | e.value.args[0], 78 | ) 79 | 80 | @pytest.mark.parametrize("second_argument", (None,)) 81 | @code_module 82 | def test_positional_arguments_after_tsm_context( 83 | self, standalone_tsm_context_com_object, second_argument 84 | ): 85 | assert isinstance(standalone_tsm_context_com_object, SemiconductorModuleContext) 86 | 87 | 88 | @pytest.mark.pin_map("codemoduleapi.pinmap") 89 | @code_module 90 | def test_function_converts(standalone_tsm_context_com_object): 91 | assert isinstance(standalone_tsm_context_com_object, SemiconductorModuleContext) 92 | 93 | 94 | @pytest.mark.pin_map("codemoduleapi.pinmap") 95 | @code_module 96 | def test_function_does_not_convert(standalone_tsm_context): 97 | assert isinstance(standalone_tsm_context, SemiconductorModuleContext) 98 | -------------------------------------------------------------------------------- /tests/test_custom_instruments.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from nitsm.codemoduleapi import SemiconductorModuleContext 3 | from nitsm.pinquerycontexts import PinQueryContext 4 | 5 | 6 | @pytest.fixture 7 | def simulated_custom_instrument_sessions(standalone_tsm_context): 8 | (instrument_names, channel_group_ids, _) = standalone_tsm_context.get_custom_instrument_names( 9 | TestCustomInstruments.pin_map_instrument_type_id 10 | ) 11 | sessions = tuple(range(len(instrument_names))) 12 | for instrument_name, channel_group_id, session in zip( 13 | instrument_names, channel_group_ids, sessions 14 | ): 15 | standalone_tsm_context.set_custom_session( 16 | TestCustomInstruments.pin_map_instrument_type_id, 17 | instrument_name, 18 | channel_group_id, 19 | session, 20 | ) 21 | return sessions 22 | 23 | 24 | @pytest.mark.pin_map("custom_instruments.pinmap") 25 | class TestCustomInstruments: 26 | pin_map_instruments = ["PXI0::16"] 27 | pin_map_dut_pins = ["DUTPin1"] 28 | pin_map_system_pins = ["SystemPin1"] 29 | pin_map_instrument_type_id = "Relay1_Id" 30 | 31 | def test_get_custom_instrument_names(self, standalone_tsm_context: SemiconductorModuleContext): 32 | ( 33 | instrument_names, 34 | channel_group_ids, 35 | channel_lists, 36 | ) = standalone_tsm_context.get_custom_instrument_names(self.pin_map_instrument_type_id) 37 | assert isinstance(instrument_names, tuple) 38 | assert isinstance(channel_group_ids, tuple) 39 | assert isinstance(channel_lists, tuple) 40 | assert len(instrument_names) == len(self.pin_map_instruments) 41 | assert len(instrument_names) == len(channel_group_ids) 42 | assert len(instrument_names) == len(channel_lists) 43 | for instrument_name, channel_group_id, channel_list in zip( 44 | instrument_names, channel_group_ids, channel_lists 45 | ): 46 | assert isinstance(instrument_name, str) 47 | assert isinstance(channel_group_id, str) 48 | assert isinstance(channel_list, str) 49 | assert instrument_name in self.pin_map_instruments 50 | 51 | def test_set_custom_session(self, standalone_tsm_context: SemiconductorModuleContext): 52 | ( 53 | instrument_names, 54 | channel_group_ids, 55 | _, 56 | ) = standalone_tsm_context.get_custom_instrument_names(self.pin_map_instrument_type_id) 57 | sessions = tuple(range(len(instrument_names))) 58 | for instrument_name, channel_group_id, session in zip( 59 | instrument_names, channel_group_ids, sessions 60 | ): 61 | standalone_tsm_context.set_custom_session( 62 | self.pin_map_instrument_type_id, instrument_name, channel_group_id, session 63 | ) 64 | assert SemiconductorModuleContext._sessions[id(session)] is session 65 | 66 | def test_get_all_custom_sessions( 67 | self, 68 | standalone_tsm_context: SemiconductorModuleContext, 69 | simulated_custom_instrument_sessions, 70 | ): 71 | ( 72 | queried_sessions, 73 | queried_channel_group_ids, 74 | queried_channel_lists, 75 | ) = standalone_tsm_context.get_all_custom_sessions(self.pin_map_instrument_type_id) 76 | assert isinstance(queried_sessions, tuple) 77 | assert isinstance(queried_channel_group_ids, tuple) 78 | assert isinstance(queried_channel_lists, tuple) 79 | assert len(queried_sessions) == len(simulated_custom_instrument_sessions) 80 | assert len(queried_sessions) == len(queried_channel_group_ids) 81 | assert len(queried_sessions) == len(queried_channel_lists) 82 | for queried_session, queried_channel_group_id, queried_channel_list in zip( 83 | queried_sessions, queried_channel_group_ids, queried_channel_lists 84 | ): 85 | assert isinstance(queried_session, int) 86 | assert isinstance(queried_channel_group_id, str) 87 | assert isinstance(queried_channel_list, str) 88 | assert queried_session in simulated_custom_instrument_sessions 89 | 90 | def test_pins_to_custom_session_single_pin( 91 | self, 92 | standalone_tsm_context: SemiconductorModuleContext, 93 | simulated_custom_instrument_sessions, 94 | ): 95 | ( 96 | pin_query_context, 97 | queried_session, 98 | queried_channel_group_id, 99 | queried_channel_list, 100 | ) = standalone_tsm_context.pins_to_custom_session( 101 | self.pin_map_instrument_type_id, "SystemPin1" 102 | ) 103 | assert isinstance(pin_query_context, PinQueryContext) 104 | assert isinstance(queried_session, int) 105 | assert isinstance(queried_channel_group_id, str) 106 | assert isinstance(queried_channel_list, str) 107 | assert queried_session in simulated_custom_instrument_sessions 108 | 109 | def test_pins_to_custom_session_multiple_pins( 110 | self, 111 | standalone_tsm_context: SemiconductorModuleContext, 112 | simulated_custom_instrument_sessions, 113 | ): 114 | all_pins = self.pin_map_dut_pins + self.pin_map_system_pins 115 | ( 116 | pin_query_context, 117 | queried_session, 118 | queried_channel_group_id, 119 | queried_channel_list, 120 | ) = standalone_tsm_context.pins_to_custom_session(self.pin_map_instrument_type_id, all_pins) 121 | assert isinstance(pin_query_context, PinQueryContext) 122 | assert isinstance(queried_session, int) 123 | assert isinstance(queried_channel_group_id, str) 124 | assert isinstance(queried_channel_list, str) 125 | assert queried_session in simulated_custom_instrument_sessions 126 | 127 | def test_pins_to_custom_sessions_single_pin( 128 | self, 129 | standalone_tsm_context: SemiconductorModuleContext, 130 | simulated_custom_instrument_sessions, 131 | ): 132 | ( 133 | pin_query_context, 134 | queried_sessions, 135 | queried_channel_group_ids, 136 | queried_channel_lists, 137 | ) = standalone_tsm_context.pins_to_custom_sessions( 138 | self.pin_map_instrument_type_id, "PinGroup1" 139 | ) 140 | assert isinstance(pin_query_context, PinQueryContext) 141 | assert isinstance(queried_sessions, tuple) 142 | assert isinstance(queried_channel_group_ids, tuple) 143 | assert isinstance(queried_channel_lists, tuple) 144 | assert len(queried_sessions) == len(simulated_custom_instrument_sessions) 145 | assert len(queried_sessions) == len(queried_channel_group_ids) 146 | assert len(queried_sessions) == len(queried_channel_lists) 147 | for queried_session, queried_channel_group_id, queried_channel_list in zip( 148 | queried_sessions, queried_channel_group_ids, queried_channel_lists 149 | ): 150 | assert isinstance(queried_session, int) 151 | assert isinstance(queried_channel_group_id, str) 152 | assert isinstance(queried_channel_list, str) 153 | assert queried_session in simulated_custom_instrument_sessions 154 | 155 | def test_pins_to_custom_sessions_multiple_pin( 156 | self, 157 | standalone_tsm_context: SemiconductorModuleContext, 158 | simulated_custom_instrument_sessions, 159 | ): 160 | all_pins = self.pin_map_dut_pins + self.pin_map_system_pins 161 | ( 162 | pin_query_context, 163 | queried_sessions, 164 | queried_channel_group_ids, 165 | queried_channel_lists, 166 | ) = standalone_tsm_context.pins_to_custom_sessions( 167 | self.pin_map_instrument_type_id, all_pins 168 | ) 169 | assert isinstance(pin_query_context, PinQueryContext) 170 | assert isinstance(queried_sessions, tuple) 171 | assert isinstance(queried_channel_group_ids, tuple) 172 | assert isinstance(queried_channel_lists, tuple) 173 | assert len(queried_sessions) == len(simulated_custom_instrument_sessions) 174 | assert len(queried_sessions) == len(queried_channel_group_ids) 175 | assert len(queried_sessions) == len(queried_channel_lists) 176 | for queried_session, queried_channel_group_id, queried_channel_list in zip( 177 | queried_sessions, queried_channel_group_ids, queried_channel_lists 178 | ): 179 | assert isinstance(queried_session, int) 180 | assert isinstance(queried_channel_group_id, str) 181 | assert isinstance(queried_channel_list, str) 182 | assert queried_session in simulated_custom_instrument_sessions 183 | -------------------------------------------------------------------------------- /tests/test_general_and_advanced.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from nitsm.codemoduleapi import Capability, InstrumentTypeIdConstants 3 | 4 | 5 | @pytest.mark.pin_map("general_and_advanced.pinmap") 6 | class TestGeneralAndAdvanced: 7 | pin_map_dut_pins = ["DUTPin1"] 8 | pin_map_system_pins = ["SystemPin1"] 9 | 10 | def test_get_pin_names(self, standalone_tsm_context): 11 | queried_dut_pins, queried_system_pins = standalone_tsm_context.get_pin_names( 12 | InstrumentTypeIdConstants.ANY, Capability.ALL 13 | ) 14 | assert isinstance(queried_dut_pins, tuple) 15 | assert isinstance(queried_system_pins, tuple) 16 | assert len(queried_dut_pins) == len(self.pin_map_dut_pins) 17 | assert len(queried_system_pins) == len(self.pin_map_system_pins) 18 | for dut_pin in queried_dut_pins: 19 | assert isinstance(dut_pin, str) 20 | assert dut_pin in self.pin_map_dut_pins 21 | for system_pin in queried_system_pins: 22 | assert isinstance(system_pin, str) 23 | assert system_pin in self.pin_map_system_pins 24 | 25 | def test_filter_pins_by_instrument_type(self, standalone_tsm_context): 26 | filtered_pins = standalone_tsm_context.filter_pins_by_instrument_type( 27 | self.pin_map_dut_pins, "CustomInstrumentTypeId1", Capability.ALL 28 | ) 29 | assert isinstance(filtered_pins, tuple) 30 | assert len(filtered_pins) == len(self.pin_map_dut_pins) 31 | for filtered_pin in filtered_pins: 32 | assert isinstance(filtered_pin, str) 33 | assert filtered_pin in self.pin_map_dut_pins 34 | 35 | @pytest.mark.parametrize("pin_groups", ("PinGroup1", ["PinGroup1"])) 36 | def test_get_pins_in_pin_groups(self, standalone_tsm_context, pin_groups): 37 | pin_group_pins = self.pin_map_dut_pins + self.pin_map_system_pins 38 | queried_pins = standalone_tsm_context.get_pins_in_pin_groups(pin_groups) 39 | assert isinstance(queried_pins, tuple) 40 | assert len(queried_pins) == len(pin_group_pins) 41 | for queried_pin in queried_pins: 42 | assert isinstance(queried_pin, str) 43 | assert queried_pin in pin_group_pins 44 | -------------------------------------------------------------------------------- /tests/test_nidaqmx.py: -------------------------------------------------------------------------------- 1 | import nidaqmx 2 | import pytest 3 | from nitsm.codemoduleapi import SemiconductorModuleContext 4 | from nitsm.pinquerycontexts import PinQueryContext 5 | 6 | 7 | @pytest.fixture 8 | def simulated_nidaqmx_tasks(standalone_tsm_context): 9 | task_names, channel_lists = standalone_tsm_context.get_all_nidaqmx_task_names("") 10 | tasks = [nidaqmx.Task(tsk_name) for tsk_name in task_names] 11 | for task_name, task in zip(task_names, tasks): 12 | standalone_tsm_context.set_nidaqmx_task(task_name, task) 13 | yield tasks 14 | for task in tasks: 15 | task.close() 16 | 17 | 18 | @pytest.mark.pin_map("nidaqmx.pinmap") 19 | class TestNIDAQmx: 20 | pin_map_instruments = ["DAQmx1", "DAQmx2"] 21 | pin_map_dut_pins = ["DUTPin1", "DUTPin2"] 22 | pin_map_system_pins = ["SystemPin1"] 23 | 24 | def test_get_all_nidaqmx_task_names(self, standalone_tsm_context: SemiconductorModuleContext): 25 | task_names, channel_lists = standalone_tsm_context.get_all_nidaqmx_task_names("") 26 | assert isinstance(task_names, tuple) 27 | assert isinstance(channel_lists, tuple) 28 | assert len(task_names) == len(channel_lists) 29 | for task_name, channel_list in zip(task_names, channel_lists): 30 | assert isinstance(task_name, str) 31 | assert isinstance(channel_list, str) 32 | assert task_name in self.pin_map_instruments 33 | 34 | def test_set_nidaqmx_task(self, standalone_tsm_context: SemiconductorModuleContext): 35 | task_names, channel_lists = standalone_tsm_context.get_all_nidaqmx_task_names("") 36 | for task_name, channel_list in zip(task_names, channel_lists): 37 | with nidaqmx.Task(task_name) as task: 38 | standalone_tsm_context.set_nidaqmx_task(task_name, task) 39 | assert SemiconductorModuleContext._sessions[id(task)] is task 40 | 41 | def test_get_all_nidaqmx_tasks(self, standalone_tsm_context, simulated_nidaqmx_tasks): 42 | queried_tasks = standalone_tsm_context.get_all_nidaqmx_tasks("") 43 | assert isinstance(queried_tasks, tuple) 44 | assert len(queried_tasks) == len(simulated_nidaqmx_tasks) 45 | for queried_task in queried_tasks: 46 | assert isinstance(queried_task, nidaqmx.Task) 47 | assert queried_task in simulated_nidaqmx_tasks 48 | 49 | def test_pins_to_nidaqmx_task_single_pin(self, standalone_tsm_context, simulated_nidaqmx_tasks): 50 | ( 51 | pin_query_context, 52 | queried_task, 53 | queried_channel_list, 54 | ) = standalone_tsm_context.pins_to_nidaqmx_task("SystemPin1") 55 | assert isinstance(pin_query_context, PinQueryContext) 56 | assert isinstance(queried_task, nidaqmx.Task) 57 | assert isinstance(queried_channel_list, str) 58 | assert queried_task in simulated_nidaqmx_tasks 59 | 60 | def test_pins_to_nidaqmx_task_multiple_pins( 61 | self, standalone_tsm_context, simulated_nidaqmx_tasks 62 | ): 63 | ( 64 | pin_query_context, 65 | queried_task, 66 | queried_channel_list, 67 | ) = standalone_tsm_context.pins_to_nidaqmx_task(self.pin_map_dut_pins) 68 | assert isinstance(pin_query_context, PinQueryContext) 69 | assert isinstance(queried_task, nidaqmx.Task) 70 | assert isinstance(queried_channel_list, str) 71 | assert queried_task in simulated_nidaqmx_tasks 72 | 73 | def test_pins_to_nidaqmx_tasks_single_pin( 74 | self, standalone_tsm_context, simulated_nidaqmx_tasks 75 | ): 76 | ( 77 | pin_query_context, 78 | queried_tasks, 79 | queried_channel_lists, 80 | ) = standalone_tsm_context.pins_to_nidaqmx_tasks("PinGroup1") 81 | assert isinstance(pin_query_context, PinQueryContext) 82 | assert isinstance(queried_tasks, tuple) 83 | assert isinstance(queried_channel_lists, tuple) 84 | assert len(queried_tasks) == len(queried_channel_lists) 85 | for queried_task, queried_channel_list in zip(queried_tasks, queried_channel_lists): 86 | assert isinstance(queried_task, nidaqmx.Task) 87 | assert isinstance(queried_channel_list, str) 88 | assert queried_task in simulated_nidaqmx_tasks 89 | 90 | def test_pins_to_nidaqmx_tasks_multiple_pins( 91 | self, standalone_tsm_context, simulated_nidaqmx_tasks 92 | ): 93 | all_pins = self.pin_map_dut_pins + self.pin_map_system_pins 94 | ( 95 | pin_query_context, 96 | queried_tasks, 97 | queried_channel_lists, 98 | ) = standalone_tsm_context.pins_to_nidaqmx_tasks(all_pins) 99 | assert isinstance(pin_query_context, PinQueryContext) 100 | assert isinstance(queried_tasks, tuple) 101 | assert isinstance(queried_channel_lists, tuple) 102 | assert len(queried_tasks) == len(queried_channel_lists) 103 | for queried_task, queried_channel_list in zip(queried_tasks, queried_channel_lists): 104 | assert isinstance(queried_task, nidaqmx.Task) 105 | assert isinstance(queried_channel_list, str) 106 | assert queried_task in simulated_nidaqmx_tasks 107 | -------------------------------------------------------------------------------- /tests/test_nidcpower.py: -------------------------------------------------------------------------------- 1 | import nidcpower 2 | import pytest 3 | from nitsm.pinquerycontexts import PinQueryContext 4 | 5 | OPTIONS = {"Simulate": True, "DriverSetup": {"Model": "4162"}} 6 | 7 | 8 | @pytest.fixture 9 | def simulated_nidcpower_sessions(standalone_tsm_context): 10 | resource_strings = standalone_tsm_context.get_all_nidcpower_resource_strings() 11 | sessions = [ 12 | nidcpower.Session(resource_string, options=OPTIONS) for resource_string in resource_strings 13 | ] 14 | for resource_string, session in zip(resource_strings, sessions): 15 | standalone_tsm_context.set_nidcpower_session(resource_string, session) 16 | yield sessions 17 | for session in sessions: 18 | session.close() 19 | 20 | 21 | @pytest.mark.pin_map("nidcpower.pinmap") 22 | @pytest.mark.filterwarnings("ignore::DeprecationWarning") 23 | class TestNIDCPower: 24 | def test_get_all_nidcpower_resource_strings(self, standalone_tsm_context): 25 | resource_strings = standalone_tsm_context.get_all_nidcpower_resource_strings() 26 | assert isinstance(resource_strings, tuple) 27 | for resource_string in resource_strings: 28 | assert isinstance(resource_string, str) 29 | 30 | def test_set_nidcpower_session(self, standalone_tsm_context): 31 | resource_strings = standalone_tsm_context.get_all_nidcpower_resource_strings() 32 | for resource_string in resource_strings: 33 | with nidcpower.Session(resource_string, options=OPTIONS) as session: 34 | standalone_tsm_context.set_nidcpower_session(resource_string, session) 35 | assert standalone_tsm_context._sessions[id(session)] is session 36 | 37 | def test_get_all_nidcpower_sessions(self, standalone_tsm_context, simulated_nidcpower_sessions): 38 | queried_sessions = standalone_tsm_context.get_all_nidcpower_sessions() 39 | assert isinstance(queried_sessions, tuple) 40 | assert len(queried_sessions) == len(simulated_nidcpower_sessions) 41 | for queried_session in queried_sessions: 42 | assert isinstance(queried_session, nidcpower.Session) 43 | assert queried_session in simulated_nidcpower_sessions 44 | 45 | def test_pins_to_nidcpower_session_single_pin( 46 | self, standalone_tsm_context, simulated_nidcpower_sessions 47 | ): 48 | ( 49 | pin_query_context, 50 | queried_session, 51 | queried_channel_string, 52 | ) = standalone_tsm_context.pins_to_nidcpower_session("SystemPin1") 53 | assert isinstance(pin_query_context, PinQueryContext) 54 | assert isinstance(queried_session, nidcpower.Session) 55 | assert isinstance(queried_channel_string, str) 56 | assert queried_session in simulated_nidcpower_sessions 57 | 58 | def test_pins_to_nidcpower_session_multiple_pins( 59 | self, standalone_tsm_context, simulated_nidcpower_sessions 60 | ): 61 | ( 62 | pin_query_context, 63 | queried_session, 64 | queried_channel_string, 65 | ) = standalone_tsm_context.pins_to_nidcpower_session(["DUTPin1", "DUTPin2"]) 66 | assert isinstance(pin_query_context, PinQueryContext) 67 | assert isinstance(queried_session, nidcpower.Session) 68 | assert isinstance(queried_channel_string, str) 69 | assert queried_session in simulated_nidcpower_sessions 70 | 71 | def test_pins_to_nidcpower_sessions_single_pin( 72 | self, standalone_tsm_context, simulated_nidcpower_sessions 73 | ): 74 | ( 75 | pin_query_context, 76 | queried_sessions, 77 | queried_channel_strings, 78 | ) = standalone_tsm_context.pins_to_nidcpower_sessions("PinGroup1") 79 | assert isinstance(pin_query_context, PinQueryContext) 80 | assert isinstance(queried_sessions, tuple) 81 | assert isinstance(queried_channel_strings, tuple) 82 | assert len(queried_sessions) == len(queried_channel_strings) 83 | for queried_session, queried_channel_string in zip( 84 | queried_sessions, queried_channel_strings 85 | ): 86 | assert isinstance(queried_session, nidcpower.Session) 87 | assert isinstance(queried_channel_string, str) 88 | assert queried_session in simulated_nidcpower_sessions 89 | 90 | def test_pins_to_nidcpower_sessions_multiple_pins( 91 | self, standalone_tsm_context, simulated_nidcpower_sessions 92 | ): 93 | ( 94 | pin_query_context, 95 | queried_sessions, 96 | queried_channel_strings, 97 | ) = standalone_tsm_context.pins_to_nidcpower_sessions(["DUTPin1", "DUTPin3"]) 98 | assert isinstance(pin_query_context, PinQueryContext) 99 | assert isinstance(queried_sessions, tuple) 100 | assert isinstance(queried_channel_strings, tuple) 101 | assert len(queried_sessions) == len(queried_channel_strings) 102 | for queried_session, queried_channel_string in zip( 103 | queried_sessions, queried_channel_strings 104 | ): 105 | assert isinstance(queried_session, nidcpower.Session) 106 | assert isinstance(queried_channel_string, str) 107 | assert queried_session in simulated_nidcpower_sessions 108 | -------------------------------------------------------------------------------- /tests/test_nidigital.py: -------------------------------------------------------------------------------- 1 | import os.path 2 | import nidigital 3 | import pytest 4 | from nitsm.pinquerycontexts import PinQueryContext 5 | 6 | 7 | @pytest.fixture 8 | def simulated_nidigital_sessions(standalone_tsm_context): 9 | instrument_names = standalone_tsm_context.get_all_nidigital_instrument_names() 10 | sessions = [ 11 | nidigital.Session( 12 | instrument_name, options={"Simulate": True, "driver_setup": {"Model": "6570"}} 13 | ) 14 | for instrument_name in instrument_names 15 | ] 16 | for instrument_name, session in zip(instrument_names, sessions): 17 | standalone_tsm_context.set_nidigital_session(instrument_name, session) 18 | yield sessions 19 | for session in sessions: 20 | session.close() 21 | 22 | 23 | @pytest.mark.pin_map("nidigital.pinmap") 24 | class TestNIDigital: 25 | pin_map_instruments = ["DigitalPattern1", "DigitalPattern2"] 26 | pin_map_dut_pins = ["DUTPin1", "DUTPin2"] 27 | pin_map_system_pins = ["SystemPin1"] 28 | pin_map_file_path = os.path.join(os.path.dirname(__file__), "nidigital.pinmap") 29 | 30 | def test_get_all_nidigital_instrument_names(self, standalone_tsm_context): 31 | instrument_names = standalone_tsm_context.get_all_nidigital_instrument_names() 32 | assert isinstance(instrument_names, tuple) 33 | assert len(instrument_names) == len(self.pin_map_instruments) 34 | for instrument_name in instrument_names: 35 | assert isinstance(instrument_name, str) 36 | assert instrument_name in self.pin_map_instruments 37 | 38 | def test_set_nidigital_session(self, standalone_tsm_context): 39 | instrument_names = standalone_tsm_context.get_all_nidigital_instrument_names() 40 | for instrument_name in instrument_names: 41 | with nidigital.Session( 42 | instrument_name, options={"Simulate": True, "driver_setup": {"Model": "6570"}} 43 | ) as session: 44 | standalone_tsm_context.set_nidigital_session(instrument_name, session) 45 | assert standalone_tsm_context._sessions[id(session)] is session 46 | 47 | def test_get_all_nidigital_sessions(self, standalone_tsm_context, simulated_nidigital_sessions): 48 | queried_sessions = standalone_tsm_context.get_all_nidigital_sessions() 49 | assert isinstance(queried_sessions, tuple) 50 | assert len(queried_sessions) == len(simulated_nidigital_sessions) 51 | for queried_session in queried_sessions: 52 | assert isinstance(queried_session, nidigital.Session) 53 | assert queried_session in simulated_nidigital_sessions 54 | 55 | def test_pins_to_nidigital_session_for_ppmu_single_pin( 56 | self, standalone_tsm_context, simulated_nidigital_sessions 57 | ): 58 | ( 59 | pin_query_context, 60 | queried_session, 61 | pin_set_string, 62 | ) = standalone_tsm_context.pins_to_nidigital_session_for_ppmu("SystemPin1") 63 | assert isinstance(pin_query_context, PinQueryContext) 64 | assert isinstance(queried_session, nidigital.Session) 65 | assert isinstance(pin_set_string, str) 66 | assert queried_session in simulated_nidigital_sessions 67 | 68 | def test_pins_to_nidigital_session_for_pattern_single_pin( 69 | self, standalone_tsm_context, simulated_nidigital_sessions 70 | ): 71 | ( 72 | pin_query_context, 73 | queried_session, 74 | site_list, 75 | ) = standalone_tsm_context.pins_to_nidigital_session_for_pattern("SystemPin1") 76 | assert isinstance(pin_query_context, PinQueryContext) 77 | assert isinstance(queried_session, nidigital.Session) 78 | assert isinstance(site_list, str) 79 | assert queried_session in simulated_nidigital_sessions 80 | 81 | def test_pins_to_nidigital_session_for_ppmu_multiple_pins( 82 | self, standalone_tsm_context, simulated_nidigital_sessions 83 | ): 84 | ( 85 | pin_query_context, 86 | queried_session, 87 | pin_set_string, 88 | ) = standalone_tsm_context.pins_to_nidigital_session_for_ppmu(self.pin_map_dut_pins) 89 | assert isinstance(pin_query_context, PinQueryContext) 90 | assert isinstance(queried_session, nidigital.Session) 91 | assert isinstance(pin_set_string, str) 92 | assert queried_session in simulated_nidigital_sessions 93 | 94 | def test_pins_to_nidigital_session_for_pattern_multiple_pins( 95 | self, standalone_tsm_context, simulated_nidigital_sessions 96 | ): 97 | ( 98 | pin_query_context, 99 | queried_session, 100 | site_list, 101 | ) = standalone_tsm_context.pins_to_nidigital_session_for_pattern(self.pin_map_dut_pins) 102 | assert isinstance(pin_query_context, PinQueryContext) 103 | assert isinstance(queried_session, nidigital.Session) 104 | assert isinstance(site_list, str) 105 | assert queried_session in simulated_nidigital_sessions 106 | 107 | def test_pins_to_nidigital_sessions_for_ppmu_single_pin( 108 | self, standalone_tsm_context, simulated_nidigital_sessions 109 | ): 110 | ( 111 | pin_query_context, 112 | queried_sessions, 113 | pin_set_strings, 114 | ) = standalone_tsm_context.pins_to_nidigital_sessions_for_ppmu("PinGroup1") 115 | assert isinstance(pin_query_context, PinQueryContext) 116 | assert isinstance(queried_sessions, tuple) 117 | assert isinstance(pin_set_strings, tuple) 118 | assert len(queried_sessions) == len(simulated_nidigital_sessions) 119 | assert len(queried_sessions) == len(pin_set_strings) 120 | for queried_session, pin_set_string in zip(queried_sessions, pin_set_strings): 121 | assert isinstance(queried_session, nidigital.Session) 122 | assert isinstance(pin_set_string, str) 123 | assert queried_session in simulated_nidigital_sessions 124 | 125 | def test_pins_to_nidigital_sessions_for_pattern_single_pin( 126 | self, standalone_tsm_context, simulated_nidigital_sessions 127 | ): 128 | ( 129 | pin_query_context, 130 | queried_sessions, 131 | site_lists, 132 | ) = standalone_tsm_context.pins_to_nidigital_sessions_for_pattern("PinGroup1") 133 | assert isinstance(pin_query_context, PinQueryContext) 134 | assert isinstance(queried_sessions, tuple) 135 | assert isinstance(site_lists, tuple) 136 | assert len(queried_sessions) == len(simulated_nidigital_sessions) 137 | assert len(queried_sessions) == len(site_lists) 138 | for queried_session, site_list in zip(queried_sessions, site_lists): 139 | assert isinstance(queried_session, nidigital.Session) 140 | assert isinstance(site_list, str) 141 | assert queried_session in simulated_nidigital_sessions 142 | 143 | def test_pins_to_nidigital_sessions_for_ppmu_multiple_pins( 144 | self, standalone_tsm_context, simulated_nidigital_sessions 145 | ): 146 | all_pins = self.pin_map_dut_pins + self.pin_map_system_pins 147 | ( 148 | pin_query_context, 149 | queried_sessions, 150 | pin_set_strings, 151 | ) = standalone_tsm_context.pins_to_nidigital_sessions_for_ppmu(all_pins) 152 | assert isinstance(pin_query_context, PinQueryContext) 153 | assert isinstance(queried_sessions, tuple) 154 | assert isinstance(pin_set_strings, tuple) 155 | assert len(queried_sessions) == len(simulated_nidigital_sessions) 156 | assert len(queried_sessions) == len(pin_set_strings) 157 | for queried_session, pin_set_string in zip(queried_sessions, pin_set_strings): 158 | assert isinstance(queried_session, nidigital.Session) 159 | assert isinstance(pin_set_string, str) 160 | assert queried_session in simulated_nidigital_sessions 161 | 162 | def test_pins_to_nidigital_sessions_for_pattern_multiple_pins( 163 | self, standalone_tsm_context, simulated_nidigital_sessions 164 | ): 165 | all_pins = self.pin_map_dut_pins + self.pin_map_system_pins 166 | ( 167 | pin_query_context, 168 | queried_sessions, 169 | site_lists, 170 | ) = standalone_tsm_context.pins_to_nidigital_sessions_for_pattern(all_pins) 171 | assert isinstance(pin_query_context, PinQueryContext) 172 | assert isinstance(queried_sessions, tuple) 173 | assert isinstance(site_lists, tuple) 174 | assert len(queried_sessions) == len(simulated_nidigital_sessions) 175 | assert len(queried_sessions) == len(site_lists) 176 | for queried_session, site_list in zip(queried_sessions, site_lists): 177 | assert isinstance(queried_session, nidigital.Session) 178 | assert isinstance(site_list, str) 179 | assert queried_session in simulated_nidigital_sessions 180 | 181 | def test_pin_map_file_path(self, standalone_tsm_context): 182 | assert isinstance(standalone_tsm_context.pin_map_file_path, str) 183 | assert standalone_tsm_context.pin_map_file_path == self.pin_map_file_path 184 | 185 | # not implemented in standalone tsm and therefore can't be tested: 186 | # nidigital_project_specifications_file_paths 187 | # nidigital_project_levels_file_paths 188 | # nidigital_project_timing_file_paths 189 | # nidigital_project_pattern_file_paths 190 | # nidigital_project_source_waveform_file_paths 191 | # nidigital_project_capture_waveform_file_paths 192 | -------------------------------------------------------------------------------- /tests/test_nidmm.py: -------------------------------------------------------------------------------- 1 | import nidmm 2 | import pytest 3 | from nitsm.codemoduleapi import SemiconductorModuleContext 4 | from nitsm.pinquerycontexts import PinQueryContext 5 | 6 | 7 | @pytest.fixture 8 | def simulated_nidmm_sessions(standalone_tsm_context): 9 | instrument_names = standalone_tsm_context.get_all_nidmm_instrument_names() 10 | sessions = [ 11 | nidmm.Session(instrument_name, options={"Simulate": True}) 12 | for instrument_name in instrument_names 13 | ] 14 | for instrument_name, session in zip(instrument_names, sessions): 15 | standalone_tsm_context.set_nidmm_session(instrument_name, session) 16 | yield sessions 17 | for session in sessions: 18 | session.close() 19 | 20 | 21 | @pytest.mark.pin_map("nidmm.pinmap") 22 | class TestNIDMM: 23 | pin_map_instruments = ["DMM1", "DMM2", "DMM3"] 24 | pin_map_dut_pins = ["DUTPin1"] 25 | pin_map_system_pins = ["SystemPin1"] 26 | 27 | def test_get_all_nidmm_instrument_names( 28 | self, standalone_tsm_context: SemiconductorModuleContext 29 | ): 30 | instrument_names = standalone_tsm_context.get_all_nidmm_instrument_names() 31 | assert isinstance(instrument_names, tuple) 32 | assert len(instrument_names) == len(self.pin_map_instruments) 33 | for instrument_name in instrument_names: 34 | assert isinstance(instrument_name, str) 35 | assert instrument_name in self.pin_map_instruments 36 | 37 | def test_set_nidmm_session(self, standalone_tsm_context: SemiconductorModuleContext): 38 | instrument_names = standalone_tsm_context.get_all_nidmm_instrument_names() 39 | for instrument_name in instrument_names: 40 | with nidmm.Session(instrument_name, options={"Simulate": True}) as session: 41 | standalone_tsm_context.set_nidmm_session(instrument_name, session) 42 | assert SemiconductorModuleContext._sessions[id(session)] is session 43 | 44 | def test_get_all_nidmm_sessions( 45 | self, standalone_tsm_context: SemiconductorModuleContext, simulated_nidmm_sessions 46 | ): 47 | queried_sessions = standalone_tsm_context.get_all_nidmm_sessions() 48 | assert isinstance(queried_sessions, tuple) 49 | assert len(queried_sessions) == len(simulated_nidmm_sessions) 50 | for queried_session in queried_sessions: 51 | assert isinstance(queried_session, nidmm.Session) 52 | assert queried_session in simulated_nidmm_sessions 53 | 54 | def test_pin_to_nidmm_session( 55 | self, standalone_tsm_context: SemiconductorModuleContext, simulated_nidmm_sessions 56 | ): 57 | pin_query_context, queried_session = standalone_tsm_context.pin_to_nidmm_session( 58 | "SystemPin1" 59 | ) 60 | assert isinstance(pin_query_context, PinQueryContext) 61 | assert isinstance(queried_session, nidmm.Session) 62 | assert queried_session in simulated_nidmm_sessions 63 | 64 | def test_pins_to_nidmm_sessions_single_pin( 65 | self, standalone_tsm_context: SemiconductorModuleContext, simulated_nidmm_sessions 66 | ): 67 | pin_query_context, queried_sessions = standalone_tsm_context.pins_to_nidmm_sessions( 68 | "PinGroup1" 69 | ) 70 | assert isinstance(pin_query_context, PinQueryContext) 71 | assert isinstance(queried_sessions, tuple) 72 | for queried_session in queried_sessions: 73 | assert isinstance(queried_session, nidmm.Session) 74 | assert queried_session in simulated_nidmm_sessions 75 | 76 | def test_pins_to_nidmm_sessions_multiple_pins( 77 | self, standalone_tsm_context: SemiconductorModuleContext, simulated_nidmm_sessions 78 | ): 79 | all_pins = self.pin_map_dut_pins + self.pin_map_system_pins 80 | pin_query_context, queried_sessions = standalone_tsm_context.pins_to_nidmm_sessions( 81 | all_pins 82 | ) 83 | assert isinstance(pin_query_context, PinQueryContext) 84 | assert isinstance(queried_sessions, tuple) 85 | for queried_session in queried_sessions: 86 | assert isinstance(queried_session, nidmm.Session) 87 | assert queried_session in simulated_nidmm_sessions 88 | -------------------------------------------------------------------------------- /tests/test_nifgen.py: -------------------------------------------------------------------------------- 1 | import nifgen 2 | import pytest 3 | from nitsm.pinquerycontexts import PinQueryContext 4 | 5 | OPTIONS = {"Simulate": True, "DriverSetup": {"Model": "5442", "BoardType": "PXIe"}} 6 | 7 | 8 | @pytest.fixture 9 | def simulated_nifgen_sessions(standalone_tsm_context): 10 | instrument_names = standalone_tsm_context.get_all_nifgen_instrument_names() 11 | sessions = [ 12 | nifgen.Session(instrument_name, options=OPTIONS) for instrument_name in instrument_names 13 | ] 14 | for instrument_name, session in zip(instrument_names, sessions): 15 | standalone_tsm_context.set_nifgen_session(instrument_name, session) 16 | yield sessions 17 | for session in sessions: 18 | session.close() 19 | 20 | 21 | @pytest.mark.pin_map("nifgen.pinmap") 22 | class TestNIFGen: 23 | pin_map_instruments = ["FGen1", "FGen2"] 24 | pin_map_dut_pins = ["DUTPin1"] 25 | pin_map_system_pins = ["SystemPin1", "SystemPin2"] 26 | 27 | def test_get_all_nifgen_instrument_names(self, standalone_tsm_context): 28 | instrument_names = standalone_tsm_context.get_all_nifgen_instrument_names() 29 | assert isinstance(instrument_names, tuple) 30 | assert len(instrument_names) == len(self.pin_map_instruments) 31 | for instrument_name in instrument_names: 32 | assert isinstance(instrument_name, str) 33 | assert instrument_name in self.pin_map_instruments 34 | 35 | def test_set_nifgen_session(self, standalone_tsm_context): 36 | instrument_names = standalone_tsm_context.get_all_nifgen_instrument_names() 37 | for instrument_name in instrument_names: 38 | with nifgen.Session(instrument_name, options=OPTIONS) as session: 39 | standalone_tsm_context.set_nifgen_session(instrument_name, session) 40 | assert standalone_tsm_context._sessions[id(session)] is session 41 | 42 | def test_get_all_nifgen_sessions(self, standalone_tsm_context, simulated_nifgen_sessions): 43 | queried_sessions = standalone_tsm_context.get_all_nifgen_sessions() 44 | assert len(queried_sessions) == len(simulated_nifgen_sessions) 45 | for queried_session in queried_sessions: 46 | assert isinstance(queried_session, nifgen.Session) 47 | assert queried_session in simulated_nifgen_sessions 48 | 49 | def test_pin_to_nifgen_session_single_pin( 50 | self, standalone_tsm_context, simulated_nifgen_sessions 51 | ): 52 | ( 53 | pin_query_context, 54 | queried_session, 55 | queried_channel_list, 56 | ) = standalone_tsm_context.pins_to_nifgen_session("SystemPin1") 57 | assert isinstance(pin_query_context, PinQueryContext) 58 | assert isinstance(queried_session, nifgen.Session) 59 | assert isinstance(queried_channel_list, str) 60 | assert queried_session in simulated_nifgen_sessions 61 | 62 | def test_pins_to_nifgen_session_muliple_pins( 63 | self, standalone_tsm_context, simulated_nifgen_sessions 64 | ): 65 | ( 66 | pin_query_context, 67 | queried_session, 68 | queried_channel_list, 69 | ) = standalone_tsm_context.pins_to_nifgen_session(self.pin_map_system_pins) 70 | assert isinstance(pin_query_context, PinQueryContext) 71 | assert isinstance(queried_session, nifgen.Session) 72 | assert isinstance(queried_channel_list, str) 73 | assert queried_session in simulated_nifgen_sessions 74 | 75 | def test_pins_to_nifgen_sessions_single_pin( 76 | self, standalone_tsm_context, simulated_nifgen_sessions 77 | ): 78 | ( 79 | pin_query_context, 80 | queried_sessions, 81 | queried_channel_lists, 82 | ) = standalone_tsm_context.pins_to_nifgen_sessions("PinGroup1") 83 | assert isinstance(pin_query_context, PinQueryContext) 84 | assert isinstance(queried_sessions, tuple) 85 | assert isinstance(queried_channel_lists, tuple) 86 | assert len(queried_sessions) == len(simulated_nifgen_sessions) 87 | assert len(queried_sessions) == len(queried_channel_lists) 88 | for queried_session, queried_channel_list in zip(queried_sessions, queried_channel_lists): 89 | assert isinstance(queried_session, nifgen.Session) 90 | assert isinstance(queried_channel_list, str) 91 | assert queried_session in simulated_nifgen_sessions 92 | 93 | def test_pins_to_nifgen_sessions_multiple_pins( 94 | self, standalone_tsm_context, simulated_nifgen_sessions 95 | ): 96 | all_pins = self.pin_map_dut_pins + self.pin_map_system_pins 97 | ( 98 | pin_query_context, 99 | queried_sessions, 100 | queried_channel_lists, 101 | ) = standalone_tsm_context.pins_to_nifgen_sessions(all_pins) 102 | assert isinstance(pin_query_context, PinQueryContext) 103 | assert isinstance(queried_sessions, tuple) 104 | assert isinstance(queried_channel_lists, tuple) 105 | assert len(queried_sessions) == len(simulated_nifgen_sessions) 106 | assert len(queried_sessions) == len(queried_channel_lists) 107 | for queried_session, queried_channel_list in zip(queried_sessions, queried_channel_lists): 108 | assert isinstance(queried_session, nifgen.Session) 109 | assert isinstance(queried_channel_list, str) 110 | assert queried_session in simulated_nifgen_sessions 111 | -------------------------------------------------------------------------------- /tests/test_nirelaydriver.py: -------------------------------------------------------------------------------- 1 | import niswitch 2 | import pytest 3 | from niswitch.enums import RelayAction, RelayPosition 4 | from nitsm.codemoduleapi import SemiconductorModuleContext 5 | 6 | 7 | @pytest.fixture 8 | def simulated_niswitch_sessions(standalone_tsm_context): 9 | instrument_names = standalone_tsm_context.get_relay_driver_module_names() 10 | sessions = [ 11 | niswitch.Session("", topology="2567/Independent", simulate=True) for _ in instrument_names 12 | ] # resource name must be empty string to simulate an niswitch 13 | for instrument_name, session in zip(instrument_names, sessions): 14 | standalone_tsm_context.set_relay_driver_niswitch_session(instrument_name, session) 15 | yield sessions 16 | for session in sessions: 17 | session.close() 18 | 19 | 20 | def assert_relay_positions(standalone_tsm_context, pin_map_relays, relay_position): 21 | ( 22 | niswitch_sessions, 23 | niswitch_relay_names, 24 | ) = standalone_tsm_context.relays_to_relay_driver_niswitch_sessions(pin_map_relays) 25 | for niswitch_session, relay_names in zip(niswitch_sessions, niswitch_relay_names): 26 | for relay_name in relay_names.split(","): 27 | assert niswitch_session.get_relay_position(relay_name) == relay_position 28 | 29 | 30 | @pytest.mark.pin_map("nirelaydriver.pinmap") 31 | class TestNIRelayDriver: 32 | pin_map_instruments = ["RelayDriver1", "RelayDriver2"] 33 | pin_map_site_relays = ["SiteRelay1", "SiteRelay2"] 34 | pin_map_system_relays = ["SystemRelay1"] 35 | 36 | def test_get_relay_driver_module_names( 37 | self, standalone_tsm_context: SemiconductorModuleContext 38 | ): 39 | instrument_names = standalone_tsm_context.get_relay_driver_module_names() 40 | assert isinstance(instrument_names, tuple) 41 | assert len(instrument_names) == len(self.pin_map_instruments) 42 | for instrument_name in instrument_names: 43 | assert isinstance(instrument_name, str) 44 | assert instrument_name in self.pin_map_instruments 45 | 46 | def test_get_relay_names(self, standalone_tsm_context: SemiconductorModuleContext): 47 | site_relays, system_relays = standalone_tsm_context.get_relay_names() 48 | assert isinstance(site_relays, tuple) 49 | assert isinstance(system_relays, tuple) 50 | for site_relay in site_relays: 51 | assert isinstance(site_relay, str) 52 | assert site_relay in self.pin_map_site_relays 53 | for system_relay in system_relays: 54 | assert isinstance(system_relay, str) 55 | assert system_relay in self.pin_map_system_relays 56 | 57 | def test_set_relay_driver_niswitch_session( 58 | self, standalone_tsm_context: SemiconductorModuleContext 59 | ): 60 | instrument_names = standalone_tsm_context.get_relay_driver_module_names() 61 | for instrument_name in instrument_names: 62 | with niswitch.Session("", topology="2567/Independent", simulate=True) as session: 63 | standalone_tsm_context.set_relay_driver_niswitch_session(instrument_name, session) 64 | assert SemiconductorModuleContext._sessions[id(session)] is session 65 | 66 | def test_get_all_relay_driver_niswitch_sessions( 67 | self, standalone_tsm_context: SemiconductorModuleContext, simulated_niswitch_sessions 68 | ): 69 | queried_niswitch_sessions = standalone_tsm_context.get_all_relay_driver_niswitch_sessions() 70 | assert isinstance(queried_niswitch_sessions, tuple) 71 | assert len(queried_niswitch_sessions) == len(simulated_niswitch_sessions) 72 | for queried_niswitch_session in queried_niswitch_sessions: 73 | assert isinstance(queried_niswitch_session, niswitch.Session) 74 | assert queried_niswitch_session in simulated_niswitch_sessions 75 | 76 | def test_relays_to_relay_driver_niswitch_session_single_relay( 77 | self, standalone_tsm_context: SemiconductorModuleContext, simulated_niswitch_sessions 78 | ): 79 | ( 80 | queried_niswitch_session, 81 | queried_niswitch_relay_names, 82 | ) = standalone_tsm_context.relays_to_relay_driver_niswitch_session("SystemRelay1") 83 | assert isinstance(queried_niswitch_session, niswitch.Session) 84 | assert isinstance(queried_niswitch_relay_names, str) 85 | assert queried_niswitch_session in simulated_niswitch_sessions 86 | 87 | def test_relays_to_relay_driver_niswitch_session_multiple_relays( 88 | self, standalone_tsm_context: SemiconductorModuleContext, simulated_niswitch_sessions 89 | ): 90 | ( 91 | queried_niswitch_session, 92 | queried_niswitch_relay_names, 93 | ) = standalone_tsm_context.relays_to_relay_driver_niswitch_session(self.pin_map_site_relays) 94 | assert isinstance(queried_niswitch_session, niswitch.Session) 95 | assert isinstance(queried_niswitch_relay_names, str) 96 | assert queried_niswitch_session in simulated_niswitch_sessions 97 | 98 | def test_relays_to_relay_driver_niswitch_sessions_single_relay( 99 | self, standalone_tsm_context: SemiconductorModuleContext, simulated_niswitch_sessions 100 | ): 101 | ( 102 | queried_niswitch_sessions, 103 | queried_niswitch_relay_names, 104 | ) = standalone_tsm_context.relays_to_relay_driver_niswitch_sessions("RelayGroup1") 105 | assert isinstance(queried_niswitch_sessions, tuple) 106 | assert isinstance(queried_niswitch_relay_names, tuple) 107 | assert len(queried_niswitch_sessions) == len(queried_niswitch_relay_names) 108 | for queried_niswitch_session, queried_relay_name in zip( 109 | queried_niswitch_sessions, queried_niswitch_relay_names 110 | ): 111 | assert isinstance(queried_niswitch_session, niswitch.Session) 112 | assert isinstance(queried_relay_name, str) 113 | assert queried_niswitch_session in simulated_niswitch_sessions 114 | 115 | def test_relays_to_relay_driver_niswitch_sessions_multiple_relays( 116 | self, standalone_tsm_context: SemiconductorModuleContext, simulated_niswitch_sessions 117 | ): 118 | all_relays = self.pin_map_site_relays + self.pin_map_system_relays 119 | ( 120 | queried_niswitch_sessions, 121 | queried_niswitch_relay_names, 122 | ) = standalone_tsm_context.relays_to_relay_driver_niswitch_sessions(all_relays) 123 | assert isinstance(queried_niswitch_sessions, tuple) 124 | assert isinstance(queried_niswitch_relay_names, tuple) 125 | assert len(queried_niswitch_sessions) == len(queried_niswitch_relay_names) 126 | for queried_niswitch_session, queried_relay_name in zip( 127 | queried_niswitch_sessions, queried_niswitch_relay_names 128 | ): 129 | assert isinstance(queried_niswitch_session, niswitch.Session) 130 | assert isinstance(queried_relay_name, str) 131 | assert queried_niswitch_session in simulated_niswitch_sessions 132 | 133 | @pytest.mark.usefixtures("simulated_niswitch_sessions") 134 | def test_apply_relay_configuration(self, standalone_tsm_context: SemiconductorModuleContext): 135 | standalone_tsm_context.apply_relay_configuration("RelayConfiguration1") 136 | assert_relay_positions( 137 | standalone_tsm_context, self.pin_map_site_relays, RelayPosition.CLOSED 138 | ) 139 | assert_relay_positions( 140 | standalone_tsm_context, self.pin_map_system_relays, RelayPosition.OPEN 141 | ) 142 | 143 | @pytest.mark.usefixtures("simulated_niswitch_sessions") 144 | def test_control_relays_single_action_open_system_relay( 145 | self, standalone_tsm_context: SemiconductorModuleContext 146 | ): 147 | ( 148 | niswitch_session, 149 | niswitch_relay_name, 150 | ) = standalone_tsm_context.relays_to_relay_driver_niswitch_session("SystemRelay1") 151 | standalone_tsm_context.control_relays("SystemRelay1", RelayAction.OPEN) 152 | assert niswitch_session.get_relay_position(niswitch_relay_name) == RelayPosition.OPEN 153 | 154 | @pytest.mark.usefixtures("simulated_niswitch_sessions") 155 | def test_control_relays_single_action_close_system_relay( 156 | self, standalone_tsm_context: SemiconductorModuleContext 157 | ): 158 | ( 159 | niswitch_session, 160 | niswitch_relay_name, 161 | ) = standalone_tsm_context.relays_to_relay_driver_niswitch_session("SystemRelay1") 162 | standalone_tsm_context.control_relays("SystemRelay1", RelayAction.CLOSE) 163 | assert niswitch_session.get_relay_position(niswitch_relay_name) == RelayPosition.CLOSED 164 | 165 | @pytest.mark.usefixtures("simulated_niswitch_sessions") 166 | def test_control_relays_single_action_open_all_site_relays( 167 | self, standalone_tsm_context: SemiconductorModuleContext 168 | ): 169 | standalone_tsm_context.control_relays(self.pin_map_site_relays, RelayAction.OPEN) 170 | assert_relay_positions(standalone_tsm_context, self.pin_map_site_relays, RelayPosition.OPEN) 171 | 172 | @pytest.mark.usefixtures("simulated_niswitch_sessions") 173 | def test_control_relays_single_action_close_all_site_relays( 174 | self, standalone_tsm_context: SemiconductorModuleContext 175 | ): 176 | standalone_tsm_context.control_relays(self.pin_map_site_relays, RelayAction.CLOSE) 177 | assert_relay_positions( 178 | standalone_tsm_context, self.pin_map_site_relays, RelayPosition.CLOSED 179 | ) 180 | 181 | @pytest.mark.usefixtures("simulated_niswitch_sessions") 182 | def test_control_relays_multiple_action_open_all_site_relays( 183 | self, standalone_tsm_context: SemiconductorModuleContext 184 | ): 185 | standalone_tsm_context.control_relays( 186 | self.pin_map_site_relays, [RelayAction.OPEN] * len(self.pin_map_site_relays) 187 | ) 188 | assert_relay_positions(standalone_tsm_context, self.pin_map_site_relays, RelayPosition.OPEN) 189 | 190 | @pytest.mark.usefixtures("simulated_niswitch_sessions") 191 | def test_control_relays_multiple_action_close_all_site_relays( 192 | self, standalone_tsm_context: SemiconductorModuleContext 193 | ): 194 | standalone_tsm_context.control_relays( 195 | self.pin_map_site_relays, [RelayAction.CLOSE] * len(self.pin_map_site_relays) 196 | ) 197 | assert_relay_positions( 198 | standalone_tsm_context, self.pin_map_site_relays, RelayPosition.CLOSED 199 | ) 200 | 201 | @pytest.mark.usefixtures("simulated_niswitch_sessions") 202 | def test_control_relays_multiple_action_mixed_site_relay_positions( 203 | self, standalone_tsm_context: SemiconductorModuleContext 204 | ): 205 | relay_actions = [ 206 | RelayAction.OPEN if i % 2 else RelayAction.CLOSE 207 | for i in range(len(self.pin_map_site_relays)) 208 | ] 209 | standalone_tsm_context.control_relays(self.pin_map_site_relays, relay_actions) 210 | for pin_map_site_relay, relay_action in zip(self.pin_map_site_relays, relay_actions): 211 | relay_position = ( 212 | RelayPosition.OPEN if relay_action == RelayAction.OPEN else RelayPosition.CLOSED 213 | ) 214 | assert_relay_positions(standalone_tsm_context, [pin_map_site_relay], relay_position) 215 | -------------------------------------------------------------------------------- /tests/test_niscope.py: -------------------------------------------------------------------------------- 1 | import niscope 2 | import pytest 3 | from nitsm.codemoduleapi import SemiconductorModuleContext 4 | from nitsm.pinquerycontexts import PinQueryContext 5 | 6 | 7 | @pytest.fixture 8 | def simulated_niscope_sessions(standalone_tsm_context): 9 | instrument_names = standalone_tsm_context.get_all_niscope_instrument_names() 10 | sessions = [ 11 | niscope.Session(instrument_name, options={"Simulate": True}) 12 | for instrument_name in instrument_names 13 | ] 14 | for instrument_name, session in zip(instrument_names, sessions): 15 | standalone_tsm_context.set_niscope_session(instrument_name, session) 16 | yield sessions 17 | for session in sessions: 18 | session.close() 19 | 20 | 21 | @pytest.mark.pin_map("niscope.pinmap") 22 | class TestNIScope: 23 | pin_map_instruments = ["Scope1,Scope2,Scope3"] 24 | pin_map_dut_pins = ["DUTPin1"] 25 | pin_map_system_pins = ["SystemPin1"] 26 | 27 | def test_get_all_niscope_instrument_names( 28 | self, standalone_tsm_context: SemiconductorModuleContext 29 | ): 30 | instrument_names = standalone_tsm_context.get_all_niscope_instrument_names() 31 | assert isinstance(instrument_names, tuple) 32 | assert len(instrument_names) == len(self.pin_map_instruments) 33 | for instrument_name in instrument_names: 34 | assert isinstance(instrument_name, str) 35 | assert instrument_name in self.pin_map_instruments 36 | 37 | def test_set_niscope_session(self, standalone_tsm_context: SemiconductorModuleContext): 38 | instrument_names = standalone_tsm_context.get_all_niscope_instrument_names() 39 | for instrument_name in instrument_names: 40 | with niscope.Session(instrument_name, options={"Simulate": True}) as session: 41 | standalone_tsm_context.set_niscope_session(instrument_name, session) 42 | assert SemiconductorModuleContext._sessions[id(session)] is session 43 | 44 | def test_get_all_niscope_sessions( 45 | self, standalone_tsm_context: SemiconductorModuleContext, simulated_niscope_sessions 46 | ): 47 | queried_sessions = standalone_tsm_context.get_all_niscope_sessions() 48 | assert isinstance(queried_sessions, tuple) 49 | assert len(queried_sessions) == len(simulated_niscope_sessions) 50 | for queried_session in queried_sessions: 51 | assert isinstance(queried_session, niscope.Session) 52 | assert queried_session in simulated_niscope_sessions 53 | 54 | def test_pins_to_niscope_session_single_pin( 55 | self, standalone_tsm_context: SemiconductorModuleContext, simulated_niscope_sessions 56 | ): 57 | ( 58 | pin_query_context, 59 | queried_session, 60 | queried_channel_list, 61 | ) = standalone_tsm_context.pins_to_niscope_session("SystemPin1") 62 | assert isinstance(pin_query_context, PinQueryContext) 63 | assert isinstance(queried_session, niscope.Session) 64 | assert isinstance(queried_channel_list, str) 65 | assert queried_session in simulated_niscope_sessions 66 | 67 | def test_pins_to_niscope_session_multiple_pins( 68 | self, standalone_tsm_context: SemiconductorModuleContext, simulated_niscope_sessions 69 | ): 70 | all_pins = self.pin_map_dut_pins + self.pin_map_system_pins 71 | ( 72 | pin_query_context, 73 | queried_session, 74 | queried_channel_list, 75 | ) = standalone_tsm_context.pins_to_niscope_session(all_pins) 76 | assert isinstance(pin_query_context, PinQueryContext) 77 | assert isinstance(queried_session, niscope.Session) 78 | assert isinstance(queried_channel_list, str) 79 | assert queried_session in simulated_niscope_sessions 80 | 81 | def test_pins_to_niscope_sessions_single_pin( 82 | self, standalone_tsm_context: SemiconductorModuleContext, simulated_niscope_sessions 83 | ): 84 | ( 85 | pin_query_context, 86 | queried_sessions, 87 | queried_channel_lists, 88 | ) = standalone_tsm_context.pins_to_niscope_sessions("PinGroup1") 89 | assert isinstance(pin_query_context, PinQueryContext) 90 | assert isinstance(queried_sessions, tuple) 91 | assert isinstance(queried_channel_lists, tuple) 92 | assert len(queried_sessions) == len(queried_channel_lists) 93 | for queried_session, queried_channel_list in zip(queried_sessions, queried_channel_lists): 94 | assert isinstance(queried_session, niscope.Session) 95 | assert isinstance(queried_channel_list, str) 96 | assert queried_session in simulated_niscope_sessions 97 | 98 | def test_pins_to_niscope_sessions_multiple_pins( 99 | self, standalone_tsm_context: SemiconductorModuleContext, simulated_niscope_sessions 100 | ): 101 | all_pins = self.pin_map_dut_pins + self.pin_map_system_pins 102 | ( 103 | pin_query_context, 104 | queried_sessions, 105 | queried_channel_lists, 106 | ) = standalone_tsm_context.pins_to_niscope_sessions(all_pins) 107 | assert isinstance(pin_query_context, PinQueryContext) 108 | assert isinstance(queried_sessions, tuple) 109 | assert isinstance(queried_channel_lists, tuple) 110 | assert len(queried_sessions) == len(queried_channel_lists) 111 | for queried_session, queried_channel_list in zip(queried_sessions, queried_channel_lists): 112 | assert isinstance(queried_session, niscope.Session) 113 | assert isinstance(queried_channel_list, str) 114 | assert queried_session in simulated_niscope_sessions 115 | -------------------------------------------------------------------------------- /tests/test_pinquerycontext.py: -------------------------------------------------------------------------------- 1 | from typing import TYPE_CHECKING 2 | import nidigital 3 | import pytest 4 | 5 | if TYPE_CHECKING: 6 | from nitsm.codemoduleapi import SemiconductorModuleContext 7 | 8 | 9 | @pytest.fixture 10 | def simulated_nidigital_sessions(standalone_tsm_context): 11 | instrument_names = standalone_tsm_context.get_all_nidigital_instrument_names() 12 | sessions = [ 13 | nidigital.Session( 14 | instrument_name, options={"Simulate": True, "driver_setup": {"Model": "6570"}} 15 | ) 16 | for instrument_name in instrument_names 17 | ] 18 | for instrument_name, session in zip(instrument_names, sessions): 19 | standalone_tsm_context.set_nidigital_session(instrument_name, session) 20 | yield sessions 21 | for session in sessions: 22 | session.close() 23 | 24 | 25 | @pytest.mark.pin_map("pinquerycontext.pinmap") 26 | class TestPinQueryContext: 27 | @pytest.mark.usefixtures("simulated_nidigital_sessions") 28 | def test_get_session_and_channel_index( 29 | self, standalone_tsm_context: "SemiconductorModuleContext" 30 | ): 31 | pin_query_context, _, _ = standalone_tsm_context.pins_to_nidigital_sessions_for_ppmu( 32 | ["DUTPin2", "DUTPin1"] 33 | ) 34 | session_index, channel_index = pin_query_context.get_session_and_channel_index(1, "DUTPin1") 35 | assert session_index == 1 36 | assert channel_index == 1 37 | -------------------------------------------------------------------------------- /tests/test_publish.py: -------------------------------------------------------------------------------- 1 | import random 2 | import pytest 3 | from nitsm.codemoduleapi import SemiconductorModuleContext 4 | 5 | 6 | @pytest.fixture 7 | def simulated_nidigital_sessions(standalone_tsm_context: SemiconductorModuleContext): 8 | instrument_names = standalone_tsm_context.get_all_nidigital_instrument_names() 9 | for instrument_name in instrument_names: 10 | fake_session = instrument_name 11 | standalone_tsm_context.set_nidigital_session(instrument_name, fake_session) 12 | 13 | 14 | @pytest.mark.pin_map("publish.pinmap") 15 | @pytest.mark.usefixtures("simulated_nidigital_sessions") 16 | class TestSinglePinScalar: 17 | _PIN = "SystemPin1" 18 | _NUM_SITES = 3 19 | 20 | @pytest.fixture 21 | def pin_query_context(self, standalone_tsm_context): 22 | pin_query_context, *_ = standalone_tsm_context.pins_to_nidigital_session_for_ppmu(self._PIN) 23 | return pin_query_context 24 | 25 | def test_publish_float_scalar(self, pin_query_context, published_data_reader): 26 | test_data = random.random() 27 | pin_query_context.publish(test_data) 28 | published_data = published_data_reader.get_and_clear_published_data() 29 | assert len(published_data) == self._NUM_SITES 30 | for published_data_point in published_data: 31 | assert published_data_point.double_value == test_data 32 | 33 | def test_publish_bool_scalar(self, pin_query_context, published_data_reader): 34 | pin_query_context.publish(True) 35 | published_data = published_data_reader.get_and_clear_published_data() 36 | assert len(published_data) == self._NUM_SITES 37 | for published_data_point in published_data: 38 | assert published_data_point.boolean_value 39 | 40 | 41 | @pytest.mark.pin_map("publish.pinmap") 42 | @pytest.mark.usefixtures("simulated_nidigital_sessions") 43 | class TestSinglePin1d: 44 | _PIN = "DUTPin1" 45 | _NUM_SITES = 3 46 | 47 | @pytest.fixture 48 | def pin_query_context(self, standalone_tsm_context): 49 | pin_query_context, *_ = standalone_tsm_context.pins_to_nidigital_session_for_ppmu(self._PIN) 50 | return pin_query_context 51 | 52 | def test_publish_float_1d(self, pin_query_context, published_data_reader): 53 | test_data = [random.random() for _ in range(self._NUM_SITES)] 54 | pin_query_context.publish(test_data) 55 | published_data = published_data_reader.get_and_clear_published_data() 56 | assert len(published_data) == len(test_data) 57 | for published_data_point, test_data_point in zip(published_data, test_data): 58 | assert published_data_point.double_value == test_data_point 59 | 60 | def test_publish_bool_1d(self, pin_query_context, published_data_reader): 61 | test_data = [bool(i % 2) for i in range(self._NUM_SITES)] 62 | pin_query_context.publish(test_data) 63 | published_data = published_data_reader.get_and_clear_published_data() 64 | assert len(published_data) == len(test_data) 65 | for published_data_point, test_data_point in zip(published_data, test_data): 66 | assert published_data_point.boolean_value == test_data_point 67 | 68 | def test_publish_pattern(self, standalone_tsm_context, published_data_reader): 69 | ( 70 | pin_query_context, 71 | session, 72 | site_list, 73 | ) = standalone_tsm_context.pins_to_nidigital_session_for_pattern(self._PIN) 74 | test_data = {1: False, 0: True, 2: True} # alternate True and False 75 | pin_query_context.publish_pattern_results(test_data) 76 | published_data = published_data_reader.get_and_clear_published_data() 77 | assert len(published_data) == len(test_data) 78 | for published_data_point, site_number in zip( 79 | published_data, standalone_tsm_context.site_numbers 80 | ): 81 | assert published_data_point.boolean_value == test_data[site_number] 82 | 83 | 84 | @pytest.mark.pin_map("publish.pinmap") 85 | @pytest.mark.usefixtures("simulated_nidigital_sessions") 86 | class TestSinglePin2d: 87 | _PIN = "DUTPin3" 88 | 89 | @pytest.fixture 90 | def pin_query_context(self, standalone_tsm_context): 91 | pin_query_context, *_ = standalone_tsm_context.pins_to_nidigital_sessions_for_ppmu( 92 | self._PIN 93 | ) 94 | return pin_query_context 95 | 96 | def test_publish_float_2d(self, pin_query_context, published_data_reader): 97 | # [[DigitalPattern1(ch6)], [DigitalPattern2(ch0), DigitalPattern2(ch1)]] 98 | test_data = [[1150.0], [1952.5, 60417]] 99 | pin_query_context.publish(test_data) 100 | published_data = published_data_reader.get_and_clear_published_data() 101 | flattened_test_data = [data_point for row in test_data for data_point in row] 102 | assert len(published_data) == len(flattened_test_data) 103 | for published_data_point, test_data_point in zip(published_data, flattened_test_data): 104 | assert published_data_point.double_value == test_data_point 105 | 106 | def test_publish_bool_2d(self, pin_query_context, published_data_reader): 107 | # [[DigitalPattern1(ch6)], [DigitalPattern2(ch0), DigitalPattern2(ch1)]] 108 | test_data = [[True], [False, True]] 109 | pin_query_context.publish(test_data) 110 | published_data = published_data_reader.get_and_clear_published_data() 111 | flattened_test_data = [data_point for row in test_data for data_point in row] 112 | assert len(published_data) == len(flattened_test_data) 113 | for published_data_point, test_data_point in zip(published_data, flattened_test_data): 114 | assert published_data_point.boolean_value == test_data_point 115 | 116 | def test_publish_pattern(self, standalone_tsm_context, published_data_reader): 117 | ( 118 | pin_query_context, 119 | sessions, 120 | site_lists, 121 | ) = standalone_tsm_context.pins_to_nidigital_sessions_for_pattern(self._PIN) 122 | expected_results = [True, False, True] # test data across sites [0, 1, 2] 123 | test_data = [{0: True}, {2: True, 1: False}] 124 | pin_query_context.publish_pattern_results(test_data) 125 | published_data = published_data_reader.get_and_clear_published_data() 126 | assert len(published_data) == len(expected_results) 127 | for published_data_point, expected_result in zip(published_data, expected_results): 128 | assert published_data_point.boolean_value == expected_result 129 | 130 | 131 | @pytest.mark.pin_map("publish.pinmap") 132 | @pytest.mark.usefixtures("simulated_nidigital_sessions") 133 | class TestMultiplePins1d: 134 | _PINS = ["DUTPin1", "DUTPin2"] 135 | _NUM_SITES = 3 136 | 137 | @pytest.fixture 138 | def pin_query_context(self, standalone_tsm_context): 139 | pin_query_context, *_ = standalone_tsm_context.pins_to_nidigital_session_for_ppmu( 140 | self._PINS 141 | ) 142 | return pin_query_context 143 | 144 | def test_publish_float_1d(self, pin_query_context, published_data_reader): 145 | test_data = [random.random() for _ in range(len(self._PINS) * self._NUM_SITES)] 146 | pin_query_context.publish(test_data) 147 | published_data = published_data_reader.get_and_clear_published_data() 148 | assert len(published_data) == len(test_data) 149 | for published_data_point, test_data_point in zip(published_data, test_data): 150 | assert published_data_point.double_value == test_data_point 151 | 152 | def test_publish_bool_1d(self, pin_query_context, published_data_reader): 153 | test_data = [bool(i % 2) for i in range(len(self._PINS) * self._NUM_SITES)] 154 | pin_query_context.publish(test_data) 155 | published_data = published_data_reader.get_and_clear_published_data() 156 | assert len(published_data) == len(test_data) 157 | for published_data_point, test_data_point in zip(published_data, test_data): 158 | assert published_data_point.boolean_value == test_data_point 159 | 160 | def test_publish_pattern(self, standalone_tsm_context, published_data_reader): 161 | ( 162 | pin_query_context, 163 | session, 164 | site_list, 165 | ) = standalone_tsm_context.pins_to_nidigital_session_for_pattern(self._PINS) 166 | test_data = {1: False, 0: True, 2: True} 167 | pin_query_context.publish_pattern_results(test_data) 168 | published_data = published_data_reader.get_and_clear_published_data() 169 | assert len(published_data) == len(test_data) 170 | for published_data_point, site_number in zip( 171 | published_data, standalone_tsm_context.site_numbers 172 | ): 173 | assert published_data_point.boolean_value == test_data[site_number] 174 | 175 | 176 | @pytest.mark.pin_map("publish.pinmap") 177 | @pytest.mark.usefixtures("simulated_nidigital_sessions") 178 | class TestMultiplePins2d: 179 | _PINS = ["DUTPin2", "DUTPin3"] 180 | 181 | @pytest.fixture 182 | def pin_query_context(self, standalone_tsm_context): 183 | pin_query_context, *_ = standalone_tsm_context.pins_to_nidigital_sessions_for_ppmu( 184 | self._PINS 185 | ) 186 | return pin_query_context 187 | 188 | def test_publish_float_2d(self, pin_query_context, published_data_reader): 189 | # [DigitalPattern1(ch3,ch4,ch5,ch6), DigitalPattern2(ch0,ch1)] 190 | test_data = [[1150.0, 20.5, 30.5, -1.0], [1952.5, -60417]] 191 | pin_query_context.publish(test_data) 192 | published_data = published_data_reader.get_and_clear_published_data() 193 | flattened_test_data = [data_point for row in test_data for data_point in row] 194 | assert len(published_data) == len(flattened_test_data) 195 | for published_data_point, test_data_point in zip(published_data, flattened_test_data): 196 | assert published_data_point.double_value == test_data_point 197 | 198 | def test_publish_bool_2d(self, pin_query_context, published_data_reader): 199 | # [DigitalPattern1(ch3,ch4,ch5,ch6), DigitalPattern2(ch0,ch1)] 200 | test_data = [[True, False, True, False], [True, False]] 201 | pin_query_context.publish(test_data) 202 | published_data = published_data_reader.get_and_clear_published_data() 203 | flattened_test_data = [data_point for row in test_data for data_point in row] 204 | assert len(published_data) == len(flattened_test_data) 205 | for published_data_point, test_data_point in zip(published_data, flattened_test_data): 206 | assert published_data_point.boolean_value == test_data_point 207 | 208 | def test_publish_pattern(self, standalone_tsm_context, published_data_reader): 209 | ( 210 | pin_query_context, 211 | sessions, 212 | site_lists, 213 | ) = standalone_tsm_context.pins_to_nidigital_sessions_for_pattern(self._PINS) 214 | # [DigitalPattern1: site0, site1, site2], [DigitalPattern2: site1, site2] 215 | test_data = [{1: False, 0: True, 2: True}, {1: True, 2: True}] 216 | expected_results = [True, False, True] # test_data AND'd across site 217 | pin_query_context.publish_pattern_results(test_data) 218 | published_data = published_data_reader.get_and_clear_published_data() 219 | assert len(published_data) == len(expected_results) 220 | for published_data_point, expected_result in zip(published_data, expected_results): 221 | assert published_data_point.boolean_value == expected_result 222 | 223 | 224 | @pytest.mark.pin_map("publish.pinmap") 225 | class TestPerSiteMultiSite: 226 | @pytest.mark.parametrize( 227 | "test_data", [[1150.0, 1952.5, -4.0], [11500, 19525, -4]], ids=["floats", "ints"] 228 | ) 229 | def test_publish_per_site_numeric_1d( 230 | self, standalone_tsm_context, published_data_reader, test_data 231 | ): 232 | standalone_tsm_context.publish_per_site(test_data, "id", "DUTPin1") 233 | published_data = published_data_reader.get_and_clear_published_data() 234 | for published_data_point in published_data: 235 | site_index = standalone_tsm_context.site_numbers.index(published_data_point.site_number) 236 | assert published_data_point.double_value == test_data[site_index] 237 | assert published_data_point.published_data_id == "id" 238 | assert published_data_point.pin == "DUTPin1" 239 | 240 | def test_publish_per_site_bool_1d(self, standalone_tsm_context, published_data_reader): 241 | test_data = [False, True, False] 242 | standalone_tsm_context.publish_per_site(test_data, "id", "DUTPin2") 243 | published_data = published_data_reader.get_and_clear_published_data() 244 | for published_data_point in published_data: 245 | site_index = standalone_tsm_context.site_numbers.index(published_data_point.site_number) 246 | assert published_data_point.boolean_value == test_data[site_index] 247 | assert published_data_point.published_data_id == "id" 248 | assert published_data_point.pin == "DUTPin2" 249 | 250 | def test_publish_per_site_string_1d(self, standalone_tsm_context, published_data_reader): 251 | test_data = ["holy", "hand", "grenade"] 252 | standalone_tsm_context.publish_per_site(test_data, "id", "DUTPin3") 253 | published_data = published_data_reader.get_and_clear_published_data() 254 | for published_data_point in published_data: 255 | site_index = standalone_tsm_context.site_numbers.index(published_data_point.site_number) 256 | assert published_data_point.string_value == test_data[site_index] 257 | assert published_data_point.published_data_id == "id" 258 | assert published_data_point.pin == "DUTPin3" 259 | 260 | 261 | @pytest.mark.pin_map("publish_single_site.pinmap") 262 | class TestPerSiteSingleSite: 263 | @pytest.mark.parametrize("test_data", [1150.0, 11500], ids=["float", "int"]) 264 | def test_publish_per_site_numeric_scalar( 265 | self, standalone_tsm_context, published_data_reader, test_data 266 | ): 267 | standalone_tsm_context.publish_per_site(test_data, "id", "DUTPin1") 268 | published_data = published_data_reader.get_and_clear_published_data()[0] 269 | assert published_data.double_value == test_data 270 | assert published_data.published_data_id == "id" 271 | assert published_data.pin == "DUTPin1" 272 | 273 | def test_publish_per_site_bool_scalar(self, standalone_tsm_context, published_data_reader): 274 | standalone_tsm_context.publish_per_site(True, "id", "DUTPin2") 275 | published_data = published_data_reader.get_and_clear_published_data()[0] 276 | assert published_data.boolean_value 277 | assert published_data.published_data_id == "id" 278 | assert published_data.pin == "DUTPin2" 279 | 280 | def test_publish_per_site_string_scalar(self, standalone_tsm_context, published_data_reader): 281 | standalone_tsm_context.publish_per_site("Tis but a scratch.", "id", "DUTPin3") 282 | published_data = published_data_reader.get_and_clear_published_data()[0] 283 | assert published_data.string_value == "Tis but a scratch." 284 | assert published_data.published_data_id == "id" 285 | assert published_data.pin == "DUTPin3" 286 | -------------------------------------------------------------------------------- /tests/test_site_and_global_data.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | 4 | @pytest.mark.pin_map("site_and_global_data.pinmap") 5 | class TestSiteAndGlobalData: 6 | site_data_id = "site_data_id" 7 | site_data = ["IES"] 8 | global_data_id = "global_data_id" 9 | global_data = "NI" 10 | 11 | @pytest.fixture 12 | def simulated_site_data(self, standalone_tsm_context): 13 | standalone_tsm_context.set_site_data(self.site_data_id, self.site_data) 14 | 15 | def test_get_site_data(self, standalone_tsm_context, simulated_site_data): 16 | queried_data = standalone_tsm_context.get_site_data(self.site_data_id) 17 | assert isinstance(queried_data, tuple) 18 | for data in queried_data: 19 | assert data in self.site_data 20 | 21 | def test_site_data_exists(self, standalone_tsm_context, simulated_site_data): 22 | assert standalone_tsm_context.site_data_exists(self.site_data_id) 23 | 24 | @pytest.fixture 25 | def simulated_global_data(self, standalone_tsm_context): 26 | standalone_tsm_context.set_global_data(self.global_data_id, self.global_data) 27 | 28 | def test_get_global_data(self, standalone_tsm_context, simulated_global_data): 29 | queried_data = standalone_tsm_context.get_global_data(self.global_data_id) 30 | assert queried_data == self.global_data 31 | 32 | def test_global_data_exists(self, standalone_tsm_context, simulated_global_data): 33 | assert standalone_tsm_context.global_data_exists(self.global_data_id) 34 | -------------------------------------------------------------------------------- /tests/test_switch.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | import nitsm.codemoduleapi as tsm 3 | 4 | 5 | @pytest.fixture 6 | def simulated_switch_sessions(standalone_tsm_context): 7 | switch_names = standalone_tsm_context.get_all_switch_names( 8 | tsm.InstrumentTypeIdConstants.NI_GENERIC_MULTIPLEXER 9 | ) 10 | for switch_name in switch_names: 11 | standalone_tsm_context.set_switch_session( 12 | switch_name, switch_name, tsm.InstrumentTypeIdConstants.NI_GENERIC_MULTIPLEXER 13 | ) 14 | yield switch_names 15 | 16 | 17 | @pytest.mark.pin_map("switch.pinmap") 18 | @pytest.mark.parametrize( 19 | "multiplexer_type_id", 20 | ["NIGenericMultiplexer", tsm.InstrumentTypeIdConstants.NI_GENERIC_MULTIPLEXER], 21 | ) 22 | class TestSwitch: 23 | switches = ["Multiplexer1"] 24 | 25 | def test_get_all_switch_names(self, standalone_tsm_context, multiplexer_type_id): 26 | switch_names = standalone_tsm_context.get_all_switch_names(multiplexer_type_id) 27 | assert isinstance(switch_names, tuple) 28 | assert len(switch_names) == len(self.switches) 29 | for switch_name in switch_names: 30 | assert isinstance(switch_name, str) 31 | assert switch_name in self.switches 32 | 33 | def test_set_switch_session(self, standalone_tsm_context, multiplexer_type_id): 34 | switch_names = standalone_tsm_context.get_all_nidmm_instrument_names() 35 | for switch_name in switch_names: 36 | standalone_tsm_context.set_switch_session(switch_name, switch_name, multiplexer_type_id) 37 | assert tsm.SemiconductorModuleContext._sessions[id(switch_name)] is switch_name 38 | 39 | def test_get_all_switch_sessions( 40 | self, standalone_tsm_context, simulated_switch_sessions, multiplexer_type_id 41 | ): 42 | queried_sessions = standalone_tsm_context.get_all_switch_sessions(multiplexer_type_id) 43 | assert isinstance(queried_sessions, tuple) 44 | assert len(queried_sessions) == len(simulated_switch_sessions) 45 | for queried_session in queried_sessions: 46 | assert isinstance(queried_session, str) 47 | assert queried_session in simulated_switch_sessions 48 | 49 | def test_pin_to_switch_sessions( 50 | self, standalone_tsm_context, simulated_switch_sessions, multiplexer_type_id 51 | ): 52 | tsm_contexts, sessions, switch_routes = standalone_tsm_context.pin_to_switch_sessions( 53 | "DUTPin1", multiplexer_type_id 54 | ) 55 | assert isinstance(tsm_contexts, tuple) 56 | assert isinstance(sessions, tuple) 57 | assert isinstance(switch_routes, tuple) 58 | assert len(tsm_contexts) == len(sessions) 59 | assert len(sessions) == len(switch_routes) 60 | for tsm_context, session, switch_route in zip(tsm_contexts, sessions, switch_routes): 61 | assert isinstance(tsm_context, tsm.SemiconductorModuleContext) 62 | assert len(tsm_context.site_numbers) == 1 63 | assert isinstance(session, str) 64 | assert session in simulated_switch_sessions 65 | assert isinstance(switch_route, str) 66 | assert switch_route == f"DUTPin1Site{tsm_context.site_numbers[0]}" 67 | -------------------------------------------------------------------------------- /tools/makepy_pinmapinterfaces.py: -------------------------------------------------------------------------------- 1 | """Generates _pinmapinterfaces.py 2 | 3 | You must register the correct version of TSM with TestStand Version Selector prior to running this 4 | script. 5 | """ 6 | 7 | import sys 8 | import os.path 9 | from win32com.client import makepy 10 | 11 | output_file = os.path.join(os.path.dirname(__file__), "_pinmapinterfaces.py") 12 | teststand_public_path = os.environ["TestStandPublic64"] 13 | pmi_type_library = os.path.join( 14 | teststand_public_path, 15 | "Bin", 16 | "NationalInstruments.TestStand.SemiconductorModule.PinMapInterfaces.tlb", 17 | ) 18 | 19 | sys.argv = ["makepy", "-o", output_file, pmi_type_library] 20 | 21 | if __name__ == "__main__": 22 | makepy.main() 23 | --------------------------------------------------------------------------------