"""
Package: collate_instrument.
:Date: |today|
:Author: Semi-ATE <info@Semi-ATE.org>
this collate_instrument package provides utilities to collate Instruments
it provides the following
Classes:
Interface:
enumeration of instrument interface types
InterfaceItem:
dataclass to classify interface item
InstrumentItem
dataclass to classify instrument item
DefInter:
singleton class to determine the default interface and optional backend
CollateInstrument:
singleton class to collate all instrument instantiations
note::
DefInter.check4nidriver() should find the national instrument driver if exist
the used key depends from the pyvisa-version
search for an independent solution !!
"""
import os
from enum import Enum
from dataclasses import dataclass
from typing import Any
from pylab_ml.common.singleton import Singleton
import logging
logger = logging.getLogger("pylab_ml.base_instrument")
[docs]
class Interface(Enum):
gpib = 1
usbserial = 2
pxie = 3
tcpip = 4
singular = 5
generic = 6
[docs]
@dataclass
class InterfaceItem:
interface: Interface
backend: str
hostname: str
port: str
addr: str
[docs]
@dataclass
class InstrumentItem:
instName: str
moduleName: str
className: str
interfaceItem: InterfaceItem
identity: str
initialized: bool
instance: Any
[docs]
class DefInter(object, metaclass=Singleton):
import pyvisa
visa_backends = ["@py", "@ivi", "@ni"]
[docs]
def __init__(self, style=None):
self.style(style)
def style(self, style=None):
if style == "Linux":
self.default_interface = [Interface.tcpip, Interface.usbserial]
self.default_backend = "@py"
elif style == "usbserial" or style == "Serial":
if os.sys.platform == "win32":
self.default_interface = [Interface.tcpip, Interface.usbserial]
default_backend = self.check4nidriver()
if default_backend:
self.default_backend = default_backend
else:
self.default_backend = "@py"
else:
self.default_interface = [Interface.tcpip, Interface.usbserial]
self.default_backend = "@py"
else:
if os.sys.platform == "win32":
self.default_interface = [Interface.tcpip, Interface.gpib]
default_backend = self.check4nidriver()
if default_backend:
self.default_backend = default_backend
else:
self.default_backend = "@py"
else:
self.default_interface = [Interface.tcpip, Interface.usbserial]
self.default_backend = "@py"
[docs]
def check4nidriver(self):
"""
Search for the national instrument driver if exist.
The used key depends from the pyvisa-version
search for an independent solution !!
returns @default_backend or False
"""
default_backend = False
if "ni" in self.pyvisa.util.get_system_details()["backends"]: # pyvisa 1.10.1
vlib = self.pyvisa.util.get_system_details()["backends"]["ni"]
default_backend = "@ni"
elif "ivi" in self.pyvisa.util.get_system_details()["backends"]: # pyvisa 1.11.3
vlib = self.pyvisa.util.get_system_details()["backends"]["ivi"]
default_backend = "@ivi"
else:
print("unknown key in pyvisa, please check your pyvisa-version")
return False
if "Binary library" in vlib and vlib["Binary library"] == "Not found":
return False
return default_backend
[docs]
class CollateInstrument(object, metaclass=Singleton):
[docs]
def __init__(self):
self.interface_addresses = {}
def list_int(self, interface=[]):
if interface is None:
interface = DefInter().default_interface
if interface == []:
interface = Interface
for inter in interface:
print("{} :".format(inter))
if inter in self.interface_addresses:
for k in self.interface_addresses[inter]:
instr = self.interface_addresses[inter][k]
intrf = instr.interfaceItem
if instr.instance is not None:
inam = instr.instance.find_names()
else:
inam = None
print(" {} : {}".format(k, instr.instName))
print(" : {}".format(inam))
print(" : {} : {}".format(instr.moduleName, instr.className))
print(" : {} : {}".format(intrf.interface, intrf.backend))
print(" : {} : {}".format(intrf.hostname, intrf.port))
print(" : {}".format(intrf.addr))
print(" : {} : {}".format(instr.identity, instr.initialized))
print(" : {}".format(instr.instance))
def add(self, instrument):
if instrument.interface not in self.interface_addresses:
self.interface_addresses[instrument.interface] = {}
if instrument.instName is None:
instrument.instName = "{!r}".format(instrument)
if instrument.addr == "?":
addr = 0
while addr in self.interface_addresses[instrument.interface].keys():
addr += 1
instrument.addr = addr
logger.info("Allocating first free {!r} address {!r} to {!r}".format(instrument.interface.name, instrument.addr, instrument.__class__.__name__))
interfaceItem = InterfaceItem(instrument.interface, instrument.backend, instrument.hostname, instrument.port, instrument.addr)
instrumentItem = InstrumentItem(instrument.instName, instrument.__module__, instrument.__class__.__name__, interfaceItem, "", False, instrument)
otherInstrumentItem = self.get_instrument(instrument.interface, instrument.addr)
if otherInstrumentItem:
logger.info("Replacing already occupied {!r} {!r} by {}".format(instrument.interface.name, instrument.addr, otherInstrumentItem.__class__.__name__))
if otherInstrumentItem.instance:
try:
otherInstrumentItem.instance.close()
except Exception:
logger.error("could not close other instrument {}".format(otherInstrumentItem.className))
self.interface_addresses[instrument.interface][instrument.addr] = instrumentItem
return instrument
def identification(self, interface, address, identity=None, module=None, initialized=None, instance=None, instName=None):
if interface in self.interface_addresses:
if address in self.interface_addresses[interface]:
if self.interface_addresses[interface][address] is None:
self.interface_addresses[interface][address] = InstrumentItem("", "", "", None, "", False, None)
if module is not None:
self.interface_addresses[interface][address].moduleName = module
if identity is not None:
self.interface_addresses[interface][address].identity = identity
if initialized is not None:
self.interface_addresses[interface][address].initialized = initialized
if instance is not None:
self.interface_addresses[interface][address].instance = instance
if instName is not None:
self.interface_addresses[interface][address].instName = instName
def get_instrument(self, interface, address):
if interface in self.interface_addresses:
if address in self.interface_addresses[interface]:
return self.interface_addresses[interface][address]
else:
return None
def find_instrument(self, instName):
for interface in self.interface_addresses:
for address in self.interface_addresses[interface]:
instrument = self.get_instrument(interface, address)
if instrument.instName == instName:
return instrument
return None
def drop(self, interface, address, force=False):
if interface in self.interface_addresses:
if address in self.interface_addresses[interface]:
if force or self.interface_addresses[interface][address].initialized:
del self.interface_addresses[interface][address]
[docs]
@dataclass
class InstrumentDefinition:
nickName: str
moduleName: str
className: str
[docs]
class InstrumentLibrary(metaclass=Singleton):
[docs]
def __init__(self):
logger.debug("Class {}".format(self.__class__.__name__))
self.tcc_pythonpath = os.environ["TCC_PYTHONPATH"]
self.collation = CollateInstrument()
self.library = {}
self.library["smu.Keithley2000"] = InstrumentDefinition("smu.Keithley2000", "pylab_ml.smu.keithley.keithley2000", "Keithley2000")
self.library["smu.Keithley2400"] = InstrumentDefinition("smu.Keithley2400", "pylab_ml.smu.keithley.keithley2400", "Keithley2400")
self.library["smu.Keithley2602"] = InstrumentDefinition("smu.Keithley2602", "pylab_ml.smu.keithley.keithley2602", "Keithley2602")
self.library["scope.Lecroy.Wavesurfer3054"] = InstrumentDefinition(
"scope.Lecroy.Wavesurfer3054", "pylab_ml.scope.lecroy.wavesurfer.wavesurfer3054", "Wavesurfer3054"
)