driver for Lakeshore Model 370 resistivity measurement
- this does not (yet) include temperatures and control loop - including stringio-server for test purposes Change-Id: I414ae2e6663bb0773fe60db1798401dfc9dde018 Reviewed-on: https://forge.frm2.tum.de/review/c/sine2020/secop/playground/+/22005 Tested-by: JenkinsCodeReview <bjoern_pedersen@frm2.tum.de> Reviewed-by: Markus Zolliker <markus.zolliker@psi.ch>
This commit is contained in:
141
bin/stringio-server
Executable file
141
bin/stringio-server
Executable file
@ -0,0 +1,141 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# pylint: disable=invalid-name
|
||||||
|
# -*- 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>
|
||||||
|
# *****************************************************************************
|
||||||
|
"""server for a string communicator
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
bin/stringio-server <communciator> <server port>
|
||||||
|
|
||||||
|
open a server on <server port> to communicate with the string based <communicator> over TCP/IP.
|
||||||
|
|
||||||
|
Use cases, mainly for test purposes:
|
||||||
|
- as a T, if the hardware allows only one connection, and more than one is needed
|
||||||
|
- relay to a communicator not using TCP/IP, if Frappy should run on an other host
|
||||||
|
- relay to a hardware simulation written as a communicator
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
from os import path
|
||||||
|
import asyncore
|
||||||
|
import socket
|
||||||
|
|
||||||
|
# Add import path for inplace usage
|
||||||
|
sys.path.insert(0, path.abspath(path.join(path.dirname(__file__), '..')))
|
||||||
|
|
||||||
|
from secop.lib import get_class, formatException
|
||||||
|
|
||||||
|
class LineHandler(asyncore.dispatcher_with_send):
|
||||||
|
|
||||||
|
def __init__(self, sock):
|
||||||
|
self.buffer = b""
|
||||||
|
asyncore.dispatcher_with_send.__init__(self, sock)
|
||||||
|
self.crlf = 0
|
||||||
|
|
||||||
|
def handle_read(self):
|
||||||
|
data = self.recv(8192)
|
||||||
|
if data:
|
||||||
|
parts = data.split(b"\n")
|
||||||
|
if len(parts) == 1:
|
||||||
|
self.buffer += data
|
||||||
|
else:
|
||||||
|
self.handle_line((self.buffer + parts[0]).decode('latin_1'))
|
||||||
|
for part in parts[1:-1]:
|
||||||
|
if part[-1] == b"\r":
|
||||||
|
self.crlf = True
|
||||||
|
part = part[:-1]
|
||||||
|
else:
|
||||||
|
self.crlf = False
|
||||||
|
self.handle_line(part.decode('latin_1'))
|
||||||
|
self.buffer = parts[-1]
|
||||||
|
|
||||||
|
def send_line(self, line):
|
||||||
|
self.send((line + ("\r\n" if self.crlf else "\n")).encode('latin_1'))
|
||||||
|
|
||||||
|
|
||||||
|
class LineServer(asyncore.dispatcher):
|
||||||
|
|
||||||
|
def __init__(self, host, port, lineHandlerClass):
|
||||||
|
asyncore.dispatcher.__init__(self)
|
||||||
|
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
self.set_reuse_addr()
|
||||||
|
self.bind((host, port))
|
||||||
|
self.listen(5)
|
||||||
|
self.lineHandlerClass = lineHandlerClass
|
||||||
|
|
||||||
|
def handle_accept(self):
|
||||||
|
pair = self.accept()
|
||||||
|
if pair is not None:
|
||||||
|
sock, addr = pair
|
||||||
|
print ("Incoming connection from %s" % repr(addr))
|
||||||
|
self.lineHandlerClass(sock)
|
||||||
|
|
||||||
|
def loop(self):
|
||||||
|
asyncore.loop()
|
||||||
|
|
||||||
|
|
||||||
|
class Server(LineServer):
|
||||||
|
|
||||||
|
class Dispatcher:
|
||||||
|
def announce_update(self, *_):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def announce_update_error(self, *_):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwds):
|
||||||
|
super().__init__(*args, **kwds)
|
||||||
|
self.dispatcher = self.Dispatcher()
|
||||||
|
|
||||||
|
|
||||||
|
class Handler(LineHandler):
|
||||||
|
def handle_line(self, line):
|
||||||
|
try:
|
||||||
|
reply = module.do_communicate(line.strip())
|
||||||
|
print('%-40s | %s' % (line, reply))
|
||||||
|
except Exception:
|
||||||
|
print(formatException(verbose=True))
|
||||||
|
self.send_line(reply)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class Logger:
|
||||||
|
def debug(self, *args):
|
||||||
|
print(*args)
|
||||||
|
info = exception = debug
|
||||||
|
|
||||||
|
|
||||||
|
if len(sys.argv) < 2:
|
||||||
|
sys.argv.append('secop_psi.ls370sim.Ls370Sim')
|
||||||
|
if len(sys.argv) < 3:
|
||||||
|
sys.argv.append('4567')
|
||||||
|
communicatorname = sys.argv[1]
|
||||||
|
serverport = int(sys.argv[2])
|
||||||
|
opts = {'.description':'simulator'}
|
||||||
|
for arg in sys.argv[3:]:
|
||||||
|
k, v = arg.split('=',1)
|
||||||
|
opts[k] = v
|
||||||
|
|
||||||
|
srv = Server('localhost', serverport, Handler)
|
||||||
|
module = get_class(communicatorname)(communicatorname, Logger(), opts, srv)
|
||||||
|
module.earlyInit()
|
||||||
|
srv.loop()
|
24
cfg/ls370sim.cfg
Normal file
24
cfg/ls370sim.cfg
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
[node LscSIM.psi.ch]
|
||||||
|
description = Lsc Simulation at PSI
|
||||||
|
|
||||||
|
[interface tcp]
|
||||||
|
type = tcp
|
||||||
|
bindto = 0.0.0.0
|
||||||
|
bindport = 5000
|
||||||
|
|
||||||
|
[module res]
|
||||||
|
class = secop_psi.ls370res.ResChannel
|
||||||
|
.channel = 3
|
||||||
|
.description = resistivity
|
||||||
|
.main = lsmain
|
||||||
|
.iodev = lscom
|
||||||
|
|
||||||
|
[module lsmain]
|
||||||
|
class = secop_psi.ls370res.Main
|
||||||
|
.description = main control of Lsc controller
|
||||||
|
.iodev = lscom
|
||||||
|
|
||||||
|
[module lscom]
|
||||||
|
class = secop_psi.ls370sim.Ls370Sim
|
||||||
|
.description = simulated serial communicator to a LS 370
|
||||||
|
.visibility = 3
|
26
cfg/ls370test.cfg
Normal file
26
cfg/ls370test.cfg
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
[node LscSIM.psi.ch]
|
||||||
|
description = Lsc370 Test
|
||||||
|
|
||||||
|
[interface tcp]
|
||||||
|
type = tcp
|
||||||
|
bindto = 0.0.0.0
|
||||||
|
bindport = 5000
|
||||||
|
|
||||||
|
[module res]
|
||||||
|
class = secop_psi.ls370res.ResChannel
|
||||||
|
vexc = '2mV'
|
||||||
|
.channel = 3
|
||||||
|
.description = resistivity
|
||||||
|
.main = lsmain
|
||||||
|
.iodev = lscom
|
||||||
|
|
||||||
|
[module lsmain]
|
||||||
|
class = secop_psi.ls370res.Main
|
||||||
|
.description = main control of Lsc controller
|
||||||
|
.iodev = lscom
|
||||||
|
|
||||||
|
[module lscom]
|
||||||
|
class = secop_psi.ls370res.StringIO
|
||||||
|
.uri=localhost:4567
|
||||||
|
.description = serial communicator to an LS 370
|
||||||
|
.visibility = 3
|
226
secop_psi/ls370res.py
Normal file
226
secop_psi/ls370res.py
Normal file
@ -0,0 +1,226 @@
|
|||||||
|
#!/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>
|
||||||
|
# *****************************************************************************
|
||||||
|
"""LakeShore Model 370 resistance channel"""
|
||||||
|
|
||||||
|
import time
|
||||||
|
|
||||||
|
from secop.modules import Module, Readable, Drivable, Parameter, Override, Property, Attached
|
||||||
|
from secop.metaclass import Done
|
||||||
|
from secop.datatypes import FloatRange, IntRange, EnumType, BoolType
|
||||||
|
from secop.stringio import HasIodev
|
||||||
|
from secop.poller import Poller, REGULAR
|
||||||
|
import secop.commandhandler
|
||||||
|
|
||||||
|
Status = Drivable.Status
|
||||||
|
|
||||||
|
|
||||||
|
class CmdHandler(secop.commandhandler.CmdHandler):
|
||||||
|
CMDARGS = ['channel']
|
||||||
|
CMDSEPARATOR = ';'
|
||||||
|
|
||||||
|
rdgrng = CmdHandler('rdgrng', 'RDGRNG?%(channel)d', '%d,%d,%d,%d,%d')
|
||||||
|
inset = CmdHandler('inset', 'INSET?%(channel)d', '%d,%d,%d,%d,%d')
|
||||||
|
filterhdl = CmdHandler('filt', 'FILTER?%(channel)d', '%d,%d,%d')
|
||||||
|
scan = CmdHandler('scan', 'SCAN?', '%d,%d')
|
||||||
|
|
||||||
|
|
||||||
|
STATUS_TEXT = {0: ''}
|
||||||
|
for bit, text in enumerate('CS_OVL VCM_OVL VMIX_OVL R_OVER R_UNDER T_OVER T_UNDER'.split()):
|
||||||
|
for i in range(1 << bit, 2 << bit):
|
||||||
|
STATUS_TEXT[i] = text
|
||||||
|
|
||||||
|
|
||||||
|
class StringIO(secop.stringio.StringIO):
|
||||||
|
identification = [('*IDN?', 'LSCI,MODEL370,.*')]
|
||||||
|
|
||||||
|
|
||||||
|
class Main(HasIodev, Module):
|
||||||
|
parameters = {
|
||||||
|
'channel':
|
||||||
|
Parameter('the current channel', poll=REGULAR, datatype=IntRange(), readonly=False, handler=scan),
|
||||||
|
'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),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
pollerClass = Poller
|
||||||
|
|
||||||
|
def analyze_scan(self, channel, autoscan):
|
||||||
|
self.channel, self.autoscan = channel, autoscan
|
||||||
|
|
||||||
|
def change_scan(self, new, *args):
|
||||||
|
return new.channel, new.autoscan
|
||||||
|
|
||||||
|
|
||||||
|
class ResChannel(HasIodev, Readable):
|
||||||
|
'''temperature channel on Lakeshore 336'''
|
||||||
|
|
||||||
|
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]}
|
||||||
|
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]}
|
||||||
|
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]))}
|
||||||
|
|
||||||
|
pollerClass = Poller
|
||||||
|
|
||||||
|
properties = {
|
||||||
|
'channel':
|
||||||
|
Property('the Lakeshore channel', datatype=IntRange(), export=False),
|
||||||
|
'main':
|
||||||
|
Attached()
|
||||||
|
}
|
||||||
|
|
||||||
|
parameters = {
|
||||||
|
'value':
|
||||||
|
Override(datatype=FloatRange(unit='Ohm')),
|
||||||
|
'pollinterval':
|
||||||
|
Override(visibility=3),
|
||||||
|
'range':
|
||||||
|
Parameter('reading range', readonly=False,
|
||||||
|
datatype=EnumType(**RES_RANGE), handler=rdgrng),
|
||||||
|
'minrange':
|
||||||
|
Parameter('minimum range for software autorange', readonly=False, default=1,
|
||||||
|
datatype=EnumType(**RES_RANGE)),
|
||||||
|
'autorange':
|
||||||
|
Parameter('autorange', datatype=EnumType(off=0, hard=1, soft=2),
|
||||||
|
readonly=False, handler=rdgrng, default=2),
|
||||||
|
'iexc':
|
||||||
|
Parameter('current excitation', datatype=EnumType(off=0, **CUR_RANGE), readonly=False, handler=rdgrng),
|
||||||
|
'vexc':
|
||||||
|
Parameter('voltage excitation', datatype=EnumType(off=0, **VOLT_RANGE), readonly=False, handler=rdgrng),
|
||||||
|
'enable':
|
||||||
|
Parameter('is this channel enabled?', datatype=BoolType(), readonly=False, handler=inset),
|
||||||
|
'pause':
|
||||||
|
Parameter('pause after channel change', datatype=IntRange(), readonly=False, handler=inset),
|
||||||
|
'dwell':
|
||||||
|
Parameter('dwell time with autoscan', datatype=IntRange(), readonly=False, handler=inset),
|
||||||
|
'filter':
|
||||||
|
Parameter('filter time', datatype=IntRange(), readonly=False, handler=filterhdl),
|
||||||
|
}
|
||||||
|
|
||||||
|
def startModule(self, started_callback):
|
||||||
|
self._last_range_change = 0
|
||||||
|
self._main = self.DISPATCHER.get_module(self.main)
|
||||||
|
super().startModule(started_callback)
|
||||||
|
|
||||||
|
def read_value(self):
|
||||||
|
if self.channel != self._main.channel:
|
||||||
|
return Done
|
||||||
|
result = self.sendRecv('RDGR?%d' % self.channel)
|
||||||
|
result = float(result)
|
||||||
|
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
|
||||||
|
if self.status[1] == '':
|
||||||
|
if abs(result) > self.RES_SCALE[rng]:
|
||||||
|
if rng < 22:
|
||||||
|
rng += 1
|
||||||
|
self.log.info('chan %d: increased range to %.3g' %
|
||||||
|
(self.channel, self.RES_SCALE[rng]))
|
||||||
|
else:
|
||||||
|
lim = 0.2
|
||||||
|
while rng > self.minrange and abs(result) < lim * self.RES_SCALE[rng]:
|
||||||
|
rng -= 1
|
||||||
|
lim -= 0.05 # not more than 4 steps at once
|
||||||
|
# effectively: <0.16 %: 4 steps, <1%: 3 steps, <5%: 2 steps, <20%: 1 step
|
||||||
|
if lim != 0.2:
|
||||||
|
self.log.info('chan %d: lowered range to %.3g' %
|
||||||
|
(self.channel, self.RES_SCALE[rng]))
|
||||||
|
elif rng < 22:
|
||||||
|
rng = min(22, rng + 1)
|
||||||
|
self.log.info('chan: %d, %s, increased range to %.3g' %
|
||||||
|
(self.channel, self.status[1], self.RES_SCALE[rng]))
|
||||||
|
if rng != self.range:
|
||||||
|
self.write_range(rng)
|
||||||
|
self._last_range_change = now
|
||||||
|
return result
|
||||||
|
|
||||||
|
def read_status(self):
|
||||||
|
if self.channel != self._main.channel:
|
||||||
|
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)
|
||||||
|
statustext = STATUS_TEXT[result]
|
||||||
|
if statustext:
|
||||||
|
return [self.Status.ERROR, statustext]
|
||||||
|
return [self.Status.IDLE, '']
|
||||||
|
|
||||||
|
def analyze_rdgrng(self, iscur, exc, rng, autorange, excoff):
|
||||||
|
if excoff:
|
||||||
|
self.iexc, self.vexc = 0,0
|
||||||
|
elif iscur:
|
||||||
|
self.iexc, self.vexc = exc, 0
|
||||||
|
else:
|
||||||
|
self.iexc, self.vexc = 0, exc
|
||||||
|
if autorange:
|
||||||
|
self.autorange = 'hard'
|
||||||
|
else:
|
||||||
|
if self.autorange == 'hard':
|
||||||
|
self.autorange = 'soft'
|
||||||
|
else:
|
||||||
|
self.autorange = self.autorange
|
||||||
|
self.range = rng
|
||||||
|
|
||||||
|
def change_rdgrng(self, new, iscur, exc, rng, autorange, excoff):
|
||||||
|
if new.vexc != self.vexc: # in case vext is changed, do not consider iexc
|
||||||
|
new.iexc = 0
|
||||||
|
if new.iexc != 0: # we need '!= 0' here, as bool(enum) is always True!
|
||||||
|
iscur = 1
|
||||||
|
exc = new.iexc
|
||||||
|
excoff = 0
|
||||||
|
elif new.vexc != 0: # we need '!= 0' here, as bool(enum) is always True!
|
||||||
|
iscur = 0
|
||||||
|
exc = new.vexc
|
||||||
|
excoff = 0
|
||||||
|
else:
|
||||||
|
excoff = 1
|
||||||
|
rng = new.range
|
||||||
|
if new.autorange == 'hard':
|
||||||
|
autorange = 1
|
||||||
|
else:
|
||||||
|
autorange = 0
|
||||||
|
if new.autorange == 'soft':
|
||||||
|
if rng < new.minrange:
|
||||||
|
rng = new.minrange
|
||||||
|
return iscur, exc, rng, autorange, excoff
|
||||||
|
|
||||||
|
def analyze_inset(self, on, dwell, pause, curve, tempco):
|
||||||
|
self.enabled, self.dwell, self.pause = on, dwell, pause
|
||||||
|
|
||||||
|
def change_inset(self, new, on, dwell, pause, curve, tempco):
|
||||||
|
return new.enable, new.dwell, new.pause, curve, tempco
|
||||||
|
|
||||||
|
def analyze_filt(self, on, settle, window):
|
||||||
|
self.filter = settle if on else 0
|
||||||
|
|
||||||
|
def change_filt(self, new, on, settle, window):
|
||||||
|
if new.filter:
|
||||||
|
return 1, new.filter, 80 # always use 80% filter
|
||||||
|
return 0, settle, window
|
77
secop_psi/ls370sim.py
Normal file
77
secop_psi/ls370sim.py
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
#!/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>
|
||||||
|
# *****************************************************************************
|
||||||
|
"""a very simple simulator for a LakeShore Model 370"""
|
||||||
|
|
||||||
|
from secop.modules import Communicator
|
||||||
|
#from secop.lib import mkthread
|
||||||
|
|
||||||
|
class Ls370Sim(Communicator):
|
||||||
|
CHANNEL_COMMANDS = [
|
||||||
|
('RDGR?%d', '1.0'),
|
||||||
|
('RDGST?%d', '0'),
|
||||||
|
('RDGRNG?%d', '0,5,5,0,0'),
|
||||||
|
('INSET?%d', '1,5,5,0,0'),
|
||||||
|
('FILTER?%d', '1,5,80'),
|
||||||
|
]
|
||||||
|
OTHER_COMMANDS = [
|
||||||
|
('*IDN?', 'LSCI,MODEL370,370184,05302003'),
|
||||||
|
('SCAN?', '3,1'),
|
||||||
|
]
|
||||||
|
def earlyInit(self):
|
||||||
|
self._data = dict(self.OTHER_COMMANDS)
|
||||||
|
for fmt, v in self.CHANNEL_COMMANDS:
|
||||||
|
for chan in range(1,17):
|
||||||
|
self._data[fmt % chan] = v
|
||||||
|
# mkthread(self.run)
|
||||||
|
|
||||||
|
def do_communicate(self, command):
|
||||||
|
# simulation part, time independent
|
||||||
|
for channel in range(1,17):
|
||||||
|
_, _, _, _, excoff = self._data['RDGRNG?%d' % channel].split(',')
|
||||||
|
if excoff == '1':
|
||||||
|
self._data['RDGST?%d' % channel] = '4'
|
||||||
|
else:
|
||||||
|
self._data['RDGST?%d' % channel] = '0'
|
||||||
|
|
||||||
|
chunks = command.split(';')
|
||||||
|
reply = []
|
||||||
|
for chunk in chunks:
|
||||||
|
if '?' in chunk:
|
||||||
|
reply.append(self._data[chunk])
|
||||||
|
else:
|
||||||
|
for nqarg in (1,0):
|
||||||
|
if nqarg == 0:
|
||||||
|
qcmd, arg = chunk.split(' ', 1)
|
||||||
|
qcmd += '?'
|
||||||
|
else:
|
||||||
|
qcmd, arg = chunk.split(',', nqarg)
|
||||||
|
qcmd = qcmd.replace(' ', '?', 1)
|
||||||
|
if qcmd in self._data:
|
||||||
|
self._data[qcmd] = arg
|
||||||
|
break
|
||||||
|
#if command.startswith('R'):
|
||||||
|
# print('> %s\t< %s' % (command, reply))
|
||||||
|
return ';'.join(reply)
|
||||||
|
|
||||||
|
#def run(self):
|
||||||
|
# # time dependent simulation
|
||||||
|
# while True:
|
||||||
|
# time.sleep(1)
|
Reference in New Issue
Block a user