pax_global_header00006660000000000000000000000064137352201000014503gustar00rootroot0000000000000052 comment=729abb16eea18d2f76ad20240f2ef8accb27813c pyvisa-py-0.5.1/000077500000000000000000000000001373522010000134475ustar00rootroot00000000000000pyvisa-py-0.5.1/.coveragerc000066400000000000000000000005121373522010000155660ustar00rootroot00000000000000[run] branch = True include = */pyvisa-py/* omit = */pyvisa-py/testsuite/* [report] # Regexes for lines to exclude from consideration exclude_lines = # Have to re-enable the standard pragma pragma: no cover # Don't complain if tests don't hit defensive assertion code: raise NotImplementedError pass pyvisa-py-0.5.1/.github/000077500000000000000000000000001373522010000150075ustar00rootroot00000000000000pyvisa-py-0.5.1/.github/ISSUE_TEMPLATE/000077500000000000000000000000001373522010000171725ustar00rootroot00000000000000pyvisa-py-0.5.1/.github/ISSUE_TEMPLATE/bug_report.md000066400000000000000000000006211373522010000216630ustar00rootroot00000000000000--- name: Bug report about: Create a report to help us improve title: '' labels: '' assignees: '' --- To Reproduce -------------- Steps to reproduce the behavior: 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' 4. See error **Output of `pyvisa-info`** pyvisa-py-0.5.1/.github/ISSUE_TEMPLATE/instrument-communication-issue.md000066400000000000000000000016711373522010000257220ustar00rootroot00000000000000--- name: Instrument communication issue about: Template for requesting help communcating with a specific instrument title: "[COM] Communication issue with XXX using XXX" labels: instrument assignees: '' --- Instrument details ------------------ * Model: * Communication: TCPIP, GPIB, .... * Link to the documentation (if available): Output of `pyvisa-info` ----------------------- pyvisa-py-0.5.1/.github/PULL_REQUEST_TEMPLATE.md000066400000000000000000000025061373522010000206130ustar00rootroot00000000000000 - [ ] Closes # (insert issue number if relevant) - [ ] Executed ``black . && isort -c . && flake8`` with no errors - [ ] The change is fully covered by automated unit tests - [ ] Documented in docs/ as appropriate - [ ] Added an entry to the CHANGES file pyvisa-py-0.5.1/.github/workflows/000077500000000000000000000000001373522010000170445ustar00rootroot00000000000000pyvisa-py-0.5.1/.github/workflows/ci.yml000066400000000000000000000046301373522010000201650ustar00rootroot00000000000000name: Continuous Integration on: schedule: - cron: '0 0 * * 2' push: branches: - master - staging - trying pull_request: branches: - master paths: - .github/workflows/ci.yml - pyvisa_py/* - pyproject.toml - setup.cfg - setup.py jobs: formatting: name: Check code formatting runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Set up Python uses: actions/setup-python@v2 with: python-version: 3.8 - name: Install tools run: | python -m pip install --upgrade pip pip install flake8 black isort mypy pip install git+https://github.com/pyvisa/pyvisa.git#egg=pyvisa - name: Isort run: | isort pyvisa_y -c; - name: Black run: | black pyvisa_py --check; - name: Flake8 run: | flake8 pyvisa_py; - name: Mypy run: | mypy pyvisa_py; tests: name: Unit tests runs-on: ${{ matrix.os }} strategy: matrix: os: [ubuntu-latest, windows-latest, macos-latest] python-version: [3.6, 3.7, 3.8] steps: - uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v2 with: python-version: ${{ matrix.python-version }} - name: Install dependencies run: | python -m pip install --upgrade pip pip install git+https://github.com/pyvisa/pyvisa.git#egg=pyvisa - name: Install project run: | pip install -e . - name: Test with pytest run: | pip install pytest-cov pytest pyvisa_py/testsuite --cov pyvisa_py --cov-report xml - name: Upload coverage to Codecov uses: codecov/codecov-action@v1 with: token: ${{ secrets.CODECOV_TOKEN }} flags: unittests name: codecov-umbrella fail_ci_if_error: true # Added to summarize the matrix (otherwise we would need to list every single # job in bors.toml) tests-result: name: Tests result if: always() needs: - tests runs-on: ubuntu-latest steps: - name: Mark the job as a success if: needs.tests.result == 'success' run: exit 0 - name: Mark the job as a failure if: needs.tests.result != 'success' run: exit 1 pyvisa-py-0.5.1/.github/workflows/docs.yml000066400000000000000000000017561373522010000205300ustar00rootroot00000000000000name: Documentation building on: schedule: - cron: '0 0 * * 2' push: branches: - master - staging - trying pull_request: branches: - master paths: - .github/workflows/docs.yml - pyvisa_py/* - docs/* - setup.py jobs: docs: name: Docs building runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Set up Python uses: actions/setup-python@v2 - name: Install dependencies run: | python -m pip install --upgrade pip pip install git+https://github.com/pyvisa/pyvisa.git - name: Install project run: | python setup.py develop - name: Install graphviz uses: kamiazya/setup-graphviz@v1 - name: Install doc building tools run: | pip install sphinx sphinx_rtd_theme - name: Build documentation run: | mkdir docs_output; sphinx-build docs/source docs_output -W -b html; pyvisa-py-0.5.1/.gitignore000066400000000000000000000003041373522010000154340ustar00rootroot00000000000000*~ __pycache__ *egg-info* *.pyc .DS_Store docs/_build/ .idea build/ dist/ MANIFEST .tox .eggs # WebDAV file system cache files .DAV/ _test/ .spyproject/ .mypy_cache/ .pytest_cache/ .cache/ .vscodepyvisa-py-0.5.1/.pre-commit-config.yaml000066400000000000000000000007501373522010000177320ustar00rootroot00000000000000repos: - repo: https://github.com/pre-commit/mirrors-isort rev: v4.3.21 hooks: - id: isort - repo: https://github.com/psf/black rev: stable hooks: - id: black language_version: python3.7 - repo: https://github.com/pre-commit/pre-commit-hooks rev: v2.3.0 hooks: - id: flake8 - repo: https://github.com/pre-commit/mirrors-mypy rev: '' # Use the sha / tag you want to point at hooks: - id: mypy additional_dependencies: [numpy, typing_extensions]pyvisa-py-0.5.1/AUTHORS000066400000000000000000000011411373522010000145140ustar00rootroot00000000000000pyvisa-py is written and maintained by Hernan E. Grecco . Other contributors, listed alphabetically, are: * Alex Forencich * Alexander Bessman * Colin Marquardt * Lance McCulley * Martin Ritter * Matthieu Dartiailh * Sebastian Held * Thomas Kopp <20.kopp@gmail.com> * Thorsten Liebig * Tobias Müller (If you think that your name belongs here, please let the maintainer know) pyvisa-py-0.5.1/CHANGES000066400000000000000000000116711373522010000144500ustar00rootroot00000000000000PyVISA-py Changelog =================== 0.5.1 (30-09-2020) ------------------ - list serial resources under Windows without the COM prefix #269 - fix writing to serial resources PR #277 - fix return value of USB close method PR #265 - fix pyvisa version constraint PR #268 0.5.0 (16-09-2020) ------------------ In 0.5.0, the Python package installed in site-packages in now called pyvisa_py which makes it a valid python package. This change was decided because the old name was causing issues with tools such as Mypy and PyInstaller. - start running testsuite on Keysight buildbot PR #247 #252 - fix import in gpib.py when using linux-gpib PR #246 - fix opening some TCPIP resources PR #242 Some instrument do not indicate that the returned packet is the last one, so fix the number of returned packet for a TCPIP INSTR resource. - add typing validation through mypy PR #238 - use Numpy style dosctrings PR #238 - format the code with black and isort PR #236 - drop Python 2 support and run against PyVISA 1.11 PR #222 - usbtmc: improve support for USB488 devices. PR #241 For instrument that support REN_CONTROL, we now always assert the REN line. - fix a crash on Windows when opening multiple USBTMC devices 0.4.1 (2020-05-27) ------------------ - fix typo in tcpip.py PR #233 (back-ported) 0.4.0 (2020-05-06) ------------------ - support device lan name in TCPIP INSTR resources PR #226 - fix handling of VXI11 flags in device_write PR #226 - do not enforce 4 bytes padding RFC 1014 since some instrument do not respect it (Issue # 225) PR #226 - fix not setting stop bits with serial devices PR #205 - fix improper wait time before a timeout in the TCPIP backend PR # 173 - add GPIB support for proprietary device drivers on Windows and Linux (experimental): try importing gpib-ctypes if linux-gpib is not present. fix #105 #137 - fix return types of PyVisaLibrary and Session methods to match pyvisa.highlevel.VisaLibraryBase fix #169 PR #170 - avoid double closing of gpib resources PR #171 - fix initialization of timeout for the USB resources (the default was set before creating the underlying connection to which the timeout must be passed and was not). PR #167 - implement USBTMC abort sequence after read timeout to avoid losing communication with the instrument after a timeout occurs. PR #179 - fix custom timeout for USB instruments. PR #179 - fix triggering for all protocols. PR #180 - add support for "quirky" devices made by Rigol. PR #186 PR #207 - add support for Visa flush operation. PR #208 - fix reading large amounts of data from some instruments when using VXI-11. PR #209 0.3.1 (2018-09-12) ------------------ - Fix initialization of timeout (issue identified in TCPIP resources) PR #160 0.3 (2018-09-05) ---------------- - Fix handling of seesion registration under Python 3.7 PR #155 - Add read_stb, assert_trigger, lock, unlock to highlevel PR #139 - Fix timeout handling in usb PR #144 - Add gpib_command and assert_trigger to GPIB PR # 136 - Handle ValueError in usb list fix #131 PR #132 - Fix reading on GPIB and implement clear and gpib_send_ifc PR #132 - Do not error when listing USB devices PR #126 - Fix an error in the handling of the termchar for TCPIP INSTR PR #126 - Make list_resources return an empty tuple instead of erroring PR #121 - Proper support for timeout in TCPIP INSTR sessions PR #120 #127 #130 #144 - Proper encoding of data before transfer for all backends PR #119 - Unify use of StatusCode PR #118 - Improve handling of sessions attrs PR #116 - TCPIP SOCKET timeout handling improvement PR #115 - Fix compatibility with pyserial 3.0 PR #112 - TCPIP SOCKET handler read should not block PR #107 - TCPIP error handling fixes PR #100 - Use repr() instead of str() to log RPC record PR #97 - Speed up large transfer over GPIB 2beb52a5bcea2dae32d4a9908dc19f7874bfc0b7 - Catch GPIB errors while enumerating devices 9fea9d5c40cc6c33ce1244c209e5e576a33abfc2 - Add a serial poll function to GPIB backend PR #67 - Handle timeout in USB TMC backend PR #64 - Make USB TMC backend faster by transferring multiple bytes PR #63 - Fix issue with encoding before data transfer PR #59 # - Get Linux GPIB version PR #55 - Fix broken import in TCPIP sessions PR #51 0.2 (2015-08-25) ---------------- - Added support for TCPIP Socket. (Issue #38, thanks Thorsten Liebig) - Added support for GPIB INSTR using linux-gpib. (Issue #24, thanks bessman) - Added support for USB RAW. (Issue #18, kopp) - Better error reporting when pyusb or pyserial is missing. - Fixed logging of unicode strings. (Issue #54) - Fixed timeout in SerialSession. (Issue #44) - Moved resource name parsing to PyVISA. - VXI11 protocol performance enhancement. (thanks alexforencich) - Improved pyusb importing. - Fixed large binary reads in TCPIP. - Added backend information to logger. - Use pyvisa compat/struct.py for python < 2.7.8 (thanks Martin Ritter) 0.1 (2015-02-08) ---------------- - Initial release. Preliminary support for: - USB INSTR - TCPIP INSTR - ASRL INSTR pyvisa-py-0.5.1/LICENSE000066400000000000000000000021241373522010000144530ustar00rootroot00000000000000The MIT License Copyright (c) 2014 PyVISA-py Authors and contributors. See AUTHORS Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. pyvisa-py-0.5.1/MANIFEST.in000066400000000000000000000002461373522010000152070ustar00rootroot00000000000000include README AUTHORS CHANGES LICENSE recursive-include pyvisa-py * recursive-include docs * prune docs/_build global-exclude *.pyc *~ .DS_Store *__pycache__* *.pyo pyvisa-py-0.5.1/README.rst000066400000000000000000000060771373522010000151500ustar00rootroot00000000000000PyVISA-py ========= .. image:: https://github.com/pyvisa/pyvisa-py/workflows/Continuous%20Integration/badge.svg :target: https://github.com/pyvisa/pyvisa-py/actions :alt: Continuous integration .. image:: https://github.com/pyvisa/pyvisa-py/workflows/Documentation%20building/badge.svg :target: https://github.com/pyvisa/pyvisa-py/actions :alt: Documentation building .. image:: https://dev.azure.com/pyvisa/pyvisa-py/_apis/build/status/pyvisa.pyvisa-py.keysight-assisted?branchName=master :target: https://dev.azure.com/pyvisa/pyvisa-py/_build :alt: Keysight assisted testing .. image:: https://codecov.io/gh/pyvisa/pyvisa-py/branch/master/graph/badge.svg :target: https://codecov.io/gh/pyvisa/pyvisa-py :alt: Code Coverage .. image:: https://readthedocs.org/projects/pyvisa-py/badge/?version=latest :target: https://pyvisa.readthedocs.io/en/latest/?badge=latest :alt: Documentation Status .. image:: https://img.shields.io/pypi/l/PyVISA-py :target: https://pypi.python.org/pypi/pyvisa-py :alt: PyPI - License .. image:: https://img.shields.io/pypi/v/PyVISA-py :target: https://pypi.python.org/pypi/pyvisa-py :alt: PyPI A PyVISA backend that implements a large part of the "Virtual Instrument Software Architecture" (VISA_) in pure Python (with the help of some nice cross platform libraries python packages!). Description ----------- PyVISA started as wrapper for the IVI-VISA library and therefore you need to install a VISA library in your system (National Instruments, Keysight, etc). This works most of the time, for most people. But IVI-VISA implementations are proprietary libraries that only works on certain systems. That is when PyVISA-py jumps in. Starting from version 1.6, PyVISA allows to use different backends. These backends can be dynamically loaded. PyVISA-py is one of such backends. It implements most of the methods for Message Based communication (Serial/USB/GPIB/Ethernet) using Python and some well developed, easy to deploy and cross platform libraries .. _VISA: http://www.ivifoundation.org/Downloads/Specifications.htm VISA and Python --------------- Python has a couple of features that make it very interesting for measurement controlling: - Python is an easy-to-learn scripting language with short development cycles. - It represents a high abstraction level, which perfectly blends with the abstraction level of measurement programs. - It has a very rich set of native libraries, including numerical and plotting modules for data analysis and visualisation. - A large set of books (in many languages) and on-line publications is available. Requirements ------------ - Python (tested with 3.6+) - PyVISA 1.11+ Optionally - PySerial (to interface with Serial instruments) - PyUSB (to interface with USB instruments) - linux-gpib (to interface with gpib instruments, only on linux) - gpib-ctypes (to interface with GPIB instruments on Windows and Linux) Installation -------------- Using pip: $ pip install pyvisa-py Documentation -------------- The documentation can be read online at https://pyvisa-py.readthedocs.org pyvisa-py-0.5.1/azure-pipelines.yml000066400000000000000000000034531373522010000173130ustar00rootroot00000000000000# Python package # Create and test a Python package on multiple Python versions. # Add steps that analyze code, save the dist with the build record, publish to a PyPI-compatible index, and more: # https://docs.microsoft.com/azure/devops/pipelines/languages/python trigger: branches: include: - master - staging - trying pr: - master variables: PYVISA_KEYSIGHT_VIRTUAL_INSTR: 1 pool: name: default demands: KEYSIGHT -equals TCPIP steps: - script: | echo Activate conda call $(CONDA_PATH)\activate.bat echo Create environment conda create -n test_ python=3.7 numpy --yes displayName: 'Create environment' - script: | echo Activate conda call $(CONDA_PATH)\activate.bat echo Activate environment call conda activate test_ echo Install project pip install git+https://github.com/pyvisa/pyvisa.git#egg=pyvisa pip install -e . displayName: 'Install dependencies' - script: | echo Activate conda call $(CONDA_PATH)\activate.bat echo Activate environment call conda activate test_ echo Install pytest and co pip install pytest pytest-azurepipelines pytest-cov echo Run pytest python -X dev -m pytest --pyargs pyvisa_py --cov pyvisa_py --cov-report xml -v displayName: 'Run tests' - script: | echo Activate conda call $(CONDA_PATH)\activate.bat echo Activate environment call conda activate test_ echo Install codecov pip install codecov echo Run codecov codecov --file coverage.xml --token $(CODECOV_TOKEN) --env PYVISA_KEYSIGHT_VIRTUAL_INSTR --tries 5 --required -F unittest --name codecov-umbrella displayName: 'Upload test coverage results' - script: | call $(CONDA_PATH)\activate.bat conda remove -n test_ --all --yes displayName: 'Remove test environment' condition: always() pyvisa-py-0.5.1/bors.toml000066400000000000000000000002311373522010000153050ustar00rootroot00000000000000status = ["Check code formatting", "Tests result", "Docs building", "pyvisa.pyvisa-py.keysight-assisted"] delete-merged-branches = true timeout_sec = 300pyvisa-py-0.5.1/dev-requirements.txt000066400000000000000000000000521373522010000175040ustar00rootroot00000000000000black flake8 isort sphinx sphinx-rtd-themepyvisa-py-0.5.1/docs/000077500000000000000000000000001373522010000143775ustar00rootroot00000000000000pyvisa-py-0.5.1/docs/Makefile000066400000000000000000000127001373522010000160370ustar00rootroot00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = build # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: -rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/pyvisa.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/pyvisa.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/pyvisa" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/pyvisa" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." pyvisa-py-0.5.1/docs/index.rst000066400000000000000000000076301373522010000162460ustar00rootroot00000000000000:orphan: PyVISA-py: Pure Python backend for PyVISA ========================================= .. image:: _static/logo-full.jpg :alt: PyVISA PyVISA-py is a backend for PyVISA_. It implements most of the methods for Message Based communication (Serial/USB/GPIB/Ethernet) using Python and some well developed, easy to deploy and cross platform libraries. You can select the PyVISA-py backend using **@py** when instantiating the visa Resource Manager: >>> import pyvisa >>> rm = pyvisa.ResourceManager('@py') >>> rm.list_resources() ('USB0::0x1AB1::0x0588::DS1K00005888::INSTR') >>> inst = rm.open_resource('USB0::0x1AB1::0x0588::DS1K00005888::INSTR') >>> print(inst.query("*IDN?")) That's all! Except for **@py**, the code is exactly what you would write to using the NI-VISA backend for PyVISA. Installation ============ Just run the following command in your console: pip install pyvisa-py You can report a problem or ask for features in the `issue tracker`_. Or get the code in GitHub_. FAQ === Which libraries are used by PyVISA-py? -------------------------------------- It depends on the interface type. For **ASRL** and **USB** we use PySerial_ and PyUSB_, respectively. PySerial_ version 3.0 or newer is required. For **TCPIP** we use the :py:mod:`socket` module in the Python Standard Library. On Linux, **GPIB** resources are supported using the `linux-gpib`_ project's Python bindings. On Windows as well as Linux systems with proprietary GPIB device drivers, experimental GPIB support is available through `gpib-ctypes`_. The `gpib-ctypes`_ library is still in development so please report any issues you may encounter. If I only need **TCPIP**, do I need to install PySerial, PyUSB, linux-gpib, or gpib-ctypes? ------------------------------------------------------------------------------------------- No. Libraries are loaded on demand. How do I know if PyVISA-py is properly installed? ------------------------------------------------- Using the pyvisa information tool. Run in your console:: pyvisa-info You will get info about PyVISA, the installed backends and their options. Which resource types are supported? ----------------------------------- Now: - ASRL INSTR - USB INSTR - TCPIP INSTR - USB RAW - TCPIP SOCKET - GPIB INSTR Are all VISA attributes and methods implemented? ------------------------------------------------ No. We have implemented those attributes and methods that are most commonly needed. We would like to reach feature parity. If there is something that you need, let us know. Why are you developing this? ---------------------------- The `National Instruments's VISA`_ is a proprietary library that only works on certain systems. We wanted to provide a compatible alternative. Why not using LibreVISA? ------------------------ LibreVISA_ is still young. However, you can already use it with the NI backend as it has the same API. We think that PyVISA-py is easier to hack and we can quickly reach feature parity with NI-VISA for message-based instruments. Why putting PyVISA in the middle? --------------------------------- Because it allows you to change the backend easily without changing your application. In other projects we implemented classes to call USBTMC devices without PyVISA. But this leads to code duplication or an adapter class in your code. By using PyVISA as a frontend to many backends, we abstract these things from higher level applications. .. _PySerial: https://pythonhosted.org/pyserial/ .. _PyVISA: http://pyvisa.readthedocs.org/ .. _PyUSB: https://github.com/pyusb/pyusb .. _PyPI: https://pypi.python.org/pypi/PyVISA-py .. _GitHub: https://github.com/pyvisa/pyvisa-py .. _`National Instruments's VISA`: http://ni.com/visa/ .. _`LibreVISA`: http://www.librevisa.org/ .. _`issue tracker`: https://github.com/pyvisa/pyvisa-py/issues .. _`linux-gpib`: http://linux-gpib.sourceforge.net/ .. _`gpib-ctypes`: https://pypi.org/project/gpib-ctypes/ pyvisa-py-0.5.1/docs/make.bat000066400000000000000000000122521373522010000160060ustar00rootroot00000000000000@ECHO OFF REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) set BUILDDIR=build set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% source set I18NSPHINXOPTS=%SPHINXOPTS% . if NOT "%PAPER%" == "" ( set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% ) if "%1" == "" goto help if "%1" == "help" ( :help echo.Please use `make ^` where ^ is one of echo. html to make standalone HTML files echo. dirhtml to make HTML files named index.html in directories echo. singlehtml to make a single large HTML file echo. pickle to make pickle files echo. json to make JSON files echo. htmlhelp to make HTML files and a HTML help project echo. qthelp to make HTML files and a qthelp project echo. devhelp to make HTML files and a Devhelp project echo. epub to make an epub echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter echo. text to make text files echo. man to make manual pages echo. texinfo to make Texinfo files echo. gettext to make PO message catalogs echo. changes to make an overview over all changed/added/deprecated items echo. linkcheck to check all external links for integrity echo. doctest to run all doctests embedded in the documentation if enabled goto end ) if "%1" == "clean" ( for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i del /q /s %BUILDDIR%\* goto end ) if "%1" == "html" ( %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/html. goto end ) if "%1" == "dirhtml" ( %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. goto end ) if "%1" == "singlehtml" ( %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. goto end ) if "%1" == "pickle" ( %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the pickle files. goto end ) if "%1" == "json" ( %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the JSON files. goto end ) if "%1" == "htmlhelp" ( %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run HTML Help Workshop with the ^ .hhp project file in %BUILDDIR%/htmlhelp. goto end ) if "%1" == "qthelp" ( %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run "qcollectiongenerator" with the ^ .qhcp project file in %BUILDDIR%/qthelp, like this: echo.^> qcollectiongenerator %BUILDDIR%\qthelp\pyvisa.qhcp echo.To view the help file: echo.^> assistant -collectionFile %BUILDDIR%\qthelp\pyvisa.ghc goto end ) if "%1" == "devhelp" ( %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp if errorlevel 1 exit /b 1 echo. echo.Build finished. goto end ) if "%1" == "epub" ( %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub if errorlevel 1 exit /b 1 echo. echo.Build finished. The epub file is in %BUILDDIR%/epub. goto end ) if "%1" == "latex" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex if errorlevel 1 exit /b 1 echo. echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. goto end ) if "%1" == "text" ( %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text if errorlevel 1 exit /b 1 echo. echo.Build finished. The text files are in %BUILDDIR%/text. goto end ) if "%1" == "man" ( %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man if errorlevel 1 exit /b 1 echo. echo.Build finished. The manual pages are in %BUILDDIR%/man. goto end ) if "%1" == "texinfo" ( %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo if errorlevel 1 exit /b 1 echo. echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. goto end ) if "%1" == "gettext" ( %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale if errorlevel 1 exit /b 1 echo. echo.Build finished. The message catalogs are in %BUILDDIR%/locale. goto end ) if "%1" == "changes" ( %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes if errorlevel 1 exit /b 1 echo. echo.The overview file is in %BUILDDIR%/changes. goto end ) if "%1" == "linkcheck" ( %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck if errorlevel 1 exit /b 1 echo. echo.Link check complete; look for any errors in the above output ^ or in %BUILDDIR%/linkcheck/output.txt. goto end ) if "%1" == "doctest" ( %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest if errorlevel 1 exit /b 1 echo. echo.Testing of doctests in the sources finished, look at the ^ results in %BUILDDIR%/doctest/output.txt. goto end ) :end pyvisa-py-0.5.1/docs/source/000077500000000000000000000000001373522010000156775ustar00rootroot00000000000000pyvisa-py-0.5.1/docs/source/_static/000077500000000000000000000000001373522010000173255ustar00rootroot00000000000000pyvisa-py-0.5.1/docs/source/_static/logo-full.jpg000066400000000000000000000252221373522010000217320ustar00rootroot00000000000000JFIFHH@ICC_PROFILE0appl mntrRGB XYZ   acspAPPLappl-appl dscmdescogXYZlwtptrXYZbXYZrTRCcprt8chad,gTRCbTRCmluc enUS&~esES&daDK.deDE,fiFI(frFU(*itIT(VnlNL(nbNO&ptBR&svSE&jaJPRkoKR@zhTWlzhCNruRU"plPL,Yleinen RGB-profiiliGenerisk RGB-profilProfil Gnrique RVBN, RGB 000000u( RGB r_icϏPerfil RGB GenricoAllgemeines RGB-Profilfn RGB cϏeNGenerel RGB-beskrivelseAlgemeen RGB-profiel| RGB \ |Profilo RGB GenericoGeneric RGB Profile1I89 ?@>D8;L RGBUniwersalny profil RGBdescGeneric RGB ProfileGeneric RGB ProfileXYZ Zus4XYZ RXYZ tM=XYZ (6curvtextCopyright 2007 Apple Inc., all rights reserved.sf32 B&lExifMM*bj(1r2iHHAdobe Photoshop CS4 Macintosh2013:08:26 20:57:27C     C  " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?(((((((((((((((((?>0 vr_M.p5?/ShFE0<ٺ;7 #Ě'KM!I?P1_",G;In=:lw{e%$qƬd+?ioSKI ^iyyY_$XnkOyw eZns&}hE()8}gu|}x3,\lI>|H֗Jϊ[Wc,Y2Dk*|UVnRi䉃# ??\_`n\{>7sjo#Xy<c+g'@uI%ƻi4}떅ʬB_G)kM'B=~IT:(:(((((((((Ց_iz52X[b}4jH4{^^X[ZZ4~ /$0x~1|d_?/=F~q^#H ؠ*Af㠯nT䜏ˎ>ʊjֹ֓3eN+i{O~(|h}Yn-|w$D2Å8<%Ə/\xIrNɯ[U9:ౖ\GʼFRjT{4i|1SASѵ,1"δ1>ܼ8g܏TmtN}w~ֿqyY_8⠻g~A~x3[Wڋlv+6`ihcBt*:r:(` ( ( ( ( ( ( ( ( ֿa?zK?8?} _:~K?/KXV]aqNr}n _ SџfI֐<#wַ<mO? &^&_ Bff:U%&좯#Zw9%{v,6U#" ks4g"8vq?}t1Ox#?%cmk" KUS{8.vp: q]Sx[E p0ݲ1.#Sd }Z\ͫ~yg؊׳}vg??t߮8)S7Ka|\.+ >[މy:sq+'O*v;Gg&s5NY3qg3ʶ??vzX :Odz ' 'I5{t'Ҿ|SjWY_A!ʟcJu%y?6 j9EEPEPEPEPEPEPEPEP\{?x3Y5a-P}pt9EhJ->oE a)T0yiwD1>?K -{7W>ǶG h"[|%xb2{7|wc/ x Eu kwl4E'a}3Tk>25ә3.c7T%B>lgǐG9Qu[ͫk'Kt9+Yrݖ5EhZ*K[: 9ϯJcxPKǚö==է`9om*.V3@˃Аq>|-O <;gnw 1sƺ<kHUcBk\neB*O[H+q 7 S2oqඞ?.u~\5_Z01{Ud򬾖 ?.c*b3?QEEPEPEPEPEPEPEPEPEPUg]{֭Td ٬3(KiK?ķs F<5ϼ7=kWBҵ0j=j˿vpZQ&xS%&,Q4 s2 c澁6EyaxRIG2JC$( }~'0xzή-F]#l]xs6cTWލo.⋿?\k&%s"8ݜk1"!|gϦjljUo)^87Tf lY,k9\g5dwu;|~55jlhFsW!n]VҠ>M=tѱEӻ'VR\sY';L麄:^=FLyX $g߲o߈?q:]˧77S4JCIbcbNH5' qIŻ[&m cqJ?MZ+h=ew%-tb}$t 33^vU8&zJU%W=Ҋ}|o#m-3OU..aK3ʇQq99f_g,-t#nS"Oq! G @$9՞SJ:XjՔRVC6W7wQ_fo|ocj4JIDDۈs=_7>4|6GiGOE9 8\mĪ'9>kk<_6qE|/2~>:-\u_ _^heiW3 "HPgI8'%H-BI_1ң[Nu>Z=Z|w$wXgv fcyşv-/y<.|Ckʴ_-|E?h1RO \ޘ4=CNIl:>ˢ> un0KkLHh }~} .^:$*Wk? N&rX*ivO#|Iً6P]:;PoHxC. /Nf&`IP | x BQ~V嘼Mhk (ۃψ+x'FHԵ *KY2LQ!hI*ge1у}{fY0XyVGW.o[ Ũ\#U2 ;Q0XQ]{[7VL(;¼5& 5&0^?0`8:]_şw߈]Vk5)C(T?*i x+Km?I-migTE`5\SI4(u$GinUm="#Egeq֭3­gJ7e՟gxo-uAeitKyL$*2#,p{şG>6x3ִwhxգu J4M?H<6~>a!|^4T+cpΗ~͟xuׁ[MM^0a O?1_q ,NX^|}MBs_x4Jw^y>rp/ᦛvqV"Kg_ ߱gtɡh O+ \0;BG5 "EE,q"F0M_7*V[ɷAN;E$|;|ɟxk+$>/WIeG&}LĿ9\%}rsO>'5=LVmJװ*&WPDWmE|u9MK!i5C:̰[ϪXr@iiap2;p>~W_/*xD~wW uYntI,DDO8_=6|u ?|W&]n!qyLs$97͢N=UTuG?V[ž1|oxG\״giJwV⺚IQ>Ҕ] ^V 4UyntkG'OVl΄c{If̂+ 2CfI7WW4qpd\VP'+?_{ _/?EWb>&ة4U>&ة4_'2y@?6?'R{3zӥ0x#I++N8kolRr蝗|]?=_YxŖ$A[+'̒BW4Q_wl} C OlE[^e֭vF庈mg_ß$Sn+ç륧_ɽN]@~6~~~~V6۶e\gKq;dIA1 }/ Ԏ*\>s ^>vվhcgu*8e5{Za?~W^4f⛟2" H2T#xTkMō֖y2o& 9%>'6ZMm>;[XD5R,+zN旧E}fKXAr׫V*?og^- ^ŬoGuuhs/nU/c_d7QжnH@y9cNOfu=>"}cR+u8|i!d\t7 -$%\z Q>6WQF[KVn}9 +lt, j6L-\cH''6s<^U3ӂ;d#a8,w-=74|Sy7(SE&}LĿ9\%rK?`o3qUI>?jt)ş`:h0|<ަmW2u%5> ' G ? a%~/l]&Ǟiy20 A=i;KhNլ%o#捿V ] <-(1UnX{Cq޾l`1w<Dou)SX{Q6ݟ)as*(Jm??o? o(}xy`*I aXe*yh֚o_5[u-J6 2GT+3Q/i3oXX81 ,D潿uSQbj9{&8*0}ҷ~ؚW~-cĚVZho@~5:xPg\Wd㬾ro(>'*8tjzml̖KwD]@$ kqEH)?7d:9j6$p]`#ºDw]>I[i^6 )rXPI~_A9=-Qss{Y߽#ُw4w|YstFu_3DAmml23}3úekA.5GI 8Qyncө:9JإqIhZ-G )6o]Oƈ>$ֿ5Z\WnP1\0aw&ހ9qei:agaN.e%u'C_tW."TiYgSib?<<|fu<]CwGѮ$I5 WYusGo @#v(9+ំOVD" $GZh+4kcdcV=<&[r~h~Oث⏇ 77pI\%>21I+d|W}V9i[YTi[M|-m{__foz핮xᗆtۨu(.c- )Ul_8_UeylWRN=4NgSPڳ24{ Z5I#{2g/UN$d8$sZQ^cbQE ( e v documentation". # html_title = None # A shorter title for the navigation bar. Default is the same as html_title. # html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. # html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. # html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ["_static"] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. # html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. # html_use_smartypants = True # Custom sidebar templates, maps document names to template names. # html_sidebars = {} html_sidebars = { "index": ["sidebarintro.html", "sourcelink.html", "searchbox.html"], "**": [ "sidebarlogo.html", "localtoc.html", "relations.html", "sourcelink.html", "searchbox.html", ], } # Additional templates that should be rendered to pages, maps page names to # template names. # html_additional_pages = {} # If false, no module index is generated. # html_domain_indices = True # If false, no index is generated. # html_use_index = True # If true, the index is split into individual pages for each letter. # html_split_index = False # If true, links to the reST sources are added to the pages. # html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. # html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. # html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. # html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). # html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = "pyvisa-pytdoc" # -- Options for LaTeX output -------------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). #'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). #'pointsize': '10pt', # Additional stuff for the LaTeX preamble. #'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ ("index", "pyvisa-py.tex", "PyVISA Documentation", "PyVISA Authors", "manual"), ] # The name of an image file (relative to this directory) to place at the top of # the title page. # latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. # latex_use_parts = False # If true, show page references after internal links. # latex_show_pagerefs = False # If true, show URL addresses after external links. # latex_show_urls = False # Documents to append as an appendix to all manuals. # latex_appendices = [] # If false, no module index is generated. # latex_domain_indices = True # -- Options for manual page output -------------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [("index", "pyvisa-py", "PyVISA Documentation", ["PyVISA Authors"], 1)] # If true, show URL addresses after external links. # man_show_urls = False # -- Options for Texinfo output ------------------------------------------------ # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ( "index", "PyVISA", "PyVISA Documentation", "PyVISA Authors", "PyVISA", "One line description of project.", "Miscellaneous", ), ] # Documents to append as an appendix to all manuals. # texinfo_appendices = [] # If false, no module index is generated. # texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. # texinfo_show_urls = 'footnote' # -- Options for Epub output --------------------------------------------------- # Bibliographic Dublin Core info. epub_title = project epub_author = author epub_publisher = author epub_copyright = copyright # The language of the text. It defaults to the language option # or en if the language is not set. # epub_language = '' # The scheme of the identifier. Typical schemes are ISBN or URL. # epub_scheme = '' # The unique identifier of the text. This can be a ISBN number # or the project homepage. # epub_identifier = '' # A unique identification for the text. # epub_uid = '' # A tuple containing the cover image and cover page html template filenames. # epub_cover = () # HTML files that should be inserted before the pages created by sphinx. # The format is a list of tuples containing the path and title. # epub_pre_files = [] # HTML files shat should be inserted after the pages created by sphinx. # The format is a list of tuples containing the path and title. # epub_post_files = [] # A list of files that should not be packed into the epub file. # epub_exclude_files = [] # The depth of the table of contents in toc.ncx. # epub_tocdepth = 3 # Allow duplicate toc entries. # epub_tocdup = True # Example configuration for intersphinx: refer to the Python standard library. intersphinx_mapping = {"python": ("http://docs.python.org/3", None)} pyvisa-py-0.5.1/docs/source/faq.rst000066400000000000000000000043251373522010000172040ustar00rootroot00000000000000.. _faq: FAQ === Are all VISA attributes and methods implemented? ------------------------------------------------ No. We have implemented those attributes and methods that are most commonly needed. We would like to reach feature parity. If there is something that you need, let us know. Why are you developing this? ---------------------------- The IVI compliant VISA implementation available (`National Instruments's VISA`_ , Keysight, Tektronik, etc) are proprietary libraries that only works on certain systems. We wanted to provide a compatible alternative. Can PyVISA-py be used from a VM? -------------------------------- Because PyVISA-py access hardware resources (such as USB ports) running from a VM can cause issues, such as unexpected timeouts because the VM does not receive the response. You may be able to set the VM in such that it works but you should refer to your VM manual. (see https://github.com/pyvisa/pyvisa-py/issues/243 for the kind of issue it can cause) Why not using LibreVISA? ------------------------ LibreVISA_ is still young and appears mostly unmaintained at this point. However, you can already use it with the IVI backend as it has the same API. We think that PyVISA-py is easier to hack and we can quickly reach feature parity with other IVI-VISA implementation for message-based instruments. Why putting PyVISA in the middle? --------------------------------- Because it allows you to change the backend easily without changing your application. In other projects, we implemented classes to call USBTMC devices without PyVISA. But this leads to code duplication or an adapter class in your code. By using PyVISA as a frontend to many backends, we abstract these things from higher level applications. .. _PySerial: https://pythonhosted.org/pyserial/ .. _PyVISA: http://pyvisa.readthedocs.org/ .. _PyUSB: https://github.com/pyusb/pyusb .. _PyPI: https://pypi.python.org/pypi/PyVISA-py .. _GitHub: https://github.com/pyvisa/pyvisa-py .. _`National Instruments's VISA`: http://ni.com/visa/ .. _`LibreVISA`: http://www.librevisa.org/ .. _`issue tracker`: https://github.com/pyvisa/pyvisa-py/issues .. _`linux-gpib`: http://linux-gpib.sourceforge.net/ .. _`gpib-ctypes`: https://pypi.org/project/gpib-ctypes/ pyvisa-py-0.5.1/docs/source/index.rst000066400000000000000000000024231373522010000175410ustar00rootroot00000000000000:orphan: PyVISA-py: Pure Python backend for PyVISA ========================================= .. image:: _static/logo-full.jpg :alt: PyVISA PyVISA-py is a backend for PyVISA_. It implements most of the methods for Message Based communication (Serial/USB/GPIB/Ethernet) using Python and some well developed, easy to deploy and cross platform libraries. You can select the PyVISA-py backend using **@py** when instantiating the visa Resource Manager: >>> import visa >>> rm = visa.ResourceManager('@py') >>> rm.list_resources() ('USB0::0x1AB1::0x0588::DS1K00005888::INSTR') >>> inst = rm.open_resource('USB0::0x1AB1::0x0588::DS1K00005888::INSTR') >>> print(inst.query("*IDN?")) That's all! Except for **@py**, the code is exactly what you would write to using the NI-VISA backend for PyVISA. Currently Pyvisa-py support the following resources: - TCPIP INSTR - TCPIP SOCKET - GPIB INSTR - ASRL INSTR - USB INSTR - USB RAW You can report a problem or ask for features in the `issue tracker`_. Or get the code in GitHub_. .. toctree:: :maxdepth: 2 Installation FAQ .. _PyVISA: http://pyvisa.readthedocs.org/ .. _GitHub: https://github.com/pyvisa/pyvisa-py .. _`issue tracker`: https://github.com/pyvisa/pyvisa-py/issues pyvisa-py-0.5.1/docs/source/installation.rst000066400000000000000000000064441373522010000211420ustar00rootroot00000000000000.. _installation: Installation ============ Pyvisa-py is available on PyPI_ and can be easily installed using pip: pip install pyvisa-py Pyvisa-py runs on Python 3.6+. If you do not install any extra library pyvisa-py will only be able to access tcpip resources. The following sections will describe what extra libraries you need to install and how to configure them to use other resources. Ethernet resources: TCPIP INSTR/SOCKET -------------------------------------- Pyvisa-py relies on :py:mod:`socket` module in the Python Standard Library to interact with the instrument which you do not need to install any extra library to access those resources. Serial resources: ASRL INSTR ---------------------------- To access serial resources, you should install PySerial_. Version 3.0 or newer is required. No special configuration is required. GPIB resources: GPIB INSTR -------------------------- On all platforms, using **GPIB** resources requires to install a gpib driver. On Windows, it is install as part of NI-VISA or Keysight VISA for example. On MacOSX, you should install the NI-488 library from National instrument. On Linux, you can use a commercial driver (NI) or the `linux-gpib`_ project. On Linux, `linux-gpib`_ comes with Python bindings so you do not have to install any extra library. On all systems with GPIB device drivers, GPIB support is available through `gpib-ctypes`_. You should not have to perform any special configuration after the install. USB resources: USB INSTR/RAW ---------------------------- For **USB** resources, you need to install PyUSB_. PyUSB_ relies on USB driver library such as libusb 0.1, libusb 1.0, libusbx, libusb-win32 and OpenUSB that you should also install. Please refer to PyUSB_ documentation for more details. On Unix system, one may have to modify udev rules to allow non-root access to the device you are trying to connect to. The following tutorial describes how to do it http://ask.xmodulo.com/change-usb-device-permission-linux.html. On Windows, you may have to uninstall the USBTMC specific driver installed by Windows and re-install a generic driver. Note that on Windows, devices that are already open cannot be detected and will not be returned by ``ResourceManager.list_resources``. Another useful reference for how to configure your system is h ttps://github.com/python-ivi/python-usbtmc. How do I know if PyVISA-py is properly installed? ------------------------------------------------- Using the pyvisa information tool. Run in your console:: python -m visa info You will get info about PyVISA, the installed backends and their options. Using the development version ----------------------------- You can install the latest development version (at your own risk) directly form GitHub_:: $ pip install -U git+https://github.com/pyvisa/pyvisa-py.git .. _PySerial: https://pythonhosted.org/pyserial/ .. _PyVISA: http://pyvisa.readthedocs.org/ .. _PyUSB: https://github.com/pyusb/pyusb .. _PyPI: https://pypi.python.org/pypi/PyVISA-py .. _GitHub: https://github.com/pyvisa/pyvisa-py .. _`National Instruments's VISA`: http://ni.com/visa/ .. _`LibreVISA`: http://www.librevisa.org/ .. _`issue tracker`: https://github.com/pyvisa/pyvisa-py/issues .. _`linux-gpib`: http://linux-gpib.sourceforge.net/ .. _`gpib-ctypes`: https://pypi.org/project/gpib-ctypes/pyvisa-py-0.5.1/pyproject.toml000066400000000000000000000002241373522010000163610ustar00rootroot00000000000000[build-system] requires = ["setuptools>=42", "wheel", "setuptools_scm[toml]>=3.4.3"] build-backend = "setuptools.build_meta" [tool.setuptools_scm] pyvisa-py-0.5.1/pyvisa_py/000077500000000000000000000000001373522010000154725ustar00rootroot00000000000000pyvisa-py-0.5.1/pyvisa_py/__init__.py000066400000000000000000000011101373522010000175740ustar00rootroot00000000000000# -*- coding: utf-8 -*- """Pure Python backend for PyVISA. :copyright: 2014-2020 by PyVISA-py Authors, see AUTHORS for more details. :license: MIT, see LICENSE for more details. """ import sys if sys.version_info >= (3, 8): from importlib.metadata import PackageNotFoundError, version else: from importlib_metadata import PackageNotFoundError, version # type: ignore __version__ = "unknown" try: __version__ = version(__name__) except PackageNotFoundError: # package is not installed pass from .highlevel import PyVisaLibrary WRAPPER_CLASS = PyVisaLibrary pyvisa-py-0.5.1/pyvisa_py/common.py000066400000000000000000000011561373522010000173370ustar00rootroot00000000000000# -*- coding: utf-8 -*- """Common code. :copyright: 2014-2020 by PyVISA-sim Authors, see AUTHORS for more details. :license: MIT, see LICENSE for more details. """ import logging from pyvisa import logger logger = logging.LoggerAdapter(logger, {"backend": "py"}) # type: ignore class NamedObject(object): """A class to construct named sentinels.""" #: Name used to identify the sentinel name: str def __init__(self, name) -> None: self.name = name def __repr__(self) -> str: return "<%s>" % self.name __str__ = __repr__ int_to_byte = lambda val: val.to_bytes(1, "big") pyvisa-py-0.5.1/pyvisa_py/gpib.py000066400000000000000000000766311373522010000170020ustar00rootroot00000000000000# -*- coding: utf-8 -*- """GPIB Session implementation using linux-gpib or gpib-ctypes. :copyright: 2015-2020 by PyVISA-py Authors, see AUTHORS for more details. :license: MIT, see LICENSE for more details. """ import ctypes # Used for missing bindings not ideal from bisect import bisect from typing import Any, Iterator, List, Tuple, Union from pyvisa import attributes, constants, logger from pyvisa.constants import ResourceAttribute, StatusCode from pyvisa.rname import GPIBInstr, GPIBIntfc from .sessions import Session, UnknownAttribute try: GPIB_CTYPES = True from gpib_ctypes import gpib # typing: ignore from gpib_ctypes.Gpib import Gpib # typing: ignore from gpib_ctypes.gpib.gpib import _lib as gpib_lib # typing: ignore # Add some extra binding not available by default extra_funcs = [ ("ibcac", [ctypes.c_int, ctypes.c_int], ctypes.c_int), ("ibgts", [ctypes.c_int, ctypes.c_int], ctypes.c_int), ("ibpct", [ctypes.c_int], ctypes.c_int), ] for name, argtypes, restype in extra_funcs: libfunction = gpib_lib[name] libfunction.argtypes = argtypes libfunction.restype = restype except ImportError: GPIB_CTYPES = False try: import gpib # typing: ignore from Gpib import Gpib # typing: ignore except ImportError as e: Session.register_unavailable( constants.InterfaceType.gpib, "INSTR", "Please install linux-gpib (Linux) or " "gpib-ctypes (Windows, Linux) to use " "this resource type. Note that installing" " gpib-ctypes will give you access to a " "broader range of funcionality.\n%s" % e, ) raise # patch Gpib to avoid double closing of handles def _patch_Gpib() -> None: if not hasattr(Gpib, "close"): _old_del = Gpib.__del__ def _inner(self): _old_del(self) self._own = False Gpib.__del__ = _inner Gpib.close = _inner _patch_Gpib() def _find_boards() -> Iterator[Tuple[int, int]]: """Find GPIB board addresses.""" for board in range(16): try: yield board, gpib.ask(board, 1) except gpib.GpibError as e: logger.debug("GPIB board %i error in _find_boards(): %s", board, repr(e)) def _find_listeners() -> Iterator[Tuple[int, int]]: """Find GPIB listeners.""" for board, boardpad in _find_boards(): for i in range(31): try: if boardpad != i and gpib.listener(board, i): yield board, i except gpib.GpibError as e: logger.debug( "GPIB board %i addr %i error in _find_listeners(): %s", board, i, repr(e), ) def _analyse_lines_value(value: int, line: int): """Determine the state of a GPIB line based on the iblines byte. Parameters ---------- value : int Value returned by iblines. line : int One of constants.VI_ATTR_GPIB_***_STATE where the *** can be REN, ATN, NDAC, or SRQ Returns ------- constants.LineState State of the line StatusCode Library StatusCode for the operation. """ if line == constants.VI_ATTR_GPIB_REN_STATE: # REN bit valid = 0x10, REN bit value = 0x100 validity_mask = 0x10 value_mask = 0x100 elif line == constants.VI_ATTR_GPIB_ATN_STATE: # ATN bit valid = 0x40, ATN bit value = 0x4000 validity_mask = 0x40 value_mask = 0x4000 elif line == constants.VI_ATTR_GPIB_NDAC_STATE: # NDAC bit valid = 0x2, NDAC bit value = 0x200 validity_mask = 0x2 value_mask = 0x200 elif line == constants.VI_ATTR_GPIB_SRQ_STATE: # SRQ bit valid = 0x20, SRQ bit value = 0x2000 validity_mask = 0x20 value_mask = 0x2000 if not value & validity_mask: return constants.LineState.unknown, StatusCode.success else: if value & value_mask: return constants.LineState.asserted, StatusCode.success else: return constants.LineState.unasserted, StatusCode.success # linux-gpib timeout constants, in seconds. See GPIBSession._set_timeout. TIMETABLE = ( 0, 10e-6, 30e-6, 100e-6, 300e-6, 1e-3, 3e-3, 10e-3, 30e-3, 100e-3, 300e-3, 1.0, 3.0, 10.0, 30.0, 100.0, 300.0, 1000.0, ) def convert_gpib_error( error: gpib.GpibError, status: int, operation: str ) -> StatusCode: """Convert a GPIB error to a VISA StatusCode. Parameters ---------- error : gpib.GpibError Error to use to determine the proper status code. status : int Status byte of the GPIB library. operation : str Name of the operation that caused an exception. Used in logging. Returns ------- StatusCode Status code matching the GPIB error. """ # First check the imeout condition in the status byte if status & 0x4000: return StatusCode.error_timeout # All other cases are hard errors. # In particular linux-gpib simply gives a string we could parse but that # feels brittle. As a consequence we only try to be smart when using # gpib-ctypes. However in both cases we log the exception at debug level. else: logger.debug("Failed to %s.", exc_info=error) if not GPIB_CTYPES: return StatusCode.error_system_error if error.code == 1: return StatusCode.error_not_cic elif error.code == 2: return StatusCode.error_no_listeners elif error.code == 4: return StatusCode.error_invalid_mode elif error.code == 11: return StatusCode.error_nonsupported_operation elif error.code == 1: return StatusCode.error_not_cic elif error.code == 21: return StatusCode.error_resource_locked else: return StatusCode.error_system_error def convert_gpib_status(status: int) -> StatusCode: if status & 0x4000: return StatusCode.error_timeout elif status & 0x8000: return StatusCode.error_system_error else: return StatusCode.success class _GPIBCommon(Session): """Common base class for GPIB sessions. Both INSTR and INTFC resources share the following attributes: - VI_ATTR_INTF_TYPE - VI_ATTR_TMO_VALUE - VI_ATTR_INTF_INST_NAME - VI_ATTR_INTF_NUM - VI_ATTR_DMA_ALLOW_EN - VI_ATTR_SEND_END_EN - VI_ATTR_TERMCHAR - VI_ATTR_TERM_CHAR_EN - VI_ATTR_RD_BUF_OPER_MODE - VI_ATTR_WR_BUF_OPER_MODE - VI_ATTR_FILE_APPEND_EN - VI_ATTR_GPIB_PRIMARY_ADDR - VI_ATTR_GPIB_SECONDARY_ADDR - VI_ATTR_GPIB_REN_STATE """ # Override parsed to take into account the fact that this class is only used # for a specific kind of resource parsed: Union[GPIBIntfc, GPIBInstr] #: Bus wide controller. controller: Gpib @classmethod def get_low_level_info(cls) -> str: try: ver = gpib.version() except AttributeError: ver = "< 4.0" return "via Linux GPIB (%s)" % ver def after_parsing(self) -> None: minor = int(self.parsed.board) sad = 0 timeout = 13 send_eoi = 1 eos_mode = 0 self.interface = None if isinstance(self.parsed, GPIBInstr): pad = int(self.parsed.primary_address) # Used to talk to a specific resource self.interface = Gpib( name=minor, pad=pad, sad=sad, timeout=timeout, send_eoi=send_eoi, eos_mode=eos_mode, ) # Bus wide operation self.controller = Gpib(name=minor) # Force timeout setting to interface self.set_attribute( constants.ResourceAttribute.timeout_value, attributes.AttributesByID[constants.VI_ATTR_TMO_VALUE].default, ) for name in ("TERMCHAR", "TERMCHAR_EN"): attribute = getattr(constants, "VI_ATTR_" + name) self.attrs[attribute] = attributes.AttributesByID[attribute].default def _get_timeout( self, attribute: constants.ResourceAttribute ) -> Tuple[int, StatusCode]: if self.interface: # 0x3 is the hexadecimal reference to the IbaTMO (timeout) configuration # option in linux-gpib. gpib_timeout = self.interface.ask(3) if gpib_timeout and gpib_timeout < len(TIMETABLE): self.timeout = TIMETABLE[gpib_timeout] else: # value is 0 or out of range -> infinite self.timeout = None return super(_GPIBCommon, self)._get_timeout(attribute) def _set_timeout(self, attribute: constants.ResourceAttribute, value: int): """Set the timeout value. linux-gpib only supports 18 discrete timeout values. If a timeout value other than these is requested, it will be rounded up to the closest available value. Values greater than the largest available timout value will instead be rounded down. The available timeout values are: 0 Never timeout. 1 10 microseconds 2 30 microseconds 3 100 microseconds 4 300 microseconds 5 1 millisecond 6 3 milliseconds 7 10 milliseconds 8 30 milliseconds 9 100 milliseconds 10 300 milliseconds 11 1 second 12 3 seconds 13 10 seconds 14 30 seconds 15 100 seconds 16 300 seconds 17 1000 seconds """ status = super(_GPIBCommon, self)._set_timeout(attribute, value) # Inspect the result of setting the value to decide how to translate the result # on the interface. if self.interface: if self.timeout is None: gpib_timeout = 0 else: # round up only values that are higher by 0.1% than discrete values gpib_timeout = min(bisect(TIMETABLE, 0.999 * self.timeout), 17) self.timeout = TIMETABLE[gpib_timeout] self.interface.timeout(gpib_timeout) return status def close(self) -> StatusCode: if self.interface: self.interface.close() self.controller.close() return StatusCode.success def read(self, count: int) -> Tuple[bytes, StatusCode]: """Reads data from device or interface synchronously. Corresponds to viRead function of the VISA library. Parameters ---------- count : int Number of bytes to be read. Returns ------- bytes Data read from the interface. StatusCode Return value of the library call. """ # INTFC don't have an interface so use the controller ifc = self.interface or self.controller # END 0x2000 checker = lambda current: ifc.ibsta() & 0x2000 reader = lambda: ifc.read(count) return self._read(reader, count, checker, False, None, False, gpib.GpibError) def write(self, data: bytes) -> Tuple[int, StatusCode]: """Writes data to device or interface synchronously. Corresponds to viWrite function of the VISA library. Parameters ---------- data : bytes Data to be written. Returns ------- int Number of bytes actually transferred StatusCode Return value of the library call. """ logger.debug("GPIB.write %r" % data) # INTFC don't have an interface so use the controller ifc = self.interface or self.controller try: ifc.write(data) count = ifc.ibcnt() # number of bytes transmitted return count, StatusCode.success except gpib.GpibError as e: return 0, convert_gpib_error(e, ifc.ibsta(), "write") def gpib_control_ren(self, mode: constants.RENLineOperation) -> StatusCode: """Controls the state of the GPIB Remote Enable (REN) interface line. Optionally the remote/local state of the device is also controlled. Corresponds to viGpibControlREN function of the VISA library. Parameters ---------- mode : constants.RENLineOperation Specifies the state of the REN line and optionally the device remote/local state. Returns ------- StatusCode Return value of the library call. """ if isinstance(self.parsed, GPIBIntfc): if mode not in ( constants.VI_GPIB_REN_ASSERT, constants.VI_GPIB_REN_DEASSERT, constants.VI_GPIB_REN_ASSERT_LLO, ): return constants.StatusCode.error_nonsupported_operation # INTFC don't have an interface so use the controller ifc = self.interface or self.controller try: if mode == constants.VI_GPIB_REN_DEASSERT_GTL: # Send GTL command byte (cf linux-gpib documentation) ifc.command(chr(1)) if mode in ( constants.VI_GPIB_REN_DEASSERT, constants.VI_GPIB_REN_DEASSERT_GTL, ): self.controller.remote_enable(0) if mode == constants.VI_GPIB_REN_ASSERT_LLO: # LLO ifc.command(b"0x11") elif mode == constants.VI_GPIB_REN_ADDRESS_GTL: # GTL ifc.command(b"0x1") elif mode == constants.VI_GPIB_REN_ASSERT_ADDRESS_LLO: pass elif mode in ( constants.VI_GPIB_REN_ASSERT, constants.VI_GPIB_REN_ASSERT_ADDRESS, ): ifc.remote_enable(1) if ( isinstance(self.parsed, GPIBInstr) and mode == constants.VI_GPIB_REN_ASSERT_ADDRESS ): # 0 for the secondary address means don't use it ifc.listener( self.parsed.primary_address, self.parsed.secondary_address ) except gpib.GpibError as e: return convert_gpib_error(e, self.interface.ibsta(), "perform control REN") return constants.StatusCode.success def _get_attribute(self, attribute: ResourceAttribute) -> Tuple[Any, StatusCode]: """Get the value for a given VISA attribute for this session. Use to implement custom logic for attributes. Parameters ---------- attribute : ResourceAttribute Attribute for which the state query is made Returns ------- Any The state of the queried attribute for a specified resource StatusCode Return value of the library call. """ # TODO implement the following attributes # - VI_ATTR_INTF_INST_NAME RO # - VI_ATTR_DMA_ALLOW_EN RW # - VI_ATTR_RD_BUF_OPER_MODE RW # - VI_ATTR_WR_BUF_OPER_MODE RW # - VI_ATTR_FILE_APPEND_EN RW # INTFC don't have an interface so use the controller ifc = self.interface or self.controller if attribute == ResourceAttribute.gpib_primary_address: # IbaPAD 0x1 return ifc.ask(1), StatusCode.success elif attribute == ResourceAttribute.gpib_secondary_address: # IbaSAD 0x2 # Remove 0x60 because National Instruments. _ = ifc.ask(2) if ifc.ask(2): return ifc.ask(2) - 96, StatusCode.success else: return constants.VI_NO_SEC_ADDR, StatusCode.success elif attribute == ResourceAttribute.gpib_ren_state: try: lines = self.controller.lines() return _analyse_lines_value(lines, attribute) except AttributeError: # some versions of linux-gpib do not expose Gpib.lines() return constants.VI_STATE_UNKNOWN, StatusCode.success elif attribute == ResourceAttribute.send_end_enabled: # Do not use IbaEndBitIsNormal 0x1a which relates to EOI on read() # not write(). see issue #196 # IbcEOT 0x4 if ifc.ask(4): return constants.VI_TRUE, StatusCode.success else: return constants.VI_FALSE, StatusCode.success elif attribute == ResourceAttribute.interface_number: # IbaBNA 0x200 return ifc.ask(512), StatusCode.success elif attribute == ResourceAttribute.interface_type: return constants.InterfaceType.gpib, StatusCode.success raise UnknownAttribute(attribute) def _set_attribute( self, attribute: constants.ResourceAttribute, attribute_state: Any ) -> StatusCode: """Sets the state of an attribute. Corresponds to viSetAttribute function of the VISA library. Parameters ---------- attribute : constants.ResourceAttribute Attribute for which the state is to be modified. (Attributes.*) attribute_state : Any The state of the attribute to be set for the specified object. Returns ------- StatusCode Return value of the library call. """ # TODO implement the following attributes # - VI_ATTR_DMA_ALLOW_EN RW # - VI_ATTR_RD_BUF_OPER_MODE RW # - VI_ATTR_WR_BUF_OPER_MODE RW # - VI_ATTR_FILE_APPEND_EN RW # INTFC don't have an interface so use the controller ifc = self.interface or self.controller if attribute == ResourceAttribute.gpib_readdress_enabled: # IbcREADDR 0x6 # Setting has no effect in linux-gpib. if isinstance(attribute_state, int): ifc.config(6, attribute_state) return StatusCode.success else: return StatusCode.error_nonsupported_attribute_state elif attribute == ResourceAttribute.gpib_primary_address: # IbcPAD 0x1 if isinstance(attribute_state, int) and 0 <= attribute_state <= 30: ifc.config(1, attribute_state) return StatusCode.success else: return StatusCode.error_nonsupported_attribute_state elif attribute == ResourceAttribute.gpib_secondary_address: # IbcSAD 0x2 # Add 0x60 because National Instruments. if isinstance(attribute_state, int) and 0 <= attribute_state <= 30: if ifc.ask(2): ifc.config(2, attribute_state + 96) return StatusCode.success else: return StatusCode.error_nonsupported_attribute else: return StatusCode.error_nonsupported_attribute_state elif attribute == ResourceAttribute.gpib_unadress_enable: # IbcUnAddr 0x1b try: ifc.config(27, attribute_state) return StatusCode.success except gpib.GpibError: return StatusCode.error_nonsupported_attribute_state elif attribute == ResourceAttribute.send_end_enabled: # Do not use IbaEndBitIsNormal 0x1a which relates to EOI on read() # not write(). see issue #196 # IbcEOT 0x4 if isinstance(attribute_state, int): ifc.config(4, attribute_state) return StatusCode.success else: return StatusCode.error_nonsupported_attribute_state raise UnknownAttribute(attribute) # TODO: Check secondary addresses. @Session.register(constants.InterfaceType.gpib, "INSTR") class GPIBSession(_GPIBCommon): """A GPIB Session that uses linux-gpib to do the low level communication.""" # Override parsed to take into account the fact that this class is only used # for a specific kind of resource parsed: GPIBInstr @staticmethod def list_resources() -> List[str]: return ["GPIB%d::%d::INSTR" % (board, pad) for board, pad in _find_listeners()] def clear(self) -> StatusCode: """Clears a device. Corresponds to viClear function of the VISA library. Returns ------- StatusCode Return value of the library call. """ logger.debug("GPIB.device clear") try: self.interface.clear() return StatusCode.success except gpib.GpibError as e: return convert_gpib_error(e, self.interface.ibsta(), "clear") def assert_trigger(self, protocol: constants.TriggerProtocol) -> StatusCode: """Asserts hardware trigger. Parameters ---------- protocol : constants.TriggerProtocol Triggering protocol to use. Only supports constants.TriggerProtocol.default Returns ------- StatusCode Return value of the library call. """ logger.debug("GPIB.device assert hardware trigger") try: if protocol == constants.VI_TRIG_PROT_DEFAULT: self.interface.trigger() return StatusCode.success else: return StatusCode.error_nonsupported_operation except gpib.GpibError as e: return convert_gpib_error(e, self.interface.ibsta(), "assert trigger") def read_stb(self) -> Tuple[int, StatusCode]: """Read the device status byte.""" try: return self.interface.serial_poll(), StatusCode.success except gpib.GpibError as e: return 0, convert_gpib_error(e, self.interface.ibsta(), "read STB") def _get_attribute( self, attribute: constants.ResourceAttribute ) -> Tuple[Any, StatusCode]: """Get the value for a given VISA attribute for this session. Use to implement custom logic for attributes. GPIB::INSTR have the following specific attributes: - VI_ATTR_TRIG_ID - VI_ATTR_IO_PROT - VI_ATTR_SUPPRESS_END_EN - VI_ATTR_GPIB_READDR_EN - VI_ATTR_GPIB_UNADDR_EN Parameters ---------- attribute : constants.ResourceAttribute Resource attribute for which the state query is made Returns ------- Any State of the queried attribute for a specified resource StatusCode Return value of the library call. """ # TODO implement the following attributes # - VI_ATTR_TRIG_ID RW or RO see specs # - VI_ATTR_IO_PROT RW # - VI_ATTR_SUPPRESS_END_EN RW ifc = self.interface if attribute == constants.VI_ATTR_GPIB_READDR_EN: # IbaREADDR 0x6 # Setting has no effect in linux-gpib. return ifc.ask(6), StatusCode.success elif attribute == constants.VI_ATTR_GPIB_UNADDR_EN: # IbaUnAddr 0x1b if ifc.ask(27): return constants.VI_TRUE, StatusCode.success else: return constants.VI_FALSE, StatusCode.success return super(GPIBSession, self)._get_attribute(attribute) def _set_attribute( self, attribute: ResourceAttribute, attribute_state: Any ) -> StatusCode: """Sets the state of an attribute. Corresponds to viSetAttribute function of the VISA library. Parameters ---------- attribute : ResourceAttribute Attribute for which the state is to be modified. (Attributes.*) attribute_state : Any The state of the attribute to be set for the specified object. Returns ------- StatusCode Return value of the library call. """ # TODO implement the following attributes # - VI_ATTR_TRIG_ID RW or RO see specs # - VI_ATTR_IO_PROT RW # - VI_ATTR_SUPPRESS_END_EN RW ifc = self.interface if attribute == constants.VI_ATTR_GPIB_READDR_EN: # IbcREADDR 0x6 # Setting has no effect in linux-gpib. if isinstance(attribute_state, int): ifc.config(6, attribute_state) return StatusCode.success else: return StatusCode.error_nonsupported_attribute_state elif attribute == constants.VI_ATTR_GPIB_UNADDR_EN: # IbcUnAddr 0x1b try: ifc.config(27, attribute_state) return StatusCode.success except gpib.GpibError: return StatusCode.error_nonsupported_attribute_state return super(GPIBSession, self)._set_attribute(attribute, attribute_state) @Session.register(constants.InterfaceType.gpib, "INTFC") class GPIBInterface(_GPIBCommon): """A GPIB Interface that uses linux-gpib to do the low level communication.""" # Override parsed to take into account the fact that this class is only used # for a specific kind of resource parsed: GPIBIntfc @staticmethod def list_resources() -> List[str]: return ["GPIB%d::INTFC" % board for board, pad in _find_boards()] def gpib_command(self, command_bytes: bytes) -> Tuple[int, StatusCode]: """Write GPIB command byte on the bus. Corresponds to viGpibCommand function of the VISA library. See: https://linux-gpib.sourceforge.io/doc_html/gpib-protocol.html#REFERENCE-COMMAND-BYTES Parameters ---------- command_bytes : bytes Command bytes to send Returns ------- int Number of written bytes, StatusCode Return value of the library call. """ try: return self.controller.command(command_bytes), StatusCode.success except gpib.GpibError as e: return 0, convert_gpib_error(e, self.controller.ibsta(), "gpib command") def gpib_send_ifc(self) -> StatusCode: """Pulse the interface clear line (IFC) for at least 100 microseconds. Corresponds to viGpibSendIFC function of the VISA library. """ logger.debug("GPIB.interface clear") try: self.controller.interface_clear() return StatusCode.success except gpib.GpibError as e: return convert_gpib_error(e, self.controller.ibsta(), "send IFC") def gpib_control_atn(self, mode: constants.ATNLineOperation) -> StatusCode: """Specifies the state of the ATN line and the local active controller state. Corresponds to viGpibControlATN function of the VISA library. Parameters ---------- mode : constants.ATNLineOperation Specifies the state of the ATN line and optionally the local active controller state. Returns ------- StatusCode Return value of the library call. """ logger.debug("GPIB.control atn") if mode == constants.VI_GPIB_ATN_ASSERT: status = gpib_lib.ibcac(self.controller.id, 0) elif mode == constants.VI_GPIB_ATN_DEASSERT: status = gpib_lib.ibgts(self.controller.id, 0) elif mode == constants.VI_GPIB_ATN_ASSERT_IMMEDIATE: # Asynchronous assertion (the name is counter intuitive) status = gpib_lib.ibcac(self.controller.id, 1) elif mode == constants.VI_GPIB_ATN_DEASSERT_HANDSHAKE: status = gpib_lib.ibgts(self.controller.id, 1) else: return constants.StatusCode.error_invalid_mode return convert_gpib_status(status) def gpib_pass_control( self, primary_address: int, secondary_address: int ) -> StatusCode: """Tell a GPIB device to become controller in charge (CIC). Corresponds to viGpibPassControl function of the VISA library. Parameters ---------- primary_address : int Primary address of the GPIB device to which you want to pass control. secondary_address : int Secondary address of the targeted GPIB device. If the targeted device does not have a secondary address, this parameter should contain the value Constants.VI_NO_SEC_ADDR. Returns ------- StatusCode Return value of the library call. """ # ibpct need to get the device id matching the primary and secondary address logger.debug("GPIB.pass control") try: did = gpib.dev(self.parsed.board, primary_address, secondary_address) except gpib.GpibError: logger.exception( "Failed to get id for %s, %d", primary_address, secondary_address ) return StatusCode.error_resource_not_found status = gpib_lib.ibpct(did) return convert_gpib_status(status) def _get_attribute(self, attribute: ResourceAttribute) -> Tuple[Any, StatusCode]: """Get the value for a given VISA attribute for this session. Use to implement custom logic for attributes. GPIB::INTFC have the following specific attributes: - VI_ATTR_DEV_STATUS_BYTE - VI_ATTR_GPIB_ATN_STATE - VI_ATTR_GPIB_NDAC_STATE - VI_ATTR_GPIB_SRQ_STATE - VI_ATTR_GPIB_CIC_STATE - VI_ATTR_GPIB_SYS_CNTRL_STATE - VI_ATTR_GPIB_HS488_CBL_LEN - VI_ATTR_GPIB_ADDR_STATE Parameters ---------- attribute: ResourceAttribute Resource attribute for which the state query is made. Returns ------- Any The state of the queried attribute for a specified resource, StatusCode Return value of the library call. """ # TODO implement the following attributes # - VI_ATTR_DEV_STATUS_BYTE RW # - VI_ATTR_GPIB_SYS_CNTRL_STATE RW # - VI_ATTR_GPIB_HS488_CBL_LEN RO # - VI_ATTR_GPIB_ADDR_STATE RO ifc = self.controller if attribute == constants.VI_ATTR_GPIB_CIC_STATE: # ibsta CIC = 0x0020 if ifc.ibsta() & 0x0020: return constants.VI_TRUE, StatusCode.success else: return constants.VI_FALSE, StatusCode.success elif attribute in ( constants.VI_ATTR_GPIB_ATN_STATE, constants.VI_ATTR_GPIB_NDAC_STATE, constants.VI_ATTR_GPIB_SRQ_STATE, ): try: lines = ifc.lines() return _analyse_lines_value(lines, attribute) except AttributeError: # some versions of linux-gpib do not expose Gpib.lines() return constants.VI_STATE_UNKNOWN, StatusCode.success return super()._get_attribute(attribute) def _set_attribute( self, attribute: ResourceAttribute, attribute_state: Any ) -> StatusCode: """Sets the state of an attribute. Corresponds to viSetAttribute function of the VISA library. Parameters ---------- attribute : ResourceAttribute Attribute for which the state is to be modified. attribute_state : Any The state of the attribute to be set for the specified object. Returns ------- StatusCode Return value of the library call. """ # TODO implement the following attributes # - VI_ATTR_GPIB_SYS_CNTRL_STATE # - VI_ATTR_DEV_STATUS_BYTE # INTFC don't have an interface so use the controller _ = self.controller return super()._set_attribute(attribute, attribute_state) pyvisa-py-0.5.1/pyvisa_py/highlevel.py000066400000000000000000000606151373522010000200230ustar00rootroot00000000000000# -*- coding: utf-8 -*- """Highlevel wrapper of the VISA Library. :copyright: 2014-2020 by PyVISA-py Authors, see AUTHORS for more details. :license: MIT, see LICENSE for more details. """ import random from collections import OrderedDict from typing import Any, Dict, Iterable, List, Optional, Tuple, Union, cast from pyvisa import constants, highlevel, rname from pyvisa.constants import StatusCode from pyvisa.typing import VISAEventContext, VISARMSession, VISASession from pyvisa.util import LibraryPath from . import sessions from .common import logger class PyVisaLibrary(highlevel.VisaLibraryBase): """A pure Python backend for PyVISA. The object is basically a dispatcher with some common functions implemented. When a new resource object is requested to pyvisa, the library creates a Session object (that knows how to perform low-level communication operations) associated with a session handle (a number, usually refered just as session). A call to a library function is handled by PyVisaLibrary if it involves a resource agnostic function or dispatched to the correct session object (obtained from the session id). Importantly, the user is unaware of this. PyVisaLibrary behaves for the user just as NIVisaLibrary. """ #: Live session object identified by a randon session ID sessions: Dict[int, sessions.Session] # Try to import packages implementing lower level functionality. try: from .serial import SerialSession logger.debug("SerialSession was correctly imported.") except Exception as e: logger.debug("SerialSession was not imported %s." % e) try: from .usb import USBRawSession, USBSession logger.debug("USBSession and USBRawSession were correctly imported.") except Exception as e: logger.debug("USBSession and USBRawSession were not imported %s." % e) try: from .tcpip import TCPIPInstrSession, TCPIPSocketSession logger.debug("TCPIPSession was correctly imported.") except Exception as e: logger.debug("TCPIPSession was not imported %s." % e) try: from .gpib import GPIBSession logger.debug("GPIBSession was correctly imported.") except Exception as e: logger.debug("GPIBSession was not imported %s." % e) @staticmethod def get_library_paths() -> Iterable[LibraryPath]: """List a dummy library path to allow to create the library.""" return (LibraryPath("py"),) @staticmethod def get_debug_info() -> Dict[str, Union[str, List[str], Dict[str, str]]]: """Return a list of lines with backend info.""" from . import __version__ d: OrderedDict[str, Union[str, List[str], Dict[str, str]]] = OrderedDict() d["Version"] = "%s" % __version__ for key, val in sessions.Session.iter_valid_session_classes(): key_name = "%s %s" % (key[0].name.upper(), key[1]) d[key_name] = "Available " + val.get_low_level_info() for key, issue in sessions.Session.iter_session_classes_issues(): key_name = "%s %s" % (key[0].name.upper(), key[1]) d[key_name] = issue.split("\n") return d def _init(self) -> None: """Custom initialization code.""" # Map session handle to session object. self.sessions = {} def _register(self, obj: object) -> VISASession: """Creates a random but unique session handle for a session object. Register it in the sessions dictionary and return the value. """ session = None while session is None or session in self.sessions: session = random.randint(1000000, 9999999) self.sessions[session] = obj return session def open( self, session: VISARMSession, resource_name: str, access_mode: constants.AccessModes = constants.AccessModes.no_lock, open_timeout: int = constants.VI_TMO_IMMEDIATE, ) -> Tuple[VISASession, StatusCode]: """Opens a session to the specified resource. Corresponds to viOpen function of the VISA library. Parameters ---------- session : VISARMSession Resource Manager session (should always be a session returned from open_default_resource_manager()). resource_name : str Unique symbolic name of a resource. access_mode : constants.AccessModes, optional Specifies the mode by which the resource is to be accessed. open_timeout : int Specifies the maximum time period (in milliseconds) that this operation waits before returning an error. constants.VI_TMO_IMMEDIATE and constants.VI_TMO_INFINITE are used as min and max. Returns ------- VISASession Unique logical identifier reference to a session StatusCode Return value of the library call. """ try: open_timeout = int(open_timeout) except ValueError: raise ValueError( "open_timeout (%r) must be an integer (or compatible type)" % open_timeout ) try: parsed = rname.parse_resource_name(resource_name) except rname.InvalidResourceName: return ( VISASession(0), self.handle_return_value(None, StatusCode.error_invalid_resource_name), ) cls = sessions.Session.get_session_class( parsed.interface_type_const, parsed.resource_class ) sess = cls(session, resource_name, parsed, open_timeout) return self._register(sess), StatusCode.success def clear(self, session: VISASession) -> StatusCode: """Clears a device. Corresponds to viClear function of the VISA library. Parameters ---------- session : typin.VISASession Unique logical identifier to a session. Returns ------- StatusCode Return value of the library call. """ try: sess = self.sessions[session] except KeyError: return self.handle_return_value(session, StatusCode.error_invalid_object) return self.handle_return_value(session, sess.clear()) def flush( self, session: VISASession, mask: constants.BufferOperation ) -> StatusCode: """Flush the specified buffers. The buffers can be associated with formatted I/O operations and/or serial communication. Corresponds to viFlush function of the VISA library. Parameters ---------- session : VISASession Unique logical identifier to a session. mask : constants.BufferOperation Specifies the action to be taken with flushing the buffer. The values can be combined using the | operator. However multiple operations on a single buffer cannot be combined. Returns ------- StatusCode Return value of the library call. """ try: sess = self.sessions[session] except KeyError: return self.handle_return_value(session, StatusCode.error_invalid_object) return self.handle_return_value(session, sess.flush(mask)) def gpib_command( self, session: VISASession, command_byte: bytes ) -> Tuple[int, StatusCode]: """Write GPIB command bytes on the bus. Corresponds to viGpibCommand function of the VISA library. Parameters ---------- session : VISASession Unique logical identifier to a session. command_byte : bytes Data to write. Returns ------- int Number of written bytes StatusCode Return value of the library call. """ try: written, st = self.sessions[session].gpib_command(command_byte) return written, self.handle_return_value(session, st) except KeyError: return 0, self.handle_return_value(session, StatusCode.error_invalid_object) def assert_trigger( self, session: VISASession, protocol: constants.TriggerProtocol ) -> StatusCode: """Assert software or hardware trigger. Corresponds to viAssertTrigger function of the VISA library. Parameters ---------- session : VISASession Unique logical identifier to a session. protocol : constants.TriggerProtocol Trigger protocol to use during assertion. Returns ------- StatusCode Return value of the library call. """ try: return self.handle_return_value( session, self.sessions[session].assert_trigger(protocol) ) except KeyError: return self.handle_return_value(session, StatusCode.error_invalid_object) def gpib_send_ifc(self, session: VISASession) -> StatusCode: """Pulse the interface clear line (IFC) for at least 100 microseconds. Corresponds to viGpibSendIFC function of the VISA library. Parameters ---------- session : VISASession Unique logical identifier to a session. Returns ------- StatusCode Return value of the library call. """ try: return self.handle_return_value( session, self.sessions[session].gpib_send_ifc() ) except KeyError: return self.handle_return_value(session, StatusCode.error_invalid_object) def gpib_control_ren( self, session: VISASession, mode: constants.RENLineOperation ) -> StatusCode: """Controls the state of the GPIB Remote Enable (REN) interface line. Optionally the remote/local state of the device can also be set. Corresponds to viGpibControlREN function of the VISA library. Parameters ---------- session : VISASession Unique logical identifier to a session. mode : constants.RENLineOperation State of the REN line and optionally the device remote/local state. Returns ------- StatusCode Return value of the library call. """ try: return self.handle_return_value( session, self.sessions[session].gpib_control_ren(mode) ) except KeyError: return self.handle_return_value(session, StatusCode.error_invalid_object) def gpib_control_atn( self, session: VISASession, mode: constants.ATNLineOperation ) -> StatusCode: """Specifies the state of the ATN line and the local active controller state. Corresponds to viGpibControlATN function of the VISA library. Parameters ---------- session : VISASession Unique logical identifier to a session. mode : constants.ATNLineOperation State of the ATN line and optionally the local active controller state. Returns ------- StatusCode Return value of the library call. """ try: return self.handle_return_value( session, self.sessions[session].gpib_control_atn(mode) ) except KeyError: return self.handle_return_value(session, StatusCode.error_invalid_object) def gpib_pass_control( self, session: VISASession, primary_address: int, secondary_address: int ) -> StatusCode: """Tell a GPIB device to become controller in charge (CIC). Corresponds to viGpibPassControl function of the VISA library. Parameters ---------- session : VISASession Unique logical identifier to a session. primary_address : int Primary address of the GPIB device to which you want to pass control. secondary_address : int Secondary address of the targeted GPIB device. If the targeted device does not have a secondary address, this parameter should contain the value Constants.VI_NO_SEC_ADDR. Returns ------- StatusCode Return value of the library call. """ try: return self.handle_return_value( session, self.sessions[session].gpib_pass_control( primary_address, secondary_address ), ) except KeyError: return self.handle_return_value(session, StatusCode.error_invalid_object) def read_stb(self, session: VISASession) -> Tuple[int, StatusCode]: """Reads a status byte of the service request. Corresponds to viReadSTB function of the VISA library. Parameters ---------- session : VISASession Unique logical identifier to a session. Returns ------- int Service request status byte StatusCode Return value of the library call. """ try: sess = self.sessions[session] except KeyError: return 0, self.handle_return_value(session, StatusCode.error_invalid_object) stb, status_code = sess.read_stb() return stb, self.handle_return_value(session, status_code) def close( self, session: Union[VISASession, VISAEventContext, VISARMSession] ) -> StatusCode: """Closes the specified session, event, or find list. Corresponds to viClose function of the VISA library. Parameters --------- session : Union[VISASession, VISAEventContext, VISARMSession] Unique logical identifier to a session, event, resource manager. Returns ------- StatusCode Return value of the library call. """ try: sess = self.sessions[session] # The RM session directly references the library. if sess is not self: return self.handle_return_value(session, sess.close()) else: return self.handle_return_value(session, StatusCode.success) except KeyError: return self.handle_return_value(session, StatusCode.error_invalid_object) def open_default_resource_manager(self) -> Tuple[VISARMSession, StatusCode]: """This function returns a session to the Default Resource Manager resource. Corresponds to viOpenDefaultRM function of the VISA library. Returns ------- VISARMSession Unique logical identifier to a Default Resource Manager session StatusCode Return value of the library call. """ return ( cast(VISARMSession, self._register(self)), self.handle_return_value(None, StatusCode.success), ) def list_resources( self, session: VISARMSession, query: str = "?*::INSTR" ) -> Tuple[str, ...]: """Return a tuple of all connected devices matching query. Parameters ---------- session : VISARMSession Unique logical identifier to the resource manager session. query : str Regular expression used to match devices. Returns ------- Tuple[str, ...] Resource names of all the connected devices matching the query. """ # For each session type, ask for the list of connected resources and # merge them into a single list. # HINT: the cast should not be necessary here resources: List[str] = [] for key, st in sessions.Session.iter_valid_session_classes(): resources += st.list_resources() return rname.filter(resources, query) def read(self, session: VISASession, count: int) -> Tuple[bytes, StatusCode]: """Reads data from device or interface synchronously. Corresponds to viRead function of the VISA library. Parameters ---------- session : VISASession Unique logical identifier to a session. count : int Number of bytes to be read. Returns ------- bytes Date read StatusCode Return value of the library call. """ # from the session handle, dispatch to the read method of the session object. try: data, status_code = self.sessions[session].read(count) except KeyError: return ( b"", self.handle_return_value(session, StatusCode.error_invalid_object), ) return data, self.handle_return_value(session, status_code) def write(self, session: VISASession, data: bytes) -> Tuple[int, StatusCode]: """Write data to device or interface synchronously. Corresponds to viWrite function of the VISA library. Parameters ---------- session : VISASession Unique logical identifier to a session. data : bytes Data to be written. Returns ------- int Number of bytes actually transferred StatusCode Return value of the library call. """ # from the session handle, dispatch to the write method of the session object. try: written, status_code = self.sessions[session].write(data) except KeyError: return 0, self.handle_return_value(session, StatusCode.error_invalid_object) return written, self.handle_return_value(session, status_code) def buffer_read(self, session: VISASession, count: int) -> Tuple[bytes, StatusCode]: """Reads data through the use of a formatted I/O read buffer. The data can be read from a device or an interface. Corresponds to viBufRead function of the VISA library. Parameters ---------- session : VISASession\ Unique logical identifier to a session. count : int Number of bytes to be read. Returns ------- bytes Data read StatusCode Return value of the library call. """ return self.read(session, count) def buffer_write(self, session: VISASession, data: bytes) -> Tuple[int, StatusCode]: """Writes data to a formatted I/O write buffer synchronously. Corresponds to viBufWrite function of the VISA library. Parameters ---------- session : VISASession Unique logical identifier to a session. data : bytes Data to be written. Returns ------- int number of written bytes StatusCode return value of the library call. """ return self.write(session, data) def get_attribute( self, session: Union[VISASession, VISAEventContext, VISARMSession], attribute: Union[constants.ResourceAttribute, constants.EventAttribute], ) -> Tuple[Any, StatusCode]: """Retrieves the state of an attribute. Corresponds to viGetAttribute function of the VISA library. Parameters ---------- session : Union[VISASession, VISAEventContext] Unique logical identifier to a session, event, or find list. attribute : Union[constants.ResourceAttribute, constants.EventAttribute] Resource or event attribute for which the state query is made. Returns ------- Any State of the queried attribute for a specified resource StatusCode Return value of the library call. """ try: sess = self.sessions[session] except KeyError: return ( None, self.handle_return_value(session, StatusCode.error_invalid_object), ) state, status_code = sess.get_attribute( cast(constants.ResourceAttribute, attribute) ) return state, self.handle_return_value(session, status_code) def set_attribute( self, session: VISASession, attribute: constants.ResourceAttribute, attribute_state: Any, ) -> StatusCode: """Set the state of an attribute. Corresponds to viSetAttribute function of the VISA library. Parameters ---------- session : VISASession Unique logical identifier to a session. attribute : constants.ResourceAttribute Attribute for which the state is to be modified. attribute_state : Any The state of the attribute to be set for the specified object. Returns ------- StatusCode Return value of the library call. """ try: return self.handle_return_value( session, self.sessions[session].set_attribute(attribute, attribute_state), ) except KeyError: return self.handle_return_value(session, StatusCode.error_invalid_object) def lock( self, session: VISASession, lock_type: constants.Lock, timeout: int, requested_key: Optional[str] = None, ) -> Tuple[str, StatusCode]: """Establishes an access mode to the specified resources. Corresponds to viLock function of the VISA library. Parameters ---------- session : VISASession Unique logical identifier to a session. lock_type : constants.Lock Specifies the type of lock requested. timeout : int Absolute time period (in milliseconds) that a resource waits to get unlocked by the locking session before returning an error. requested_key : Optional[str], optional Requested locking key in the case of a shared lock. For an exclusive lock it should be None. Returns ------- str Key that can then be passed to other sessions to share the lock, or None for an exclusive lock. StatusCode Return value of the library call. """ try: sess = self.sessions[session] except KeyError: return ( "", self.handle_return_value(session, StatusCode.error_invalid_object), ) key, status_code = sess.lock(lock_type, timeout, requested_key) return key, self.handle_return_value(session, status_code) def unlock(self, session: VISASession) -> StatusCode: """Relinquish a lock for the specified resource. Corresponds to viUnlock function of the VISA library. Parameters ---------- session : VISASession Unique logical identifier to a session. Returns ------- StatusCode Return value of the library call. """ try: sess = self.sessions[session] except KeyError: return self.handle_return_value(session, StatusCode.error_invalid_object) return self.handle_return_value(session, sess.unlock()) def disable_event( self, session: VISASession, event_type: constants.EventType, mechanism: constants.EventMechanism, ) -> StatusCode: """Disable notification for an event type(s) via the specified mechanism(s). Corresponds to viDisableEvent function of the VISA library. Parameters ---------- session : VISASession Unique logical identifier to a session. event_type : constants.EventType Event type. mechanism : constants.EventMechanism Event handling mechanisms to be disabled. Returns ------- StatusCode Return value of the library call. """ pass def discard_events( self, session: VISASession, event_type: constants.EventType, mechanism: constants.EventMechanism, ) -> StatusCode: """Discard event occurrences for a given type and mechanisms in a session. Corresponds to viDiscardEvents function of the VISA library. Parameters ---------- session : VISASession Unique logical identifier to a session. event_type : constans.EventType Logical event identifier. mechanism : constants.EventMechanism Specifies event handling mechanisms to be discarded. Returns ------- StatusCode Return value of the library call. """ pass pyvisa-py-0.5.1/pyvisa_py/protocols/000077500000000000000000000000001373522010000175165ustar00rootroot00000000000000pyvisa-py-0.5.1/pyvisa_py/protocols/__init__.py000066400000000000000000000003461373522010000216320ustar00rootroot00000000000000# -*- coding: utf-8 -*- """Implements protocols on top of lower level libraries to talk to instruments. :copyright: 2014-2020 by PyVISA-py Authors, see AUTHORS for more details. :license: MIT, see LICENSE for more details. """ pyvisa-py-0.5.1/pyvisa_py/protocols/rpc.py000066400000000000000000000743771373522010000206760ustar00rootroot00000000000000# -*- coding: utf-8 -*- """Sun RPC version 2 -- RFC1057 This file is drawn from Python's RPC demo, updated for python 3. XXX There should be separate exceptions for the various reasons why XXX an RPC can fail, rather than using RuntimeError for everything XXX The UDP version of the protocol resends requests when it does XXX not receive a timely reply -- use only for idempotent calls! Original source: http://svn.python.org/projects/python/trunk/Demo/rpc/rpc.py :copyright: 2014-2020 by PyVISA-py Authors, see AUTHORS for more details. :license: MIT, see LICENSE for more details. """ import enum import select import socket import struct import sys import time import xdrlib from ..common import logger #: Version of the protocol RPCVERSION = 2 class MessagegType(enum.IntEnum): call = 0 reply = 1 class AuthorizationFlavor(enum.IntEnum): null = 0 unix = 1 short = 2 des = 3 class ReplyStatus(enum.IntEnum): accepted = 0 denied = 1 class AcceptStatus(enum.IntEnum): #: RPC executed successfully success = 0 #: remote hasn't exported program program_unavailable = 1 #: remote can't support version program_mismatch = 2 #: program can't support procedure procedure_unavailable = 3 #: procedure can't decode params garbage_args = 4 class RejectStatus(enum.IntEnum): #: RPC version number != 2 rpc_mismatch = 0 #: remote can't authenticate caller auth_error = 1 class AuthStatus(enum.IntEnum): ok = 0 #: bad credentials (seal broken) bad_credentials = 1 #: client must begin new session rejected_credentials = 2 #: bad verifier (seal broken) bad_verifier = 3 #: verifier expired or replayed rejected_verifier = 4 #: rejected for security reasons too_weak = 5 # Exceptions class RPCError(Exception): pass class RPCBadFormat(RPCError): pass class RPCBadVersion(RPCError): pass class RPCGarbageArgs(RPCError): pass class RPCUnpackError(RPCError): pass def make_auth_null(): return b"" class Packer(xdrlib.Packer): def pack_auth(self, auth): flavor, stuff = auth self.pack_enum(flavor) self.pack_opaque(stuff) def pack_auth_unix(self, stamp, machinename, uid, gid, gids): self.pack_uint(stamp) self.pack_string(machinename) self.pack_uint(uid) self.pack_uint(gid) self.pack_uint(len(gids)) for i in gids: self.pack_uint(i) def pack_callheader(self, xid, prog, vers, proc, cred, verf): self.pack_uint(xid) self.pack_enum(MessagegType.call) self.pack_uint(RPCVERSION) self.pack_uint(prog) self.pack_uint(vers) self.pack_uint(proc) self.pack_auth(cred) self.pack_auth(verf) # Caller must add procedure-specific part of call def pack_replyheader(self, xid, verf): self.pack_uint(xid) self.pack_enum(MessagegType.reply) self.pack_uint(ReplyStatus.accepted) self.pack_auth(verf) self.pack_enum(AcceptStatus.success) # Caller must add procedure-specific part of reply class Unpacker(xdrlib.Unpacker): def unpack_auth(self): flavor = self.unpack_enum() stuff = self.unpack_opaque() return flavor, stuff def unpack_callheader(self): xid = self.unpack_uint() temp = self.unpack_enum() if temp != MessagegType.call: raise RPCBadFormat("no CALL but %r" % (temp,)) temp = self.unpack_uint() if temp != RPCVERSION: raise RPCBadVersion("bad RPC version %r" % (temp,)) prog = self.unpack_uint() vers = self.unpack_uint() proc = self.unpack_uint() cred = self.unpack_auth() verf = self.unpack_auth() return xid, prog, vers, proc, cred, verf # Caller must add procedure-specific part of call def unpack_replyheader(self): xid = self.unpack_uint() mtype = self.unpack_enum() if mtype != MessagegType.reply: raise RPCUnpackError("no reply but %r" % (mtype,)) stat = self.unpack_enum() if stat == ReplyStatus.denied: stat = self.unpack_enum() if stat == RejectStatus.rpc_mismatch: low = self.unpack_uint() high = self.unpack_uint() raise RPCUnpackError("denied: rpc_mismatch: %r" % ((low, high),)) if stat == RejectStatus.auth_error: stat = self.unpack_uint() raise RPCUnpackError("denied: auth_error: %r" % (stat,)) raise RPCUnpackError("denied: %r" % (stat,)) if stat != ReplyStatus.accepted: raise RPCUnpackError("Neither denied nor accepted: %r" % (stat,)) verf = self.unpack_auth() stat = self.unpack_enum() if stat == AcceptStatus.program_unavailable: raise RPCUnpackError("call failed: program_unavailable") if stat == AcceptStatus.program_mismatch: low = self.unpack_uint() high = self.unpack_uint() raise RPCUnpackError("call failed: program_mismatch: %r" % ((low, high),)) if stat == AcceptStatus.procedure_unavailable: raise RPCUnpackError("call failed: procedure_unavailable") if stat == AcceptStatus.garbage_args: raise RPCGarbageArgs if stat != AcceptStatus.success: raise RPCUnpackError("call failed: %r" % (stat,)) return xid, verf # Caller must get procedure-specific part of reply class Client(object): """Common base class for clients.""" def __init__(self, host, prog, vers, port): self.host = host self.prog = prog self.vers = vers self.port = port self.lastxid = 0 # XXX should be more random? self.cred = None self.verf = None def make_call(self, proc, args, pack_func, unpack_func): # Don't normally override this (but see Broadcast) logger.debug("Make call %r, %r, %r, %r", proc, args, pack_func, unpack_func) if pack_func is None and args is not None: raise TypeError("non-null args with null pack_func") self.start_call(proc) if pack_func: pack_func(args) self.do_call() if unpack_func: result = unpack_func() else: result = None # N.B. Some devices may pad responses beyond RFC 1014 4-byte # alignment, so skip self.unpacker.done() call here which # would raise an exception in that case. See issue #225. return result def start_call(self, proc): # Don't override this self.lastxid += 1 cred = self.mkcred() verf = self.mkverf() p = self.packer p.reset() p.pack_callheader(self.lastxid, self.prog, self.vers, proc, cred, verf) p.proc = proc def do_call(self): # This MUST be overridden raise RPCError("do_call not defined") def mkcred(self): # Override this to use more powerful credentials if self.cred is None: self.cred = (AuthorizationFlavor.null, make_auth_null()) return self.cred def mkverf(self): # Override this to use a more powerful verifier if self.verf is None: self.verf = (AuthorizationFlavor.null, make_auth_null()) return self.verf def call_0(self): # Procedure 0 is always like this return self.make_call(0, None, None, None) # Record-Marking standard support def sendfrag(sock, last, frag): x = len(frag) if last: x = x | 0x80000000 header = struct.pack(">I", x) sock.send(header + frag) def _sendrecord(sock, record, fragsize=None, timeout=None): logger.debug("Sending record through %s: %r", sock, record) if timeout is not None: r, w, x = select.select([], [sock], [], timeout) if sock not in w: msg = "socket.timeout: The instrument seems to have stopped " "responding." raise socket.timeout(msg) last = False if not fragsize: fragsize = 0x7FFFFFFF while not last: record_len = len(record) if record_len <= fragsize: fragsize = record_len last = True if last: fragsize = fragsize | 0x80000000 header = struct.pack(">I", fragsize) sock.send(header + record[:fragsize]) record = record[fragsize:] def _recvrecord(sock, timeout, read_fun=None, min_packages=0): record = bytearray() buffer = bytearray() if not read_fun: read_fun = sock.recv wait_header = True last = False exp_length = 4 packages_received = 0 if min_packages != 0: logger.debug("Start receiving at least %i packages" % min_packages) # minimum is in interval 1 - 100ms based on timeout or for infinite it is # 1 sec min_select_timeout = ( max(min(timeout / 100.0, 0.1), 0.001) if timeout is not None else 1.0 ) # initial 'select_timeout' is half of timeout or max 2 secs # (max blocking time). # min is from 'min_select_timeout' select_timeout = ( max(min(timeout / 2.0, 2.0), min_select_timeout) if timeout is not None else 1.0 ) # time, when loop shall finish finish_time = time.time() + timeout if timeout is not None else 0 while True: # if more data for the current fragment is needed, use select # to wait for read ready, max `select_timeout` seconds if len(buffer) < exp_length: r, w, x = select.select([sock], [], [], select_timeout) read_data = b"" if sock in r: read_data = read_fun(exp_length) buffer.extend(read_data) logger.debug("received %r" % read_data) # Timeout was reached if not read_data: # no response or empty response if timeout is not None and time.time() >= finish_time: logger.debug( ( "Time out encountered in %s." "Already receieved %d bytes. Last fragment is %d " "bytes long and we were expecting %d" ), sock, len(record), len(buffer), exp_length, ) msg = ( "socket.timeout: The instrument seems to have stopped " "responding." ) raise socket.timeout(msg) elif min_packages != 0 and packages_received >= min_packages: logger.debug( "Stop receiving after %i of %i requested packages. Received record through %s: %r", packages_received, min_packages, sock, record, ) return bytes(record) else: # `select_timeout` decreased to 50% of previous or # min_select_timeout select_timeout = max(select_timeout / 2.0, min_select_timeout) continue if wait_header: # need to find header if len(buffer) >= exp_length: header = buffer[:exp_length] buffer = buffer[exp_length:] x = struct.unpack(">I", header)[0] last = (x & 0x80000000) != 0 exp_length = int(x & 0x7FFFFFFF) wait_header = False else: if len(buffer) >= exp_length: record.extend(buffer[:exp_length]) buffer = buffer[exp_length:] if last: logger.debug("Received record through %s: %r", sock, record) return bytes(record) else: wait_header = True exp_length = 4 packages_received += 1 def _connect(sock, host, port, timeout=0): try: sock.setblocking(0) sock.connect_ex((host, port)) except Exception: sock.close() return False finally: sock.setblocking(1) # minimum is in interval 100 - 500ms based on timeout min_select_timeout = max(min(timeout / 10.0, 0.5), 0.1) # initial 'select_timout' is half of timeout or max 2 secs # (max blocking time). # min is from 'min_select_timeout' select_timout = max(min(timeout / 2.0, 2.0), min_select_timeout) # time, when loop shall finish finish_time = time.time() + timeout while True: # use select to wait for socket ready, max `select_timout` seconds r, w, x = select.select([sock], [sock], [], select_timout) if sock in r or sock in w: return True if time.time() >= finish_time: # reached timeout return False # `select_timout` decreased to 50% of previous or min_select_timeout select_timout = max(select_timout / 2.0, min_select_timeout) class RawTCPClient(Client): """Client using TCP to a specific port.""" def __init__(self, host, prog, vers, port, open_timeout=5000): Client.__init__(self, host, prog, vers, port) self.connect((open_timeout / 1000.0) + 1.0) # self.timeout defaults higher than the default 2 second VISA timeout, # ensuring that VISA timeouts take precedence. self.timeout = 4.0 def make_call(self, proc, args, pack_func, unpack_func): """Overridden to allow for utilizing io_timeout (passed in args).""" if proc == 11: # vxi11.DEVICE_WRITE self.timeout = args[1] / 1000.0 elif proc in (12, 22): # vxi11.DEVICE_READ or vxi11.DEVICE_DOCMD self.timeout = args[2] / 1000.0 elif proc in (13, 14, 15, 16, 17): # vxi11.DEVICE_READSTB, vxi11.DEVICE_TRIGGER, vxi11.DEVICE_CLEAR, # vxi11.DEVICE_REMOTE, or vxi11.DEVICE_LOCAL self.timeout = args[3] / 1000.0 else: self.timeout = 4.0 # In case of a timeout because the instrument cannot answer, the # instrument should let use something went wrong. If we hit the hard # timeout of the rpc, it means something worse happened (cable # unplugged). self.timeout += 1.0 return super(RawTCPClient, self).make_call(proc, args, pack_func, unpack_func) def connect(self, timeout=5.0): logger.debug( "RawTCPClient: connecting to socket at (%s, %s)", self.host, self.port ) self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) if not _connect(self.sock, self.host, self.port, timeout): raise RPCError("can't connect to server") def close(self): logger.debug("RawTCPClient: closing socket") self.sock.close() def do_call(self): call = self.packer.get_buf() _sendrecord(self.sock, call, timeout=self.timeout) try: min_packages = int(self.packer.proc == 3) logger.debug("RawTCPClient: procedure type %i" % self.packer.proc) # if the command is get_port, we only expect one package. # This is a workaround for misbehaving instruments. except AttributeError: min_packages = 0 reply = _recvrecord(self.sock, self.timeout, min_packages=min_packages) u = self.unpacker u.reset(reply) xid, verf = u.unpack_replyheader() if xid != self.lastxid: # Can't really happen since this is TCP... msg = "wrong xid in reply {0} instead of {1}" raise RPCError(msg.format(xid, self.lastxid)) class RawUDPClient(Client): """Client using UDP to a specific port.""" def __init__(self, host, prog, vers, port): Client.__init__(self, host, prog, vers, port) self.connect() def connect(self): logger.debug( "RawTCPClient: connecting to socket at (%s, %s)", self.host, self.port ) self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self.sock.connect((self.host, self.port)) def close(self): logger.debug("RawTCPClient: closing socket") self.sock.close() def do_call(self): call = self.packer.get_buf() self.sock.send(call) BUFSIZE = 8192 # Max UDP buffer size timeout = 1 count = 5 while 1: r, w, x = [self.sock], [], [] if select: r, w, x = select.select(r, w, x, timeout) if self.sock not in r: count = count - 1 if count < 0: raise RPCError("timeout") if timeout < 25: timeout = timeout * 2 self.sock.send(call) continue reply = self.sock.recv(BUFSIZE) u = self.unpacker u.reset(reply) xid, verf = u.unpack_replyheader() if xid != self.lastxid: continue break class RawBroadcastUDPClient(RawUDPClient): """Client using UDP broadcast to a specific port.""" def __init__(self, bcastaddr, prog, vers, port): RawUDPClient.__init__(self, bcastaddr, prog, vers, port) self.reply_handler = None self.timeout = 30 def connect(self): self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) def set_reply_handler(self, reply_handler): self.reply_handler = reply_handler def set_timeout(self, timeout): self.timeout = timeout # Use None for infinite timeout def make_call(self, proc, args, pack_func, unpack_func): if pack_func is None and args is not None: raise TypeError("non-null args with null pack_func") self.start_call(proc) if pack_func: pack_func(args) call = self.packer.get_buf() self.sock.sendto(call, (self.host, self.port)) BUFSIZE = 8192 # Max UDP buffer size (for reply) replies = [] if unpack_func is None: def dummy(): pass unpack_func = dummy while 1: r, w, x = [self.sock], [], [] if select: if self.timeout is None: r, w, x = select.select(r, w, x) else: r, w, x = select.select(r, w, x, self.timeout) if self.sock not in r: break reply, fromaddr = self.sock.recvfrom(BUFSIZE) u = self.unpacker u.reset(reply) xid, verf = u.unpack_replyheader() if xid != self.lastxid: continue reply = unpack_func() self.unpacker.done() replies.append((reply, fromaddr)) if self.reply_handler: self.reply_handler(reply, fromaddr) return replies # Port mapper interface # Program number, version and port number PMAP_PROG = 100000 PMAP_VERS = 2 PMAP_PORT = 111 class PortMapperVersion(enum.IntEnum): #: (void) -> void null = 0 #: (mapping) -> bool set = 1 #: (mapping) -> bool unset = 2 #: (mapping) -> unsigned int get_port = 3 #: (void) -> pmaplist dump = 4 #: (call_args) -> call_result call_it = 5 # A mapping is (prog, vers, prot, port) and prot is one of: IPPROTO_TCP = 6 IPPROTO_UDP = 17 # A pmaplist is a variable-length list of mappings, as follows: # either (1, mapping, pmaplist) or (0). # A call_args is (prog, vers, proc, args) where args is opaque; # a call_result is (port, res) where res is opaque. class PortMapperPacker(Packer): def pack_mapping(self, mapping): prog, vers, prot, port = mapping self.pack_uint(prog) self.pack_uint(vers) self.pack_uint(prot) self.pack_uint(port) def pack_pmaplist(self, list): self.pack_list(list, self.pack_mapping) def pack_call_args(self, ca): prog, vers, proc, args = ca self.pack_uint(prog) self.pack_uint(vers) self.pack_uint(proc) self.pack_opaque(args) class PortMapperUnpacker(Unpacker): def unpack_mapping(self): prog = self.unpack_uint() vers = self.unpack_uint() prot = self.unpack_uint() port = self.unpack_uint() return prog, vers, prot, port def unpack_pmaplist(self): return self.unpack_list(self.unpack_mapping) def unpack_call_result(self): port = self.unpack_uint() res = self.unpack_opaque() return port, res class PartialPortMapperClient(object): def __init__(self): self.packer = PortMapperPacker() self.unpacker = PortMapperUnpacker("") def set(self, mapping): return self.make_call( PortMapperVersion.set, mapping, self.packer.pack_mapping, self.unpacker.unpack_uint, ) def unset(self, mapping): return self.make_call( PortMapperVersion.unset, mapping, self.packer.pack_mapping, self.unpacker.unpack_uint, ) def get_port(self, mapping): return self.make_call( PortMapperVersion.get_port, mapping, self.packer.pack_mapping, self.unpacker.unpack_uint, ) def dump(self): return self.make_call( PortMapperVersion.dump, None, None, self.unpacker.unpack_pmaplist ) def callit(self, ca): return self.make_call( PortMapperVersion.call_it, ca, self.packer.pack_call_args, self.unpacker.unpack_call_result, ) class TCPPortMapperClient(PartialPortMapperClient, RawTCPClient): def __init__(self, host, open_timeout=5000): RawTCPClient.__init__(self, host, PMAP_PROG, PMAP_VERS, PMAP_PORT, open_timeout) PartialPortMapperClient.__init__(self) class UDPPortMapperClient(PartialPortMapperClient, RawUDPClient): def __init__(self, host): RawUDPClient.__init__(self, host, PMAP_PROG, PMAP_VERS, PMAP_PORT) PartialPortMapperClient.__init__(self) class BroadcastUDPPortMapperClient(PartialPortMapperClient, RawBroadcastUDPClient): def __init__(self, bcastaddr): RawBroadcastUDPClient.__init__(self, bcastaddr, PMAP_PROG, PMAP_VERS, PMAP_PORT) PartialPortMapperClient.__init__(self) class TCPClient(RawTCPClient): """A TCP Client that find their server through the Port mapper""" def __init__(self, host, prog, vers, open_timeout=5000): pmap = TCPPortMapperClient(host, open_timeout) port = pmap.get_port((prog, vers, IPPROTO_TCP, 0)) pmap.close() if port == 0: raise RPCError("program not registered") RawTCPClient.__init__(self, host, prog, vers, port, open_timeout) class UDPClient(RawUDPClient): """A UDP Client that find their server through the Port mapper""" def __init__(self, host, prog, vers): pmap = UDPPortMapperClient(host) port = pmap.get_port((prog, vers, IPPROTO_UDP, 0)) pmap.close() if port == 0: raise RPCError("program not registered") RawUDPClient.__init__(self, host, prog, vers, port) class BroadcastUDPClient(Client): """A Broadcast UDP Client that find their server through the Port mapper""" def __init__(self, bcastaddr, prog, vers): self.pmap = BroadcastUDPPortMapperClient(bcastaddr) self.pmap.set_reply_handler(self.my_reply_handler) self.prog = prog self.vers = vers self.user_reply_handler = None self.addpackers() def close(self): self.pmap.close() def set_reply_handler(self, reply_handler): self.user_reply_handler = reply_handler def set_timeout(self, timeout): self.pmap.set_timeout(timeout) def my_reply_handler(self, reply, fromaddr): port, res = reply self.unpacker.reset(res) result = self.unpack_func() self.unpacker.done() self.replies.append((result, fromaddr)) if self.user_reply_handler is not None: self.user_reply_handler(result, fromaddr) def make_call(self, proc, args, pack_func, unpack_func): self.packer.reset() if pack_func: pack_func(args) if unpack_func is None: def dummy(): pass self.unpack_func = dummy else: self.unpack_func = unpack_func self.replies = [] packed_args = self.packer.get_buf() _ = self.pmap.Callit((self.prog, self.vers, proc, packed_args)) return self.replies # Server classes # These are not symmetric to the Client classes # XXX No attempt is made to provide authorization hooks yet class Server(object): def __init__(self, host, prog, vers, port): self.host = host # Should normally be '' for default interface self.prog = prog self.vers = vers self.port = port # Should normally be 0 for random port self.port = port self.addpackers() def register(self): mapping = self.prog, self.vers, self.prot, self.port p = TCPPortMapperClient(self.host) if not p.set(mapping): raise RPCError("register failed") def unregister(self): mapping = self.prog, self.vers, self.prot, self.port p = TCPPortMapperClient(self.host) if not p.unset(mapping): raise RPCError("unregister failed") def handle(self, call): # Don't use unpack_header but parse the header piecewise # XXX I have no idea if I am using the right error responses! self.unpacker.reset(call) self.packer.reset() xid = self.unpacker.unpack_uint() self.packer.pack_uint(xid) temp = self.unpacker.unpack_enum() if temp != MessagegType.call: return None # Not worthy of a reply self.packer.pack_uint(MessagegType.reply) temp = self.unpacker.unpack_uint() if temp != RPCVERSION: self.packer.pack_uint(ReplyStatus.denied) self.packer.pack_uint(RejectStatus.rpc_mismatch) self.packer.pack_uint(RPCVERSION) self.packer.pack_uint(RPCVERSION) return self.packer.get_buf() self.packer.pack_uint(ReplyStatus.accepted) self.packer.pack_auth((AuthorizationFlavor.null, make_auth_null())) prog = self.unpacker.unpack_uint() if prog != self.prog: self.packer.pack_uint(AcceptStatus.program_unavailable) return self.packer.get_buf() vers = self.unpacker.unpack_uint() if vers != self.vers: self.packer.pack_uint(AcceptStatus.program_mismatch) self.packer.pack_uint(self.vers) self.packer.pack_uint(self.vers) return self.packer.get_buf() proc = self.unpacker.unpack_uint() methname = "handle_" + repr(proc) try: meth = getattr(self, methname) except AttributeError: self.packer.pack_uint(AcceptStatus.procedure_unavailable) return self.packer.get_buf() cred = self.unpacker.unpack_auth() # noqa verf = self.unpacker.unpack_auth() # noqa try: meth() # Unpack args, call turn_around(), pack reply except (EOFError, RPCGarbageArgs): # Too few or too many arguments self.packer.reset() self.packer.pack_uint(xid) self.packer.pack_uint(MessagegType.reply) self.packer.pack_uint(ReplyStatus.accepted) self.packer.pack_auth((AuthorizationFlavor.null, make_auth_null())) self.packer.pack_uint(AcceptStatus.garbage_args) return self.packer.get_buf() def turn_around(self): try: self.unpacker.done() except RuntimeError: raise RPCGarbageArgs self.packer.pack_uint(AcceptStatus.success) def handle_0(self): # Handle NULL message self.turn_around() def addpackers(self): # Override this to use derived classes from Packer/Unpacker self.packer = Packer() self.unpacker = Unpacker("") class TCPServer(Server): def __init__(self, host, prog, vers, port): Server.__init__(self, host, prog, vers, port) self.connect() def connect(self): self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.prot = IPPROTO_TCP self.sock.bind((self.host, self.port)) def loop(self): self.sock.listen(0) while 1: self.session(self.sock.accept()) def session(self, connection): sock, (host, port) = connection while 1: try: call = _recvrecord(sock, None) except EOFError: break except socket.error: logger.exception("socket error: %r", sys.exc_info()[0]) break reply = self.handle(call) if reply is not None: _sendrecord(sock, reply) def forkingloop(self): # Like loop but uses forksession() self.sock.listen(0) while 1: self.forksession(self.sock.accept()) def forksession(self, connection): # Like session but forks off a subprocess import os # Wait for deceased children try: while 1: pid, sts = os.waitpid(0, 1) except os.error: pass pid = None try: pid = os.fork() if pid: # Parent connection[0].close() return # Child self.session(connection) finally: # Make sure we don't fall through in the parent if pid == 0: os._exit(0) class UDPServer(Server): def __init__(self, host, prog, vers, port): Server.__init__(self, host, prog, vers, port) self.connect() def connect(self): self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self.prot = IPPROTO_UDP self.sock.bind((self.host, self.port)) def loop(self): while 1: self.session() def session(self): call, host_port = self.sock.recvfrom(8192) reply = self.handle(call) if reply is not None: self.sock.sendto(reply, host_port) pyvisa-py-0.5.1/pyvisa_py/protocols/usbraw.py000066400000000000000000000044271373522010000214020ustar00rootroot00000000000000# -*- coding: utf-8 -*- """Implements Session to control USB Raw devices Loosely based on PyUSBTMC:python module to handle USB-TMC(Test and Measurement class) devices. by Noboru Yamamot, Accl. Lab, KEK, JAPAN This file is an offspring of the Lantz Project. :copyright: 2014-2020 by PyVISA-py Authors, see AUTHORS for more details. :license: MIT, see LICENSE for more details. """ from .usbtmc import USBRaw as USBRaw from .usbutil import find_devices, find_interfaces def find_raw_devices( vendor=None, product=None, serial_number=None, custom_match=None, **kwargs ): """Find connected USB RAW devices. See usbutil.find_devices for more info.""" def is_usbraw(dev): if custom_match and not custom_match(dev): return False return bool(find_interfaces(dev, bInterfaceClass=0xFF, bInterfaceSubClass=0xFF)) return find_devices(vendor, product, serial_number, is_usbraw, **kwargs) class USBRawDevice(USBRaw): RECV_CHUNK = 1024 ** 2 find_devices = staticmethod(find_raw_devices) def __init__(self, vendor=None, product=None, serial_number=None, **kwargs): super(USBRawDevice, self).__init__(vendor, product, serial_number, **kwargs) if not (self.usb_recv_ep and self.usb_send_ep): raise ValueError( "USBRAW device must have both Bulk-In and Bulk-out endpoints." ) def write(self, data): """Send raw bytes to the instrument. :param data: bytes to be sent to the instrument :type data: bytes """ begin, end, size = 0, 0, len(data) bytes_sent = 0 raw_write = super(USBRawDevice, self).write while not end > size: begin = end end = begin + self.RECV_CHUNK bytes_sent += raw_write(data[begin:end]) return bytes_sent def read(self, size): """Read raw bytes from the instrument. :param size: amount of bytes to be sent to the instrument :type size: integer :return: received bytes :return type: bytes """ raw_read = super(USBRawDevice, self).read received = bytearray() while not len(received) >= size: resp = raw_read(self.RECV_CHUNK) received.extend(resp) return bytes(received) pyvisa-py-0.5.1/pyvisa_py/protocols/usbtmc.py000066400000000000000000000336431373522010000213760ustar00rootroot00000000000000# -*- coding: utf-8 -*- """Implements Session to control USBTMC instruments Loosely based on PyUSBTMC:python module to handle USB-TMC(Test and Measurement class) devices. by Noboru Yamamot, Accl. Lab, KEK, JAPAN This file is an offspring of the Lantz Project. :copyright: 2014-2020 by PyVISA-py Authors, see AUTHORS for more details. :license: MIT, see LICENSE for more details. """ import enum import struct import time import warnings from collections import namedtuple import usb from .usbutil import find_devices, find_endpoint, find_interfaces, usb_find_desc class MsgID(enum.IntEnum): """From USB-TMC table2""" dev_dep_msg_out = 1 request_dev_dep_msg_in = 2 dev_dep_msg_in = 2 vendor_specific_out = 126 request_vendor_specific_in = 127 vendor_specific_in = 127 # USB488 trigger = 128 class Request(enum.IntEnum): initiate_abort_bulk_out = 1 check_abort_bulk_out_status = 2 initiate_abort_bulk_in = 3 check_abort_bulk_in_status = 4 initiate_clear = 5 check_clear_status = 6 get_capabilities = 7 indicator_pulse = 64 # USB488 read_status_byte = 128 ren_control = 160 go_to_local = 161 local_lockout = 162 class UsbTmcStatus(enum.IntEnum): success = 1 pending = 2 failed = 0x80 transfer_not_in_progress = 0x81 split_not_in_progress = 0x82 split_in_progress = 0x83 UsbTmcCapabilities = namedtuple("UsbTmcCapabilities", "usb488 ren_control trigger") def find_tmc_devices( vendor=None, product=None, serial_number=None, custom_match=None, **kwargs ): """Find connected USBTMC devices. See usbutil.find_devices for more info.""" def is_usbtmc(dev): if custom_match and not custom_match(dev): return False return bool(find_interfaces(dev, bInterfaceClass=0xFE, bInterfaceSubClass=3)) return find_devices(vendor, product, serial_number, is_usbtmc, **kwargs) class BulkOutMessage(object): """The Host uses the Bulk-OUT endpoint to send USBTMC command messages to the device. """ @staticmethod def build_array(btag, eom, chunk): size = len(chunk) return ( struct.pack("BBBx", MsgID.dev_dep_msg_out, btag, ~btag & 0xFF) + struct.pack(" 1: desc = "\n".join(str(dev) for dev in devices) raise ValueError( "{} devices found:\n{}\nPlease narrow the search" " criteria".format(len(devices), desc) ) self.usb_dev = devices[0] try: if self.usb_dev.is_kernel_driver_active(0): self.usb_dev.detach_kernel_driver(0) except (usb.core.USBError, NotImplementedError): pass try: self.usb_dev.set_configuration() except usb.core.USBError as e: raise Exception("failed to set configuration\n %s" % e) try: self.usb_dev.set_interface_altsetting() except usb.core.USBError: pass self.usb_intf = self._find_interface(self.usb_dev, self.INTERFACE) self.usb_recv_ep, self.usb_send_ep = self._find_endpoints( self.usb_intf, self.ENDPOINTS ) def _find_interface(self, dev, setting): return self.usb_dev.get_active_configuration()[self.INTERFACE] def _find_endpoints(self, interface, setting): recv, send = setting if recv is None: recv = find_endpoint(interface, usb.ENDPOINT_IN, usb.ENDPOINT_TYPE_BULK) else: recv = usb_find_desc(interface, bEndpointAddress=recv) if send is None: send = find_endpoint(interface, usb.ENDPOINT_OUT, usb.ENDPOINT_TYPE_BULK) else: send = usb_find_desc(interface, bEndpointAddress=send) return recv, send def write(self, data): """Send raw bytes to the instrument. :param data: bytes to be sent to the instrument :type data: bytes """ try: return self.usb_send_ep.write(data) except usb.core.USBError as e: raise ValueError(str(e)) def read(self, size): """Receive raw bytes to the instrument. :param size: number of bytes to receive :return: received bytes :return type: bytes """ if size <= 0: size = 1 data = self.usb_recv_ep.read(size, self.timeout).tobytes() return data def close(self): return usb.util.dispose_resources(self.usb_dev) class USBTMC(USBRaw): # Maximum number of bytes per transfer (for sending and receiving). RECV_CHUNK = 1024 ** 2 find_devices = staticmethod(find_tmc_devices) def __init__(self, vendor=None, product=None, serial_number=None, **kwargs): super(USBTMC, self).__init__(vendor, product, serial_number, **kwargs) self.usb_intr_in = find_endpoint( self.usb_intf, usb.ENDPOINT_IN, usb.ENDPOINT_TYPE_INTERRUPT ) self.usb_dev.reset() self.usb_dev.set_configuration() time.sleep(0.01) self._capabilities = self._get_capabilities() self._btag = 0 if not (self.usb_recv_ep and self.usb_send_ep): msg = "TMC device must have both Bulk-In and Bulk-out endpoints." raise ValueError(msg) self._enable_remote_control() def _enable_remote_control(self): if not self._capabilities.ren_control: return self.usb_dev.ctrl_transfer( usb.util.build_request_type( usb.util.CTRL_IN, usb.util.CTRL_TYPE_CLASS, usb.util.CTRL_RECIPIENT_INTERFACE, ), Request.ren_control, 1, self.usb_intf.index, 1, timeout=self.timeout, ) def _get_capabilities(self): c = self.usb_dev.ctrl_transfer( usb.util.build_request_type( usb.util.CTRL_IN, usb.util.CTRL_TYPE_CLASS, usb.util.CTRL_RECIPIENT_INTERFACE, ), Request.get_capabilities, 0x0000, self.usb_intf.index, 0x0018, timeout=self.timeout, ) usb488_capabilities = c[0xE] # bit #2: The interface is a 488.2 USB488 interface. # bit #1: The interface accepts REN_CONTROL, GO_TO_LOCAL, # and LOCAL_LOCKOUT requests. # bit #0: The interface accepts the MsgID = TRIGGER # USBTMC command message and forwards # TRIGGER requests to the Function Layer. return UsbTmcCapabilities( usb488=bool(usb488_capabilities & (1 << 2)), ren_control=bool(usb488_capabilities & (1 << 1)), trigger=bool(usb488_capabilities & (1 << 0)), ) def _find_interface(self, dev, setting): interfaces = find_interfaces(dev, bInterfaceClass=0xFE, bInterfaceSubClass=3) if not interfaces: raise ValueError("USB TMC interface not found.") elif len(interfaces) > 1: pass return interfaces[0] def _abort_bulk_in(self, btag): """Request that the device abort a pending Bulk-IN operation.""" abort_timeout_ms = 5000 # Send INITIATE_ABORT_BULK_IN. # According to USBTMC 1.00 4.2.1.4: # wValue = bTag value of transfer to be aborted # wIndex = Bulk-IN endpoint # wLength = 0x0002 (length of device response) data = self.usb_dev.ctrl_transfer( usb.util.build_request_type( usb.util.CTRL_IN, usb.util.CTRL_TYPE_CLASS, usb.util.CTRL_RECIPIENT_ENDPOINT, ), Request.initiate_abort_bulk_in, btag, self.usb_recv_ep.bEndpointAddress, 0x0002, timeout=abort_timeout_ms, ) if data[0] != UsbTmcStatus.success: # Abort Bulk-IN failed. Ignore it. return # Read remaining data from Bulk-IN endpoint. self.usb_recv_ep.read(self.RECV_CHUNK, abort_timeout_ms) # Send CHECK_ABORT_BULK_IN_STATUS until it completes. # According to USBTMC 1.00 4.2.1.5: # wValue = 0x0000 # wIndex = Bulk-IN endpoint # wLength = 0x0008 (length of device response) for retry in range(100): data = self.usb_dev.ctrl_transfer( usb.util.build_request_type( usb.util.CTRL_IN, usb.util.CTRL_TYPE_CLASS, usb.util.CTRL_RECIPIENT_ENDPOINT, ), Request.check_abort_bulk_in_status, 0x0000, self.usb_recv_ep.bEndpointAddress, 0x0008, timeout=abort_timeout_ms, ) if data[0] != UsbTmcStatus.pending: break time.sleep(0.05) def write(self, data): """Send raw bytes to the instrument. :param data: bytes to be sent to the instrument :type data: bytes """ begin, end, size = 0, 0, len(data) bytes_sent = 0 raw_write = super(USBTMC, self).write # Send all data via one or more Bulk-OUT transfers. # Set the EOM flag on the last transfer only. # Send at least one transfer (possibly empty). while (end == 0) or (end < size): begin, end = end, begin + self.RECV_CHUNK self._btag = (self._btag % 255) + 1 eom = end >= size data = BulkOutMessage.build_array(self._btag, eom, data[begin:end]) bytes_sent += raw_write(data) return size def read(self, size): recv_chunk = self.RECV_CHUNK if size > 0 and size < recv_chunk: recv_chunk = size header_size = 12 max_padding = 511 eom = False raw_read = super(USBTMC, self).read raw_write = super(USBTMC, self).write received = bytearray() while not eom: self._btag = (self._btag % 255) + 1 req = BulkInMessage.build_array(self._btag, recv_chunk, None) raw_write(req) try: resp = raw_read(recv_chunk + header_size + max_padding) response = BulkInMessage.from_bytes(resp) except (usb.core.USBError, ValueError): # Abort failed Bulk-IN operation. self._abort_bulk_in(self._btag) raise received.extend(response.data) # Detect EOM only when device sends all expected bytes. if len(response.data) >= response.transfer_size: eom = response.transfer_attributes & 1 return bytes(received) pyvisa-py-0.5.1/pyvisa_py/protocols/usbutil.py000066400000000000000000000223011373522010000215550ustar00rootroot00000000000000# -*- coding: utf-8 -*- """Serial Session implementation using PyUSB. See the following link for more information about USB. http://www.beyondlogic.org/usbnutshell/usb5.shtml This file is an offspring of the Lantz Project. :copyright: 2014-2020 by PyVISA-py Authors, see AUTHORS for more details. :license: MIT, see LICENSE for more details. """ from fnmatch import fnmatch import usb from usb.util import find_descriptor as usb_find_desc ClassCodes = { 0x00: ("Device", "Use class information in the Interface Descriptors"), 0x01: ("Interface", "Audio"), 0x02: ("Both", "Communications and CDC Control"), 0x03: ("Interface", "HID (Human Interface Device)"), 0x05: ("Interface", "Physical"), 0x06: ("Interface", "Image"), 0x07: ("Interface", "Printer"), 0x08: ("Interface", "Mass Storage"), 0x09: ("Device", "Hub"), 0x0A: ("Interface", "CDC-Data"), 0x0B: ("Interface", "Smart Card"), 0x0D: ("Interface", "Content Security"), 0x0E: ("Interface", "Video"), 0x0F: ("Interface", "Personal Healthcare"), 0x10: ("Interface", "Audio/Video Devices"), 0xDC: ("Both", "Diagnostic Device"), 0xE0: ("Interface", "Wireless Controller"), 0xEF: ("Both", "Miscellaneous"), 0xFE: ("Interface", "Application Specific"), 0xFF: ("Both", "Vendor Specific"), } # None is 0xxx AllCodes = { (0x00, 0x00, 0x00): "Use class code info from Interface Descriptors", (0x01, None, None): "Audio device", (0x02, None, None): "Communication device class", (0x03, None, None): "HID device class", (0x05, None, None): "Physical device class", (0x06, 0x01, 0x01): "Still Imaging device", (0x07, None, None): "Printer device", (0x08, None, None): "Mass Storage device", (0x09, 0x00, 0x00): "Full speed Hub", (0x09, 0x00, 0x01): "Hi-speed hub with single TT", (0x09, 0x00, 0x02): "Hi-speed hub with multiple TTs", (0x0A, None, None): "CDC data device", (0x0B, None, None): "Smart Card device", (0x0D, 0x00, 0x00): "Content Security device", (0x0E, None, None): "Video device", (0x0F, None, None): "Personal Healthcare device", (0x10, 0x01, 0x00): "Control Interface", (0x10, 0x02, 0x00): "Data Video Streaming Interface", (0x10, 0x03, 0x00): "VData Audio Streaming Interface", (0xDC, 0x01, 0x01): "USB2 Compliance Device", (0xE0, 0x01, 0x01): "Bluetooth Programming Interface.", (0xE0, 0x01, 0x02): "UWB Radio Control Interface.", (0xE0, 0x01, 0x03): "Remote NDIS", (0xE0, 0x01, 0x04): "Bluetooth AMP Controller.", (0xE0, 0x2, 0x01): "Host Wire Adapter Control/Data interface.", (0xE0, 0x2, 0x02): "Device Wire Adapter Control/Data interface.", (0xE0, 0x2, 0x03): "Device Wire Adapter Isochronous interface.", (0xEF, 0x01, 0x01): "Active Sync device.", (0xEF, 0x01, 0x02): "Palm Sync. This class code can be used in either " "Device or Interface Descriptors.", (0xEF, 0x02, 0x01): "Interface Association Descriptor.", (0xEF, 0x02, 0x02): "Wire Adapter Multifunction Peripheral programming interface.", (0xEF, 0x03, 0x01): "Cable Based Association Framework.", (0xEF, 0x04, 0x01): "RNDIS over Ethernet. Connecting a host to the Internet via " "Ethernet mobile device. The device appears to the host as an" "Ethernet gateway device. This class code may only be used in " "Interface Descriptors.", (0xEF, 0x04, 0x02): "RNDIS over WiFi. Connecting a host to the Internet via WiFi " "enabled mobile device. The device represents itself to the host" "as an 802.11 compliant network device. This class code may only" "be used in Interface Descriptors.", (0xEF, 0x04, 0x03): "RNDIS over WiMAX. Connecting a host to the Internet via WiMAX " "enabled mobile device. The device is represented to the host " "as an 802.16 network device. This class code may only be used " "in Interface Descriptors.", ( 0xEF, 0x04, 0x04, ): "RNDIS over WWAN. Connecting a host to the Internet via a device " "using mobile broadband, i.e. WWAN (GSM/CDMA). This class code may " "only be used in Interface Descriptors.", ( 0xEF, 0x04, 0x05, ): "RNDIS for Raw IPv4. Connecting a host to the Internet using raw " "IPv4 via non-Ethernet mobile device. Devices that provide raw " "IPv4, not in an Ethernet packet, may use this form to in lieu of " "other stock types. " "This class code may only be used in Interface Descriptors.", ( 0xEF, 0x04, 0x06, ): "RNDIS for Raw IPv6. Connecting a host to the Internet using raw " "IPv6 via non-Ethernet mobile device. Devices that provide raw " "IPv6, not in an Ethernet packet, may use this form to in lieu of " "other stock types. " "This class code may only be used in Interface Descriptors.", ( 0xEF, 0x04, 0x07, ): "RNDIS for GPRS. Connecting a host to the Internet over GPRS mobile " "device using the device‚Äôs cellular radio.", (0xEF, 0x05, 0x00): "USB3 Vision Control Interface", (0xEF, 0x05, 0x01): "USB3 Vision Event Interface", (0xEF, 0x05, 0x02): "USB3 Vision Streaming Interface", (0xFE, 0x01, 0x01): "Device Firmware Upgrade.", (0xFE, 0x02, 0x00): "IRDA Bridge device.", (0xFE, 0x03, 0x00): "USB Test and Measurement Device.", ( 0xFE, 0x03, 0x01, ): "USB Test and Measurement Device conforming to the USBTMC USB488 Subclass", (0xFF, None, None): "Vendor specific", } def ep_attributes(ep): c = ep.bmAttributes attrs = [] tp = c & usb.ENDPOINT_TYPE_MASK if tp == usb.ENDPOINT_TYPE_CONTROL: attrs.append("Control") elif tp == usb.ENDPOINT_TYPE_ISOCHRONOUS: attrs.append("Isochronous") elif tp == usb.ENDPOINT_TYPE_BULK: attrs.append("Bulk") elif tp == usb.ENDPOINT_TYPE_INTERRUPT: attrs.append("Interrupt") sync = (c & 12) >> 2 if sync == 0: attrs.append("No sync") elif sync == 1: attrs.append("Async") elif sync == 2: attrs.append("Adaptive") elif sync == 3: attrs.append("Sync") usage = (c & 48) >> 4 if usage == 0: attrs.append("Data endpoint") elif usage == 1: attrs.append("Feedback endpoint") elif usage == 2: attrs.append("Subordinate Feedback endpoint") elif usage == 3: attrs.append("Reserved") return ", ".join(attrs) def find_devices( vendor=None, product=None, serial_number=None, custom_match=None, **kwargs ): """Find connected USB devices matching certain keywords. Wildcards can be used for vendor, product and serial_number. :param vendor: name or id of the vendor (manufacturer) :param product: name or id of the product :param serial_number: serial number. :param custom_match: callable returning True or False that takes a device as only input. :param kwargs: other properties to match. See usb.core.find :return: """ kwargs = kwargs or {} attrs = {} if isinstance(vendor, str): attrs["manufacturer"] = vendor elif vendor is not None: kwargs["idVendor"] = vendor if isinstance(product, str): attrs["product"] = product elif product is not None: kwargs["idProduct"] = product if serial_number: attrs["serial_number"] = str(serial_number) if attrs: def cm(dev): if custom_match is not None and not custom_match(dev): return False for attr, pattern in attrs.items(): try: value = getattr(dev, attr) except (NotImplementedError, ValueError): return False if not fnmatch(value.lower(), pattern.lower()): return False return True else: cm = custom_match return usb.core.find(find_all=True, custom_match=cm, **kwargs) def find_interfaces(device, **kwargs): """ :param device: :return: """ interfaces = [] try: for cfg in device: try: interfaces.extend(usb_find_desc(cfg, find_all=True, **kwargs)) except Exception: pass except Exception: pass return interfaces def find_endpoint(interface, direction, type): ep = usb_find_desc( interface, custom_match=lambda e: usb.util.endpoint_direction(e.bEndpointAddress) == direction and usb.util.endpoint_type(e.bmAttributes) == type, ) return ep def _patch_endpoint(ep, log_func=print): _read = ep.read _write = ep.write def new_read(*args, **kwargs): log_func("---") log_func("reading from {}".format(ep.bEndpointAddress)) log_func("args: {}".format(args)) log_func("kwargs: {}".format(kwargs)) ret = _read(*args, **kwargs) log_func("returned", ret) log_func("---") return ret def new_write(*args, **kwargs): log_func("---") log_func("writing to {}".format(ep.bEndpointAddress)) log_func("args: {}".format(args)) log_func("kwargs: {}".format(kwargs)) ret = _write(*args, **kwargs) log_func("returned", ret) log_func("---") return ret ep.read = new_read ep.write = new_write pyvisa-py-0.5.1/pyvisa_py/protocols/vxi11.py000066400000000000000000000244221373522010000210440ustar00rootroot00000000000000# -*- coding: utf-8 -*- """Implements a VX11 Session using Python Standard Library. Based on Python Sun RPC Demo and Alex Forencich python-vx11 This file is an offspring of the Lantz project. :copyright: 2014-2020 by PyVISA-py Authors, see AUTHORS for more details. :license: MIT, see LICENSE for more details. """ import enum import socket from . import rpc # fmt: off # VXI-11 RPC constants # Device async DEVICE_ASYNC_PROG = 0x0607B0 DEVICE_ASYNC_VERS = 1 DEVICE_ABORT = 1 # Device core DEVICE_CORE_PROG = 0x0607AF DEVICE_CORE_VERS = 1 CREATE_LINK = 10 DEVICE_WRITE = 11 DEVICE_READ = 12 DEVICE_READSTB = 13 DEVICE_TRIGGER = 14 DEVICE_CLEAR = 15 DEVICE_REMOTE = 16 DEVICE_LOCAL = 17 DEVICE_LOCK = 18 DEVICE_UNLOCK = 19 DEVICE_ENABLE_SRQ = 20 DEVICE_DOCMD = 22 DESTROY_LINK = 23 CREATE_INTR_CHAN = 25 DESTROY_INTR_CHAN = 26 # Device intr DEVICE_INTR_PROG = 0x0607B1 DEVICE_INTR_VERS = 1 DEVICE_INTR_SRQ = 30 # Error states class ErrorCodes(enum.IntEnum): no_error = 0 syntax_error = 1 device_not_accessible = 3 invalid_link_identifier = 4 parameter_error = 5 channel_not_established = 6 operation_not_supported = 8 out_of_resources = 9 device_locked_by_another_link = 11 no_lock_held_by_this_link = 12 io_timeout = 15 io_error = 17 abort = 23 channel_already_established = 29 # Flags OP_FLAG_WAIT_BLOCK = 1 OP_FLAG_END = 8 OP_FLAG_TERMCHAR_SET = 128 RX_REQCNT = 1 RX_CHR = 2 RX_END = 4 # fmt: on class Vxi11Error(Exception): pass class Vxi11Packer(rpc.Packer): def pack_device_link(self, link): self.pack_int(link) def pack_create_link_parms(self, params): id, lock_device, lock_timeout, device = params self.pack_int(id) self.pack_bool(lock_device) self.pack_uint(lock_timeout) self.pack_string(device.encode("ascii")) def pack_device_write_parms(self, params): link, io_timeout, lock_timeout, flags, data = params self.pack_int(link) self.pack_uint(io_timeout) self.pack_uint(lock_timeout) self.pack_int(flags) self.pack_opaque(data) def pack_device_read_parms(self, params): link, request_size, io_timeout, lock_timeout, flags, term_char = params self.pack_int(link) self.pack_uint(request_size) self.pack_uint(io_timeout) self.pack_uint(lock_timeout) self.pack_int(flags) self.pack_int(term_char) def pack_device_generic_parms(self, params): link, flags, lock_timeout, io_timeout = params self.pack_int(link) self.pack_int(flags) self.pack_uint(lock_timeout) self.pack_uint(io_timeout) def pack_device_remote_func_parms(self, params): host_addr, host_port, prog_num, prog_vers, prog_family = params self.pack_uint(host_addr) self.pack_uint(host_port) self.pack_uint(prog_num) self.pack_uint(prog_vers) self.pack_int(prog_family) def pack_device_enable_srq_parms(self, params): link, enable, handle = params self.pack_int(link) self.pack_bool(enable) if len(handle) > 40: raise Vxi11Error("array length too long") self.pack_opaque(handle) def pack_device_lock_parms(self, params): link, flags, lock_timeout = params self.pack_int(link) self.pack_int(flags) self.pack_uint(lock_timeout) def pack_device_docmd_parms(self, params): ( link, flags, io_timeout, lock_timeout, cmd, network_order, datasize, data_in, ) = params self.pack_int(link) self.pack_int(flags) self.pack_uint(io_timeout) self.pack_uint(lock_timeout) self.pack_int(cmd) self.pack_bool(network_order) self.pack_int(datasize) self.pack_opaque(data_in) class Vxi11Unpacker(rpc.Unpacker): def unpack_device_link(self): return self.unpack_int() def unpack_device_error(self): return self.unpack_int() def unpack_create_link_resp(self): error = self.unpack_int() link = self.unpack_int() abort_port = self.unpack_uint() max_recv_size = self.unpack_uint() return error, link, abort_port, max_recv_size def unpack_device_write_resp(self): error = self.unpack_int() size = self.unpack_uint() return error, size def unpack_device_read_resp(self): error = self.unpack_int() reason = self.unpack_int() data = self.unpack_opaque() return error, reason, data def unpack_device_read_stb_resp(self): error = self.unpack_int() stb = self.unpack_uint() return error, stb def unpack_device_docmd_resp(self): error = self.unpack_int() data_out = self.unpack_opaque() return error, data_out class CoreClient(rpc.TCPClient): def __init__(self, host, open_timeout=5000): self.packer = Vxi11Packer() self.unpacker = Vxi11Unpacker("") super(CoreClient, self).__init__( host, DEVICE_CORE_PROG, DEVICE_CORE_VERS, open_timeout ) def create_link(self, id, lock_device, lock_timeout, name): params = (id, lock_device, lock_timeout, name) try: return self.make_call( CREATE_LINK, params, self.packer.pack_create_link_parms, self.unpacker.unpack_create_link_resp, ) except socket.timeout: return ErrorCodes.device_not_accessible, None, None, None def device_write(self, link, io_timeout, lock_timeout, flags, data): params = (link, io_timeout, lock_timeout, flags, data) try: return self.make_call( DEVICE_WRITE, params, self.packer.pack_device_write_parms, self.unpacker.unpack_device_write_resp, ) except socket.timeout as e: return ErrorCodes.io_error, e.args[0] def device_read( self, link, request_size, io_timeout, lock_timeout, flags, term_char ): params = (link, request_size, io_timeout, lock_timeout, flags, term_char) try: return self.make_call( DEVICE_READ, params, self.packer.pack_device_read_parms, self.unpacker.unpack_device_read_resp, ) except socket.timeout as e: return ErrorCodes.io_error, e.args[0], "" def device_read_stb(self, link, flags, lock_timeout, io_timeout): params = (link, flags, lock_timeout, io_timeout) return self.make_call( DEVICE_READSTB, params, self.packer.pack_device_generic_parms, self.unpacker.unpack_device_read_stb_resp, ) def device_trigger(self, link, flags, lock_timeout, io_timeout): params = (link, flags, lock_timeout, io_timeout) return self.make_call( DEVICE_TRIGGER, params, self.packer.pack_device_generic_parms, self.unpacker.unpack_device_error, ) def device_clear(self, link, flags, lock_timeout, io_timeout): params = (link, flags, lock_timeout, io_timeout) return self.make_call( DEVICE_CLEAR, params, self.packer.pack_device_generic_parms, self.unpacker.unpack_device_error, ) def device_remote(self, link, flags, lock_timeout, io_timeout): params = (link, flags, lock_timeout, io_timeout) return self.make_call( DEVICE_REMOTE, params, self.packer.pack_device_generic_parms, self.unpacker.unpack_device_error, ) def device_local(self, link, flags, lock_timeout, io_timeout): params = (link, flags, lock_timeout, io_timeout) return self.make_call( DEVICE_LOCAL, params, self.packer.pack_device_generic_parms, self.unpacker.unpack_device_error, ) def device_lock(self, link, flags, lock_timeout): params = (link, flags, lock_timeout) return self.make_call( DEVICE_LOCK, params, self.packer.pack_device_lock_parms, self.unpacker.unpack_device_error, ) def device_unlock(self, link): return self.make_call( DEVICE_UNLOCK, link, self.packer.pack_device_link, self.unpacker.unpack_device_error, ) def device_enable_srq(self, link, enable, handle): params = (link, enable, handle) return self.make_call( DEVICE_ENABLE_SRQ, params, self.packer.pack_device_enable_srq_parms, self.unpacker.unpack_device_error, ) def device_docmd( self, link, flags, io_timeout, lock_timeout, cmd, network_order, datasize, data_in, ): params = ( link, flags, io_timeout, lock_timeout, cmd, network_order, datasize, data_in, ) return self.make_call( DEVICE_DOCMD, params, self.packer.pack_device_docmd_parms, self.unpacker.unpack_device_docmd_resp, ) def destroy_link(self, link): return self.make_call( DESTROY_LINK, link, self.packer.pack_device_link, self.unpacker.unpack_device_error, ) def create_intr_chan(self, host_addr, host_port, prog_num, prog_vers, prog_family): params = (host_addr, host_port, prog_num, prog_vers, prog_family) return self.make_call( CREATE_INTR_CHAN, params, self.packer.pack_device_docmd_parms, self.unpacker.unpack_device_error, ) def destroy_intr_chan(self): return self.make_call( DESTROY_INTR_CHAN, None, None, self.unpacker.unpack_device_error ) pyvisa-py-0.5.1/pyvisa_py/serial.py000066400000000000000000000375551373522010000173420ustar00rootroot00000000000000# -*- coding: utf-8 -*- """Serial Session implementation using PySerial. :copyright: 2014-2020 by PyVISA-py Authors, see AUTHORS for more details. :license: MIT, see LICENSE for more details. """ import sys from typing import Any, List, Optional, Tuple from pyvisa import attributes, constants, logger, rname from pyvisa.constants import ( BufferOperation, ResourceAttribute, SerialTermination, StatusCode, ) from . import common from .sessions import Session, UnknownAttribute try: import serial from serial.tools.list_ports import comports except ImportError as e: Session.register_unavailable( constants.InterfaceType.asrl, "INSTR", "Please install PySerial (>=3.0) to use this resource type.\n%s" % e, ) raise IS_WIN = sys.platform == "win32" def iter_bytes(data: bytes, mask: Optional[int] = None, send_end: bool = False): if send_end and mask is None: raise ValueError("send_end requires a valid mask.") if mask is None: for d in data: yield bytes([d]) else: for d in data[:-1]: yield bytes([d & ~mask]) if send_end: yield bytes([data[-1] | ~mask]) else: yield bytes([data[-1] & ~mask]) def to_state(boolean_input: bool) -> constants.LineState: """Convert a boolean input into a LineState value.""" if boolean_input: return constants.LineState.asserted return constants.LineState.unasserted @Session.register(constants.InterfaceType.asrl, "INSTR") class SerialSession(Session): """A serial Session that uses PySerial to do the low level communication.""" # Override parsed to take into account the fact that this class is only used # for a specific kind of resource parsed: rname.ASRLInstr @staticmethod def list_resources() -> List[str]: return [ "ASRL%s::INSTR" % (port[0][3:] if IS_WIN else port[0]) for port in comports() ] @classmethod def get_low_level_info(cls) -> str: try: ver = serial.VERSION except AttributeError: ver = "N/A" return "via PySerial (%s)" % ver def after_parsing(self) -> None: cls = serial.Serial self.interface = cls( port=("COM" if IS_WIN else "") + self.parsed.board, timeout=self.timeout, write_timeout=self.timeout, ) for name in ( "ASRL_END_IN", "ASRL_END_OUT", "SEND_END_EN", "TERMCHAR", "TERMCHAR_EN", "SUPPRESS_END_EN", ): attribute = getattr(constants, "VI_ATTR_" + name) self.attrs[attribute] = attributes.AttributesByID[attribute].default def _get_timeout(self, attribute: ResourceAttribute) -> Tuple[int, StatusCode]: if self.interface: self.timeout = self.interface.timeout return super(SerialSession, self)._get_timeout(attribute) def _set_timeout(self, attribute: ResourceAttribute, value: int) -> StatusCode: status = super(SerialSession, self)._set_timeout(attribute, value) if self.interface: self.interface.timeout = self.timeout self.interface.write_timeout = self.timeout return status def close(self) -> StatusCode: self.interface.close() return StatusCode.success def read(self, count: int) -> Tuple[bytes, StatusCode]: """Reads data from device or interface synchronously. Corresponds to viRead function of the VISA library. Parameters ----------- count : int Number of bytes to be read. Returns ------- bytes Data read from the device StatusCode Return value of the library call. """ end_in, _ = self.get_attribute(ResourceAttribute.asrl_end_in) suppress_end_en, _ = self.get_attribute(ResourceAttribute.suppress_end_enabled) reader = lambda: self.interface.read(1) if end_in == SerialTermination.none: checker = lambda current: False elif end_in == SerialTermination.last_bit: mask = 2 ** self.interface.bytesize checker = lambda current: bool(current[-1] & mask) elif end_in == SerialTermination.termination_char: end_char, _ = self.get_attribute(ResourceAttribute.termchar) checker = lambda current: current[-1] == end_char else: raise ValueError("Unknown value for VI_ATTR_ASRL_END_IN: %s" % end_in) return self._read( reader, count, checker, suppress_end_en, None, False, serial.SerialTimeoutException, ) def write(self, data: bytes) -> Tuple[int, StatusCode]: """Writes data to device or interface synchronously. Corresponds to viWrite function of the VISA library. Parameters ---------- data : bytes Data to be written. Returns ------- int Number of bytes actually transferred StatusCode Return value of the library call. """ logger.debug("Serial.write %r" % data) end_out, _ = self.get_attribute(ResourceAttribute.asrl_end_out) send_end, _ = self.get_attribute(ResourceAttribute.send_end_enabled) if end_out in (SerialTermination.none, SerialTermination.termination_break): pass elif end_out == SerialTermination.last_bit: last_bit, _ = self.get_attribute(ResourceAttribute.asrl_data_bits) mask = 1 << (last_bit - 1) data = bytes(iter_bytes(data, mask, send_end)) elif end_out == SerialTermination.termination_char: term_char, _ = self.get_attribute(ResourceAttribute.termchar) data = data + common.int_to_byte(term_char) else: raise ValueError("Unknown value for VI_ATTR_ASRL_END_OUT: %s" % end_out) try: count = self.interface.write(data) if end_out == SerialTermination.termination_break: logger.debug("Serial.sendBreak") self.interface.sendBreak() return count, StatusCode.success except serial.SerialTimeoutException: return 0, StatusCode.error_timeout def flush(self, mask: BufferOperation) -> StatusCode: """Flush the specified buffers. The buffers can be associated with formatted I/O operations and/or serial communication. Corresponds to viFlush function of the VISA library. Parameters ---------- mask : constants.BufferOperation Specifies the action to be taken with flushing the buffer. The values can be combined using the | operator. However multiple operations on a single buffer cannot be combined. Returns ------- constants.StatusCode Return value of the library call. """ if ( mask & BufferOperation.discard_read_buffer or mask & BufferOperation.discard_read_buffer_no_io or mask & BufferOperation.discard_receive_buffer or mask & BufferOperation.discard_receive_buffer2 ): self.interface.reset_input_buffer() if ( mask & BufferOperation.flush_write_buffer or mask & BufferOperation.flush_transmit_buffer ): self.interface.flush() if ( mask & BufferOperation.discard_write_buffer or mask & BufferOperation.discard_transmit_buffer ): self.interface.reset_output_buffer() return StatusCode.success def _get_attribute( self, attribute: constants.ResourceAttribute ) -> Tuple[Any, StatusCode]: """Get the value for a given VISA attribute for this session. Use to implement custom logic for attributes. Parameters ---------- attribute : ResourceAttribute Attribute for which the state query is made Returns ------- Any State of the queried attribute for a specified resource StatusCode Return value of the library call. """ if attribute == constants.VI_ATTR_ASRL_ALLOW_TRANSMIT: raise NotImplementedError elif attribute == constants.VI_ATTR_ASRL_AVAIL_NUM: return self.interface.inWaiting(), StatusCode.success elif attribute == constants.VI_ATTR_ASRL_BAUD: return self.interface.baudrate, StatusCode.success elif attribute == constants.VI_ATTR_ASRL_BREAK_LEN: raise NotImplementedError elif attribute == constants.VI_ATTR_ASRL_BREAK_STATE: raise NotImplementedError elif attribute == constants.VI_ATTR_ASRL_CONNECTED: raise NotImplementedError elif attribute == constants.VI_ATTR_ASRL_CTS_STATE: return to_state(self.interface.getCTS()), StatusCode.success elif attribute == constants.VI_ATTR_ASRL_DATA_BITS: return self.interface.bytesize, StatusCode.success elif attribute == constants.VI_ATTR_ASRL_DCD_STATE: raise NotImplementedError elif attribute == constants.VI_ATTR_ASRL_DISCARD_NULL: raise NotImplementedError elif attribute == constants.VI_ATTR_ASRL_DSR_STATE: return to_state(self.interface.getDSR()), StatusCode.success elif attribute == constants.VI_ATTR_ASRL_DTR_STATE: raise NotImplementedError elif attribute == constants.VI_ATTR_ASRL_FLOW_CNTRL: return ( ( self.interface.xonxoff * constants.VI_ASRL_FLOW_XON_XOFF | self.interface.rtscts * constants.VI_ASRL_FLOW_RTS_CTS | self.interface.dsrdtr * constants.VI_ASRL_FLOW_DTR_DSR ), StatusCode.success, ) elif attribute == constants.VI_ATTR_ASRL_PARITY: parity = self.interface.parity if parity == serial.PARITY_NONE: return constants.Parity.none, StatusCode.success elif parity == serial.PARITY_EVEN: return constants.Parity.even, StatusCode.success elif parity == serial.PARITY_ODD: return constants.Parity.odd, StatusCode.success elif parity == serial.PARITY_MARK: return constants.Parity.mark, StatusCode.success elif parity == serial.PARITY_SPACE: return constants.Parity.space, StatusCode.success raise Exception("Unknown parity value: %r" % parity) elif attribute == constants.VI_ATTR_ASRL_RI_STATE: raise NotImplementedError elif attribute == constants.VI_ATTR_ASRL_RTS_STATE: raise NotImplementedError elif attribute == constants.VI_ATTR_ASRL_STOP_BITS: bits = self.interface.stopbits if bits == serial.STOPBITS_ONE: return constants.StopBits.one, StatusCode.success elif bits == serial.STOPBITS_ONE_POINT_FIVE: return constants.StopBits.one_and_a_half, StatusCode.success elif bits == serial.STOPBITS_TWO: return constants.StopBits.two, StatusCode.success raise Exception("Unknown bits value: %r" % bits) elif attribute == constants.VI_ATTR_ASRL_XOFF_CHAR: raise NotImplementedError elif attribute == constants.VI_ATTR_INTF_TYPE: return constants.InterfaceType.asrl, StatusCode.success raise UnknownAttribute(attribute) def _set_attribute( self, attribute: constants.ResourceAttribute, attribute_state: Any ) -> StatusCode: """Sets the state of an attribute. Corresponds to viSetAttribute function of the VISA library. Parameters ---------- attribute : constants.ResourceAttribute Attribute for which the state is to be modified. (Attributes.*) attribute_state : Any The state of the attribute to be set for the specified object. Returns ------- StatusCode Return value of the library call. """ if attribute == constants.VI_ATTR_ASRL_ALLOW_TRANSMIT: raise NotImplementedError elif attribute == constants.VI_ATTR_ASRL_BAUD: self.interface.baudrate = attribute_state return StatusCode.success elif attribute == constants.VI_ATTR_ASRL_BREAK_LEN: raise NotImplementedError elif attribute == constants.VI_ATTR_ASRL_BREAK_STATE: raise NotImplementedError elif attribute == constants.VI_ATTR_ASRL_CONNECTED: raise NotImplementedError elif attribute == constants.VI_ATTR_ASRL_DATA_BITS: self.interface.bytesize = attribute_state return StatusCode.success elif attribute == constants.VI_ATTR_ASRL_DCD_STATE: raise NotImplementedError elif attribute == constants.VI_ATTR_ASRL_DISCARD_NULL: raise NotImplementedError elif attribute == constants.VI_ATTR_ASRL_DSR_STATE: raise NotImplementedError elif attribute == constants.VI_ATTR_ASRL_DTR_STATE: raise NotImplementedError elif attribute == constants.VI_ATTR_ASRL_FLOW_CNTRL: if not isinstance(attribute_state, int): return StatusCode.error_nonsupported_attribute_state if not 0 < attribute_state < 8: return StatusCode.error_nonsupported_attribute_state try: self.interface.xonxoff = ( attribute_state & constants.VI_ASRL_FLOW_XON_XOFF ) self.interface.rtscts = attribute_state & constants.VI_ASRL_FLOW_RTS_CTS self.interface.dsrdtr = attribute_state & constants.VI_ASRL_FLOW_DTR_DSR return StatusCode.success except Exception: return StatusCode.error_nonsupported_attribute_state elif attribute == constants.VI_ATTR_ASRL_PARITY: if attribute_state == constants.Parity.none: self.interface.parity = serial.PARITY_NONE return StatusCode.success elif attribute_state == constants.Parity.even: self.interface.parity = serial.PARITY_EVEN return StatusCode.success elif attribute_state == constants.Parity.odd: self.interface.parity = serial.PARITY_ODD return StatusCode.success elif attribute_state == serial.PARITY_MARK: self.interface.parity = serial.PARITY_MARK return StatusCode.success elif attribute_state == constants.Parity.space: self.interface.parity = serial.PARITY_SPACE return StatusCode.success return StatusCode.error_nonsupported_attribute_state elif attribute == constants.VI_ATTR_ASRL_RI_STATE: raise NotImplementedError elif attribute == constants.VI_ATTR_ASRL_RTS_STATE: raise NotImplementedError elif attribute == constants.VI_ATTR_ASRL_STOP_BITS: if attribute_state == constants.StopBits.one: self.interface.stopbits = serial.STOPBITS_ONE return StatusCode.success if attribute_state == constants.StopBits.one_and_a_half: self.interface.stopbits = serial.STOPBITS_ONE_POINT_FIVE return StatusCode.success if attribute_state == constants.StopBits.two: self.interface.stopbits = serial.STOPBITS_TWO return StatusCode.success return StatusCode.error_nonsupported_attribute_state elif attribute == constants.VI_ATTR_ASRL_XOFF_CHAR: raise NotImplementedError raise UnknownAttribute(attribute) pyvisa-py-0.5.1/pyvisa_py/sessions.py000066400000000000000000000670271373522010000177260ustar00rootroot00000000000000# -*- coding: utf-8 -*- """Base Session class. :copyright: 2014-2020 by PyVISA-py Authors, see AUTHORS for more details. :license: MIT, see LICENSE for more details. """ import abc import time from typing import Any, Callable, Dict, Iterator, List, Optional, Tuple, Type, TypeVar from pyvisa import attributes, constants, logger, rname from pyvisa.constants import ResourceAttribute, StatusCode from pyvisa.typing import VISARMSession from .common import int_to_byte #: Type var used when typing register. T = TypeVar("T", bound=Type["Session"]) class UnknownAttribute(Exception): """Custom exception signaling a VISA attribute is not supported.""" def __init__(self, attribute: constants.ResourceAttribute) -> None: self.attribute = attribute def __str__(self) -> str: attr = self.attribute if isinstance(attr, int): try: name = attributes.AttributesByID[attr].visa_name except KeyError: name = "Name not found" return "Unknown attribute %s (%s - %s)" % (attr, hex(attr), name) return "Unknown attribute %s" % attr __repr__ = __str__ class Session(metaclass=abc.ABCMeta): """A base class for Session objects. Just makes sure that common methods are defined and information is stored. Parameters ---------- resource_manager_session : VISARMSession Session handle of the parent Resource Manager resource_name : str Name of the resource this session is communicating with parsed : rname.ResourceName, optional Parsed representation of the resource name. The default is False meaning that the provided resource name will be parsed. """ @abc.abstractmethod def _get_attribute( self, attribute: constants.ResourceAttribute ) -> Tuple[Any, StatusCode]: """Get the value for a given VISA attribute for this session. Use to implement custom logic for attributes. Parameters ---------- attribute : constants.ResourceAttribute Resource attribute for which the state query is made Returns ------- Any State of the queried attribute for a specified resource constants.StatusCode Return value of the library call. """ @abc.abstractmethod def _set_attribute( self, attribute: constants.ResourceAttribute, attribute_state: Any ) -> StatusCode: """Set the attribute_state value for a given VISA attribute for this session. Use to implement custom logic for attributes. Parameters ---------- attribute : constants.ResourceAttribute Resource attribute for which the state query is made. attribute_state : Any Value to which to set the attribute. Returns ------- StatusCode The return value of the library call. """ @abc.abstractmethod def close(self) -> StatusCode: """Close the session. Use it to do final clean ups. """ #: Session handle of the parent Resource Manager resource_manager_session: VISARMSession #: Name of the resource this session is communicating with resource_name: str #: Parsed representation of the resource name. parsed: rname.ResourceName #: Session type as (Interface Type, Resource Class) session_type: Tuple[constants.InterfaceType, str] #: Timeout in seconds to use when opening the resource. open_timeout: Optional[float] #: Value of the timeout in seconds used for general operation timeout: Optional[float] #: Used as a place holder for the object doing the lowlevel communication. interface: Any #: Used for attributes not handled by the underlying interface. #: Values are get or set automatically by get_attribute and set_attribute attrs: Dict[constants.ResourceAttribute, Any] #: Maps (Interface Type, Resource Class) to Python class encapsulating that #: resource. #: dict[(Interface Type, Resource Class) , Session] _session_classes: Dict[ Tuple[constants.InterfaceType, str], Type["Session"] ] = dict() @staticmethod def list_resources() -> List[str]: """List the resources available for the resource class.""" return [] @classmethod def get_low_level_info(cls) -> str: """Get info about the backend used by the session.""" return "" @classmethod def iter_valid_session_classes( cls, ) -> Iterator[Tuple[Tuple[constants.InterfaceType, str], Type["Session"]]]: """Iterator over valid sessions classes infos.""" for key, val in cls._session_classes.items(): if issubclass(val, Session): yield key, val @classmethod def iter_session_classes_issues( cls, ) -> Iterator[Tuple[Tuple[constants.InterfaceType, str], str]]: """Iterator over invalid sessions classes (i.e. those with import errors).""" for key, val in cls._session_classes.items(): try: yield key, getattr(val, "session_issue") except AttributeError: pass @classmethod def get_session_class( cls, interface_type: constants.InterfaceType, resource_class: str ) -> Type["Session"]: """Get the session class for a given interface type and resource class. Parameters ---------- interface_type : constants.InterfaceType Type of interface. resource_class : str Class of resource. Returns ------- Sessions Session subclass the most appropriate for the resource. """ try: return cls._session_classes[(interface_type, resource_class)] except KeyError: raise ValueError( "No class registered for %s, %s" % (interface_type, resource_class) ) @classmethod def register( cls, interface_type: constants.InterfaceType, resource_class: str ) -> Callable[[T], T]: """Register a session class for a given interface type and resource class. Parameters ---------- interface_type : constants.InterfaceType Type of interface. resource_class : str Class of the resource Returns ------- Callable[[T], T] Decorator function to register a session subclass. """ def _internal(python_class): if (interface_type, resource_class) in cls._session_classes: logger.warning( "%s is already registered in the " "ResourceManager. Overwriting with %s", ((interface_type, resource_class), python_class), ) python_class.session_type = (interface_type, resource_class) cls._session_classes[(interface_type, resource_class)] = python_class return python_class return _internal @classmethod def register_unavailable( cls, interface_type: constants.InterfaceType, resource_class: str, msg: str ) -> None: """Register that no session class exists. This creates a fake session that will raise a ValueError if called. Parameters ---------- interface_type : constants.InterfaceType Type of interface. resource_class : str Class of the resource msg : str Message detailing why no session class exists for this particular interface type, resource class pair. Returns ------- Type[Session] Fake session. """ class _internal(Session): #: Message detailing why no session is available. session_issue: str = msg def __init__(self, *args, **kwargs) -> None: raise ValueError(msg) def _get_attribute(self, attr): raise NotImplementedError() def _set_attribute(self, attr, value): raise NotImplementedError() def close(self): raise NotImplementedError() if (interface_type, resource_class) in cls._session_classes: logger.warning( "%s is already registered in the ResourceManager. " "Overwriting with unavailable %s", ((interface_type, resource_class), msg), ) cls._session_classes[(interface_type, resource_class)] = _internal def __init__( self, resource_manager_session: VISARMSession, resource_name: str, parsed: Optional[rname.ResourceName] = None, open_timeout: Optional[float] = None, ) -> None: if parsed is None: parsed = rname.parse_resource_name(resource_name) self.parsed = parsed self.open_timeout = open_timeout #: Used as a place holder for the object doing the lowlevel communication. self.interface = None #: Used for attributes not handled by the underlying interface. #: Values are get or set automatically by get_attribute and #: set_attribute #: Add your own by overriding after_parsing. self.attrs = { ResourceAttribute.resource_manager_session: resource_manager_session, ResourceAttribute.resource_name: str(parsed), ResourceAttribute.resource_class: parsed.resource_class, ResourceAttribute.interface_type: parsed.interface_type_const, ResourceAttribute.timeout_value: (self._get_timeout, self._set_timeout), } #: Timeout expressed in second or None for the absence of a timeout. #: The default value is set when calling self.set_attribute(attr, default_timeout) self.timeout = None #: Set the default timeout from constants attr = ResourceAttribute.timeout_value default_timeout = attributes.AttributesByID[attr].default self.set_attribute(attr, default_timeout) self.after_parsing() def after_parsing(self) -> None: """Override this method to provide custom initialization code, to be called after the resourcename is properly parsed ResourceSession can register resource specific attributes handling of them into self.attrs. It is also possible to change handling of already registerd common attributes. List of attributes is available in pyvisa package: * name is in constants module as: VI_ATTR_ * validity of attribute for resource is defined module attributes, AttrVI_ATTR_.resources For static (read only) values, simple readonly and also readwrite attributes simplified construction can be used: ` self.attrs[constants.VI_ATTR_] = 100` or ` self.attrs[constants.VI_ATTR_] = ` For more complex handling of attributes, it is possible to register getter and/or setter. When Null is used, NotSupported error is returned. Getter has same signature as see Session._get_attribute and setter has same signature as see Session._set_attribute. (It is possible to register also see Session._get_attribute and see Session._set_attribute as getter/setter). Getter and Setter are registered as tuple. For readwrite attribute: ` self.attrs[constants.VI_ATTR_] = (, )` For readonly attribute: ` self.attrs[constants.VI_ATTR_] = (, None)` For reusing of see Session._get_attribute and see Session._set_attribute ` self.attrs[constants.VI_ATTR_] = (self._get_attribute, self._set_attribute)` """ pass def write(self, data: bytes) -> Tuple[int, StatusCode]: """Writes data to device or interface synchronously. Corresponds to viWrite function of the VISA library. Parameters ---------- data : bytes Data to be written. Returns ------- int Number of bytes actually transferred StatusCode Return value of the library call. """ raise NotImplementedError def read(self, count: int) -> Tuple[bytes, StatusCode]: """Reads data from device or interface synchronously. Corresponds to viRead function of the VISA library. Parameters ----------- count : int Number of bytes to be read. Returns ------- bytes Data read from the device StatusCode Return value of the library call. """ raise NotImplementedError() def clear(self) -> StatusCode: """Clears a device. Corresponds to viClear function of the VISA library. Returns ------- StatusCode Return value of the library call. """ return StatusCode.error_nonsupported_operation def flush(self, mask: constants.BufferOperation) -> StatusCode: """Flush the specified buffers. The buffers can be associated with formatted I/O operations and/or serial communication. Corresponds to viFlush function of the VISA library. Parameters ---------- mask : constants.BufferOperation Specifies the action to be taken with flushing the buffer. The values can be combined using the | operator. However multiple operations on a single buffer cannot be combined. Returns ------- constants.StatusCode Return value of the library call. """ raise NotImplementedError def read_stb(self) -> Tuple[int, StatusCode]: """Reads a status byte of the service request. Corresponds to viReadSTB function of the VISA library. Returns ------- int Service request status byte StatusCode Return value of the library call. """ return 0, StatusCode.error_nonsupported_operation def lock( self, lock_type: constants.Lock, timeout: int, requested_key: Optional[str] = None, ): """Establishes an access mode to the specified resources. Corresponds to viLock function of the VISA library. Parameters ---------- lock_type : constants.Lock Specifies the type of lock requested. timeout : int Absolute time period (in milliseconds) that a resource waits to get unlocked by the locking session before returning an error. requested_key : Optional[str], optional Requested locking key in the case of a shared lock. For an exclusive lock it should be None. Returns ------- Optional[str] Key that can then be passed to other sessions to share the lock, or None for an exclusive lock. constants.StatusCode Return value of the library call. """ return "", StatusCode.error_nonsupported_operation def unlock(self) -> StatusCode: """Relinquishes a lock for the specified resource. Corresponds to viUnlock function of the VISA library. Returns ------- StatusCode Return value of the library call. """ return StatusCode.error_nonsupported_operation def gpib_command(self, command_byte: bytes) -> Tuple[int, StatusCode]: """Write GPIB command bytes on the bus. Corresponds to viGpibCommand function of the VISA library. Parameters ---------- data : bytes Data to write. Returns ------- int Number of written bytes constants.StatusCode Return value of the library call. """ return 0, StatusCode.error_nonsupported_operation def assert_trigger(self, protocol: constants.TriggerProtocol) -> StatusCode: """Assert software or hardware trigger. Corresponds to viAssertTrigger function of the VISA library. Parameters ---------- protocol : constants.TriggerProtocol Trigger protocol to use during assertion. Returns ------- constants.StatusCode Return value of the library call. """ raise NotImplementedError def gpib_send_ifc(self) -> StatusCode: """Pulse the interface clear line (IFC) for at least 100 microseconds. Corresponds to viGpibSendIFC function of the VISA library. Returns ------- constants.StatusCode Return value of the library call. """ return StatusCode.error_nonsupported_operation def gpib_control_ren( self, mode: constants.RENLineOperation ) -> constants.StatusCode: """Controls the state of the GPIB Remote Enable (REN) interface line. Optionally the remote/local state of the device can also be set. Corresponds to viGpibControlREN function of the VISA library. Parameters ---------- mode : constants.RENLineOperation State of the REN line and optionally the device remote/local state. Returns ------- constants.StatusCode Return value of the library call. """ return StatusCode.error_nonsupported_operation def gpib_control_atn(self, mode: constants.ATNLineOperation) -> StatusCode: """Specifies the state of the ATN line and the local active controller state. Corresponds to viGpibControlATN function of the VISA library. Parameters ---------- session : VISASession Unique logical identifier to a session. mode : constants.ATNLineOperation State of the ATN line and optionally the local active controller state. Returns ------- constants.StatusCode Return value of the library call. """ return StatusCode.error_nonsupported_operation def gpib_pass_control( self, primary_address: int, secondary_address: int ) -> StatusCode: """Tell a GPIB device to become controller in charge (CIC). Corresponds to viGpibPassControl function of the VISA library. Parameters ---------- session : VISASession Unique logical identifier to a session. primary_address : int Primary address of the GPIB device to which you want to pass control. secondary_address : int Secondary address of the targeted GPIB device. If the targeted device does not have a secondary address, this parameter should contain the value Constants.VI_NO_SEC_ADDR. Returns ------- constants.StatusCode Return value of the library call. """ return StatusCode.error_nonsupported_operation def get_attribute(self, attribute: ResourceAttribute) -> Tuple[Any, StatusCode]: """Get the value for a given VISA attribute for this session. Does a few checks before and calls before dispatching to `_get_attribute`. Parameters ---------- attribute : ResourceAttribute Resource attribute for which the state query is made Returns ------- Any The state of the queried attribute for a specified resource. StatusCode Return value of the library call. """ # Check if the attribute value is defined. try: attr = attributes.AttributesByID[attribute] except KeyError: return 0, StatusCode.error_nonsupported_attribute # Check if the attribute is defined for this session type. if not attr.in_resource(self.session_type): return 0, StatusCode.error_nonsupported_attribute # Check if reading the attribute is allowed. if not attr.read: raise Exception("Do not now how to handle write only attributes.") # First try to answer those attributes that are registered in # self.attrs, see Session.after_parsing if attribute in self.attrs: value = self.attrs[attribute] status = StatusCode.success if isinstance(value, tuple): getter = value[0] value, status = ( getter(attribute) if getter else (0, StatusCode.error_nonsupported_attribute) ) return value, status # Dispatch to `_get_attribute`, which must be implemented by subclasses try: return self._get_attribute(attribute) except UnknownAttribute as e: logger.exception(str(e)) return 0, StatusCode.error_nonsupported_attribute def set_attribute( self, attribute: ResourceAttribute, attribute_state: Any ) -> StatusCode: """Set the attribute_state value for a given VISA attribute for this session. Does a few checks before and calls before dispatching to `_gst_attribute`. Parameters ---------- attribute : ResourceAttribute Resource attribute for which the state query is made. attribute_state : Any Value. Returns ------- StatusCode The return value of the library call. """ # Check if the attribute value is defined. try: attr = attributes.AttributesByID[attribute] except KeyError: return StatusCode.error_nonsupported_attribute # Check if the attribute is defined for this session type. if not attr.in_resource(self.session_type): return StatusCode.error_nonsupported_attribute # Check if writing the attribute is allowed. if not attr.write: return StatusCode.error_attribute_read_only # First try to answer those attributes that are registered in # self.attrs, see Session.after_parsing if attribute in self.attrs: value = self.attrs[attribute] status = StatusCode.success if isinstance(value, tuple): setter = value[1] status = ( setter(attribute, attribute_state) if setter else StatusCode.error_nonsupported_attribute ) else: self.attrs[attribute] = attribute_state return status # Dispatch to `_set_attribute`, which must be implemented by subclasses try: return self._set_attribute(attribute, attribute_state) except ValueError: return StatusCode.error_nonsupported_attribute_state except NotImplementedError: e = UnknownAttribute(attribute) logger.exception(str(e)) return StatusCode.error_nonsupported_attribute except UnknownAttribute as e: logger.exception(str(e)) return StatusCode.error_nonsupported_attribute def _read( self, reader: Callable[[], bytes], count: int, end_indicator_checker: Callable[[bytes], bool], suppress_end_en: bool, termination_char: Optional[int], termination_char_en: bool, timeout_exception: Type[Exception], ) -> Tuple[bytes, StatusCode]: """Reads data from device or interface synchronously. Corresponds to viRead function of the VISA library. Parameters ---------- reader : Callable[[], bytes] Function to read one or more bytes. count : int Number of bytes to be read. end_indicator_checker : Callable[[bytes], bool] Function to check if the message is complete. suppress_end_en : bool Suppress end. termination_char : int Stop reading if this character is received. termination_char_en : bool Is termination char enabled. timeout_exception : Type[Exception] Exception to capture time out for the given interface. Returns ------- bytes Data read from the resource. StatusCode Return value of the library call. """ # NOTE: Some interfaces return not only a single byte but a complete # block for each read therefore we must handle the case that the # termination character is in the middle of the block or that the # maximum number of bytes is exceeded # Turn the termination_char store as an int in VISA attribute in a byte term_char = ( int_to_byte(termination_char) if termination_char is not None else b"" ) finish_time = None if self.timeout is None else (time.time() + self.timeout) out = bytearray() while True: try: current = reader() except timeout_exception: return out, StatusCode.error_timeout if current: out.extend(current) end_indicator_received = end_indicator_checker(current) if end_indicator_received: if not suppress_end_en: # RULE 6.1.1 return bytes(out), StatusCode.success else: if termination_char_en and (term_char in current): # RULE 6.1.2 # Return everything up to and including the termination # character return ( bytes(out[: out.index(term_char) + 1]), StatusCode.success_termination_character_read, ) elif len(out) >= count: # RULE 6.1.3 # Return at most the number of bytes requested return (bytes(out[:count]), StatusCode.success_max_count_read) if finish_time and time.time() > finish_time: return bytes(out), StatusCode.error_timeout def _get_timeout(self, attribute: ResourceAttribute) -> Tuple[int, StatusCode]: """Returns timeout calculated value from python way to VI_ way In VISA, the timeout is expressed in milliseconds or using the constants VI_TMO_INFINITE or VI_TMO_IMMEDIATE. In Python we store it as either None (VI_TMO_INFINITE), 0 (VI_TMO_IMMEDIATE) or as a floating point number in seconds. """ if self.timeout is None: ret_value = constants.VI_TMO_INFINITE elif self.timeout == 0: ret_value = constants.VI_TMO_IMMEDIATE else: ret_value = int(self.timeout * 1000.0) return ret_value, StatusCode.success def _set_timeout(self, attribute: ResourceAttribute, value: int): """Sets timeout calculated value from python way to VI_ way In VISA, the timeout is expressed in milliseconds or using the constants VI_TMO_INFINITE or VI_TMO_IMMEDIATE. In Python we store it as either None (VI_TMO_INFINITE), 0 (VI_TMO_IMMEDIATE) or as a floating point number in seconds. """ if value == constants.VI_TMO_INFINITE: self.timeout = None elif value == constants.VI_TMO_IMMEDIATE: self.timeout = 0 else: self.timeout = value / 1000.0 return StatusCode.success pyvisa-py-0.5.1/pyvisa_py/tcpip.py000066400000000000000000000605171373522010000171740ustar00rootroot00000000000000# -*- coding: utf-8 -*- """TCPIP Session implementation using Python Standard library. :copyright: 2014-2020 by PyVISA-py Authors, see AUTHORS for more details. :license: MIT, see LICENSE for more details. """ import random import select import socket import time from typing import Any, List, Optional, Tuple from pyvisa import attributes, constants, errors, rname from pyvisa.constants import ResourceAttribute, StatusCode from . import common from .protocols import rpc, vxi11 from .sessions import Session, UnknownAttribute # Conversion between VXI11 error codes and VISA status # TODO this is so far a best guess, in particular 6 and 29 are likely wrong VXI11_ERRORS_TO_VISA = { 0: StatusCode.success, # no_error 1: StatusCode.error_invalid_format, # syntax_error 3: StatusCode.error_connection_lost, # device_no_accessible 4: StatusCode.error_invalid_access_key, # invalid_link_identifier 5: StatusCode.error_invalid_parameter, # parameter_error 6: StatusCode.error_handler_not_installed, # channel_not_established 8: StatusCode.error_nonsupported_operation, # operation_not_supported 9: StatusCode.error_allocation, # out_of_resources 11: StatusCode.error_resource_locked, # device_locked_by_another_link 12: StatusCode.error_session_not_locked, # no_lock_held_by_this_link 15: StatusCode.error_timeout, # io_timeout 17: StatusCode.error_io, # io_error 23: StatusCode.error_abort, # abort 29: StatusCode.error_window_already_mapped, # channel_already_established } @Session.register(constants.InterfaceType.tcpip, "INSTR") class TCPIPInstrSession(Session): """A TCPIP Session built on socket standard library using VXI-11 protocol.""" #: Maximum size of a chunk of data in bytes. max_recv_size: int #: Time to wait before erroring with a timeout when trying to acquire a lock lock_timeout: int #: Unique ID of the client used to authenticate messages. client_id: int #: ID of the link used for VXI-11 communication link: int # Override parsed to take into account the fact that this class is only used # for a specific kind of resource parsed: rname.TCPIPInstr @staticmethod def list_resources() -> List[str]: # TODO: is there a way to get this? return [] def after_parsing(self) -> None: # TODO: board_number not handled # vx11 expect all timeouts to be expressed in ms and should be integers try: self.interface = vxi11.CoreClient( self.parsed.host_address, self.open_timeout ) except rpc.RPCError: raise errors.VisaIOError(constants.VI_ERROR_RSRC_NFOUND) self.lock_timeout = 10000 self.client_id = random.getrandbits(31) error, link, abort_port, max_recv_size = self.interface.create_link( self.client_id, 0, self.lock_timeout, self.parsed.lan_device_name ) if error: raise Exception("error creating link: %d" % error) self.link = link self.max_recv_size = min(max_recv_size, 2 ** 30) # 1GB for name in ("SEND_END_EN", "TERMCHAR", "TERMCHAR_EN"): attribute = getattr(constants, "VI_ATTR_" + name) self.attrs[attribute] = attributes.AttributesByID[attribute].default def close(self) -> StatusCode: try: self.interface.destroy_link(self.link) except (errors.VisaIOError, socket.error, rpc.RPCError) as e: print("Error closing VISA link: {}".format(e)) self.interface.close() self.link = 0 self.interface = None return StatusCode.success def read(self, count: int) -> Tuple[bytes, StatusCode]: """Reads data from device or interface synchronously. Corresponds to viRead function of the VISA library. Parameters ---------- count : int Number of bytes to be read. Returns ------- bytes Data read StatusCode Return value of the library call. """ if count < self.max_recv_size: chunk_length = count else: chunk_length = self.max_recv_size if self.get_attribute(ResourceAttribute.termchar_enabled)[0]: term_char, _ = self.get_attribute(ResourceAttribute.termchar) flags = vxi11.OP_FLAG_TERMCHAR_SET else: term_char = flags = 0 read_data = bytearray() reason = 0 # Stop on end of message or when a termination character has been # encountered. end_reason = vxi11.RX_END | vxi11.RX_CHR read_fun = self.interface.device_read status = StatusCode.success timeout = self._io_timeout start_time = time.time() while reason & end_reason == 0: # Decrease timeout so that the total timeout does not get larger # than the specified timeout. timeout = max(0, timeout - int((time.time() - start_time) * 1000)) error, reason, data = read_fun( self.link, chunk_length, timeout, self.lock_timeout, flags, term_char ) if error == vxi11.ErrorCodes.io_timeout: return bytes(read_data), StatusCode.error_timeout elif error: return bytes(read_data), StatusCode.error_io read_data.extend(data) count -= len(data) if count <= 0: status = StatusCode.success_max_count_read break chunk_length = min(count, chunk_length) return bytes(read_data), status def write(self, data: bytes) -> Tuple[int, StatusCode]: """Writes data to device or interface synchronously. Corresponds to viWrite function of the VISA library. Parameters ---------- data : bytes Data to be written. Returns ------- int Number of bytes actually transferred StatusCode Return value of the library call. """ send_end, _ = self.get_attribute(ResourceAttribute.send_end_enabled) chunk_size = 1024 try: flags = 0 num = len(data) offset = 0 while num > 0: if num <= chunk_size: flags |= vxi11.OP_FLAG_END block = data[offset : offset + self.max_recv_size] error, size = self.interface.device_write( self.link, self._io_timeout, self.lock_timeout, flags, block ) if error == vxi11.ErrorCodes.io_timeout: return offset, StatusCode.error_timeout elif error or size < len(block): return offset, StatusCode.error_io offset += size num -= size return offset, StatusCode.success except vxi11.Vxi11Error: return 0, StatusCode.error_timeout def _get_attribute(self, attribute: ResourceAttribute) -> Tuple[Any, StatusCode]: """Get the value for a given VISA attribute for this session. Use to implement custom logic for attributes. Parameters ---------- attribute : Resource attribute for which the state query is made Returns ------- Any The state of the queried attribute for a specified resource StatusCode Return value of the library call. """ if attribute == constants.VI_ATTR_TCPIP_ADDR: return self.parsed.host_address, StatusCode.success elif attribute == constants.VI_ATTR_TCPIP_DEVICE_NAME: raise NotImplementedError elif attribute == constants.VI_ATTR_TCPIP_HOSTNAME: raise NotImplementedError elif attribute == constants.VI_ATTR_TCPIP_KEEPALIVE: raise NotImplementedError elif attribute == constants.VI_ATTR_TCPIP_NODELAY: raise NotImplementedError elif attribute == constants.VI_ATTR_TCPIP_PORT: raise NotImplementedError elif attribute == constants.VI_ATTR_SUPPRESS_END_EN: raise NotImplementedError raise UnknownAttribute(attribute) def _set_attribute( self, attribute: ResourceAttribute, attribute_state: Any ) -> StatusCode: """Sets the state of an attribute. Corresponds to viSetAttribute function of the VISA library. Parameters ---------- attribute : ResourceAttribute Attribute for which the state is to be modified. attribute_state : Any The state of the attribute to be set for the specified object. Returns ------- StatusCode Return value of the library call. """ raise UnknownAttribute(attribute) def assert_trigger(self, protocol: constants.TriggerProtocol): """Asserts software or hardware trigger. Corresponds to viAssertTrigger function of the VISA library. Parameters ---------- protocol : constants.TriggerProtocol Trigger protocol to use during assertion. Only default is supported. Returns ------- StatusCode Return value of the library call. """ # XXX make this nicer (either validate protocol or pass it) error = self.interface.device_trigger( self.link, 0, self.lock_timeout, self._io_timeout ) return VXI11_ERRORS_TO_VISA[error] def clear(self) -> StatusCode: """Clears a device. Corresponds to viClear function of the VISA library. """ error = self.interface.device_clear( self.link, 0, self.lock_timeout, self._io_timeout ) return VXI11_ERRORS_TO_VISA[error] def read_stb(self) -> Tuple[int, StatusCode]: """Reads a status byte of the service request. Corresponds to viReadSTB function of the VISA library. Returns ------- int Service request status byte StatusCode Return value of the library call. """ error, stb = self.interface.device_read_stb( self.link, 0, self.lock_timeout, self._io_timeout ) return stb, VXI11_ERRORS_TO_VISA[error] def lock( self, lock_type: constants.Lock, timeout: int, requested_key: Optional[str] = None, ) -> Tuple[str, constants.StatusCode]: """Establishes an access mode to the specified resources. Corresponds to viLock function of the VISA library. Parameters ---------- session : VISASession Unique logical identifier to a session. lock_type : constants.Lock Specifies the type of lock requested. timeout : int Absolute time period (in milliseconds) that a resource waits to get unlocked by the locking session before returning an error. requested_key : Optional[str], optional Requested locking key in the case of a shared lock. For an exclusive lock it should be None. Returns ------- Optional[str] Key that can then be passed to other sessions to share the lock, or None for an exclusive lock. StatusCode Return value of the library call. """ # TODO: lock type not implemented flags = 0 error = self.interface.device_lock(self.link, flags, self.lock_timeout) return "", VXI11_ERRORS_TO_VISA[error] def unlock(self) -> constants.StatusCode: """Relinquish a lock for the specified resource. Corresponds to viUnlock function of the VISA library. Returns ------- StatusCode Return value of the library call. """ error = self.interface.device_unlock(self.link) return VXI11_ERRORS_TO_VISA[error] def _set_timeout(self, attribute: ResourceAttribute, value: int) -> StatusCode: """Sets timeout calculated value from python way to VI_ way""" if value == constants.VI_TMO_INFINITE: self.timeout = None self._io_timeout = 2 ** 32 - 1 elif value == constants.VI_TMO_IMMEDIATE: self.timeout = 0 self._io_timeout = 0 else: self.timeout = value / 1000.0 self._io_timeout = int(self.timeout * 1000) return StatusCode.success @Session.register(constants.InterfaceType.tcpip, "SOCKET") class TCPIPSocketSession(Session): """A TCPIP Session that uses the network standard library to do the low level communication. """ # Details about implementation: # On Windows, select is not interrupted by KeyboardInterrupt, to avoid # blocking for very long time, we use a decreasing timeout in select. # A minimum select timeout which prevents using too short select interval # is also calculated and select timeout is not lower that that minimum # timeout. The absolute minimum is 1 ms as a consequence. # This is valid for connect and read operations #: Maximum size of a chunk of data in bytes. max_recv_size: int # Override parsed to take into account the fact that this class is only used # for a specific kind of resource parsed: rname.TCPIPSocket @staticmethod def list_resources() -> List[str]: # TODO: is there a way to get this? return [] def after_parsing(self) -> None: # TODO: board_number not handled ret_status = self._connect() if ret_status != StatusCode.success: self.close() raise Exception("could not connect: {0}".format(str(ret_status))) self.max_recv_size = 4096 # This buffer is used to store the bytes that appeared after # termination char self._pending_buffer = bytearray() self.attrs[ResourceAttribute.tcpip_address] = self.parsed.host_address self.attrs[ResourceAttribute.tcpip_port] = self.parsed.port self.attrs[ResourceAttribute.interface_number] = self.parsed.board self.attrs[ResourceAttribute.tcpip_nodelay] = ( self._get_tcpip_nodelay, self._set_attribute, ) self.attrs[ResourceAttribute.tcpip_hostname] = "" self.attrs[ResourceAttribute.tcpip_keepalive] = ( self._get_tcpip_keepalive, self._set_tcpip_keepalive, ) # to use default as ni visa driver (NI-VISA 15.0) self.attrs[ResourceAttribute.suppress_end_enabled] = True for name in ("TERMCHAR", "TERMCHAR_EN"): attribute = getattr(constants, "VI_ATTR_" + name) self.attrs[attribute] = attributes.AttributesByID[attribute].default def _connect(self) -> StatusCode: timeout = self.open_timeout / 1000.0 if self.open_timeout else 10.0 try: self.interface = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.interface.setblocking(False) self.interface.connect_ex((self.parsed.host_address, int(self.parsed.port))) except Exception as e: raise Exception("could not connect: {0}".format(str(e))) finally: self.interface.setblocking(True) # minimum is in interval 100 - 500ms based on timeout min_select_timeout = max(min(timeout / 10.0, 0.5), 0.1) # initial 'select_timout' is half of timeout or max 2 secs # (max blocking time). min is from 'min_select_timeout' select_timout = max(min(timeout / 2.0, 2.0), min_select_timeout) # time, when loop shall finish finish_time = time.time() + timeout while True: # use select to wait for socket ready, max `select_timout` seconds r, w, x = select.select( [self.interface], [self.interface], [], select_timout ) if self.interface in r or self.interface in w: return StatusCode.success if time.time() >= finish_time: # reached timeout return StatusCode.error_timeout # `select_timout` decreased to 50% of previous or # min_select_timeout select_timout = max(select_timout / 2.0, min_select_timeout) def close(self) -> StatusCode: self.interface.close() self.interface = None return StatusCode.success def read(self, count: int) -> Tuple[bytes, StatusCode]: """Reads data from device or interface synchronously. Corresponds to viRead function of the VISA library. Parameters ----------- count : int Number of bytes to be read. Returns ------- bytes Data read from the device StatusCode Return value of the library call. """ if count < self.max_recv_size: chunk_length = count else: chunk_length = self.max_recv_size term_char, _ = self.get_attribute(ResourceAttribute.termchar) term_byte = common.int_to_byte(term_char) if term_char else b"" term_char_en, _ = self.get_attribute(ResourceAttribute.termchar_enabled) suppress_end_en, _ = self.get_attribute(ResourceAttribute.suppress_end_enabled) read_fun = self.interface.recv # minimum is in interval 1 - 100ms based on timeout, 1sec if no timeout # defined min_select_timeout = ( 1 if self.timeout is None else max(min(self.timeout / 100.0, 0.1), 0.001) ) # initial 'select_timout' is half of timeout or max 2 secs # (max blocking time). min is from 'min_select_timeout' select_timout = ( 2.0 if self.timeout is None else max(min(self.timeout / 2.0, 2.0), min_select_timeout) ) # time, when loop shall finish, None means never ending story if no # data arrives finish_time = None if self.timeout is None else (time.time() + self.timeout) while True: # check, if we have any data received (from pending buffer or # further reading) if term_char_en and term_byte in self._pending_buffer: term_byte_index = self._pending_buffer.index(term_byte) + 1 if term_byte_index > count: term_byte_index = count status = StatusCode.success_max_count_read else: status = StatusCode.success_termination_character_read out = bytes(self._pending_buffer[:term_byte_index]) self._pending_buffer = self._pending_buffer[term_byte_index:] return out, status if len(self._pending_buffer) >= count: out = bytes(self._pending_buffer[:count]) self._pending_buffer = self._pending_buffer[count:] return out, StatusCode.success_max_count_read # use select to wait for read ready, max `select_timout` seconds r, w, x = select.select([self.interface], [], [], select_timout) read_data = b"" if self.interface in r: read_data = read_fun(chunk_length) self._pending_buffer.extend(read_data) if not read_data: # can't read chunk or timeout if self._pending_buffer and not suppress_end_en: # we have some data without termchar but no further data # expected out = bytes(self._pending_buffer[:count]) self._pending_buffer = self._pending_buffer[count:] return out, StatusCode.success if finish_time and time.time() >= finish_time: # reached timeout out = bytes(self._pending_buffer[:count]) self._pending_buffer = self._pending_buffer[count:] return out, StatusCode.error_timeout # `select_timout` decreased to 50% of previous or # min_select_timeout select_timout = max(select_timout / 2.0, min_select_timeout) def write(self, data: bytes) -> Tuple[int, StatusCode]: """Writes data to device or interface synchronously. Corresponds to viWrite function of the VISA library. Parameters ---------- data : bytes Data to be written. Returns ------- int Number of bytes actually transferred StatusCode Return value of the library call. """ chunk_size = 4096 num = sz = len(data) offset = 0 while num > 0: block = data[offset : min(offset + chunk_size, sz)] try: # use select to wait for write ready select.select([], [self.interface], []) size = self.interface.send(block) except socket.timeout: return offset, StatusCode.error_io if size < len(block): return offset, StatusCode.error_io offset += size num -= size return offset, StatusCode.success def clear(self) -> StatusCode: """Clears a device. Corresponds to viClear function of the VISA library. """ self._pending_buffer.clear() while True: r, w, x = select.select([self.interface], [], [], 0.1) if not r: break r[0].recv(4096) return StatusCode.success def _get_tcpip_nodelay( self, attribute: ResourceAttribute ) -> Tuple[constants.VisaBoolean, StatusCode]: if self.interface: value = self.interface.getsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY) return ( constants.VisaBoolean.true if value == 1 else constants.VisaBoolean.false, StatusCode.success, ) return constants.VisaBoolean.false, StatusCode.error_nonsupported_attribute def _set_tcpip_nodelay( self, attribute: ResourceAttribute, attribute_state: bool ) -> StatusCode: if self.interface: self.interface.setsockopt( socket.IPPROTO_TCP, socket.TCP_NODELAY, 1 if attribute_state else 0 ) return StatusCode.success return StatusCode.error_nonsupported_attribute def _get_tcpip_keepalive( self, attribute: ResourceAttribute ) -> Tuple[constants.VisaBoolean, StatusCode]: if self.interface: value = self.interface.getsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE) return ( constants.VisaBoolean.true if value == 1 else constants.VisaBoolean.false, StatusCode.success, ) return constants.VisaBoolean.false, StatusCode.error_nonsupported_attribute def _set_tcpip_keepalive( self, attribute: ResourceAttribute, attribute_state: bool ) -> StatusCode: if self.interface: self.interface.setsockopt( socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1 if attribute_state else 0 ) return StatusCode.success return StatusCode.error_nonsupported_attribute def _get_attribute(self, attribute: ResourceAttribute) -> Tuple[Any, StatusCode]: """Get the value for a given VISA attribute for this session. Use to implement custom logic for attributes. Parameters ---------- attribute : ResourceAttribute Attribute for which the state query is made Returns ------- Any State of the queried attribute for a specified resource StatusCode Return value of the library call. """ raise UnknownAttribute(attribute) def _set_attribute( self, attribute: ResourceAttribute, attribute_state: Any ) -> StatusCode: """Sets the state of an attribute. Corresponds to viSetAttribute function of the VISA library. Parameters ---------- attribute : constants.ResourceAttribute Attribute for which the state is to be modified. (Attributes.*) attribute_state : Any The state of the attribute to be set for the specified object. Returns ------- StatusCode Return value of the library call. """ raise UnknownAttribute(attribute) pyvisa-py-0.5.1/pyvisa_py/testsuite/000077500000000000000000000000001373522010000175235ustar00rootroot00000000000000pyvisa-py-0.5.1/pyvisa_py/testsuite/__init__.py000066400000000000000000000011331373522010000216320ustar00rootroot00000000000000# -*- coding: utf-8 -*- import os import unittest # Set the environment variable to use PyVISA-py as backend os.environ["PYVISA_LIBRARY"] = "@py" def testsuite(): """A testsuite that has all the pyvisa-py tests.""" return unittest.TestLoader().discover(os.path.dirname(__file__)) def main(): """Runs the testsuite as command line application.""" try: unittest.main() except Exception as e: print("Error: %s" % e) def run() -> unittest.TestResult: """Run all tests. """ test_runner = unittest.TextTestRunner() return test_runner.run(testsuite()) pyvisa-py-0.5.1/pyvisa_py/testsuite/keysight_assisted_tests/000077500000000000000000000000001373522010000244735ustar00rootroot00000000000000pyvisa-py-0.5.1/pyvisa_py/testsuite/keysight_assisted_tests/__init__.py000066400000000000000000000007771373522010000266170ustar00rootroot00000000000000# -*- coding: utf-8 -*- """Test relying on the Keysight virtual instrument. The PyVISA builbot is connected to a fake instrument implemented using the Keysight Virtual Instrument IO Test software. For this part of the testsuite to be run, you need to set the PYVISA_KEYSIGHT_VIRTUAL_INSTR environment value. See pyvisa/testsuite/keysight_assisted_tests/__init__.py for more details. :copyright: 2014-2020 by PyVISA-py Authors, see AUTHORS for more details. :license: MIT, see LICENSE for more details. """ pyvisa-py-0.5.1/pyvisa_py/testsuite/keysight_assisted_tests/test_resource_manager.py000066400000000000000000000017141373522010000314300ustar00rootroot00000000000000# -*- coding: utf-8 -*- """Test the Resource manager. """ import pytest from pyvisa.testsuite.keysight_assisted_tests import copy_func, require_virtual_instr from pyvisa.testsuite.keysight_assisted_tests.test_resource_manager import ( TestResourceManager as BaseTestResourceManager, ) from pyvisa.testsuite.keysight_assisted_tests.test_resource_manager import ( TestResourceParsing as BaseTestResourceParsing, ) @require_virtual_instr class TestPyResourceManager(BaseTestResourceManager): """""" test_list_resource = pytest.mark.xfail( copy_func(BaseTestResourceManager.test_list_resource) ) test_last_status = pytest.mark.xfail( copy_func(BaseTestResourceManager.test_last_status) ) test_opening_resource_with_lock = pytest.mark.xfail( copy_func(BaseTestResourceManager.test_opening_resource_with_lock) ) @require_virtual_instr class TestPyResourceParsing(BaseTestResourceParsing): """""" pass pyvisa-py-0.5.1/pyvisa_py/testsuite/keysight_assisted_tests/test_tcpip_resources.py000066400000000000000000000072211373522010000313170ustar00rootroot00000000000000# -*- coding: utf-8 -*- """Test the TCPIP based resources. """ import pytest from pyvisa.testsuite.keysight_assisted_tests import copy_func, require_virtual_instr from pyvisa.testsuite.keysight_assisted_tests.test_tcpip_resources import ( TestTCPIPInstr as TCPIPInstrBaseTest, TestTCPIPSocket as TCPIPSocketBaseTest, ) @require_virtual_instr class TestTCPIPInstr(TCPIPInstrBaseTest): """Test pyvisa-py against a TCPIP INSTR resource.""" #: Type of resource being tested in this test case. #: See RESOURCE_ADDRESSES in the __init__.py file of this package for #: acceptable values RESOURCE_TYPE = "TCPIP::INSTR" #: Minimal timeout value accepted by the resource. When setting the timeout #: to VI_TMO_IMMEDIATE, Visa (Keysight at least) may actually use a #: different value depending on the values supported by the resource. MINIMAL_TIMEOUT = 0 # XXX should we try to have this match VISA ? # XXX Skip test clear to see if it has some bad side effect test_clear = pytest.mark.skip(copy_func(TCPIPInstrBaseTest.test_clear)) test_wrapping_handler = pytest.mark.xfail( copy_func(TCPIPInstrBaseTest.test_wrapping_handler) ) test_managing_visa_handler = pytest.mark.xfail( copy_func(TCPIPInstrBaseTest.test_managing_visa_handler) ) test_wait_on_event = pytest.mark.xfail( copy_func(TCPIPInstrBaseTest.test_wait_on_event) ) test_wait_on_event_timeout = pytest.mark.xfail( copy_func(TCPIPInstrBaseTest.test_wait_on_event_timeout) ) test_getting_unknown_buffer = pytest.mark.xfail( copy_func(TCPIPInstrBaseTest.test_getting_unknown_buffer) ) test_manual_async_read = pytest.mark.xfail( copy_func(TCPIPInstrBaseTest.test_manual_async_read) ) test_uninstall_all_handlers = pytest.mark.xfail( copy_func(TCPIPInstrBaseTest.test_uninstall_all_handlers) ) test_handler_clean_up_on_resource_del = pytest.mark.xfail( copy_func(TCPIPInstrBaseTest.test_handler_clean_up_on_resource_del) ) test_uninstalling_missing_visa_handler = pytest.mark.xfail( copy_func(TCPIPInstrBaseTest.test_uninstalling_missing_visa_handler) ) test_handling_invalid_handler = pytest.mark.xfail( copy_func(TCPIPInstrBaseTest.test_handling_invalid_handler) ) test_write_raw_read_bytes = pytest.mark.xfail( copy_func(TCPIPInstrBaseTest.test_write_raw_read_bytes) ) test_io_prot_attr = pytest.mark.xfail( copy_func(TCPIPInstrBaseTest.test_io_prot_attr) ) test_shared_locking = pytest.mark.xfail( copy_func(TCPIPInstrBaseTest.test_shared_locking) ) test_timeout = pytest.mark.xfail(copy_func(TCPIPInstrBaseTest.test_timeout)) test_attribute_handling = pytest.mark.xfail( copy_func(TCPIPInstrBaseTest.test_attribute_handling) ) @require_virtual_instr class TestTCPIPSocket(TCPIPSocketBaseTest): """Test pyvisa-py against a TCPIP SOCKET resource.""" #: Type of resource being tested in this test case. #: See RESOURCE_ADDRESSES in the __init__.py file of this package for #: acceptable values RESOURCE_TYPE = "TCPIP::SOCKET" #: Minimal timeout value accepted by the resource. When setting the timeout #: to VI_TMO_IMMEDIATE, Visa (Keysight at least) may actually use a #: different value depending on the values supported by the resource. MINIMAL_TIMEOUT = 1 test_timeout = pytest.mark.xfail(copy_func(TCPIPSocketBaseTest.test_timeout)) test_attribute_handling = pytest.mark.xfail( copy_func(TCPIPSocketBaseTest.test_attribute_handling) ) test_stb = pytest.mark.xfail(copy_func(TCPIPSocketBaseTest.test_stb)) pyvisa-py-0.5.1/pyvisa_py/testsuite/test_highlevel.py000066400000000000000000000013531373522010000231050ustar00rootroot00000000000000# -*- coding: utf-8 -*- """Test creating a resource manager using PyVISA-Py as a backend. :copyright: 2014-2020 by PyVISA-py Authors, see AUTHORS for more details. :license: MIT, see LICENSE for more details. """ from pyvisa.highlevel import list_backends from pyvisa.testsuite import BaseTestCase from pyvisa_py import highlevel class TestPyVisaLibrary(BaseTestCase): """Test generic property of PyVisaLibrary.""" def test_list_backends(self): """Test listing backends.""" assert "py" in list_backends() def test_debug_info(self): """Test generating debug infos for PyVISA-py.""" infos = highlevel.PyVisaLibrary.get_debug_info() for key in ("Version",): assert key in infos pyvisa-py-0.5.1/pyvisa_py/usb.py000066400000000000000000000251461373522010000166450ustar00rootroot00000000000000# -*- coding: utf-8 -*- """Serial Session implementation using PyUSB. :copyright: 2014-2020 by PyVISA-py Authors, see AUTHORS for more details. :license: MIT, see LICENSE for more details. """ import errno from typing import Any, List, Tuple, Type, Union from pyvisa import attributes, constants from pyvisa.constants import ResourceAttribute, StatusCode from pyvisa.rname import USBInstr, USBRaw from .common import logger from .sessions import Session, UnknownAttribute try: import usb from .protocols import usbraw, usbtmc, usbutil except ImportError as e: msg = "Please install PyUSB to use this resource type.\n%s" Session.register_unavailable(constants.InterfaceType.usb, "INSTR", msg % e) Session.register_unavailable(constants.InterfaceType.usb, "RAW", msg % e) raise try: _ = usb.core.find() except Exception as e: msg = ( "PyUSB does not seem to be properly installed.\n" "Please refer to PyUSB documentation and \n" "install a suitable backend like \n" "libusb 0.1, libusb 1.0, libusbx, \n" "libusb-win32 or OpenUSB.\n%s" % e ) Session.register_unavailable(constants.InterfaceType.usb, "INSTR", msg) Session.register_unavailable(constants.InterfaceType.usb, "RAW", msg) raise class USBTimeoutException(Exception): """Exception used internally to indicate USB timeout.""" pass class USBSession(Session): """Base class for drivers working with usb devices via usb port using pyUSB.""" # Override parsed to take into account the fact that this class is only used # for a specific kind of resource parsed: Union[USBInstr, USBRaw] #: Class to use when instantiating the interface _intf_cls: Union[Type[usbraw.USBRawDevice], Type[usbtmc.USBTMC]] @staticmethod def list_resources() -> List[str]: """Return list of resources for this type of USB device.""" raise NotImplementedError @classmethod def get_low_level_info(cls) -> str: try: ver = usb.__version__ except AttributeError: ver = "N/A" try: # noinspection PyProtectedMember backend = usb.core.find()._ctx.backend.__class__.__module__.split(".")[-1] except Exception: backend = "N/A" return "via PyUSB (%s). Backend: %s" % (ver, backend) def after_parsing(self) -> None: self.interface = self._intf_cls( int(self.parsed.manufacturer_id, 0), int(self.parsed.model_code, 0), self.parsed.serial_number, ) for name in ("SEND_END_EN", "TERMCHAR", "TERMCHAR_EN"): attribute = getattr(constants, "VI_ATTR_" + name) self.attrs[attribute] = attributes.AttributesByID[attribute].default # Force setting the timeout to get the proper value attribute = constants.VI_ATTR_TMO_VALUE self.set_attribute(attribute, attributes.AttributesByID[attribute].default) def _get_timeout(self, attribute: ResourceAttribute) -> Tuple[int, StatusCode]: if self.interface: if self.interface.timeout == 2 ** 32 - 1: self.timeout = None else: self.timeout = self.interface.timeout / 1000 return super(USBSession, self)._get_timeout(attribute) def _set_timeout(self, attribute: ResourceAttribute, value: int) -> StatusCode: status = super(USBSession, self)._set_timeout(attribute, value) timeout = int(self.timeout * 1000) if self.timeout else 2 ** 32 - 1 timeout = min(timeout, 2 ** 32 - 1) if self.interface: self.interface.timeout = timeout return status def read(self, count: int) -> Tuple[bytes, StatusCode]: """Reads data from device or interface synchronously. Corresponds to viRead function of the VISA library. Parameters ----------- count : int Number of bytes to be read. Returns ------- bytes Data read from the device StatusCode Return value of the library call. """ def _usb_reader(): """Data reader identifying usb timeout exception.""" try: return self.interface.read(count) except usb.USBError as exc: if exc.errno in (errno.ETIMEDOUT, -errno.ETIMEDOUT): raise USBTimeoutException() raise supress_end_en, _ = self.get_attribute(ResourceAttribute.suppress_end_enabled) if supress_end_en: raise ValueError( "VI_ATTR_SUPPRESS_END_EN == True is currently unsupported by pyvisa-py" ) term_char, _ = self.get_attribute(ResourceAttribute.termchar) term_char_en, _ = self.get_attribute(ResourceAttribute.termchar_enabled) return self._read( _usb_reader, count, lambda current: True, # USB always returns a complete message supress_end_en, term_char, term_char_en, USBTimeoutException, ) def write(self, data: bytes) -> Tuple[int, StatusCode]: """Writes data to device or interface synchronously. Corresponds to viWrite function of the VISA library. Parameters ---------- data : bytes Data to be written. Returns ------- int Number of bytes actually transferred StatusCode Return value of the library call. """ send_end, _ = self.get_attribute(ResourceAttribute.send_end_enabled) count = self.interface.write(data) return count, StatusCode.success def close(self): self.interface.close() return StatusCode.success def _get_attribute( self, attribute: constants.ResourceAttribute ) -> Tuple[Any, StatusCode]: """Get the value for a given VISA attribute for this session. Use to implement custom logic for attributes. Parameters ---------- attribute : ResourceAttribute Attribute for which the state query is made Returns ------- Any State of the queried attribute for a specified resource StatusCode Return value of the library call. """ raise UnknownAttribute(attribute) def _set_attribute( self, attribute: constants.ResourceAttribute, attribute_state: Any ) -> StatusCode: """Sets the state of an attribute. Corresponds to viSetAttribute function of the VISA library. Parameters ---------- attribute : constants.ResourceAttribute Attribute for which the state is to be modified. (Attributes.*) attribute_state : Any The state of the attribute to be set for the specified object. Returns ------- StatusCode Return value of the library call. """ raise UnknownAttribute(attribute) @Session.register(constants.InterfaceType.usb, "INSTR") class USBInstrSession(USBSession): """Class for USBTMC devices.""" # Override parsed to take into account the fact that this class is only used # for a specific kind of resource parsed: USBInstr #: Class to use when instantiating the interface _intf_cls = usbtmc.USBTMC @staticmethod def list_resources() -> List[str]: out = [] fmt = ( "USB%(board)s::%(manufacturer_id)s::%(model_code)s::" "%(serial_number)s::%(usb_interface_number)s::INSTR" ) for dev in usbtmc.find_tmc_devices(): intfc = usbutil.find_interfaces( dev, bInterfaceClass=0xFE, bInterfaceSubClass=3 ) try: intfc = intfc[0].index except (IndexError, AttributeError): intfc = 0 try: serial = dev.serial_number except (NotImplementedError, ValueError): msg = ( "Found a device whose serial number cannot be read." " The partial VISA resource name is: " + fmt ) logger.warning( msg, dict( board=0, manufacturer_id=dev.idVendor, model_code=dev.idProduct, serial_number="???", usb_interface_number=intfc, ), ) continue out.append( fmt % dict( board=0, manufacturer_id=dev.idVendor, model_code=dev.idProduct, serial_number=serial, usb_interface_number=intfc, ) ) return out @Session.register(constants.InterfaceType.usb, "RAW") class USBRawSession(USBSession): """Class for RAW devices.""" # Override parsed to take into account the fact that this class is only used # for a specific kind of resource parsed: USBRaw #: Class to use when instantiating the interface _intf_cls = usbraw.USBRawDevice @staticmethod def list_resources() -> List[str]: out = [] fmt = ( "USB%(board)s::%(manufacturer_id)s::%(model_code)s::" "%(serial_number)s::%(usb_interface_number)s::RAW" ) for dev in usbraw.find_raw_devices(): intfc = usbutil.find_interfaces(dev, bInterfaceClass=0xFF) try: intfc = intfc[0].index except (IndexError, AttributeError): intfc = 0 try: serial = dev.serial_number except (NotImplementedError, ValueError): msg = ( "Found a device whose serial number cannot be read." " The partial VISA resource name is: " + fmt ) logger.warning( msg, dict( board=0, manufacturer_id=dev.idVendor, model_code=dev.idProduct, serial_number="???", usb_interface_number=intfc, ), ) continue out.append( fmt % dict( board=0, manufacturer_id=dev.idVendor, model_code=dev.idProduct, serial_number=serial, usb_interface_number=intfc, ) ) return out pyvisa-py-0.5.1/setup.cfg000066400000000000000000000050221373522010000152670ustar00rootroot00000000000000[metadata] name = PyVISA-py author = Hernan E. Grecco author_email = hernan.grecco@gmail.com maintainer = Hernan E. Grecco maintainer_email = hernan.grecco@gmail.com license = MIT License description = Python VISA bindings for GPIB, RS232, and USB instruments keywords = Remote VISA GPIB USB serial RS232 measurement acquisition url = https://github.com/pyvisa/pyvisa-py long_description = file: README.rst, AUTHORS, CHANGES long_description_content_type = text/x-rst classifiers = Development Status :: 4 - Beta Intended Audience :: Developers Intended Audience :: Science/Research License :: OSI Approved :: MIT License Operating System :: Microsoft :: Windows Operating System :: POSIX :: Linux Operating System :: MacOS :: MacOS X Programming Language :: Python Topic :: Scientific/Engineering :: Interface Engine/Protocol Translator Topic :: Software Development :: Libraries :: Python Modules Programming Language :: Python :: 3.6 Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.8 platforms = Linux; Windows; Mac [options] packages = pyvisa_py pyvisa_py.protocols pyvisa_py.testsuite zip_safe = False install_requires = pyvisa>=1.11.0 typing_extensions importlib-metadata; python_version<"3.8" setup_requires = setuptools>=42; wheel; setuptools_scm[toml]>=3.4.3 use_2to3 = False [options.extras_require] gpib-ctypes = gpib-ctypes>=0.3.0 serial = pyserial>=3.0 usb = pyusb [flake8] exclude = .git, __pycache__, docs/source/conf.py, old, build, dist, ignore = E203, E266, E501, W503, E731 # line length is intentionally set to 80 here because pyvisa uses Bugbear # See https://github.com/psf/black/blob/master/README.md#line-length for more details max-line-length = 80 max-complexity = 18 select = B,C,E,F,W,T4,B9 per-file-ignores = pyvisa_py/protocols/vxi11.py:E221 pyvisa_py/serial.py:C901 [mypy] follow_imports = normal strict_optional = True [mypy-usb.*] ignore_missing_imports = True [mypy-serial.*] ignore_missing_imports = True [mypy-gpib.*] ignore_missing_imports = True [mypy-Gpib.*] ignore_missing_imports = True [mypy-gpib_ctypes.*] ignore_missing_imports = True # XXX Ideally remove once pytest ships typing information [mypy-pytest.*] ignore_missing_imports = True [isort] multi_line_output = 3 include_trailing_comma = true force_grid_wrap = 0 use_parentheses = true line_length = 88 skip = pyvisa-py/__init__.py known_third_party = numpy,setuptools,typing_extensions,pyvisa,pytest pyvisa-py-0.5.1/setup.py000066400000000000000000000002071373522010000151600ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- from setuptools import setup if __name__ == "__main__": setup(use_scm_version=True)