├── .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 | [](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 |
--------------------------------------------------------------------------------