main module of LS370 is now drivable

The main value of main module is the selected channel, it is 0
when pausing during scanning, and the status is busy.

+ cosmetics to make IDE more happy

Change-Id: I11d8f08ea67d25fb00f7492080b4a55efc124bfb
Reviewed-on: https://forge.frm2.tum.de/review/c/sine2020/secop/playground/+/24927
Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de>
Reviewed-by: Enrico Faulhaber <enrico.faulhaber@frm2.tum.de>
Reviewed-by: Markus Zolliker <markus.zolliker@psi.ch>
This commit is contained in:
zolliker 2021-01-26 08:42:40 +01:00
parent 9df43bb940
commit 7895470301

View File

@ -22,7 +22,7 @@
import time import time
from secop.modules import Module, Readable, Drivable, Parameter, Override, Property, Attached from secop.modules import Readable, Drivable, Parameter, Override, Property, Attached
from secop.metaclass import Done from secop.metaclass import Done
from secop.datatypes import FloatRange, IntRange, EnumType, BoolType from secop.datatypes import FloatRange, IntRange, EnumType, BoolType
from secop.stringio import HasIodev from secop.stringio import HasIodev
@ -58,27 +58,69 @@ class StringIO(secop.stringio.StringIO):
wait_before = 0.05 wait_before = 0.05
class Main(HasIodev, Module): class Main(HasIodev, Drivable):
parameters = { parameters = {
'channel': 'value': Override('the current channel', poll=REGULAR, datatype=IntRange(0, 17)),
Parameter('the current channel', poll=REGULAR, datatype=IntRange(), readonly=False, handler=scan), 'target': Override('channel to select', datatype=IntRange(0, 17)),
'autoscan': 'autoscan':
Parameter('whether to scan automatically', datatype=BoolType(), readonly=False, handler=scan), Parameter('whether to scan automatically', datatype=BoolType(), readonly=False, default=False),
'pollinterval': Parameter('sleeptime between polls', default=5, 'pollinterval': Override('sleeptime between polls', default=1),
readonly=False,
datatype=FloatRange(0.1, 120),
),
} }
pollerClass = Poller pollerClass = Poller
iodevClass = StringIO iodevClass = StringIO
_channel_changed = 0 # time of last channel change
_channels = None # dict <channel no> of <module object>
def analyze_scan(self, channel, autoscan): def earlyInit(self):
return dict(channel=channel, autoscan=autoscan) self._channels = {}
def change_scan(self, change): def register_channel(self, modobj):
change.readValues() self._channels[modobj.channel] = modobj
return change.channel, change.autoscan
def startModule(self, started_callback):
started_callback()
for ch in range(1, 16):
if ch not in self._channels:
self.sendRecv('INSET %d,0,0,0,0,0;INSET?%d' % (ch, ch))
def read_value(self):
channel, auto = scan.send_command(self)
if channel not in self._channels:
return channel
if not self._channels[channel].enabled:
# channel was disabled recently, but still selected
nextchannel = 0
for ch, mobj in self._channels.items():
if mobj.enabled:
if ch > channel:
nextchannel = ch
break
if nextchannel == 0:
nextchannel = ch
if nextchannel:
self.write_target(nextchannel)
return 0
now = time.time()
if channel != self.target:
self._channel_changed = now
self.target = channel
self.autoscan = int(auto)
if now < self._channel_changed + self._channels[channel].pause + self._channels[channel].filter:
self.status = [Status.BUSY, 'switching']
return 0
self.status = [Status.IDLE, '']
return channel
def write_target(self, channel):
scan.send_change(self, channel, self.autoscan)
# self.sendRecv('SCAN %d,%d;SCAN?' % (channel, self.autoscan))
if channel != self.value:
self.value = 0
self._channel_changed = time.time()
self.status = [Status.BUSY, 'switching']
return channel
class ResChannel(HasIodev, Readable): class ResChannel(HasIodev, Readable):
@ -86,17 +128,19 @@ class ResChannel(HasIodev, Readable):
RES_RANGE = {key: i+1 for i, key in list( RES_RANGE = {key: i+1 for i, key in list(
enumerate(mag % val for mag in ['%gmOhm', '%gOhm', '%gkOhm', '%gMOhm'] enumerate(mag % val for mag in ['%gmOhm', '%gOhm', '%gkOhm', '%gMOhm']
for val in [2, 6.32, 20, 63.2, 200, 632]))[:-2]} for val in [2, 6.32, 20, 63.2, 200, 632]))[:-2]}
RES_SCALE = [2 * 10 ** (0.5 * i) for i in range(-7, 16)] # RES_SCALE[0] is not used RES_SCALE = [2 * 10 ** (0.5 * i) for i in range(-7, 16)] # RES_SCALE[0] is not used
CUR_RANGE = {key: i + 1 for i, key in list( CUR_RANGE = {key: i + 1 for i, key in list(
enumerate(mag % val for mag in ['%gpA', '%gnA', '%guA', '%gmA'] enumerate(mag % val for mag in ['%gpA', '%gnA', '%guA', '%gmA']
for val in [1, 3.16, 10, 31.6, 100, 316]))[:-2]} for val in [1, 3.16, 10, 31.6, 100, 316]))[:-2]}
VOLT_RANGE = {key: i + 1 for i, key in list( VOLT_RANGE = {key: i + 1 for i, key in list(
enumerate(mag % val for mag in ['%guV', '%gmV'] enumerate(mag % val for mag in ['%guV', '%gmV']
for val in [2, 6.32, 20, 63.2, 200, 632]))} for val in [2, 6.32, 20, 63.2, 200, 632]))}
pollerClass = Poller pollerClass = Poller
iodevClass = StringIO iodevClass = StringIO
_main = None # main module
_last_range_change = 0 # time of last range change
properties = { properties = {
'channel': 'channel':
@ -112,10 +156,10 @@ class ResChannel(HasIodev, Readable):
Override(visibility=3), Override(visibility=3),
'range': 'range':
Parameter('reading range', readonly=False, Parameter('reading range', readonly=False,
datatype=EnumType(**RES_RANGE), handler=rdgrng), datatype=EnumType(**RES_RANGE), handler=rdgrng),
'minrange': 'minrange':
Parameter('minimum range for software autorange', readonly=False, default=1, Parameter('minimum range for software autorange', readonly=False, default=1,
datatype=EnumType(**RES_RANGE)), datatype=EnumType(**RES_RANGE)),
'autorange': 'autorange':
Parameter('autorange', datatype=EnumType(off=0, hard=1, soft=2), Parameter('autorange', datatype=EnumType(off=0, hard=1, soft=2),
readonly=False, handler=rdgrng, default=2), readonly=False, handler=rdgrng, default=2),
@ -133,13 +177,12 @@ class ResChannel(HasIodev, Readable):
Parameter('filter time', datatype=FloatRange(1, 200), readonly=False, handler=filterhdl), Parameter('filter time', datatype=FloatRange(1, 200), readonly=False, handler=filterhdl),
} }
def startModule(self, started_callback): def initModule(self):
self._last_range_change = 0
self._main = self.DISPATCHER.get_module(self.main) self._main = self.DISPATCHER.get_module(self.main)
super().startModule(started_callback) self._main.register_channel(self)
def read_value(self): def read_value(self):
if self.channel != self._main.channel: if self.channel != self._main.value:
return Done return Done
if not self.enabled: if not self.enabled:
self.status = [self.Status.DISABLED, 'disabled'] self.status = [self.Status.DISABLED, 'disabled']
@ -149,7 +192,7 @@ class ResChannel(HasIodev, Readable):
if self.autorange == 'soft': if self.autorange == 'soft':
now = time.time() now = time.time()
if now > self._last_range_change + self.pause: if now > self._last_range_change + self.pause:
rng = int(max(self.minrange, self.range)) # convert from enum to int rng = int(max(self.minrange, self.range)) # convert from enum to int
if self.status[1] == '': if self.status[1] == '':
if abs(result) > self.RES_SCALE[rng]: if abs(result) > self.RES_SCALE[rng]:
if rng < 22: if rng < 22:
@ -177,7 +220,7 @@ class ResChannel(HasIodev, Readable):
def read_status(self): def read_status(self):
if not self.enabled: if not self.enabled:
return [self.Status.DISABLED, 'disabled'] return [self.Status.DISABLED, 'disabled']
if self.channel != self._main.channel: if self.channel != self._main.value:
return Done return Done
result = int(self.sendRecv('RDGST?%d' % self.channel)) result = int(self.sendRecv('RDGST?%d' % self.channel))
result &= 0x37 # mask T_OVER and T_UNDER (change this when implementing temperatures instead of resistivities) result &= 0x37 # mask T_OVER and T_UNDER (change this when implementing temperatures instead of resistivities)
@ -190,8 +233,6 @@ class ResChannel(HasIodev, Readable):
result = dict(range=rng) result = dict(range=rng)
if autorange: if autorange:
result['autorange'] = 'hard' result['autorange'] = 'hard'
#elif self.autorange == 'hard':
# result['autorange'] = 'soft'
# else: do not change autorange # else: do not change autorange
self.log.info('%s range %r %r %r' % (self.name, rng, autorange, self.autorange)) self.log.info('%s range %r %r %r' % (self.name, rng, autorange, self.autorange))
if excoff: if excoff:
@ -242,3 +283,9 @@ class ResChannel(HasIodev, Readable):
if change.filter: if change.filter:
return 1, change.filter, 80 # always use 80% filter return 1, change.filter, 80 # always use 80% filter
return 0, settle, window return 0, settle, window
def write_enabled(self, value):
inset.write(self, 'enabled', value)
if value:
self._main.write_target(self.channel)
return Done