Skip to content

Commit

Permalink
Merge pull request #45 from lightwave-lab/ci
Browse files Browse the repository at this point in the history
CI and tests fixed. Preparing version 1.1.1.
  • Loading branch information
thomaslima authored Feb 21, 2023
2 parents 1096763 + e24f246 commit 956fdb5
Show file tree
Hide file tree
Showing 67 changed files with 851 additions and 850 deletions.
37 changes: 37 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: Python package

on: [push, pull_request]

jobs:
test_code:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
max-parallel: 12
matrix:
python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"]
os: [ubuntu-latest, windows-latest, macos-latest]
steps:
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install build
pip install -r test-requirements.txt
- name: Install package and dependencies
run: |
pip install .
- name: Build package from source
run: |
python -m build
- name: Run tests
run: |
py.test --capture=sys --cov=lightlab --cov-config .coveragerc
- name: Run linting
run: |
py.test --pylint --flake8 --pylint-rcfile=pylintrc lightlab
continue-on-error: true
11 changes: 0 additions & 11 deletions dev-requirements.txt

This file was deleted.

File renamed without changes.
File renamed without changes.
55 changes: 21 additions & 34 deletions lightlab/equipment/abstract_drivers/TekScopeAbstract.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,8 @@ def timebaseConfig(self, avgCnt=None, duration=None, position=None, nPts=None):
self.setConfigParam('DATA:START', 1)
self.setConfigParam('DATA:STOP', nPts)

presentSettings = {
'avgCnt': self.getConfigParam('ACQUIRE:NUMAVG', forceHardware=True)
}

presentSettings = dict()
presentSettings['avgCnt'] = self.getConfigParam('ACQUIRE:NUMAVG', forceHardware=True)
presentSettings['duration'] = self.getConfigParam('HORIZONTAL:MAIN:SCALE', forceHardware=True)
presentSettings['position'] = self.getConfigParam('HORIZONTAL:MAIN:POSITION', forceHardware=True)
presentSettings['nPts'] = self.getConfigParam(self._recLenParam, forceHardware=True)
Expand Down Expand Up @@ -103,19 +101,13 @@ def acquire(self, chans=None, timeout=None, **kwargs):

for c in chans:
if c > self.totalChans:
raise Exception(
(
f'Received channel: {str(c)}'
+ '. Max channels of this scope is '
)
+ str(self.totalChans)
)

raise Exception('Received channel: ' + str(c) +
'. Max channels of this scope is ' + str(self.totalChans))

# Channel select
for ich in range(1, 1 + self.totalChans):
thisState = 1 if ich in chans else 0
self.setConfigParam(f'SELECT:CH{str(ich)}', thisState)
self.setConfigParam('SELECT:CH' + str(ich), thisState)

isSampling = kwargs.get('avgCnt', 0) == 1
self._setupSingleShot(isSampling)
Expand Down Expand Up @@ -184,11 +176,12 @@ def __transferData(self, chan):
Todo:
Make this binary transfer to go even faster
'''
chStr = f'CH{str(chan)}'
chStr = 'CH' + str(chan)
self.setConfigParam('DATA:ENCDG', 'ASCII')
self.setConfigParam('DATA:SOURCE', chStr)

return self.query_ascii_values('CURV?')
voltRaw = self.query_ascii_values('CURV?')
return voltRaw

def __scaleData(self, voltRaw):
''' Scale to second and voltage units.
Expand All @@ -209,10 +202,7 @@ def __scaleData(self, voltRaw):
YZERO, the reference voltage, YOFF, the offset position, and
YSCALE, the conversion factor between position and voltage.
'''
get = lambda param: float(
self.getConfigParam(f'WFMOUTPRE:{param}', forceHardware=True)
)

get = lambda param: float(self.getConfigParam('WFMOUTPRE:' + param, forceHardware=True))
voltage = (np.array(voltRaw) - get('YOFF')) \
* get(self._yScaleParam) \
+ get('YZERO')
Expand Down Expand Up @@ -273,13 +263,10 @@ def setMeasurement(self, measIndex, chan, measType):
'''
if measIndex == 0:
raise ValueError('measIndex is 1-indexed')
measSubmenu = f'MEASUREMENT:MEAS{str(measIndex)}:'
self.setConfigParam(
measSubmenu + self._measurementSourceParam, f'CH{str(chan)}'
)

self.setConfigParam(f'{measSubmenu}TYPE', measType.upper())
self.setConfigParam(f'{measSubmenu}STATE', 1)
measSubmenu = 'MEASUREMENT:MEAS' + str(measIndex) + ':'
self.setConfigParam(measSubmenu + self._measurementSourceParam, 'CH' + str(chan))
self.setConfigParam(measSubmenu + 'TYPE', measType.upper())
self.setConfigParam(measSubmenu + 'STATE', 1)

def measure(self, measIndex):
'''
Expand All @@ -289,16 +276,16 @@ def measure(self, measIndex):
Returns:
(float)
'''
measSubmenu = f'MEASUREMENT:MEAS{str(measIndex)}:'
return float(self.getConfigParam(f'{measSubmenu}VALUE', forceHardware=True))
measSubmenu = 'MEASUREMENT:MEAS' + str(measIndex) + ':'
return float(self.getConfigParam(measSubmenu + 'VALUE', forceHardware=True))

def autoAdjust(self, chans):
''' Adjusts offsets and scaling so that waveforms are not clipped '''
# Save the current measurement status. They will be restored at the end.
self.saveConfig(dest='+autoAdjTemp', subgroup='MEASUREMENT')

for ch in chans:
chStr = f'CH{str(ch)}'
chStr = 'CH' + str(ch)

# Set up measurements
self.setMeasurement(1, ch, 'pk2pk')
Expand All @@ -312,8 +299,8 @@ def autoAdjust(self, chans):
pk2pk = self.measure(1)
mean = self.measure(2)

span = float(self.getConfigParam(f'{chStr}:SCALE'))
offs = float(self.getConfigParam(f'{chStr}:OFFSET'))
span = float(self.getConfigParam(chStr + ':SCALE'))
offs = float(self.getConfigParam(chStr + ':OFFSET'))

# Check if scale is correct within the tolerance
newSpan = None
Expand All @@ -323,7 +310,7 @@ def autoAdjust(self, chans):
elif pk2pk > 0.8 * span:
newSpan = 2 * span
if newSpan < 0.1 or newSpan > 100:
raise Exception(f'Scope channel {chStr} could not be adjusted.')
raise Exception('Scope channel ' + chStr + ' could not be adjusted.')

# Check if offset is correct within the tolerance
if abs(mean) > 0.05 * span:
Expand All @@ -334,8 +321,8 @@ def autoAdjust(self, chans):
break

# Adjust settings
self.setConfigParam(f'{chStr}:SCALE', newSpan / 10)
self.setConfigParam(f'{chStr}:OFFSET', newOffs)
self.setConfigParam(chStr + ':SCALE', newSpan / 10)
self.setConfigParam(chStr + ':OFFSET', newOffs)

# Recover the measurement setup from before adjustment
self.loadConfig(source='+autoAdjTemp', subgroup='MEASUREMENT')
Expand Down
73 changes: 43 additions & 30 deletions lightlab/equipment/abstract_drivers/configurable.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from lightlab import visalogger as logger
from pyvisa import VisaIOError
from contextlib import contextmanager
import dpath.util
import dpath
import json
from numpy import floor
from pathlib import Path
Expand Down Expand Up @@ -32,7 +32,7 @@ class TekConfig(object):

def __init__(self, initDict=None):
if initDict is None:
initDict = {}
initDict = dict()
self.dico = initDict.copy()

def __str__(self):
Expand All @@ -55,18 +55,21 @@ def get(self, cStr, asCmd=True):
asCmd (bool): if true, returns a tuple representing a command. Otherwise returns just the value
'''
try:
val = dpath.util.get(self.dico, cStr, separator=self.separator)
val = dpath.get(self.dico, cStr, separator=self.separator)
except KeyError:
raise KeyError(f'{cStr} is not present in this TekConfig instance')
raise KeyError(cStr + ' is not present in this TekConfig instance')
if type(val) is dict and '&' in val.keys():
val = val['&']
return (cStr, str(val)) if asCmd else val
if not asCmd:
return val
else:
return (cStr, str(val))

def set(self, cStr, val):
''' Takes the value only, not a dictionary '''
# First check that it does not exist as a subdir
try:
ex = dpath.util.get(self.dico, cStr, separator=self.separator)
ex = dpath.get(self.dico, cStr, separator=self.separator)
except KeyError:
# doesn't exist, we are good to go
pass
Expand All @@ -76,20 +79,20 @@ def set(self, cStr, val):
cStr = cStr + self.separator + '&'

cmd = (cStr, val)
success = dpath.util.set(self.dico, *cmd, separator=self.separator)
success = dpath.set(self.dico, *cmd, separator=self.separator)
if success != 1: # it doesn't exist yet
try:
dpath.util.new(self.dico, *cmd, separator=self.separator)
dpath.new(self.dico, *cmd, separator=self.separator)
except (ValueError, dpath.exceptions.PathNotFound):
# We probably have an integer leaf where we would also like to have a directory
parent = self.separator.join(cmd[0].split(self.separator)[:-1])
try:
oldV = self.get(parent, asCmd=False)
except KeyError:
print(f'dpath did not take {cmd}')
print('dpath did not take ' + str(cmd))
raise
dpath.util.set(self.dico, parent, {'&': oldV}, separator=self.separator)
dpath.util.new(self.dico, *cmd, separator=self.separator)
dpath.set(self.dico, parent, {'&': oldV}, separator=self.separator)
dpath.new(self.dico, *cmd, separator=self.separator)

def getList(self, subgroup='', asCmd=True):
''' Deep crawler that goes in and generates a command for every leaf.
Expand All @@ -102,7 +105,7 @@ def getList(self, subgroup='', asCmd=True):
list: list of valid commands (cstr, val) on the subgroup subdirectory
'''
cList = []
children = dpath.util.search(
children = dpath.search(
self.dico, f'{subgroup}*', yielded=True, separator=self.separator
)

Expand All @@ -116,13 +119,14 @@ def getList(self, subgroup='', asCmd=True):
cList += self.getList(subgroup=cmd[0] + self.separator)
if asCmd:
return cList
writeList = [None] * len(cList)
for i, cmd in enumerate(cList):
cStr, val = cmd
if cStr[-1] == '&': # check for tokens
cStr = cStr[:-2]
writeList[i] = f'{cStr} {str(val)}'
return writeList
else:
writeList = [None] * len(cList)
for i, cmd in enumerate(cList):
cStr, val = cmd
if cStr[-1] == '&': # check for tokens
cStr = cStr[:-2]
writeList[i] = cStr + ' ' + str(val)
return writeList

def setList(self, cmdList):
''' The inverse of getList '''
Expand All @@ -144,7 +148,7 @@ def transfer(self, source, subgroup=''):
elif type(source) is type(self):
sCon = source
else:
raise Exception(f'Invalid source for transfer. Got {str(type(source))}')
raise Exception('Invalid source for transfer. Got ' + str(type(source)))
commands = sCon.getList(subgroup=subgroup)
self.setList(commands)
return self
Expand All @@ -169,7 +173,7 @@ def __parseShorthand(cls, setResponse):
cmdGrp = None
for i in range(len(pairs)):
words = pairs[i].split(' ')
cmdLeaf, val = words[:2]
cmdLeaf, val = words[0:2]
if len(words) > 2:
print('Warning 2-value returns not handled by TekConfig class. Ignoring...')
print(*words)
Expand All @@ -192,9 +196,10 @@ def fromSETresponse(cls, setResponse, subgroup=''):
full.setList(commandList)
if subgroup == '':
return full
ret = cls()
ret.transfer(full, subgroup=subgroup)
return ret
else:
ret = cls()
ret.transfer(full, subgroup=subgroup)
return ret

def save(self, fname, subgroup='', overwrite=False):
''' Saves dictionary parameters in json format. Merges if there's something already there, unless overwrite is True.
Expand Down Expand Up @@ -247,7 +252,8 @@ def __init__(self, headerIsOptional=True, verboseIsOptional=False, precedingColo
self.colon = precedingColon
self.space = interveningSpace

self.config = {'default': None}
self.config = dict()
self.config['default'] = None
self.config['init'] = TekConfig()
self.config['live'] = TekConfig()
self.separator = self.config['live'].separator
Expand Down Expand Up @@ -347,7 +353,8 @@ def getDefaultFilename(self):
(str): the default filename
'''
info = self.instrID().split(',')
return defaultFileDir / '-'.join(info[:3]) + '.json'
deffile = defaultFileDir / '-'.join(info[:3]) + '.json'
return deffile

def saveConfig(self, dest='+user', subgroup='', overwrite=False):
'''
Expand Down Expand Up @@ -441,14 +448,17 @@ def _getHardwareConfig(self, cStrList):
cStr = cStr[:-2]

try:
ret = self.query(f'{cStr}?')
ret = self.query(cStr + '?')
except VisaIOError:
logger.error('Problematic parameter was %s.\n'
'Likely it does not exist in this instrument command structure.', cStr)
raise
logger.debug('Queried %s, got %s', cStr, ret)

val = ret.split(' ')[-1] if self.header else ret
if self.header:
val = ret.split(' ')[-1]
else:
val = ret
# Type detection
try:
val = float(val)
Expand Down Expand Up @@ -501,9 +511,12 @@ def generateDefaults(self, filename=None, overwrite=False):
cfgBuild = TekConfig()

for cmd in allSetCmds:
cStr = cmd[0] if cmd[0][-1] != '&' else cmd[0][:-2]
if cmd[0][-1] != '&': # handle the sibling subdir token
cStr = cmd[0]
else:
cStr = cmd[0][:-2]
try:
val = self.query(f'{cStr}?', withTimeout=1000)
val = self.query(cStr + '?', withTimeout=1000)
cfgBuild.set(cStr, val)
logger.info(cStr, '<--', val)
except VisaIOError:
Expand Down
Loading

0 comments on commit 956fdb5

Please sign in to comment.