
- ls372 autorange: wait one sec. more for switching - keep only one channel, even after target is reached - intermediate target only when T is raise, but not when lowered
242 lines
8.5 KiB
Python
242 lines
8.5 KiB
Python
# *****************************************************************************
|
|
#
|
|
# 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>
|
|
#
|
|
# *****************************************************************************
|
|
|
|
"""modules to access parameters"""
|
|
|
|
from frappy.core import Drivable, EnumType, IDLE, Attached, StringType, Property, \
|
|
Parameter, FloatRange, BoolType, Readable, ERROR
|
|
from frappy.errors import ConfigError
|
|
from frappy_psi.convergence import HasConvergence
|
|
from frappy_psi.mixins import HasRamp
|
|
from frappy.lib import merge_status
|
|
|
|
|
|
class Par(Readable):
|
|
value = Parameter(datatype=FloatRange(unit='$'))
|
|
read = Attached(description='<module>.<parameter> for read')
|
|
unit = Property('main unit', StringType())
|
|
|
|
def setProperty(self, key, value):
|
|
if key == 'read':
|
|
value, param = value.split('.')
|
|
setattr(self, f'{key}_param', param)
|
|
super().setProperty(key, value)
|
|
|
|
def checkProperties(self):
|
|
self.applyMainUnit(self.unit)
|
|
if self.read == self.name :
|
|
raise ConfigError('illegal recursive read/write module')
|
|
super().checkProperties()
|
|
|
|
def read_value(self):
|
|
return getattr(self.read, f'{self.read_param}')
|
|
|
|
def read_status(self):
|
|
return IDLE, ''
|
|
|
|
|
|
class Driv(Drivable):
|
|
value = Parameter(datatype=FloatRange(unit='$'))
|
|
target = Parameter(datatype=FloatRange(unit='$'))
|
|
read = Attached(description='<module>.<parameter> for read')
|
|
write = Attached(description='<module>.<parameter> for read')
|
|
unit = Property('main unit', StringType())
|
|
|
|
def setProperty(self, key, value):
|
|
if key in ('read', 'write'):
|
|
value, param = value.split('.')
|
|
setattr(self, f'{key}_param', param)
|
|
super().setProperty(key, value)
|
|
|
|
def checkProperties(self):
|
|
self.applyMainUnit(self.unit)
|
|
if self.read == self.name or self.write == self.name:
|
|
raise ConfigError('illegal recursive read/write module')
|
|
super().checkProperties()
|
|
|
|
#def registerUpdates(self):
|
|
# self.read.valueCallbacks[self.read_param].append(self.update_value)
|
|
# self.write.valueCallbacks[self.write_param].append(self.update_target)
|
|
#
|
|
#def startModule(self, start_events):
|
|
# start_events.queue(self.registerUpdates)
|
|
# super().startModule(start_events)
|
|
|
|
def read_value(self):
|
|
return getattr(self.read, f'{self.read_param}')
|
|
|
|
def read_target(self):
|
|
return getattr(self.write, f'{self.write_param}')
|
|
|
|
def read_status(self):
|
|
return IDLE, ''
|
|
|
|
def write_target(self, target):
|
|
return getattr(self.write, f'write_{self.write_param}')(target)
|
|
|
|
|
|
class Converging(HasConvergence, Driv):
|
|
"""drivable with convergence"""
|
|
pollinterval = 1
|
|
|
|
def checkProperties(self):
|
|
self.parameters['tolerance'].setProperty('unit', self.unit)
|
|
super().checkProperties()
|
|
|
|
#def update_value(self, value):
|
|
# print('UV', value)
|
|
# self.value = value
|
|
|
|
#def error_update_value(self, err):
|
|
# raise err
|
|
|
|
#def update_target(self, value):
|
|
# self.target = value
|
|
|
|
#def error_update_target(self, err):
|
|
# raise err
|
|
|
|
def write_target(self, target):
|
|
self.convergence_start()
|
|
return super().write_target(target)
|
|
|
|
|
|
class RampDriv(HasRamp, Driv):
|
|
pass
|
|
|
|
|
|
def set_enabled(modobj, value):
|
|
"""set enabled on module if available"""
|
|
if hasattr(modobj, 'enabled') and modobj.enabled != value:
|
|
modobj.write_enabled(value)
|
|
|
|
|
|
def get_value(obj, default):
|
|
"""get the value of given module. if not valid, return the limit (min_high or max_low)"""
|
|
if not getattr(obj, 'enabled', True):
|
|
return default
|
|
# consider also that a value 0 is invalid
|
|
return (obj.value if IDLE <= obj.status[0] < ERROR else 0) or default
|
|
|
|
|
|
LOW = 0
|
|
HIGH = 1
|
|
|
|
|
|
class SwitchDriv(HasConvergence, Drivable):
|
|
low = Attached(description='low range module')
|
|
high = Attached(description='high range module')
|
|
min_high = Parameter('minimum high target', FloatRange(unit='$'), readonly=False)
|
|
max_low = Parameter('maximum low target', FloatRange(unit='$'), readonly=False)
|
|
# disable_other = Parameter('whether to disable unused channel', BoolType(), readonly=False)
|
|
selected = Parameter('selected module', EnumType(low=LOW, high=HIGH), readonly=False, default=0)
|
|
autoswitch = Parameter('switch sensor automatically', BoolType(), readonly=False, default=True)
|
|
_switch_target = None # if not None, switch to selection mhen mid range is reached
|
|
|
|
# TODO: copy units from attached module
|
|
# TODO: callbacks for updates
|
|
|
|
def doPoll(self):
|
|
super().doPoll()
|
|
if self._switch_target is not None:
|
|
mid = (self.min_high + self.max_low) * 0.5
|
|
if self._switch_target == HIGH:
|
|
low = get_value(self.low, mid) # returns mid when low is invalid
|
|
if low > mid:
|
|
self.value = self.low.value
|
|
self._switch_target = None
|
|
self.write_target(self.target)
|
|
return
|
|
else:
|
|
high = get_value(self.high, mid) # return mid when high is invalid
|
|
if high < mid:
|
|
self.value = self.high.value
|
|
self._switch_target = None
|
|
self.write_target(self.target)
|
|
return
|
|
if not self.isBusy() and self.autoswitch:
|
|
low = get_value(self.low, self.max_low)
|
|
high = get_value(self.high, self.min_high)
|
|
low_valid = low < self.max_low
|
|
high_valid = high > self.min_high
|
|
if high_valid and high > self.max_low:
|
|
if not low_valid and not self.low.control_active:
|
|
set_enabled(self.low, False)
|
|
return
|
|
if low_valid and low < self.min_high:
|
|
if not high_valid and not self.high.control_active:
|
|
set_enabled(self.high, False)
|
|
return
|
|
# keep only one channel on
|
|
#set_enabled(self.low, True)
|
|
#set_enabled(self.high, True)
|
|
|
|
def get_selected(self):
|
|
low = get_value(self.low, self.max_low)
|
|
high = get_value(self.high, self.min_high)
|
|
if low < self.min_high:
|
|
return 0
|
|
if high > self.max_low:
|
|
return 1
|
|
return self.selected
|
|
|
|
def read_value(self):
|
|
return self.low.value if self.get_selected() == LOW else self.high.value
|
|
|
|
def read_status(self):
|
|
status = self.low.status if self.get_selected() == LOW else self.high.status
|
|
if status[0] >= ERROR:
|
|
return status
|
|
return super().read_status() # convergence status
|
|
|
|
def write_target(self, target):
|
|
this, other = self.low, self.high
|
|
selected = self.selected
|
|
target1 = target
|
|
self._switch_target = None
|
|
if target > self.max_low * 0.75 + self.min_high * 0.25:
|
|
if self.value < self.min_high:
|
|
target1 = min(target, self.max_low)
|
|
self._switch_target = HIGH
|
|
selected = LOW
|
|
else:
|
|
this, other = other, this
|
|
selected = HIGH
|
|
elif target < self.min_high * 0.75 + self.max_low * 0.25:
|
|
#if self.value > self.max_low:
|
|
# target1 = max(self.min_high, target)
|
|
# self._switch_target = LOW
|
|
# this, other = other, this
|
|
# selected = HIGH
|
|
#else:
|
|
selected = LOW
|
|
elif self.selected == HIGH:
|
|
this, other = other, this
|
|
if hasattr(other, 'control_off'):
|
|
other.control_off()
|
|
set_enabled(this, True)
|
|
set_enabled(other, False)
|
|
self.write_selected(selected)
|
|
self.convergence_start()
|
|
self.log.info('target=%g (%s)', target, this.name)
|
|
this.write_target(target1)
|
|
return target
|