From 9a6421a54fe6d1c96412b781f336dba143dcf0b9 Mon Sep 17 00:00:00 2001 From: Markus Zolliker Date: Wed, 31 May 2023 14:27:36 +0200 Subject: [PATCH] up to date with develop/mlz Change-Id: I5ea71bc99a2f0dffc3dbe37e1119eb188ef8a3f0 --- .pylintrc | 25 +----- README.md | 1 - bin/frappy-cfg-editor | 1 - frappy-server.spec | 38 +++++++++ frappy_psi/SR.py | 163 ++++++++++++++++--------------------- frappy_psi/lakeshore.py | 8 +- frappy_psi/thermofisher.py | 53 ++++++------ setup.py | 20 ++--- 8 files changed, 149 insertions(+), 160 deletions(-) create mode 100644 frappy-server.spec diff --git a/.pylintrc b/.pylintrc index da7f1b1..f166b5f 100644 --- a/.pylintrc +++ b/.pylintrc @@ -9,7 +9,7 @@ # Add to the black list. It should be a base name, not a # path. You may set this option multiple times. -ignore = .git +ignore = .git,resources_qt5.py,resources_qt6.py # Pickle collected data for later comparisons. persistent=yes @@ -38,27 +38,21 @@ confidence= # multiple time. disable=missing-docstring ,locally-disabled - ,locally-enabled ,fixme ,no-member - ,bad-whitespace ,wrong-import-position ,ungrouped-imports ,import-self - ,bad-continuation ,protected-access ,unused-argument ,duplicate-code ,attribute-defined-outside-init ,access-member-before-definition - ,no-self-use ,broad-except ,unneeded-not ,unidiomatic-typecheck ,undefined-loop-variable - ,redefined-variable-type - ,deprecated-lambda - + ,consider-using-f-string [REPORTS] # Set the output format. Available formats are text, parseable, colorized, msvs @@ -67,10 +61,6 @@ disable=missing-docstring msg-template={path}:{line}: [{msg_id}({symbol}), {obj}] {msg} -# Put messages in a separate file for each module / package specified on the -# command line instead of printing them on stdout. Reports (if any) will be -# written in a file name "pylint_global.[txt|html]". -files-output=no # Tells whether to display a full report or only the messages reports=no @@ -93,14 +83,11 @@ dummy-variables-rgx=_|dummy # List of additional names supposed to be defined in builtins. Remember that # you should avoid to define new builtins when possible. -additional-builtins= +additional-builtins=Node,Mod,Param,Command,Group [BASIC] -# List of builtins function names that should not be used, separated by a comma -#bad-functions=map,filter,apply,input -bad-functions=apply,input # Regular expression which should only match correct module names module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9_]+))$ @@ -155,12 +142,6 @@ notes=FIXME,XXX,TODO # Maximum number of characters on a single line. max-line-length=132 -# List of optional constructs for which whitespace checking is disabled. `dict- -# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}. -# `trailing-comma` allows a space between comma and closing bracket: (a, ). -# `empty-line` allows space-only lines. -no-space-check=trailing-comma,dict-separator - # Maximum number of lines in a module max-module-lines=1000 diff --git a/README.md b/README.md index 19aae1f..043299a 100644 --- a/README.md +++ b/README.md @@ -60,4 +60,3 @@ changes are done, eventually a sync step should happen: cp -r secop /Volumes/PPMSData/zolliker/frappy/secop it may be that additional folder have to copied ... - diff --git a/bin/frappy-cfg-editor b/bin/frappy-cfg-editor index a2c58fa..fbc8ae7 100755 --- a/bin/frappy-cfg-editor +++ b/bin/frappy-cfg-editor @@ -31,7 +31,6 @@ sys.path.insert(0, path.abspath(path.join(path.dirname(__file__), '..'))) import logging from mlzlog import ColoredConsoleHandler - from frappy.gui.qt import QApplication from frappy.gui.cfg_editor.mainwindow import MainWindow diff --git a/frappy-server.spec b/frappy-server.spec new file mode 100644 index 0000000..6755ff6 --- /dev/null +++ b/frappy-server.spec @@ -0,0 +1,38 @@ +# -*- mode: python ; coding: utf-8 -*- + +block_cipher = None + + +a = Analysis(['bin\\frappy-server'], + pathex=['.'], + binaries=[], + datas=[], + hiddenimports=['frappy.protocol', 'frappy.protocol.dispatcher', 'frappy.protocol.interface', 'frappy.protocol.interface.tcp', + 'frappy_psi.ppmssim', 'frappy_psi.ppmswindows', 'frappy_psi.ppms'], + hookspath=[], + runtime_hooks=[], + excludes=[], + win_no_prefer_redirects=False, + win_private_assemblies=False, + cipher=block_cipher, + noarchive=False) +pyz = PYZ(a.pure, a.zipped_data, + cipher=block_cipher) +exe = EXE(pyz, + a.scripts, + [], + exclude_binaries=True, + name='frappy-server', + debug=False, + bootloader_ignore_signals=False, + strip=False, + upx=True, + console=True ) +coll = COLLECT(exe, + a.binaries, + a.zipfiles, + a.datas, + strip=False, + upx=True, + upx_exclude=[], + name='frappy-server') diff --git a/frappy_psi/SR.py b/frappy_psi/SR.py index c5f5906..bd36ed7 100644 --- a/frappy_psi/SR.py +++ b/frappy_psi/SR.py @@ -22,6 +22,7 @@ from frappy.core import Readable, Parameter, FloatRange, TupleOf, \ HasIO, StringIO, IntRange, BoolType, Writable, EnumType +from frappy.errors import RangeError class SR_IO(StringIO): @@ -30,46 +31,65 @@ class SR_IO(StringIO): def communicate(self, cmd): # remove dash from terminator reply = super().communicate(cmd) - status = self._conn.readbytes(2, 0.1) # get the 2 status bytes + status = self._conn.readbytes(2, timeout=0.1) # get the 2 status bytes return reply + ';%d;%d' % tuple(status) -class Ametek(StringIO, HasIO): - ioClass = SR_IO - - def comm(self, cmd): - reply, status, overload = self.communicate(cmd).split(b';') - if overload != b'0': - self.status = (self.Status.WARN, f'overload {overload}') - self.status = (self.Status.IDLE, '') - return reply - - -class XY(Ametek, Readable): +class XY(HasIO, Readable): value = Parameter('X, Y', datatype=TupleOf(FloatRange(unit='V'), FloatRange(unit='V'))) vmode = Parameter('control mode', EnumType(both_grounded=0, A=1, B=2, A_B_diff=3), readonly=False) range = Parameter('sensitivity value', FloatRange(0.00, 1), unit='V', default=1) autosen_on = Parameter('is auto sensitivity on', BoolType(), readonly=False) - noise_control = Parameter('noise control mode', BoolType(), readonly=False) + noise_control = Parameter('is noise control mode on', BoolType(), readonly=False) phase = Parameter('reference phase control', FloatRange(-360, 360), unit='deg', readonly=False) + frequency = Parameter('oscill. frequen. control', FloatRange(0.001, 250e3), unit='Hz', readonly=False, + group='frequency') + amplitude = Parameter('oscill. amplit. control', FloatRange(0.00, 5), unit='V_rms', readonly=False) + #filter = Parameter('line frequency filter', unit='Hz') sen_range = {name: value + 1 for value, name in enumerate( ['2nV', '5nV', '10nV', '20nV', '50nV', '100nV', '200nV', '500nV', '1uV', '2uV', '5uV', '10uV', '20uV', '50uV', '100uV', '200uV', '500uV', '1mV', '2mV', '5mV', '10mV', '20mV', '50mV', '100mV', '200mV', '500mV', '1V'] )} + irange = Parameter('sensitivity index', EnumType('sensitivity index range', sen_range), readonly=False) - time_const = {value: name for value, name in enumerate( - [('10us', 'N/A'), ('20us', 'N/A'), ('50us', 'N/A'), ('100us', 'N/A'), - ('200us', 'N/A'), ('500us', '500us'), ('1ms', '1ms'), ('2ms', '2ms'), - ('5ms', '5ms'), ('10ms', '10ms'), ('20ms', 'N/A'), ('50ms', 'N/A'), - ('100ms', 'N/A'), ('200ms', 'N/A'), ('500ms', 'N/A'), ('1s', 'N/A'), - ('2s', 'N/A'), ('5s', 'N/A'), ('10s', 'N/A'), ('20s', 'N/A'), ('50s', 'N/A'), - ('100s', 'N/A'), ('200s', 'N/A'), ('500s', 'N/A'), ('1ks', 'N/A'), - ('10ks', 'N/A'), ('20ks', 'N/A'), ('50ks', 'N/A'), ('100ks', 'N/A')] + time_const = {name: value for value, name in enumerate( + ['10us', '20us', '50us', '100us', '200us', '500us', '1ms', '2ms', '5ms', '10ms', + '20ms', '50ms', '100ms', '200ms', '500ms', '1s', '2s', '5s', '10s', '20s', '50s', + '100s', '200s', '500s', '1ks', '10ks', '20ks', '50ks', '100ks'] )} + + tc = Parameter('time const. value', FloatRange(0.00005, 100000), unit='s', readonly=False) itc = Parameter('time const. index', EnumType('time const. index range', time_const), readonly=False) + ioClass = SR_IO + + def comparison(self, curr_value, new_value, value_dict): + c_ind = None # closest index + c_diff = None # closets difference + + for index, value in value_dict.items(): + if c_diff is None or diff < c_diff: + c_ind = index + c_diff = c_diff + + if abs(curr_value - new_value) < c_diff: + return c_ind + else: + for index, value in value_dict.items(): + diff = abs(new_value - value) + if c_diff is None or diff < c_diff: + c_ind = index + c_diff = diff + return c_ind + + def comm(self, cmd): + reply, status, overload = self.communicate(cmd).split(';') + if overload != '0': + self.status = (self.Status.WARN, f'overload {overload}') + self.status = (self.Status.IDLE, '') + return reply def read_vmode(self): return self.comm('VMODE') @@ -97,25 +117,11 @@ class XY(Ametek, Readable): return self.comm('SEN.') # range value def write_range(self): - self.comm(f'IMODE {0}') + self.comm(f'IMODE 0') curr_value = self.read_range() new_value = self.value - c_ind = None # closest parameters - c_diff = None - for index, value in self.sen_range.items(): - diff = abs(curr_value - value) - if c_diff is None or diff < c_diff: - c_ind = index - c_diff = diff - if abs(curr_value - new_value) < c_diff: - return self.comm(f'SEN {c_ind}') - else: - for index, value in self.sen_range.items(): - diff = abs(new_value - value) - if c_diff is None or diff < c_diff: - c_ind = index - c_diff = diff - return self.comm(f'SEN {c_ind}') + c_ind = self.comparison(curr_value, new_value, self.sen_range) + return self.comm(f'SEN {c_ind}') def read_noise_control(self): return self.comm('NOISEMODE') @@ -126,15 +132,24 @@ class XY(Ametek, Readable): def read_tc(self): return self.comm('TC.') - def read_itc(self): - return self.comm(f'TC') + def write_tc(self): + pass - # def write_tc(self, itc): - # if self.noise_control == 0: - # self.itc = self. + def read_itc(self): + return self.comm('TC') + + def write_itc(self, new_itc): + curr_value = self.read_itc() + new_value = self.time_const[self.itc] + c_ind = self.comparison(curr_value, new_value, self.time_const) + + if abs(curr_value - new_value) < c_diff: + if self.read_noise_control() == 1 and (5e-4 <= self.time_const[new_itc] <= 1e-2): + raise RangeError('not allowed with noisemode=1') + return self.comm(f'TC {new_itc}') def read_value(self): - reply = self.comm('XY.').split(',') + reply = self.comm('XY.').split(b',') x = float(reply[0]) y = float(reply[1]) return x, y @@ -142,68 +157,28 @@ class XY(Ametek, Readable): def write_value(self, value): return self.comm(f'XY {value}') - -class Frequency(XY, Writable): - value = Parameter('oscill. frequen. control', FloatRange(0.001, 250e3), unit='Hz', readonly=False) - target = Parameter('target frequency', FloatRange(0.001, 250e3), unit='Hz', readonly=False) - - def read_value(self): + def read_frequency(self): return self.comm('OF.') - def write_target(self,): - target = self.target() - return self.comm(f'OF. {target}') + def write_frequency(self, frequency): + frequency = self.frequency + return self.comm(f'OF. {frequency}') - -class Amplitude(XY, Writable): - value = Parameter('oscill. amplit. control', FloatRange(0.00, 5), unit='V_rms', readonly=False) - target = Parameter('target amplit.', FloatRange(0.00, 5), unit='V_rms', readonly=False) - - # unify the following - # dac = Parameter('output DAC channel value', datatype=TupleOf(IntRange(1, 4), FloatRange(0.0, 5000, unit='mV')), - # readonly=False, initwrite=True, default=(3,0)) - # dac = Parameter('output DAC channel value', FloatRange(-10000, 10000, unit='mV'), - # readonly=False, initwrite=True, default=0) - # oscillator amplitude module - - def read_value(self): + def read_amplitude(self): return self.comm('OA.') - def write_target(self): - target = self.target() - return self.comm(f'OA. {target}') - - # external output DAC - # def read_dac(self): - # # reply = self.comm('DAC %g' % channel) # failed to add the DAC channel you want to control - # reply = self.comm('DAC 3') # stack to channel 3 - # return reply - - # def write_dac(self, value): - # # self.comm('DAC %g %g' % channel % value) - # self.comm('DAC 3 %g' % value) - # return value + def write_amplitude(self, amplitude): + return self.comm(f'OA. {amplitude}') # phase and autophase def read_phase(self): - reply = self.comm('REFP.') - return reply + return self.comm('REFP.') def write_phase(self, value): self.comm(f'REFP {round(1000 * value)}') - self.read_phase() - return value + return self.read_phase() def aphase(self): """auto phase""" self.read_phase() return self.comm('AQN') - -# class Comp(Ametek, Readable): -# enablePoll = False -# value = Parameter(datatype=FloatRange(unit='V')) -# -# -# class arg(Ametek, Readable): -# enablePoll = False -# value = Parameter(datatype=FloatRange(unit='')) diff --git a/frappy_psi/lakeshore.py b/frappy_psi/lakeshore.py index 57787f8..3a017b4 100644 --- a/frappy_psi/lakeshore.py +++ b/frappy_psi/lakeshore.py @@ -20,10 +20,10 @@ # ***************************************************************************** import math -from frappy.core import Readable, Parameter, IntRange, EnumType, FloatRange, \ +from frappy.core import Readable, Parameter, IntRange, FloatRange, \ StringIO, HasIO, StringType, Property, Writable, Drivable, IDLE, ERROR, \ - Attached, StructOf, WARN, Done, BoolType, Enum - + StructOf, WARN, Done, BoolType, Enum +from frappy.errors import RangeError from frappy_psi.convergence import HasConvergence from frappy.mixins import HasOutputModule, HasControlledBy @@ -233,7 +233,7 @@ class HeaterOutput336(HeaterOutput): else: self._range = 1 user_current = max_current * math.sqrt(100) - self.set_par(f'HTRSET {self.loop}', <1 or 2>, 0, user_current, 1) + self.set_par(f'HTRSET {self.loop}', 1 if self.resistance < 50 else 2, 0, user_current, 1) max_power = max_current ** 2 * self.resistance self._max_power = max_power self.set_range() diff --git a/frappy_psi/thermofisher.py b/frappy_psi/thermofisher.py index 18447fd..7b79f5d 100644 --- a/frappy_psi/thermofisher.py +++ b/frappy_psi/thermofisher.py @@ -86,6 +86,29 @@ class ThermFishIO(StringIO): class SensorA10(HasIO, Readable): ioClass = ThermFishIO value = Parameter('internal temperature', unit='degC') + status_messages = [ + (ERROR, 'high tempr. cutout fault', 2, 0), + (ERROR, 'high RA tempr. fault', 2, 1), + (ERROR, 'high temperature fixed fault', 3, 7), + (ERROR, 'low temperature fixed fault', 3, 6), + (ERROR, 'high temperature fault', 3, 5), + (ERROR, 'low temperature fault', 3, 4), + (ERROR, 'low level fault', 3, 3), + (ERROR, 'circulator fault', 4, 5), + (ERROR, 'high press. cutout', 5, 2), + (ERROR, 'motor overloaded', 5, 1), + (ERROR, 'pump speed fault', 5, 0), + (WARN, 'open internal sensor', 1, 7), + (WARN, 'shorted internal sensor', 1, 6), + (WARN, 'high temperature warn', 3, 2), + (WARN, 'low temperature warn', 3, 1), + (WARN, 'low level warn', 3, 0), + (IDLE, 'max. heating', 5, 5), + (IDLE, 'heating', 5, 6), + (IDLE, 'cooling', 5, 4), + (IDLE, 'max cooling', 5, 3), + (IDLE, '', 4, 3), + ] def get_par(self, cmd): """ @@ -112,37 +135,11 @@ class SensorA10(HasIO, Readable): result_str = self.communicate('RUFS') # read unit fault status values_str = result_str.strip().split() values_int = [int(val) for val in values_str] - v1, v2, v3, v4, v5 = values_int #[:5] - status_messages = [ - (ERROR, 'high tempr. cutout fault', 2, 0), - (ERROR, 'high RA tempr. fault', 2, 1), - (ERROR, 'high temperature fixed fault', 3, 7), - (ERROR, 'low temperature fixed fault', 3, 6), - (ERROR, 'high temperature fault', 3, 5), - (ERROR, 'low temperature fault', 3, 4), - (ERROR, 'low level fault', 3, 3), - (ERROR, 'circulator fault', 4, 5), - (ERROR, 'high press. cutout', 5, 2), - (ERROR, 'motor overloaded', 5, 1), - (ERROR, 'pump speed fault', 5, 0), - (WARN, 'open internal sensor', 1, 7), - (WARN, 'shorted internal sensor', 1, 6), - (WARN, 'high temperature warn', 3, 2), - (WARN, 'low temperature warn', 3, 1), - (WARN, 'low level warn', 3, 0), - (IDLE, 'max. heating', 5, 5), - (IDLE, 'heating', 5, 6), - (IDLE, 'cooling', 5, 4), - (IDLE, 'max cooling', 5, 3), - (IDLE, '', 4, 3), - ] - - for status_type, status_msg, vi, bit in status_messages: + for status_type, status_msg, vi, bit in self.status_messages: if values_int[vi-1] & (1 << bit): - print(status_type, status_msg, vi, bit) return status_type, status_msg - return WARN, 'circulation off' + return WARN, 'circulation off' class TemperatureLoopA10(SensorA10, Drivable): diff --git a/setup.py b/setup.py index cc89e69..99d9d93 100755 --- a/setup.py +++ b/setup.py @@ -29,28 +29,28 @@ from os import listdir, path from setuptools import find_packages, setup -import secop.version +import frappy.version -scripts = glob(path.join('bin', 'secop-*')) +scripts = glob(path.join('bin', 'frappy-*')) -uidir = path.join(path.dirname(__file__), 'secop', 'gui', 'ui') +uidir = path.join(path.dirname(__file__), 'frappy', 'gui', 'ui') uis = [path.join('gui', 'ui', entry) for entry in listdir(uidir)] setup( - name='secop-core', - version=secop.version.get_version(), + name='frappy-core', + version=frappy.version.get_version(), license='GPL', author='Enrico Faulhaber', author_email='enrico.faulhaber@frm2.tum.de', description='SECoP Playground core system', packages=find_packages(exclude=['test']), - package_data={'secop': ['RELEASE-VERSION'] + uis}, + package_data={'frappy': ['RELEASE-VERSION'] + uis}, data_files=[ - ('/lib/systemd/system-generators', ['etc/secop-generator']), - ('/lib/systemd/system', ['etc/secop@.service', - 'etc/secop.target', + ('/lib/systemd/system-generators', ['etc/frappy-generator']), + ('/lib/systemd/system', ['etc/frappy@.service', + 'etc/frappy.target', ]), - ('/var/log/secop', []), + ('/var/log/frappy', []), ], scripts=scripts, classifiers=[