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
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.datatypes import FloatRange, IntRange, EnumType, BoolType
from secop.stringio import HasIodev
@ -58,27 +58,69 @@ class StringIO(secop.stringio.StringIO):
wait_before = 0.05
class Main(HasIodev, Module):
class Main(HasIodev, Drivable):
parameters = {
'channel':
Parameter('the current channel', poll=REGULAR, datatype=IntRange(), readonly=False, handler=scan),
'value': Override('the current channel', poll=REGULAR, datatype=IntRange(0, 17)),
'target': Override('channel to select', datatype=IntRange(0, 17)),
'autoscan':
Parameter('whether to scan automatically', datatype=BoolType(), readonly=False, handler=scan),
'pollinterval': Parameter('sleeptime between polls', default=5,
readonly=False,
datatype=FloatRange(0.1, 120),
),
Parameter('whether to scan automatically', datatype=BoolType(), readonly=False, default=False),
'pollinterval': Override('sleeptime between polls', default=1),
}
pollerClass = Poller
iodevClass = StringIO
_channel_changed = 0 # time of last channel change
_channels = None # dict <channel no> of <module object>
def analyze_scan(self, channel, autoscan):
return dict(channel=channel, autoscan=autoscan)
def earlyInit(self):
self._channels = {}
def change_scan(self, change):
change.readValues()
return change.channel, change.autoscan
def register_channel(self, modobj):
self._channels[modobj.channel] = modobj
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):
@ -86,17 +128,19 @@ class ResChannel(HasIodev, Readable):
RES_RANGE = {key: i+1 for i, key in list(
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
CUR_RANGE = {key: i + 1 for i, key in list(
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(
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
iodevClass = StringIO
_main = None # main module
_last_range_change = 0 # time of last range change
properties = {
'channel':
@ -112,10 +156,10 @@ class ResChannel(HasIodev, Readable):
Override(visibility=3),
'range':
Parameter('reading range', readonly=False,
datatype=EnumType(**RES_RANGE), handler=rdgrng),
datatype=EnumType(**RES_RANGE), handler=rdgrng),
'minrange':
Parameter('minimum range for software autorange', readonly=False, default=1,
datatype=EnumType(**RES_RANGE)),
datatype=EnumType(**RES_RANGE)),
'autorange':
Parameter('autorange', datatype=EnumType(off=0, hard=1, soft=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),
}
def startModule(self, started_callback):
self._last_range_change = 0
def initModule(self):
self._main = self.DISPATCHER.get_module(self.main)
super().startModule(started_callback)
self._main.register_channel(self)
def read_value(self):
if self.channel != self._main.channel:
if self.channel != self._main.value:
return Done
if not self.enabled:
self.status = [self.Status.DISABLED, 'disabled']
@ -149,7 +192,7 @@ class ResChannel(HasIodev, Readable):
if self.autorange == 'soft':
now = time.time()
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 abs(result) > self.RES_SCALE[rng]:
if rng < 22:
@ -177,7 +220,7 @@ class ResChannel(HasIodev, Readable):
def read_status(self):
if not self.enabled:
return [self.Status.DISABLED, 'disabled']
if self.channel != self._main.channel:
if self.channel != self._main.value:
return Done
result = int(self.sendRecv('RDGST?%d' % self.channel))
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)
if autorange:
result['autorange'] = 'hard'
#elif self.autorange == 'hard':
# result['autorange'] = 'soft'
# else: do not change autorange
self.log.info('%s range %r %r %r' % (self.name, rng, autorange, self.autorange))
if excoff:
@ -242,3 +283,9 @@ class ResChannel(HasIodev, Readable):
if change.filter:
return 1, change.filter, 80 # always use 80% filter
return 0, settle, window
def write_enabled(self, value):
inset.write(self, 'enabled', value)
if value:
self._main.write_target(self.channel)
return Done