improvements for flame

- frappy_psi.channelswitcher: use isBusy instead of checking value and target
- frappy_psi.ls372: remove underflow mechanism
- frappy_psi.parmod.SwitchDriv: switch the controlled module also when not buys
This commit is contained in:
l_samenv 2023-07-11 13:24:41 +02:00 committed by Markus Zolliker
parent 44c59bd818
commit cf10590245
3 changed files with 65 additions and 39 deletions

View File

@ -109,7 +109,7 @@ class ChannelSwitcher(Drivable):
def read_status(self):
now = time.monotonic()
if self.status[0] == 'BUSY':
if self.isBusy():
chan = self.channels[self.target]
if chan.is_switching(now, self._start_switch, self.switch_delay):
return self.status

View File

@ -202,6 +202,7 @@ class ResChannel(Channel):
_toggle_autorange = 'init'
_prev_rdgrng = (1, 1) # last read values for icur and exc
_last_range_change = 0
_value = None # if not None, a fresh value
rdgrng_params = 'range', 'iexc', 'vexc'
inset_params = 'enabled', 'pause', 'dwell'
STATUS_MASK = 0x3f # mask T_OVER and T_UNDER
@ -221,7 +222,7 @@ class ResChannel(Channel):
def read_status(self):
if not self.enabled:
return [self.Status.DISABLED, 'disabled']
if not self.channel == self.switcher.value == self.switcher.target:
if self.switcher.isBusy():
return self.status
result = int(self.communicate('RDGST?%d' % self.channel)) & self.STATUS_MASK
statustext = ' '.join(formatStatusBits(result, STATUS_BIT_LABELS))
@ -259,15 +260,19 @@ class ResChannel(Channel):
return result
def read_value(self):
if self.channel == self.switcher.value == self.switcher.target:
return self._read_value() or self.value
return self.value if self.enabled else 0
if self.switcher.isBusy():
return self.value if self.enabled else 0
value = self._read_value() if self._value is None else self._value
self._value = None
return self.value if value is None else value
def is_switching(self, now, last_switch, switch_delay):
last_switch = max(last_switch, self._last_range_change)
if now + 0.5 > last_switch + self.pause:
self._read_value() # adjust range only
return super().is_switching(now, last_switch, switch_delay)
result = super().is_switching(now, last_switch, switch_delay)
if not result: # the time since last switch has passed
self._value = self._read_value() # for autorange
# now check for the time since last range change
result = super().is_switching(now, self._last_range_change, switch_delay)
return result
@CommonReadHandler(rdgrng_params)
def read_rdgrng(self):
@ -377,7 +382,6 @@ class TemperatureLoop(HasConvergence, TemperatureChannel, Drivable):
HTRRNG = {n: i for i, n in enumerate(['off', '30uA', '100uA', '300uA', '1mA', '3mA', '10mA', '30mA', '100mA'])}
htrrng = Parameter('', EnumType(HTRRNG), readonly=False)
_control_active = False
_underflow = False
@Command
def control_off(self):
@ -399,16 +403,6 @@ class TemperatureLoop(HasConvergence, TemperatureChannel, Drivable):
if newhtr:
self.log.info('switched heater on %d', newhtr)
self.communicate(f'RANGE {self.loop},{newhtr};RANGE?{self.loop}')
if self.minheater:
self.log.info('underflow open loop')
self._underflow = True
self.communicate(f'OUTMODE {self.loop},2,{self.channel},0,0,1,3;*OPC?')
self.communicate(f'MOUT {self.loop}{self.min_percent()};*OPC?')
elif self._underflow:
self.log.info('switch to control after underflow')
self._underflow = False
self.communicate(f'OUTMODE {self.loop},5,{self.channel},0,0,1,3;*OPC?')
self.communicate(f'SETP {self.loop},{self.target};*OPC?')
def read_control_active(self):
self.set_htrrng()
@ -416,7 +410,7 @@ class TemperatureLoop(HasConvergence, TemperatureChannel, Drivable):
def read_target(self):
if self._control_active:
return float(self.communicate('SETP?{self.loop}'))
return float(self.communicate(f'SETP?{self.loop}'))
return 0
def write_htrrng(self, value):
@ -428,15 +422,15 @@ class TemperatureLoop(HasConvergence, TemperatureChannel, Drivable):
outmode = (5, self.channel, 0, 0, 1, 3)
prev = parse(self.communicate(f'OUTMODE?{self.loop}'))
if outmode != prev:
self.communicate(f'OUTMODE {self.loop}, %g,%g,%g,%g,%g,%g;*OPC?' % tuple(outmode))
self.communicate(f'OUTMODE {self.loop},%g,%g,%g,%g,%g,%g;*OPC?' % tuple(outmode))
self.communicate(f'MOUT {self.loop},{self.min_percent()};*OPC?')
for chan in self.switcher.channels.values():
chan._control_active = False
self._control_active = True
self.read_control_active()
self.convergence_start()
# do not resturn the readback value, as it might not yet be correct
self.communicate(f'SETP {self.loop},{target};SETP?{self.loop}')
# do not return the readback value, as it might not yet be correct
self.communicate(f'SETP {self.loop},{target};*OPC?')
return target
#def write_ctrlpars(self, ctrlpars):

View File

@ -134,7 +134,12 @@ 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
return obj.value if IDLE <= obj.status[0] < ERROR else 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):
@ -143,14 +148,32 @@ class SwitchDriv(HasConvergence, Drivable):
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=0, high=1), readonly=False, default=0)
selected = Parameter('selected module', EnumType(low=LOW, high=HIGH), readonly=False, default=0)
_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 not self.isBusy():
if self.isBusy():
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 then high is invalid
if high < mid:
self.value = self.high.value
self._switch_target = None
self.write_target(self.target)
return
else:
low = get_value(self.low, self.max_low)
high = get_value(self.high, self.min_high)
low_valid = low < self.max_low
@ -167,28 +190,36 @@ class SwitchDriv(HasConvergence, Drivable):
set_enabled(self.high, True)
def read_value(self):
return self.low.value if self.selected == self.selected.low else self.high.value
return self.low.value if self.selected == LOW else self.high.value
def read_status(self):
status = self.low.status if self.selected == self.selected.low else self.high.status
status = self.low.status if self.selected == LOW else self.high.status
if status[0] >= ERROR:
return status
return super().read_status() # convergence status
def read_target(self):
if self.selected == self.selected.low:
return self.low.target
return self.high.target
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:
this, other = other, this
selected = self.selected.high
if self.value < self.min_high:
target1 = self.max_low
self._switch_target = HIGH
selected = LOW
else:
this, other = other, this
selected = HIGH
elif target < self.min_high:
selected = self.selected.low
elif self.selected == self.selected.high:
if self.value > self.max_low:
target1 = self.min_high
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()
@ -197,4 +228,5 @@ class SwitchDriv(HasConvergence, Drivable):
self.write_selected(selected)
self.convergence_start()
self.log.info('target=%g (%s)', target, this.name)
return this.write_target(target)
this.write_target(target1)
return target