Merge branch 'wip' of gitlab.psi.ch-samenv:samenv/frappy into wip
This commit is contained in:
commit
12cb0cdade
9
cfg/stick/uniax.cfg
Normal file
9
cfg/stick/uniax.cfg
Normal file
@ -0,0 +1,9 @@
|
||||
[NODE]
|
||||
id = uniax stick
|
||||
class = protocol.router.Router
|
||||
description = router forwarding to linse-uniax apu
|
||||
node = linse-uniax:5000
|
||||
|
||||
#[INTERFACE]
|
||||
#uri=tcp://5002
|
||||
|
@ -18,7 +18,7 @@ io = drv_io
|
||||
standby_current=0.1
|
||||
maxcurrent=1.4
|
||||
acceleration=150.
|
||||
movelimit=360
|
||||
move_limit=360
|
||||
speed=40
|
||||
encoder_tolerance=3.6
|
||||
free_wheeling=0.1
|
||||
|
@ -10,6 +10,7 @@ description = force control
|
||||
class = secop_psi.uniax.Uniax
|
||||
motor = drv
|
||||
transducer = transducer
|
||||
force_offset = 15
|
||||
|
||||
#[drv_io]
|
||||
#description =
|
||||
@ -23,10 +24,10 @@ class = secop_psi.trinamic.Motor
|
||||
# io = drv_io
|
||||
uri = tcp://192.168.127.254:3002
|
||||
standby_current=0.1
|
||||
maxcurrent=0.2
|
||||
maxcurrent=0.3
|
||||
acceleration=150.
|
||||
move_limit=5
|
||||
safe_current=0.2
|
||||
safe_current=0.3
|
||||
speed=40
|
||||
encoder_tolerance=3.6
|
||||
free_wheeling=0.1
|
||||
@ -39,6 +40,7 @@ class = secop_psi.dpm.DPM3
|
||||
uri = tcp://192.168.127.254:3001
|
||||
digits = 2
|
||||
scale_factor = 0.0156
|
||||
offset = 15
|
||||
|
||||
[res]
|
||||
description = temperature on uniax stick
|
||||
|
@ -47,7 +47,7 @@ class SilentError(CommunicationFailedError):
|
||||
|
||||
class HasIO(Module):
|
||||
"""Mixin for modules using a communicator"""
|
||||
io = Attached()
|
||||
io = Attached(mandatory=False)
|
||||
uri = Property('uri for automatic creation of the attached communication module',
|
||||
StringType(), default='')
|
||||
|
||||
|
@ -18,83 +18,86 @@
|
||||
# Module authors:
|
||||
# Daniel Margineda <daniel.margineda@psi.ch>
|
||||
# *****************************************************************************
|
||||
"""WAVE FUNCTION LECROY XX: SIGNAL GENERATOR"""
|
||||
"""WAVE FUNCTION LECROY XX: SIGNAL GENERATOR
|
||||
|
||||
modifications not tested!
|
||||
"""
|
||||
|
||||
from secop.core import Readable, Parameter, FloatRange, \
|
||||
IntRange, BoolType, EnumType, Module, Property
|
||||
IntRange, BoolType, EnumType, Module, Property, HasIO
|
||||
|
||||
|
||||
class Channel(Module):
|
||||
class Channel(HasIO, Module):
|
||||
channel = Property('choose channel to manipulate', IntRange(1, 2))
|
||||
freq = Parameter('frequency', FloatRange(1e-6, 20e6, unit='Hz'),
|
||||
poll=True, initwrite=True, default=1000)
|
||||
initwrite=True, default=1000)
|
||||
amp = Parameter('exc_volt_int', FloatRange(0.00, 5, unit='Vrms'),
|
||||
poll=True, readonly=False, initwrite=True, default=0.1)
|
||||
readonly=False, initwrite=True, default=0.1)
|
||||
offset = Parameter('offset_volt_int', FloatRange(0.0, 10, unit='V'),
|
||||
poll=True, readonly=False, initwrite=True, default=0.0)
|
||||
readonly=False, initwrite=True, default=0.0)
|
||||
wave = Parameter('type of wavefunction',
|
||||
EnumType('WaveFunction', SINE=1, SQUARE=2, RAMP=3, PULSE=4, NOISE=5, ARB=6, DC=7),
|
||||
poll=True, readonly=False, default='SINE')
|
||||
readonly=False, default='SINE')
|
||||
phase = Parameter('signal phase', FloatRange(0, 360, unit='deg'),
|
||||
poll=True, readonly=False, initwrite=True, default=0)
|
||||
readonly=False, initwrite=True, default=0)
|
||||
enabled = Parameter('enable output channel', datatype=EnumType('OnOff', OFF=0, ON=1),
|
||||
readonly=False, default='OFF')
|
||||
symm = Parameter('wavefunction symmetry', FloatRange(0, 100, unit=''),
|
||||
poll=True, readonly=False, default=0)
|
||||
readonly=False, default=0)
|
||||
|
||||
def read_value(self):
|
||||
return self.sendRecv('C%d:BSWV FRQ?' % self.channel)
|
||||
return self.communicate('C%d:BSWV FRQ?' % self.channel)
|
||||
|
||||
def write_target(self, value):
|
||||
self.sendRecv('C%d:BSWV FRQ, %gHz' % (self.channel, value))
|
||||
self.communicate('C%d:BSWV FRQ, %gHz' % (self.channel, value))
|
||||
return value
|
||||
|
||||
# signal wavefunction parameter
|
||||
def read_wave(self):
|
||||
return self.sendRecv('C%d:BSWV WVTP?' % self.channel)
|
||||
return self.communicate('C%d:BSWV WVTP?' % self.channel)
|
||||
|
||||
def write_wave(self, value): # string value
|
||||
self.sendRecv('C%d:BSWV WVTP, %s' % (self.channel, value.name))
|
||||
self.communicate('C%d:BSWV WVTP, %s' % (self.channel, value.name))
|
||||
return value
|
||||
|
||||
# signal amplitude parameter
|
||||
def read_amp(self):
|
||||
return self.sendRecv('C%d:BSWV AMP?' % self.channel)
|
||||
return self.communicate('C%d:BSWV AMP?' % self.channel)
|
||||
|
||||
def write_amp(self, value):
|
||||
self.sendRecv('C%d:BSWV AMP, %g' % (self.channel, value))
|
||||
self.communicate('C%d:BSWV AMP, %g' % (self.channel, value))
|
||||
return value
|
||||
|
||||
# offset value parameter
|
||||
def read_offset(self):
|
||||
return self.sendRecv('C%d:BSWV OFST?' % self.channel)
|
||||
return self.communicate('C%d:BSWV OFST?' % self.channel)
|
||||
|
||||
def write_offset(self, value):
|
||||
self.sendRecv('C%d:BSWV OFST %g' % (self.channel, value))
|
||||
self.communicate('C%d:BSWV OFST %g' % (self.channel, value))
|
||||
return value
|
||||
|
||||
# channel symmetry
|
||||
def read_symm(self):
|
||||
return self.sendRecv('C%d:BSWV SYM?' % self.channel)
|
||||
return self.communicate('C%d:BSWV SYM?' % self.channel)
|
||||
|
||||
def write_symm(self, value):
|
||||
self.sendRecv('C%d:BSWV SYM %g' % (self.channel, value))
|
||||
self.communicate('C%d:BSWV SYM %g' % (self.channel, value))
|
||||
return value
|
||||
|
||||
# wave phase parameter
|
||||
def read_phase(self):
|
||||
return self.sendRecv('C%d:BSWV PHSE?' % self.channel)
|
||||
return self.communicate('C%d:BSWV PHSE?' % self.channel)
|
||||
|
||||
def write_phase(self, value):
|
||||
self.sendRecv('C%d:BSWV PHSE %g' % (self.channel, value))
|
||||
self.communicate('C%d:BSWV PHSE %g' % (self.channel, value))
|
||||
return value
|
||||
|
||||
# dis/enable output channel
|
||||
def read_enabled(self):
|
||||
return self.sendRecv('C%d: OUTP?' % self.channel)
|
||||
return self.communicate('C%d: OUTP?' % self.channel)
|
||||
|
||||
def write_enabled(self, value):
|
||||
self.sendRecv('C%d: OUTP %s' % (self.channel, value.name))
|
||||
self.communicate('C%d: OUTP %s' % (self.channel, value.name))
|
||||
return value
|
||||
|
||||
|
||||
|
@ -21,7 +21,7 @@
|
||||
"""Signal Recovery SR7270: lockin amplifier for AC susceptibility"""
|
||||
|
||||
from secop.core import Readable, Parameter, Command, FloatRange, TupleOf, \
|
||||
HasIodev, StringIO, Attached, IntRange, BoolType, EnumType
|
||||
HasIO, StringIO, Attached, IntRange, BoolType, EnumType
|
||||
|
||||
|
||||
class SR7270(StringIO):
|
||||
@ -33,7 +33,7 @@ class SR7270(StringIO):
|
||||
return reply + ';%d;%d' % tuple(status)
|
||||
|
||||
|
||||
class XY(HasIodev, Readable):
|
||||
class XY(HasIO, Readable):
|
||||
x = Attached()
|
||||
y = Attached()
|
||||
freq_arg = Attached()
|
||||
@ -46,29 +46,29 @@ class XY(HasIodev, Readable):
|
||||
value = Parameter('X, Y', datatype=TupleOf(FloatRange(unit='V'), FloatRange(unit='V')))
|
||||
freq = Parameter('exc_freq_int',
|
||||
FloatRange(0.001, 250e3, unit='Hz'),
|
||||
poll=True, readonly=False, initwrite=True, default=1000)
|
||||
readonly=False, initwrite=True, default=1000)
|
||||
amp = Parameter('exc_volt_int',
|
||||
FloatRange(0.00, 5, unit='Vrms'),
|
||||
poll=True, readonly=False, initwrite=True, default=0.1)
|
||||
range = Parameter('sensitivity value', FloatRange(0.00, 1, unit='V'), poll=True, default=1)
|
||||
irange = Parameter('sensitivity index', IntRange(0, 27), poll=True, readonly=False, default=25)
|
||||
readonly=False, initwrite=True, default=0.1)
|
||||
range = Parameter('sensitivity value', FloatRange(0.00, 1, unit='V'), default=1)
|
||||
irange = Parameter('sensitivity index', IntRange(0, 27), readonly=False, default=25)
|
||||
autorange = Parameter('autorange_on', EnumType('autorange', off=0, soft=1, hard=2),
|
||||
readonly=False, default=0, initwrite=True)
|
||||
tc = Parameter('time constant value', FloatRange(10e-6, 100, unit='s'), poll=True, default=0.1)
|
||||
itc = Parameter('time constant index', IntRange(0, 30), poll=True, readonly=False, initwrite=True, default=14)
|
||||
tc = Parameter('time constant value', FloatRange(10e-6, 100, unit='s'), default=0.1)
|
||||
itc = Parameter('time constant index', IntRange(0, 30), readonly=False, initwrite=True, default=14)
|
||||
nm = Parameter('noise mode', BoolType(), readonly=False, default=0)
|
||||
phase = Parameter('Reference phase control', FloatRange(-360, 360, unit='deg'),
|
||||
poll=True, readonly=False, initwrite=True, default=0)
|
||||
readonly=False, initwrite=True, default=0)
|
||||
vmode = Parameter('Voltage input configuration', IntRange(0, 3), readonly=False, default=3),
|
||||
# dac = Parameter('output DAC channel value', datatype=TupleOf(IntRange(1, 4), FloatRange(0.0, 5000, unit='mV')),
|
||||
# poll=True, readonly=False, initwrite=True, default=(3,0))
|
||||
# readonly=False, initwrite=True, default=(3,0))
|
||||
dac = Parameter('output DAC channel value', FloatRange(-10000, 10000, unit='mV'),
|
||||
poll=True, readonly=False, initwrite=True, default=0)
|
||||
readonly=False, initwrite=True, default=0)
|
||||
|
||||
iodevClass = SR7270
|
||||
ioClass = SR7270
|
||||
|
||||
def comm(self, command):
|
||||
reply, status, overload = self.sendRecv(command).split(';')
|
||||
reply, status, overload = self.communicate(command).split(';')
|
||||
if overload != '0':
|
||||
self.status = self.Status.WARN, 'overload %s' % overload
|
||||
else:
|
||||
@ -200,10 +200,10 @@ class XY(HasIodev, Readable):
|
||||
|
||||
|
||||
class Comp(Readable):
|
||||
pollerClass = None
|
||||
enablePoll = False
|
||||
value = Parameter(datatype=FloatRange(unit='V'))
|
||||
|
||||
|
||||
class arg(Readable):
|
||||
pollerClass = None
|
||||
enablePoll = False
|
||||
value = Parameter(datatype=FloatRange(unit=''))
|
||||
|
@ -22,7 +22,7 @@
|
||||
|
||||
"""driver for cryotel stirling cryocooler"""
|
||||
|
||||
from secop.core import Command, EnumType, FloatRange, HasIodev, Parameter, Drivable, StringIO, StringType
|
||||
from secop.core import Command, EnumType, FloatRange, HasIO, Parameter, Drivable, StringIO, StringType
|
||||
from secop.errors import CommunicationFailedError, HardwareError
|
||||
|
||||
|
||||
@ -48,27 +48,27 @@ class CryotelIO(StringIO):
|
||||
return reply
|
||||
|
||||
|
||||
class Cryo(HasIodev, Drivable):
|
||||
class Cryo(HasIO, Drivable):
|
||||
value = Parameter('current temperature', FloatRange(unit='deg'))
|
||||
target = Parameter('target temperature', FloatRange(unit='deg'), readonly=False)
|
||||
mode = Parameter('control mode', EnumType('mode', off=0, power=1, temperature=2), readonly=False, poll=True)
|
||||
power = Parameter('power', FloatRange(unit='W'), poll=True)
|
||||
mode = Parameter('control mode', EnumType('mode', off=0, power=1, temperature=2), readonly=False)
|
||||
power = Parameter('power', FloatRange(unit='W'))
|
||||
setpower = Parameter('requested power', FloatRange(unit='W'), default=0)
|
||||
cool_power = Parameter('power for cooling', FloatRange(unit='W'), default=240, readonly=False)
|
||||
hold_power = Parameter('power for holding T', FloatRange(unit='W'), default=120, readonly=False)
|
||||
cool_threshold = Parameter('switch to cool_power once above this value', FloatRange(unit='K'), default=100, readonly=False)
|
||||
hold_threshold = Parameter('switch to hold_power once below this value', FloatRange(unit='K'), default=95, readonly=False)
|
||||
|
||||
iodevClass = CryotelIO
|
||||
ioClass = CryotelIO
|
||||
cnt_inside = 0
|
||||
|
||||
def get(self, cmd):
|
||||
return float(self._iodev.communicate(cmd))
|
||||
return float(self.communicate(cmd))
|
||||
|
||||
def set(self, cmd, value, check=False):
|
||||
setcmd = '%s=%.2f' % (cmd, value)
|
||||
self._iodev.communicate(setcmd)
|
||||
reply = float(self._iodev.communicate(cmd))
|
||||
self.communicate(setcmd)
|
||||
reply = float(self.communicate(cmd))
|
||||
if check:
|
||||
if value != reply:
|
||||
raise HardwareError('illegal reply from %s: %g' % (cmd, reply))
|
||||
|
@ -21,10 +21,8 @@
|
||||
# *****************************************************************************
|
||||
"""transducer DPM3 read out"""
|
||||
|
||||
import time
|
||||
|
||||
from secop.core import Readable, Parameter, FloatRange, StringIO,\
|
||||
HasIodev, IntRange, Done
|
||||
HasIO, IntRange, Done
|
||||
|
||||
|
||||
class DPM3IO(StringIO):
|
||||
@ -47,22 +45,22 @@ def float2hex(value, digits):
|
||||
return '%06X' % intvalue
|
||||
|
||||
|
||||
class DPM3(HasIodev, Readable):
|
||||
class DPM3(HasIO, Readable):
|
||||
OFFSET = 0x8f
|
||||
SCALE = 0x8c
|
||||
|
||||
MAGNITUDE = {'1': 1, '2': 10, '3': 100, '4': 1e3, '5': 1e4, '6': 1e5,
|
||||
'9': -1, 'A': -10, 'B': -100, 'C': -1e3, 'D': -1e4, 'E': -1e5}
|
||||
|
||||
iodevClass = DPM3IO
|
||||
ioClass = DPM3IO
|
||||
|
||||
value = Parameter(datatype=FloatRange(unit='N'))
|
||||
digits = Parameter('number of digits for value', IntRange(0, 5), initwrite=True, readonly=False)
|
||||
# Note: we have to treat the units properly.
|
||||
# We got an output of 150 for 10N. The maximal force we want to deal with is 100N,
|
||||
# thus a maximal output of 1500. 10=150/f
|
||||
offset = Parameter('', FloatRange(-1e5, 1e5), readonly=False, poll=True)
|
||||
scale_factor = Parameter('', FloatRange(-1e5, 1e5, unit='input_units/N'), readonly=False, poll=True)
|
||||
offset = Parameter('', FloatRange(-1e5, 1e5), readonly=False)
|
||||
scale_factor = Parameter('', FloatRange(-1e5, 1e5, unit='input_units/N'), readonly=False)
|
||||
|
||||
def query(self, adr, value=None):
|
||||
if value is not None:
|
||||
@ -87,7 +85,7 @@ class DPM3(HasIodev, Readable):
|
||||
else:
|
||||
cmd = ""
|
||||
cmd = cmd + '*1G3%02X' % adr
|
||||
hexvalue = self._iodev.communicate(cmd)
|
||||
hexvalue = self.communicate(cmd)
|
||||
if adr == self.SCALE:
|
||||
mag = self.MAGNITUDE[hexvalue[0:1]]
|
||||
value = int(hexvalue[1:], 16)
|
||||
@ -97,7 +95,7 @@ class DPM3(HasIodev, Readable):
|
||||
|
||||
def write_digits(self, value):
|
||||
# value defines the number of digits
|
||||
back_value = self._iodev.communicate('*1F135%02X\r*1G135' % (value + 1))
|
||||
back_value = self.communicate('*1F135%02X\r*1G135' % (value + 1))
|
||||
self.digits = int(back_value, 16) - 1
|
||||
# recalculate proper scale and offset
|
||||
self.write_scale_factor(self.scale_factor)
|
||||
@ -105,11 +103,11 @@ class DPM3(HasIodev, Readable):
|
||||
return Done
|
||||
|
||||
def read_digits(self):
|
||||
back_value = self._iodev.communicate('*1G135')
|
||||
back_value = self.communicate('*1G135')
|
||||
return int(back_value,16) - 1
|
||||
|
||||
def read_value(self):
|
||||
return float(self._iodev.communicate('*1B1'))
|
||||
return float(self.communicate('*1B1'))
|
||||
|
||||
def read_offset(self):
|
||||
reply = self.query(self.OFFSET)
|
||||
|
@ -24,7 +24,7 @@ import time
|
||||
|
||||
from secop.datatypes import StringType, FloatRange
|
||||
from secop.modules import Parameter, Property, Readable
|
||||
from secop.io import HasIodev, StringIO
|
||||
from secop.io import HasIO, StringIO
|
||||
|
||||
|
||||
class LscIO(StringIO):
|
||||
@ -33,13 +33,13 @@ class LscIO(StringIO):
|
||||
wait_before = 0.05
|
||||
|
||||
|
||||
class ResChannel(HasIodev, Readable):
|
||||
class ResChannel(HasIO, Readable):
|
||||
"""temperature channel on Lakeshore 340"""
|
||||
|
||||
iodevClass = LscIO
|
||||
ioClass = LscIO
|
||||
|
||||
value = Parameter(datatype=FloatRange(unit='Ohm'))
|
||||
channel = Property('the channel A,B,C or D', StringType())
|
||||
|
||||
def read_value(self):
|
||||
return self._iodev.communicate('SRDG?%s' % self.channel)
|
||||
return float(self.communicate('SRDG?%s' % self.channel))
|
||||
|
@ -1,61 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# *****************************************************************************
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free Software
|
||||
# Foundation; either version 2 of the License, or (at your option) any later
|
||||
# version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#
|
||||
# Module authors:
|
||||
# Markus Zolliker <markus.zolliker@psi.ch>
|
||||
# *****************************************************************************
|
||||
"""PPMS mf proxy"""
|
||||
|
||||
import secop_psi.ppms
|
||||
from secop.core import Drivable, Enum, EnumType, FloatRange, Override, Parameter
|
||||
from secop.datatypes import StatusType
|
||||
from secop.proxy import proxy_class
|
||||
|
||||
|
||||
class Field(proxy_class(secop_psi.ppms.Field)):
|
||||
"""magnetic field"""
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
Status = Enum(Drivable.Status,
|
||||
PREPARED = 150,
|
||||
PREPARING = 340,
|
||||
RAMPING = 370,
|
||||
FINALIZING = 390,
|
||||
)
|
||||
# pylint: disable=invalid-name
|
||||
PersistentMode = Enum('PersistentMode', persistent=0, driven=1)
|
||||
ApproachMode = Enum('ApproachMode', linear=0, no_overshoot=1, oscillate=2)
|
||||
|
||||
remoteParameters = {
|
||||
'value':
|
||||
Override(datatype=FloatRange(-1, 1, unit='T'), poll=True),
|
||||
'status':
|
||||
Override(datatype=StatusType(Status), poll=True),
|
||||
'target':
|
||||
Override(datatype=FloatRange(-15, 15, unit='T'), poll=True),
|
||||
'ramp':
|
||||
Parameter('ramping speed', readonly=False,
|
||||
datatype=FloatRange(0.064, 1.19, unit='T/min'), poll=True),
|
||||
'approachmode':
|
||||
Parameter('how to approach target', readonly=False,
|
||||
datatype=EnumType(ApproachMode), poll=True),
|
||||
'persistentmode':
|
||||
Parameter('what to do after changing field', readonly=False,
|
||||
datatype=EnumType(PersistentMode), poll=True),
|
||||
'pollinterval':
|
||||
Override(visibility=3),
|
||||
}
|
@ -32,20 +32,20 @@ from secop.core import Attached, BoolType, FloatRange, IntRange, \
|
||||
|
||||
|
||||
class Temperature(Readable):
|
||||
pollerClass = None
|
||||
enablePoll = False
|
||||
|
||||
value = Parameter(datatype=FloatRange(unit='degC'))
|
||||
|
||||
|
||||
class Bcomp(Readable):
|
||||
pollerClass = None
|
||||
enablePoll = False
|
||||
|
||||
value = Parameter(datatype=FloatRange(unit='T'))
|
||||
range = Parameter('working range', FloatRange(unit='T'), default=0)
|
||||
|
||||
|
||||
class Raw(Readable):
|
||||
pollerClass = None
|
||||
enablePoll = False
|
||||
|
||||
value = Parameter(datatype=FloatRange())
|
||||
|
||||
@ -62,7 +62,7 @@ class TeslameterBase(Readable):
|
||||
y = Attached()
|
||||
z = Attached()
|
||||
|
||||
value = Parameter('B vector', poll=True,
|
||||
value = Parameter('B vector',
|
||||
datatype=TupleOf(FloatRange(unit='T'), FloatRange(unit='T'), FloatRange(unit='T')))
|
||||
usb = Parameter('usb device', StringType(), readonly=False)
|
||||
enabled = Parameter('enable data acq', datatype=BoolType(), readonly=False, default=True)
|
||||
@ -152,7 +152,7 @@ class Teslameter3MH6(TeslameterBase):
|
||||
|
||||
range = Parameter('range or 0 for autorange', FloatRange(0, 20, unit='T'), readonly=False, default=0)
|
||||
rate = Parameter('sampling rate', datatype=FloatRange(10, 15000, unit='Hz'),
|
||||
readonly=False, poll=True)
|
||||
readonly=False)
|
||||
avtime = Parameter('data acquisition time', FloatRange(), default=0)
|
||||
|
||||
SAMPLING_RATES = {0xe0: 15000, 0xd0: 7500, 0xc0: 3750, 0xb0: 2000, 0xa1: 1000,
|
||||
|
@ -22,17 +22,17 @@
|
||||
|
||||
from secop.datatypes import FloatRange, IntRange, StringType
|
||||
from secop.modules import Drivable, Parameter, Readable
|
||||
from secop.io import HasIodev
|
||||
from secop.io import HasIO
|
||||
|
||||
Status = Drivable.Status
|
||||
|
||||
|
||||
class TempLoop(HasIodev, Drivable):
|
||||
class TempLoop(HasIO, Drivable):
|
||||
"""temperature channel on Lakeshore 336"""
|
||||
|
||||
value = Parameter(datatype=FloatRange(unit='K'), default=0, poll=True)
|
||||
value = Parameter(datatype=FloatRange(unit='K'), default=0)
|
||||
status = Parameter(poll=False)
|
||||
target = Parameter(datatype=FloatRange(1.0, 402.0, unit='K'), default=1.3, poll=True)
|
||||
target = Parameter(datatype=FloatRange(1.0, 402.0, unit='K'), default=1.3)
|
||||
tolerance = Parameter('the tolerance', FloatRange(-400, 400), default=1, readonly=False)
|
||||
pollinterval = Parameter(visibility=3)
|
||||
channel = Parameter('the Lakeshore channel', datatype=StringType(), export=False)
|
||||
|
@ -32,7 +32,7 @@ class Uniax(PersistentMixin, Drivable):
|
||||
value = Parameter(unit='N')
|
||||
motor = Attached()
|
||||
transducer = Attached()
|
||||
limit = Parameter('abs limit of force', FloatRange(0, 150, unit='N'), readonly=False, default=150)
|
||||
limit = Parameter('abs limit of force', FloatRange(0, 190, unit='N'), readonly=False, default=150)
|
||||
tolerance = Parameter('force tolerance', FloatRange(0, 10, unit='N'), readonly=False, default=0.1)
|
||||
slope = PersistentParam('spring constant', FloatRange(unit='deg/N'), readonly=False,
|
||||
default=0.5, persistent='auto')
|
||||
@ -41,7 +41,7 @@ class Uniax(PersistentMixin, Drivable):
|
||||
current_step = Parameter('', FloatRange(unit='deg'), default=0)
|
||||
force_offset = PersistentParam('transducer offset', FloatRange(unit='N'), readonly=False, default=0,
|
||||
initwrite=True, persistent='auto')
|
||||
hysteresis = PersistentParam('force hysteresis', FloatRange(0, 150, unit='N'), readonly=False, default=5,
|
||||
hysteresis = PersistentParam('force hysteresis', FloatRange(0, 190, unit='N'), readonly=False, default=5,
|
||||
persistent='auto')
|
||||
adjusting = Parameter('', BoolType(), readonly=False, default=False, initwrite=True)
|
||||
adjusting_current = PersistentParam('current when adjusting force', FloatRange(0, 2.8, unit='A'), readonly=False,
|
||||
@ -71,19 +71,20 @@ class Uniax(PersistentMixin, Drivable):
|
||||
_find_target = 0
|
||||
|
||||
def earlyInit(self):
|
||||
super().earlyInit()
|
||||
self._zero_pos_tol = {}
|
||||
self._action = self.idle
|
||||
|
||||
def drive_relative(self, step, ntry=3):
|
||||
"""drive relative, try 3 times"""
|
||||
mot = self._motor
|
||||
mot = self.motor
|
||||
mot.read_value() # make sure motor value is fresh
|
||||
if self.adjusting and abs(step) > self.safe_step:
|
||||
step = math.copysign(self.safe_step, step)
|
||||
self.current_step = step
|
||||
for _ in range(ntry):
|
||||
try:
|
||||
self._mot_target = self._motor.write_target(mot.value + step)
|
||||
self._mot_target = self.motor.write_target(mot.value + step)
|
||||
self._cnt_wrerr = max(0, self._cnt_wrerr - 1)
|
||||
return True
|
||||
except Exception as e:
|
||||
@ -92,7 +93,7 @@ class Uniax(PersistentMixin, Drivable):
|
||||
if self._cnt_wrerr > 5:
|
||||
raise
|
||||
self.log.warning('motor reset')
|
||||
self._motor.reset()
|
||||
self.motor.reset()
|
||||
return False
|
||||
|
||||
def reset_filter(self, now=0.0):
|
||||
@ -100,7 +101,7 @@ class Uniax(PersistentMixin, Drivable):
|
||||
self._filter_start = now or time.time()
|
||||
|
||||
def motor_busy(self):
|
||||
mot = self._motor
|
||||
mot = self.motor
|
||||
if mot.isBusy():
|
||||
if mot.target != self._mot_target:
|
||||
self.action = self.idle
|
||||
@ -162,12 +163,12 @@ class Uniax(PersistentMixin, Drivable):
|
||||
if force * sign > self.hysteresis or force * sign > target * sign:
|
||||
if self.motor_busy():
|
||||
self.log.info('motor stopped - substantial force detected: %g', force)
|
||||
self._motor.stop()
|
||||
self.motor.stop()
|
||||
elif self.init_action():
|
||||
self.next_action(self.adjust)
|
||||
return
|
||||
if abs(force) > self.hysteresis:
|
||||
self.set_zero_pos(force, self._motor.read_value())
|
||||
self.set_zero_pos(force, self.motor.read_value())
|
||||
self.next_action(self.adjust)
|
||||
return
|
||||
if force * sign < -self.hysteresis:
|
||||
@ -176,7 +177,7 @@ class Uniax(PersistentMixin, Drivable):
|
||||
return
|
||||
if self.motor_busy():
|
||||
if sign * self._find_target < 0: # target sign changed
|
||||
self._motor.stop()
|
||||
self.motor.stop()
|
||||
self.next_action(self.find) # restart find
|
||||
return
|
||||
else:
|
||||
@ -184,26 +185,26 @@ class Uniax(PersistentMixin, Drivable):
|
||||
zero_pos = self.zero_pos(target)
|
||||
side_name = 'positive' if target > 0 else 'negative'
|
||||
if not self.init_action():
|
||||
if abs(self._motor.target - self._motor.value) > self._motor.tolerance:
|
||||
if abs(self.motor.target - self.motor.value) > self.motor.tolerance:
|
||||
# no success on last find try, try short and strong step
|
||||
self.write_adjusting(True)
|
||||
self.log.info('one step to %g', self._motor.value + self.safe_step)
|
||||
self.log.info('one step to %g', self.motor.value + self.safe_step)
|
||||
self.drive_relative(sign * self.safe_step)
|
||||
return
|
||||
if zero_pos is not None:
|
||||
self.status = 'BUSY', 'change to %s side' % side_name
|
||||
zero_pos += sign * (self.hysteresis * self.slope - self._motor.tolerance)
|
||||
if (self._motor.value - zero_pos) * sign < -self._motor.tolerance:
|
||||
zero_pos += sign * (self.hysteresis * self.slope - self.motor.tolerance)
|
||||
if (self.motor.value - zero_pos) * sign < -self.motor.tolerance:
|
||||
self.write_adjusting(False)
|
||||
self.log.info('change side to %g', zero_pos)
|
||||
self.drive_relative(zero_pos - self._motor.value)
|
||||
self.drive_relative(zero_pos - self.motor.value)
|
||||
return
|
||||
# we are already at or beyond zero_pos
|
||||
self.next_action(self.adjust)
|
||||
return
|
||||
self.write_adjusting(False)
|
||||
self.status = 'BUSY', 'find %s side' % side_name
|
||||
self.log.info('one turn to %g', self._motor.value + sign * 360)
|
||||
self.log.info('one turn to %g', self.motor.value + sign * 360)
|
||||
self.drive_relative(sign * 360)
|
||||
|
||||
def free(self, force, target):
|
||||
@ -293,7 +294,7 @@ class Uniax(PersistentMixin, Drivable):
|
||||
|
||||
def read_value(self):
|
||||
try:
|
||||
force = self._transducer.read_value()
|
||||
force = self.transducer.read_value()
|
||||
self._cnt_rderr = max(0, self._cnt_rderr - 1)
|
||||
except Exception as e:
|
||||
self._cnt_rderr += 1
|
||||
@ -323,7 +324,7 @@ class Uniax(PersistentMixin, Drivable):
|
||||
self.log.error(self.status[1])
|
||||
return Done
|
||||
if self.zero_pos(force) is None and abs(force) > self.hysteresis and self._filtered:
|
||||
self.set_zero_pos(force, self._motor.read_value())
|
||||
self.set_zero_pos(force, self.motor.read_value())
|
||||
self._action(self.value, self.target)
|
||||
return Done
|
||||
|
||||
@ -351,18 +352,18 @@ class Uniax(PersistentMixin, Drivable):
|
||||
@Command()
|
||||
def stop(self):
|
||||
self._action = self.idle
|
||||
if self._motor.isBusy():
|
||||
if self.motor.isBusy():
|
||||
self.log.info('stop motor')
|
||||
self._motor.stop()
|
||||
self.motor.stop()
|
||||
self.next_action(self.idle)
|
||||
|
||||
def write_force_offset(self, value):
|
||||
self.force_offset = value
|
||||
self._transducer.write_offset(value)
|
||||
self.transducer.write_offset(value)
|
||||
return Done
|
||||
|
||||
def write_adjusting(self, value):
|
||||
mot = self._motor
|
||||
mot = self.motor
|
||||
if value:
|
||||
mot_current = self.adjusting_current
|
||||
mot.write_move_limit(self.safe_step)
|
||||
|
Loading…
x
Reference in New Issue
Block a user