Source code for pymodaq_plugins_utils.hardware.visa

"""VISA hardware discovery cache.

Wraps :mod:`pyvisa` to enumerate available VISA resources exactly once per
process. ``pyvisa`` is an optional dependency: if it is not installed, or no
VISA backend is found, all functions return empty lists and a warning is logged.

Typical usage in a plugin::

    from pymodaq_utils.hardware.visa import list_serial_resources

    ports = list_serial_resources()  # e.g. ['ASRL/dev/ttyUSB0::INSTR']

After hot-plugging a device, refresh the cache with::

    from pymodaq_utils.hardware.visa import invalidate_cache
    invalidate_cache()
"""
import pymodaq_utils.logger as logger_module
from .base import HardwareCache

logger = logger_module.set_logger(logger_module.get_module_name(__file__))


class VisaCache(HardwareCache):
    _cache = None

    @classmethod
    def _fetch(cls) -> dict:
        try:
            import pyvisa
            rm = pyvisa.ResourceManager()
            info = dict(rm.list_resources_info())
            rm.close()
            return info
        except ImportError:
            logger.warning('pyvisa is not installed — VISA resource discovery unavailable. '
                           'Install it with: pip install pyvisa pyvisa-py')
            return {}
        except Exception as e:
            logger.warning(f'VISA resource discovery failed: {e}')
            return {}

    @classmethod
    def list_resources(cls) -> list[str]:
        """All available VISA resource strings (e.g. 'GPIB0::5::INSTR')."""
        return list(cls._get_cache().keys())

    @classmethod
    def list_serial_resources(cls) -> list[str]:
        """ASRL (serial-over-VISA) resource strings only.

        Linux:   'ASRL/dev/ttyUSB0::INSTR'
        Windows: 'ASRL3::INSTR'
        """
        return [r for r in cls._get_cache() if r.startswith('ASRL')]

    @classmethod
    def list_resource_aliases(cls) -> list[str]:
        """Human-readable aliases where available (e.g. 'COM3' on Windows)."""
        return [i.alias for i in cls._get_cache().values() if i.alias]


[docs] def list_resources() -> list[str]: """All available VISA resource strings (e.g. 'GPIB0::5::INSTR', 'TCPIP0::...').""" return VisaCache.list_resources()
[docs] def list_serial_resources() -> list[str]: """ASRL (serial-over-VISA) resource strings only. Linux: ``'ASRL/dev/ttyUSB0::INSTR'`` Windows: ``'ASRL3::INSTR'`` """ return VisaCache.list_serial_resources()
[docs] def list_resource_aliases() -> list[str]: """Human-readable aliases where available (e.g. ``'COM3'`` on Windows).""" return VisaCache.list_resource_aliases()
[docs] def invalidate_cache() -> None: """Clear the VISA resource cache so the next call re-discovers.""" VisaCache.invalidate_cache()