Skip to content

Commit

Permalink
Update PSLab plugin to work with the current versions of OpenTAP and …
Browse files Browse the repository at this point in the history
…the Python plugin
  • Loading branch information
mnause-ks authored and ivandiep-ks committed Mar 3, 2023
1 parent b93d798 commit a966d6c
Show file tree
Hide file tree
Showing 37 changed files with 693 additions and 1,020 deletions.
13 changes: 7 additions & 6 deletions .gitignore
@@ -1,10 +1,11 @@
################################################################################
# This .gitignore file was automatically created by Microsoft(R) Visual Studio.
################################################################################

# Visual Studio
/.vs/slnx.sqlite
/.vs/PSLab/v16/.suo
/.vs/VSWorkspaceState.json
/__pycache__
/Python.PSLab.dll
/Python.PSLab.cs

# PyCharm and other IntelliJ IDEs
.idea

# VS Code
.vscode
24 changes: 13 additions & 11 deletions CalibrateCapacitanceStep.py
@@ -1,18 +1,20 @@
from PythonTap import *
from OpenTap import DisplayAttribute
from OpenTap import Display
from opentap import *

from .PSLabSetterTestStep import PSLabSetterTestStep
from .Multimeter import *

@Attribute(DisplayAttribute, "Calibrate Capacitance", "Calibrates stray capacitance", Groups= ["PSLab", "Multimeter"])
class CalibrateCapacitanceStep(PSLabSetterTestStep):

@attribute(
Display("Calibrate Capacitance", "Calibrates stray capacitance", Groups=["PSLab", "Multimeter"]))
class CalibrateCapacitanceStep(TestStep):
# Properties
Multimeter = property(Multimeter, None) \
.add_attribute(Display("Multimeter", "", "Resources", 0))

def __init__(self):
super(CalibrateCapacitanceStep, self).__init__()
print("Calibrate Capacitance test step initialized")

prop = self.AddProperty("Multimeter", None, Multimeter)
prop.AddAttribute(DisplayAttribute, "Multimeter", "", "Resources", -100)

# Inherited method from PythonTap TestStep abstract class
def Run(self):
self.Multimeter.calibrate_capacitance()
super().Run() # 3.0: Required for debugging to work.

self.Multimeter.calibrate_capacitance()
110 changes: 62 additions & 48 deletions CaptureVoltageStep.py
@@ -1,62 +1,76 @@
import clr
clr.AddReference("System.Collections")
from OpenTap import AvailableValues, Display, Unit
from System import Double, Int32
from System.Collections.Generic import List
from System import Int32, Double
from System.ComponentModel import BrowsableAttribute # BrowsableAttribute can be used to hide things from the user.
from System.ComponentModel import Browsable # BrowsableAttribute can be used to hide things from the user.
from opentap import *

from PythonTap import *
from OpenTap import DisplayAttribute, UnitAttribute, AvailableValuesAttribute

from .PSLabSetterTestStep import PSLabSetterTestStep
from .PSLabPublisherTestStep import PSLabPublisherTestStep
from .Oscilloscope import *

@Attribute(DisplayAttribute, "Capture Voltage", "Captures varying voltages using Oscilloscope", Groups= ["PSLab", "Oscilloscope"])
class CaptureVoltageStep(PSLabPublisherTestStep):
def __init__(self):
super(CaptureVoltageStep, self).__init__()
print("Capture test step initialized")

prop = self.AddProperty("Oscilloscope", None, Oscilloscope)
prop.AddAttribute(DisplayAttribute, "Oscilloscope", "", "Resources", -100)
@attribute(Display("Capture Voltage", "Captures varying voltages (in V)",
Groups=["PSLab", "Oscilloscope"]))
class CaptureVoltageStep(TestStep):
# Properties
Channels = property(Int32, 1) \
.add_attribute(
Display("Channels", "Number of channels to sample from simultaneously", "Measurements", -50)) \
.add_attribute(AvailableValues("Available"))

Oscilloscope = property(Oscilloscope, None) \
.add_attribute(Display("Oscilloscope", "", "Resources", 0))

selectable = self.AddProperty("Channels", 1, Int32)
selectable.AddAttribute(AvailableValuesAttribute, "Available")
selectable.AddAttribute(DisplayAttribute, "Channels", "Number of channels to sample from simultaneously.", "Measurements", -50)
available = self.AddProperty("Available", [1,2,4], List[Int32])
available.AddAttribute(BrowsableAttribute, False)
@property(List[Int32])
@attribute(Browsable(False)) # property not visible for the user.
def Available(self):
available = List[Int32]()
available.Add(1)
available.Add(2)
available.Add(3)
available.Add(4)
return available

prop = self.AddProperty("Samples", 200, Int32)
prop.AddAttribute(DisplayAttribute, "Samples", "Number of samples to fetch. Maximum is 10000 divided by number of channels.", "Measurements", -40)
Samples = property(Int32, 200) \
.add_attribute(Display("Samples",
"Number of samples to fetch, maximum is 10000 divided by number of channels",
"Measurements", -40))

prop = self.AddProperty("Timegap", 10.0, Double)
prop.AddAttribute(DisplayAttribute, "Time Gap", "Time gap between samples in microseconds.", "Measurements", -30)
Timegap = property(Double, 10) \
.add_attribute(Display("Time Gap", "Time gap between samples", "Measurements", -30)) \
.add_attribute(Unit("ms"))

def __init__(self):
super(CaptureVoltageStep, self).__init__()

# Inherited method from PythonTap TestStep abstract class
def Run(self):
super().Run() # 3.0: Required for debugging to work.

results = self.Oscilloscope.capture(self.Channels, self.Samples, self.Timegap)

time = results[0].tolist()
channel1 = results[1].tolist()
if len(results) == 2:
for i in range(0, len(time)):
self.PublishStepResult("Oscilloscope Results", ["Time", "Voltage"], [time[i], channel1[i]])
elif len(results) == 3:
channel2 = results[1].tolist()
for i in range(0, len(time)):
self.PublishStepResult("Oscilloscope Results", ["Time", "Voltage 1", "Voltage 2"], [time[i], channel1[i], channel2[i]])
elif len(results) == 5:
channel2 = results[1].tolist()
channel3 = results[1].tolist()
channel4 = results[1].tolist()
for i in range(0, len(time)):
self.PublishStepResult("Oscilloscope Results", ["Time", "Voltage 1", "Voltage 2", "Voltage 3", "Voltage 4"], [time[i], channel1[i], channel2[i], channel3[i], channel4[i]])
pass

# Inherited method from PythonTap TestStep abstract class
def PrePlanRun(self):
pass

# Inherited method from PythonTap TestStep abstract class
def PostPlanRun(self):
pass
match len(results):
case 2:
for i in range(0, len(time)):
self.PublishResult("Oscilloscope Results", ["Time", "Voltage (V)"], [time[i], channel1[i]])
case 3:
channel2 = results[2].tolist()
for i in range(0, len(time)):
self.PublishResult("Oscilloscope Results", ["Time", "Voltage 1 (V)", "Voltage 2 (V)"],
[time[i], channel1[i], channel2[i]])
case 4:
channel2 = results[2].tolist()
channel3 = results[3].tolist()
for i in range(0, len(time)):
self.PublishResult("Oscilloscope Results",
["Time", "Voltage 1 (V)", "Voltage 2 (V)", "Voltage 3 (V)"],
[time[i], channel1[i], channel2[i], channel3[i]])
case 5:
channel2 = results[2].tolist()
channel3 = results[3].tolist()
channel4 = results[4].tolist()
for i in range(0, len(time)):
self.PublishResult("Oscilloscope Results",
["Time", "Voltage 1 (V)", "Voltage 2 (V)", "Voltage 3 (V)", "Voltage 4 (V)"],
[time[i], channel1[i], channel2[i], channel3[i], channel4[i]])
case _:
raise Exception(f"Unexpected number of results: {len(results)}")
16 changes: 8 additions & 8 deletions ConnectionHandler.py
@@ -1,26 +1,27 @@
"""
Class to handle multiple connections for multiple instruments
"""
from PythonTap import *
import threading
from pslab.serial_handler import SerialHandler

from pslab import ScienceLab
from pslab.instrument.logic_analyzer import LogicAnalyzer
from pslab.instrument.multimeter import Multimeter
from pslab.instrument.oscilloscope import Oscilloscope
from pslab.instrument.waveform_generator import WaveformGenerator, PWMGenerator
from pslab.instrument.power_supply import PowerSupply
from pslab.instrument.multimeter import Multimeter
from pslab.instrument.waveform_generator import PWMGenerator
from pslab.instrument.waveform_generator import WaveformGenerator

_lock = threading.Lock()

class ConnectionHandler(object):

class ConnectionHandler(object):
_instance = None
_device = None

def __init__(self):
raise RuntimeError("ConnectionHandler is a singleton class. Call instance() instead")

def __getScienceLab(self) -> ScienceLab:
def __getScienceLab(self) -> ScienceLab:
with _lock:
self._device = ScienceLab() if self._device is None else self._device
return self._device
Expand All @@ -29,14 +30,13 @@ def __getScienceLab(self) -> ScienceLab:
def instance(cls):
with _lock:
if cls._instance is None:
print('Creating new instance')
cls._instance = cls.__new__(cls)
cls._instance._lock = threading.Lock()
return cls._instance

def getOscilloscope(self) -> Oscilloscope:
return self.__getScienceLab().oscilloscope

def getWaveformGenerator(self) -> WaveformGenerator:
return self.__getScienceLab().waveform_generator

Expand Down
74 changes: 37 additions & 37 deletions CountPulsesStep.py
@@ -1,46 +1,46 @@
import clr
clr.AddReference("System.Collections")
from OpenTap import AvailableValues, Display, Unit
from System import Boolean, Double, String
from System.Collections.Generic import List
from System import Int32, Double, Boolean, String
from System.ComponentModel import BrowsableAttribute # BrowsableAttribute can be used to hide things from the user.
from opentap import *

from PythonTap import *
from OpenTap import DisplayAttribute, UnitAttribute, AvailableValuesAttribute

from .PSLabSetterTestStep import PSLabSetterTestStep
from .PSLabPublisherTestStep import PSLabPublisherTestStep
from .LogicAnalyzer import *

@Attribute(DisplayAttribute, "Count pulses", "Count pulses on a digital channel", Groups= ["PSLab", "Logic Analyzer"])
class CountPulsesStep(PSLabPublisherTestStep):
def __init__(self):
super(CountPulsesStep, self).__init__()
print("Count pulses step initialized")

prop = self.AddProperty("LogicAnalyzer", None, LogicAnalyzer)
prop.AddAttribute(DisplayAttribute, "Logic Analyzer", "", "Resources", -100)

selectable = self.AddProperty("channel", "FRQ", String)
selectable.AddAttribute(AvailableValuesAttribute, "Available")
selectable.AddAttribute(DisplayAttribute, "Channel", "Channel to count pulses on.", "Measurements", -50)
available = self.AddProperty("Available", ["LA1", "LA2", "LA3", "LA4", "FRQ"], List[String])
available.AddAttribute(BrowsableAttribute, False)

prop = self.AddProperty("interval", 1.0, Double)
prop.AddAttribute(DisplayAttribute, "Interval", "Time in seconds during which to count pulses.", "Measurements", -40)
@attribute(
Display("Count pulses", "Count pulses on a digital channel", Groups=["PSLab", "Logic Analyzer"]))
class CountPulsesStep(TestStep):
# Properties
channel = property(String, "FRQ") \
.add_attribute(Display("Channel", "Channel to count pulses on", "Measurements", -50)) \
.add_attribute(AvailableValues("Available"))

@property(List[String])
@attribute(Browsable(False)) # property not visible for the user.
def Available(self):
available = List[String]()
available.Add("LA1")
available.Add("LA2")
available.Add("LA3")
available.Add("LA4")
available.Add("FRQ")
return available

interval = property(Double, 1) \
.add_attribute(Display("Interval", "Time during which to count pulses", "Measurements", -40)) \
.add_attribute(Unit("s"))

block = property(Boolean, True) \
.add_attribute(
Display("Block", "Whether to block while waiting for pulses to be captured", "Measurements", -30))

LogicAnalyzer = property(LogicAnalyzer, None) \
.add_attribute(Display("Logic Analyzer", "", "Resources", 0))

prop = self.AddProperty("block", False, Boolean)
prop.AddAttribute(DisplayAttribute, "Block", "Whether to block while waiting for pulses to be captured.", "Measurements", -30)
def __init__(self):
super(CountPulsesStep, self).__init__()

# Inherited method from PythonTap TestStep abstract class
def Run(self):
data = self.LogicAnalyzer.count_pulses(self.channel, interval=self.interval, block=self.block)
pass
super().Run() # 3.0: Required for debugging to work.

# Inherited method from PythonTap TestStep abstract class
def PrePlanRun(self):
pass

# Inherited method from PythonTap TestStep abstract class
def PostPlanRun(self):
pass
data = self.LogicAnalyzer.count_pulses(self.channel, interval=self.interval, block=self.block)
print(data)
40 changes: 13 additions & 27 deletions FetchDataStep.py
@@ -1,37 +1,23 @@
import clr
clr.AddReference("System.Collections")
from System.Collections.Generic import List
from System import Int32, Double, Boolean
from System.ComponentModel import BrowsableAttribute # BrowsableAttribute can be used to hide things from the user.
from OpenTap import Display
from opentap import *

from PythonTap import *
from OpenTap import DisplayAttribute, UnitAttribute, AvailableValuesAttribute

from .PSLabSetterTestStep import PSLabSetterTestStep
from .PSLabPublisherTestStep import PSLabPublisherTestStep
from .LogicAnalyzer import *

@Attribute(DisplayAttribute, "Fetch Data", "Publishes the captured logic events", Groups= ["PSLab", "Logic Analyzer"])
class FetchDataStep(PSLabPublisherTestStep):

@attribute(
Display("Fetch Data", "Publishes the captured logic events", Groups=["PSLab", "Logic Analyzer"]))
class FetchDataStep(TestStep):
# Properties
LogicAnalyzer = property(LogicAnalyzer, None) \
.add_attribute(Display("Logic Analyzer", "", "Resources", 0))

def __init__(self):
super(FetchDataStep, self).__init__()
print("Fetch data test step initialized")

prop = self.AddProperty("LogicAnalyzer", None, LogicAnalyzer)
prop.AddAttribute(DisplayAttribute, "Logic Analyzer", "", "Resources", -100)

# Inherited method from PythonTap TestStep abstract class
def Run(self):
super().Run() # 3.0: Required for debugging to work.

data = self.LogicAnalyzer.fetch_data()
channels = [str(i + 1) for i in range(len(data))]
for i in range(len(data[0])):
self.PublishStepResult("Captured Data", channels, [data[channel][i] for channel in range(len(data))])
pass

# Inherited method from PythonTap TestStep abstract class
def PrePlanRun(self):
pass

# Inherited method from PythonTap TestStep abstract class
def PostPlanRun(self):
pass
self.PublishResult("Captured Data", channels, [data[channel][i] for channel in range(len(data))])

0 comments on commit a966d6c

Please sign in to comment.