Compare commits
66 Commits
v0.20.1-7
...
hot_lakesh
Author | SHA1 | Date | |
---|---|---|---|
aa64a48ec5 | |||
95dc8b186e | |||
265dbb1a57 | |||
73e9c8915b | |||
2e99e45aea | |||
b7bc81710d | |||
eee63ee3df | |||
fd43687465 | |||
a25a368491 | |||
4397d8db1a | |||
e60ac5e655 | |||
0b5b40cfba | |||
2a617fbaf0 | |||
72d09ea73a | |||
1ae19d03b3 | |||
41cb107f50 | |||
8b0c4c78a9 | |||
7ac10d2260 | |||
6cbb3a094b | |||
405d316568 | |||
ac92a6ca3d | |||
a9e3489325 | |||
654a472a7e | |||
ddc72d0ea7 | |||
ede07e266c | |||
4b543d02a0 | |||
a4d5d8d3b7 | |||
b37e625df3 | |||
1dbd7c145a | |||
2aa27f1ea5 | |||
b28cdefe8a | |||
e0e442814f | |||
66895f4f82 | |||
49bf0d21a9 | |||
e8cd193d0d | |||
142add9109 | |||
c2673952f4 | |||
9fc2aa65d5 | |||
09fbaedb16 | |||
5deaf4cfd9 | |||
81f7426739 | |||
c69e516873 | |||
64732eb0c8 | |||
1535448090 | |||
554996ffd3 | |||
2d824978a9 | |||
35dd166fee | |||
aee99df2d0 | |||
8e05090795 | |||
eac58982d9 | |||
0f34418435 | |||
1423800ff4 | |||
e333763105 | |||
c09e02a01e | |||
337be1b2bc | |||
752942483f | |||
0204bdfe2f | |||
facaca94eb | |||
0f0a177254 | |||
ada696e72b | |||
1d70a90270 | |||
20da7592da | |||
e750e69045 | |||
11a65b485b | |||
edf5a5568b | |||
f382a01d86 |
@ -204,6 +204,9 @@ max-statements=150
|
||||
# Maximum number of parents for a class (see R0901).
|
||||
max-parents=20
|
||||
|
||||
# Maximum number of positional arguments
|
||||
max-positional-arguments=10
|
||||
|
||||
# Maximum number of attributes for a class (see R0902).
|
||||
max-attributes=50
|
||||
|
||||
|
@ -69,7 +69,7 @@ def main(argv=None):
|
||||
console.setLevel(loglevel)
|
||||
logger.addHandler(console)
|
||||
|
||||
app = QApplication(argv)
|
||||
app = QApplication(argv, organizationName='frappy', applicationName='frappy_gui')
|
||||
|
||||
win = MainWindow(args, logger)
|
||||
app.aboutToQuit.connect(win._onQuit)
|
||||
|
@ -23,6 +23,8 @@
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from frappy.lib import generalConfig
|
||||
from frappy.logging import logger
|
||||
|
||||
# Add import path for inplace usage
|
||||
sys.path.insert(0, str(Path(__file__).absolute().parents[1]))
|
||||
@ -30,6 +32,8 @@ sys.path.insert(0, str(Path(__file__).absolute().parents[1]))
|
||||
from frappy.client.interactive import Console
|
||||
from frappy.playground import play, USAGE
|
||||
|
||||
generalConfig.init()
|
||||
logger.init()
|
||||
if len(sys.argv) > 1:
|
||||
play(sys.argv[1])
|
||||
else:
|
||||
|
128
bin/frappy-scan
Executable file
128
bin/frappy-scan
Executable file
@ -0,0 +1,128 @@
|
||||
#!/usr/bin/env python3
|
||||
# *****************************************************************************
|
||||
#
|
||||
# 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:
|
||||
# Alexander Zaft <a.zaft@fz-juelich.de>
|
||||
#
|
||||
# *****************************************************************************
|
||||
|
||||
"""SEC node autodiscovery tool."""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import select
|
||||
import socket
|
||||
import sys
|
||||
from collections import namedtuple
|
||||
from time import time as currenttime
|
||||
|
||||
UDP_PORT = 10767
|
||||
|
||||
Answer = namedtuple('Answer',
|
||||
'address, port, equipment_id, firmware, description')
|
||||
|
||||
|
||||
def decode(msg, addr):
|
||||
msg = msg.decode('utf-8')
|
||||
try:
|
||||
data = json.loads(msg)
|
||||
except Exception:
|
||||
return None
|
||||
if not isinstance(data, dict):
|
||||
return None
|
||||
if data.get('SECoP') != 'node':
|
||||
return None
|
||||
try:
|
||||
eq_id = data['equipment_id']
|
||||
fw = data['firmware']
|
||||
desc = data['description']
|
||||
port = data['port']
|
||||
except KeyError:
|
||||
return None
|
||||
addr, _scanport = addr
|
||||
return Answer(addr, port, eq_id, fw, desc)
|
||||
|
||||
|
||||
def print_answer(answer, *, short=False):
|
||||
if short:
|
||||
# NOTE: keep this easily parseable!
|
||||
print(f'{answer.equipment_id} {answer.address}:{answer.port}')
|
||||
return
|
||||
print(f'Found {answer.equipment_id} at {answer.address}:')
|
||||
print(f' Port: {answer.port}')
|
||||
print(f' Firmware: {answer.firmware}')
|
||||
desc = answer.description.replace('\n', '\n ')
|
||||
print(f' Node description: {desc}')
|
||||
print()
|
||||
|
||||
|
||||
def scan(max_wait=1.0):
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
|
||||
# send a general broadcast
|
||||
try:
|
||||
s.sendto(json.dumps(dict(SECoP='discover')).encode('utf-8'),
|
||||
('255.255.255.255', UDP_PORT))
|
||||
except OSError as e:
|
||||
print('could not send the broadcast:', e)
|
||||
# we still keep listening for self-announcements
|
||||
start = currenttime()
|
||||
seen = set()
|
||||
while currenttime() < start + max_wait:
|
||||
res = select.select([s], [], [], 0.1)
|
||||
if res[0]:
|
||||
try:
|
||||
msg, addr = s.recvfrom(1024)
|
||||
except socket.error: # pragma: no cover
|
||||
continue
|
||||
answer = decode(msg, addr)
|
||||
if answer is None:
|
||||
continue
|
||||
if (answer.address, answer.equipment_id, answer.port) in seen:
|
||||
continue
|
||||
seen.add((answer.address, answer.equipment_id, answer.port))
|
||||
yield answer
|
||||
|
||||
|
||||
def listen(*, short=False):
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
if os.name == 'nt':
|
||||
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
else:
|
||||
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
|
||||
s.bind(('0.0.0.0', UDP_PORT))
|
||||
while True:
|
||||
try:
|
||||
msg, addr = s.recvfrom(1024)
|
||||
except KeyboardInterrupt:
|
||||
break
|
||||
answer = decode(msg, addr)
|
||||
if answer:
|
||||
print_answer(answer, short=short)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('-l', '--listen', action='store_true',
|
||||
help='Print short info. '
|
||||
'Keep listening after the broadcast.')
|
||||
args = parser.parse_args(sys.argv[1:])
|
||||
for answer in scan():
|
||||
print_answer(answer, short=args.listen)
|
||||
if args.listen:
|
||||
listen(short=args.listen)
|
@ -35,7 +35,15 @@ from frappy.server import Server
|
||||
|
||||
|
||||
def parseArgv(argv):
|
||||
parser = argparse.ArgumentParser(description="Manage a SECoP server")
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Manage a SECoP server",
|
||||
epilog="""The server needs some configuration, by default from the
|
||||
generalConfig.cfg file. the keys confdir, logdir and piddir have to
|
||||
be set.
|
||||
Alternatively, one can set the environment variables FRAPPY_CONFDIR
|
||||
FRAPPY_LOGDIR and FRAPPY_PIDDIR to set the required values.
|
||||
"""
|
||||
)
|
||||
loggroup = parser.add_mutually_exclusive_group()
|
||||
loggroup.add_argument("-v", "--verbose",
|
||||
help="Output lots of diagnostic information",
|
||||
@ -60,9 +68,9 @@ def parseArgv(argv):
|
||||
action='store',
|
||||
help="comma separated list of cfg files,\n"
|
||||
"defaults to <name_of_the_instance>.\n"
|
||||
"cfgfiles given without '.cfg' extension are searched"
|
||||
" in the configuration directory,"
|
||||
" else they are treated as path names",
|
||||
"If a config file contains a slash, it is treated as a"
|
||||
"path, otherwise the file is searched for in the "
|
||||
"configuration directory.",
|
||||
default=None)
|
||||
parser.add_argument('-g',
|
||||
'--gencfg',
|
||||
@ -96,7 +104,9 @@ def main(argv=None):
|
||||
generalConfig.init(args.gencfg)
|
||||
logger.init(loglevel)
|
||||
|
||||
srv = Server(args.name, logger.log, cfgfiles=args.cfgfiles,
|
||||
cfgfiles = [s.strip() for s in args.cfgfiles.split(',')] if args.cfgfiles else None
|
||||
|
||||
srv = Server(args.name, logger.log, cfgfiles=cfgfiles,
|
||||
interface=args.port, testonly=args.test)
|
||||
|
||||
if args.daemonize:
|
||||
|
226
bin/sim-server
226
bin/sim-server
@ -22,22 +22,35 @@
|
||||
|
||||
Usage:
|
||||
|
||||
bin/stringio-server <communciator> <server port>
|
||||
bin/sim-server <communicator class> -p <server port> [-o <option1>=<value> <option2>=<value>]
|
||||
|
||||
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
|
||||
|
||||
> bin/sim-server frappy_psi.ls370sim.Ls370Sim
|
||||
|
||||
- relay to a communicator not using TCP/IP, if Frappy should run on an other host
|
||||
|
||||
> bin/sim-server frappy.io.StringIO -o uri=serial:///dev/tty...
|
||||
|
||||
- as a T, if the hardware allows only one connection, and more than one is needed:
|
||||
|
||||
> bin/sim-server frappy.io.StringIO -o uri=tcp://<host>:<port>
|
||||
|
||||
typically using communicator class frappy.io.StringIO
|
||||
"""
|
||||
|
||||
import sys
|
||||
import argparse
|
||||
from pathlib import Path
|
||||
import asyncore
|
||||
import socket
|
||||
import time
|
||||
import os
|
||||
from ast import literal_eval
|
||||
from socketserver import BaseRequestHandler, ThreadingTCPServer
|
||||
|
||||
# Add import path for inplace usage
|
||||
sys.path.insert(0, str(Path(__file__).absolute().parents[1]))
|
||||
@ -45,92 +58,6 @@ sys.path.insert(0, str(Path(__file__).absolute().parents[1]))
|
||||
from frappy.lib import get_class, formatException, mkthread
|
||||
|
||||
|
||||
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_line(self, line):
|
||||
raise NotImplementedError
|
||||
|
||||
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, port, line_handler_cls, handler_args):
|
||||
asyncore.dispatcher.__init__(self)
|
||||
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
self.set_reuse_addr()
|
||||
self.bind(('0.0.0.0', port))
|
||||
self.listen(5)
|
||||
print('accept connections at port', port)
|
||||
self.line_handler_cls = line_handler_cls
|
||||
self.handler_args = handler_args
|
||||
|
||||
def handle_accept(self):
|
||||
pair = self.accept()
|
||||
if pair is not None:
|
||||
sock, addr = pair
|
||||
print("Incoming connection from %s" % repr(addr))
|
||||
self.line_handler_cls(sock, self.handler_args)
|
||||
|
||||
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.secnode = None
|
||||
self.dispatcher = self.Dispatcher()
|
||||
|
||||
|
||||
class Handler(LineHandler):
|
||||
def __init__(self, sock, handler_args):
|
||||
super().__init__(sock)
|
||||
self.module = handler_args['module']
|
||||
self.verbose = handler_args['verbose']
|
||||
|
||||
def handle_line(self, line):
|
||||
try:
|
||||
reply = self.module.communicate(line.strip())
|
||||
if self.verbose:
|
||||
print('%-40s | %s' % (line, reply))
|
||||
except Exception:
|
||||
print(formatException(verbose=True))
|
||||
return
|
||||
self.send_line(reply)
|
||||
|
||||
|
||||
class Logger:
|
||||
def debug(self, *args):
|
||||
pass
|
||||
@ -144,43 +71,126 @@ class Logger:
|
||||
exception = error = warn = info
|
||||
|
||||
|
||||
class TcpRequestHandler(BaseRequestHandler):
|
||||
def setup(self):
|
||||
print(f'connection opened from {self.client_address}')
|
||||
self.running = True
|
||||
self.request.settimeout(1)
|
||||
self.data = b''
|
||||
|
||||
def finish(self):
|
||||
"""called when handle() terminates, i.e. the socket closed"""
|
||||
# close socket
|
||||
try:
|
||||
self.request.shutdown(socket.SHUT_RDWR)
|
||||
except Exception:
|
||||
pass
|
||||
finally:
|
||||
print(f'connection closed from {self.client_address}')
|
||||
self.request.close()
|
||||
|
||||
def poller(self):
|
||||
while True:
|
||||
time.sleep(1.0)
|
||||
self.module.doPoll()
|
||||
|
||||
def handle(self):
|
||||
"""handle a new connection"""
|
||||
# do a copy of the options, as they are consumed
|
||||
self.module = self.server.modulecls(
|
||||
'mod', Logger(), dict(self.server.options), self.server)
|
||||
self.module.earlyInit()
|
||||
|
||||
mkthread(self.poller)
|
||||
while self.running:
|
||||
try:
|
||||
newdata = self.request.recv(1024)
|
||||
if not newdata:
|
||||
return
|
||||
except socket.timeout:
|
||||
# no new data during read, continue
|
||||
continue
|
||||
self.data += newdata
|
||||
while self.running:
|
||||
message, sep, self.data = self.data.partition(b'\n')
|
||||
if not sep:
|
||||
break
|
||||
cmd = message.decode('latin-1')
|
||||
try:
|
||||
reply = self.module.communicate(cmd.strip())
|
||||
if self.server.verbose:
|
||||
print('%-40s | %s' % (cmd, reply))
|
||||
except Exception:
|
||||
print(formatException(verbose=True))
|
||||
return
|
||||
outdata = reply.encode('latin-1') + b'\n'
|
||||
try:
|
||||
self.request.sendall(outdata)
|
||||
except Exception as e:
|
||||
print(repr(e))
|
||||
self.running = False
|
||||
|
||||
|
||||
class Server(ThreadingTCPServer):
|
||||
allow_reuse_address = os.name != 'nt' # False on Windows systems
|
||||
|
||||
class Dispatcher:
|
||||
def announce_update(self, *_):
|
||||
pass
|
||||
|
||||
def announce_update_error(self, *_):
|
||||
pass
|
||||
|
||||
def __init__(self, port, modulecls, options, verbose=False):
|
||||
super().__init__(('', port), TcpRequestHandler,
|
||||
bind_and_activate=True)
|
||||
self.secnode = None
|
||||
self.dispatcher = self.Dispatcher()
|
||||
self.verbose = verbose
|
||||
self.modulecls = get_class(modulecls)
|
||||
self.options = options
|
||||
print(f'started sim-server listening on port {port}')
|
||||
|
||||
|
||||
def parse_argv(argv):
|
||||
parser = argparse.ArgumentParser(description="Simulate HW with a serial interface")
|
||||
parser = argparse.ArgumentParser(description="Relay to a communicator (simulated HW or other)")
|
||||
parser.add_argument("-v", "--verbose",
|
||||
help="output full communication",
|
||||
action='store_true', default=False)
|
||||
parser.add_argument("cls",
|
||||
type=str,
|
||||
help="simulator class.\n",)
|
||||
help="communicator class.\n",)
|
||||
parser.add_argument('-p',
|
||||
'--port',
|
||||
action='store',
|
||||
help='server port or uri',
|
||||
default=2089)
|
||||
parser.add_argument('-o',
|
||||
'--options',
|
||||
action='store',
|
||||
nargs='*',
|
||||
help='options in the form key=value',
|
||||
default=None)
|
||||
return parser.parse_args(argv)
|
||||
|
||||
|
||||
def poller(pollfunc):
|
||||
while True:
|
||||
time.sleep(1.0)
|
||||
pollfunc()
|
||||
|
||||
|
||||
def main(argv=None):
|
||||
if argv is None:
|
||||
argv = sys.argv
|
||||
|
||||
args = parse_argv(argv[1:])
|
||||
|
||||
opts = {'description': 'simulator'}
|
||||
|
||||
handler_args = {'verbose': args.verbose}
|
||||
srv = Server(int(args.port), Handler, handler_args)
|
||||
module = get_class(args.cls)(args.cls, Logger(), opts, srv)
|
||||
handler_args['module'] = module
|
||||
module.earlyInit()
|
||||
mkthread(poller, module.doPoll)
|
||||
srv.loop()
|
||||
options = {'description': ''}
|
||||
for item in args.options or ():
|
||||
key, eq, value = item.partition('=')
|
||||
if not eq:
|
||||
raise ValueError(f"missing '=' in {item}")
|
||||
try:
|
||||
value = literal_eval(value)
|
||||
except Exception:
|
||||
pass
|
||||
options[key] = value
|
||||
srv = Server(int(args.port), args.cls, options, args.verbose)
|
||||
srv.serve_forever()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
67
cfg/PEUS.py
Normal file
67
cfg/PEUS.py
Normal file
@ -0,0 +1,67 @@
|
||||
Node(equipment_id = 'pe_ultrasound.psi.ch',
|
||||
description = 'pulse echo ultra sound setup',
|
||||
interface = 'tcp://5000',
|
||||
)
|
||||
|
||||
Mod('f',
|
||||
cls = 'frappy_psi.ultrasound.Frequency',
|
||||
description = 'ultrasound frequency and acquisition loop',
|
||||
uri = 'serial:///dev/ttyS1',
|
||||
pars = 'pars',
|
||||
pollinterval = 0.1,
|
||||
time = 900, # start time
|
||||
size = 5000,
|
||||
freq = 1.17568e+06,
|
||||
basefreq = 4.14902e+07,
|
||||
control = False,
|
||||
rusmode = False,
|
||||
amp = 5.0,
|
||||
nr = 1000, #500 #300 #100 #50 #30 #10 #5 #3 #1 #1000 #500 #300 #100 #50 #30 #10 #5 #3 #1 #500
|
||||
sr = 32768, #16384
|
||||
plot = True,
|
||||
maxstep = 100000,
|
||||
bw = 10E6, #butter worth filter bandwidth
|
||||
maxy = 0.7, # y scale for plot
|
||||
curves = 'curves', # module to transmit curves:
|
||||
)
|
||||
|
||||
Mod('curves',
|
||||
cls = 'frappy_psi.ultrasound.Curves',
|
||||
description = 't, I, Q and pulse arrays for plot',
|
||||
)
|
||||
|
||||
Mod('delay',
|
||||
cls = 'frappy__psi.dg645.Delay',
|
||||
description = 'delay line with 2 channels',
|
||||
uri = 'serial:///dev/ttyS2',
|
||||
on1 = 1e-9,
|
||||
on2 = 1E-9,
|
||||
off1 = 400e-9,
|
||||
off2 = 600e-9,
|
||||
)
|
||||
|
||||
Mod('pars',
|
||||
cls = 'frappy_psi.ultrasound.Pars',
|
||||
description = 'SEA parameters',
|
||||
)
|
||||
|
||||
def roi(nr, time=None, size=300):
|
||||
Mod(f'roi{nr}',
|
||||
cls = 'frappy_psi.ultrasound.Roi',
|
||||
description = f'I/Q of region {nr}',
|
||||
main = 'f',
|
||||
time=time or 4000,
|
||||
size=size,
|
||||
enable=time is not None,
|
||||
)
|
||||
|
||||
roi(0, 2450) # you may add size as argument if not default
|
||||
roi(1, 5950)
|
||||
roi(2, 9475)
|
||||
roi(3, 12900)
|
||||
roi(4, 16100)
|
||||
roi(5) # disabled
|
||||
roi(6)
|
||||
roi(7)
|
||||
roi(8)
|
||||
roi(9)
|
62
cfg/RUS.py
Normal file
62
cfg/RUS.py
Normal file
@ -0,0 +1,62 @@
|
||||
Node(equipment_id = 'r_ultrasound.psi.ch',
|
||||
description = 'resonant ultra sound setup',
|
||||
interface = 'tcp://5000',
|
||||
)
|
||||
|
||||
Mod('f',
|
||||
cls = 'frappy_psi.ultrasound.Frequency',
|
||||
description = 'ultrasound frequency and acquisition loop',
|
||||
uri = 'serial:///dev/ttyS1',
|
||||
pars = 'pars',
|
||||
pollinterval = 0.1,
|
||||
time = 900, # start time
|
||||
size = 5000,
|
||||
freq = 1.e+03,
|
||||
basefreq = 1.E+3,
|
||||
control = False,
|
||||
rusmode = False,
|
||||
amp = 2.5,
|
||||
nr = 1, #500 #300 #100 #50 #30 #10 #5 #3 #1 #1000 #500 #300 #100 #50 #30 #10 #5 #3 #1 #500
|
||||
sr = 1E8, #16384
|
||||
plot = True,
|
||||
maxstep = 100000,
|
||||
bw = 10E6, #butter worth filter bandwidth
|
||||
maxy = 0.7, # y scale for plot
|
||||
curves = 'curves', # module to transmit curves:
|
||||
)
|
||||
|
||||
Mod('curves',
|
||||
cls = 'frappy_psi.ultrasound.Curves',
|
||||
description = 't, I, Q and pulse arrays for plot',
|
||||
)
|
||||
|
||||
Mod('roi0',
|
||||
cls = 'frappy_psi.ultrasound.Roi',
|
||||
description = 'I/Q of region in the control loop',
|
||||
time = 300, # this is the center of roi:
|
||||
size = 5000,
|
||||
main = f,
|
||||
)
|
||||
|
||||
Mod('roi1',
|
||||
cls = 'frappy_psi.ultrasound.Roi',
|
||||
description = 'I/Q of region 1',
|
||||
time = 100, # this is the center of roi:
|
||||
size = 300,
|
||||
main = f,
|
||||
)
|
||||
|
||||
Mod('delay',
|
||||
cls = 'frappy__psi.dg645.Delay',
|
||||
description = 'delay line with 2 channels',
|
||||
uri = 'serial:///dev/ttyS2',
|
||||
on1 = 1e-9,
|
||||
on2 = 1E-9,
|
||||
off1 = 400e-9,
|
||||
off2 = 600e-9,
|
||||
)
|
||||
|
||||
Mod('pars',
|
||||
cls = 'frappy_psi.ultrasound.Pars',
|
||||
description = 'SEA parameters',
|
||||
)
|
37
cfg/addons/ah2550sea_cfg.py
Normal file
37
cfg/addons/ah2550sea_cfg.py
Normal file
@ -0,0 +1,37 @@
|
||||
Node('ah2550.addon.sea.psi.ch',
|
||||
'Andeen Hagerlin 2550 Capacitance Bridge using SEA',
|
||||
)
|
||||
|
||||
Mod('sea_addons',
|
||||
'frappy_psi.sea.SeaClient',
|
||||
'SEA connection to mbe_ah2550',
|
||||
config='ah2550.addon',
|
||||
export=False,
|
||||
service='addons',
|
||||
)
|
||||
|
||||
Mod('ah',
|
||||
'frappy_psi.sea.SeaReadable', '',
|
||||
io='sea_addons',
|
||||
sea_object='cap',
|
||||
extra_modules = ['cap', 'loss']
|
||||
)
|
||||
|
||||
Mod('cap',
|
||||
'frappy_psi.sea.SeaReadable', '',
|
||||
io='sea_addons',
|
||||
single_module='ah.cap',
|
||||
value=Param(fmtstr='%.12g'),
|
||||
)
|
||||
|
||||
Mod('loss',
|
||||
'frappy_psi.sea.SeaReadable', '',
|
||||
io='sea_addons',
|
||||
single_module='ah.loss',
|
||||
)
|
||||
|
||||
Mod('capslope',
|
||||
'frappy_psi.sea.SeaReadable', '',
|
||||
io='sea_addons',
|
||||
sea_object='capslope',
|
||||
)
|
@ -5,5 +5,5 @@ Node('ah2700.frappy.psi.ch',
|
||||
Mod('cap',
|
||||
'frappy_psi.ah2700.Capacitance',
|
||||
'capacitance',
|
||||
uri='lollypop-ts:3002',
|
||||
uri='dil4-ts.psi.ch:3008',
|
||||
)
|
||||
|
@ -2,6 +2,8 @@ Node('ah2700.addon.sea.psi.ch',
|
||||
'Andeen Hagerlin 2700 Capacitance Bridge using SEA',
|
||||
)
|
||||
|
||||
sea_cfg='ah2700.addon'
|
||||
|
||||
Mod('sea_addons',
|
||||
'frappy_psi.sea.SeaClient',
|
||||
'SEA connection to mbe_ah2700',
|
||||
@ -10,10 +12,24 @@ Mod('sea_addons',
|
||||
service='addons',
|
||||
)
|
||||
|
||||
Mod('cap',
|
||||
Mod('ah',
|
||||
'frappy_psi.sea.SeaReadable', '',
|
||||
io='sea_addons',
|
||||
sea_object='cap',
|
||||
extra_modules = ['cap', 'loss']
|
||||
)
|
||||
|
||||
Mod('cap',
|
||||
'frappy_psi.sea.SeaReadable', '',
|
||||
io='sea_addons',
|
||||
single_module='ah.cap',
|
||||
value=Param(fmtstr='%.12g'),
|
||||
)
|
||||
|
||||
Mod('loss',
|
||||
'frappy_psi.sea.SeaReadable', '',
|
||||
io='sea_addons',
|
||||
single_module='ah.loss',
|
||||
)
|
||||
|
||||
Mod('capslope',
|
||||
|
@ -4,33 +4,22 @@ Node('ls340test.psi.ch',
|
||||
)
|
||||
|
||||
Mod('io',
|
||||
'frappy_psi.lakeshore.Ls340IO',
|
||||
'frappy_psi.lakeshore.IO340',
|
||||
'communication to ls340',
|
||||
uri='tcp://ldmprep56-ts:3002'
|
||||
uri='tcp://localhost:7777'
|
||||
)
|
||||
|
||||
Mod('dev',
|
||||
'frappy_psi.lakeshore.Device340',
|
||||
'device for calcurve',
|
||||
io='io',
|
||||
curve_handling=True,
|
||||
)
|
||||
Mod('T',
|
||||
'frappy_psi.lakeshore.TemperatureLoop340',
|
||||
'sample temperature',
|
||||
output_module='Heater',
|
||||
target=Param(max=470),
|
||||
io='io',
|
||||
channel='B'
|
||||
)
|
||||
|
||||
Mod('T_cold_finger',
|
||||
'frappy_psi.lakeshore.Sensor340',
|
||||
'cold finger temperature',
|
||||
io='io',
|
||||
channel='A'
|
||||
)
|
||||
|
||||
Mod('Heater',
|
||||
'frappy_psi.lakeshore.HeaterOutput',
|
||||
'heater output',
|
||||
channel='B',
|
||||
io='io',
|
||||
resistance=25,
|
||||
max_power=50,
|
||||
current=1
|
||||
'sample temperature',
|
||||
# output_module='Heater',
|
||||
device='dev',
|
||||
channel='A',
|
||||
calcurve='x29746',
|
||||
)
|
||||
|
@ -12,9 +12,9 @@ Mod('sea_main',
|
||||
Mod('tt',
|
||||
'frappy_psi.sea.SeaDrivable', '',
|
||||
io='sea_main',
|
||||
meaning=['temperature_regulation', 20],
|
||||
meaning=['temperature_regulation', 27],
|
||||
sea_object='tt',
|
||||
rel_paths=['tm', 'set', 'dblctrl'],
|
||||
rel_paths=['tm', '.', 'set', 'dblctrl'],
|
||||
)
|
||||
|
||||
Mod('cc',
|
||||
|
@ -12,9 +12,9 @@ Mod('sea_main',
|
||||
Mod('tt',
|
||||
'frappy_psi.sea.SeaDrivable', '',
|
||||
io='sea_main',
|
||||
meaning=['temperature_regulation', 20],
|
||||
meaning=['temperature_regulation', 27],
|
||||
sea_object='tt',
|
||||
rel_paths=['tm', 'set', 'dblctrl'],
|
||||
rel_paths=['tm', '.', 'set', 'dblctrl'],
|
||||
)
|
||||
|
||||
Mod('cc',
|
||||
|
@ -12,9 +12,9 @@ Mod('sea_main',
|
||||
Mod('tt',
|
||||
'frappy_psi.sea.SeaDrivable', '',
|
||||
io='sea_main',
|
||||
meaning=['temperature_regulation', 20],
|
||||
meaning=['temperature_regulation', 27],
|
||||
sea_object='tt',
|
||||
rel_paths=['tm', 'set', 'dblctrl'],
|
||||
rel_paths=['tm', '.', 'set', 'dblctrl'],
|
||||
)
|
||||
|
||||
Mod('cc',
|
||||
|
@ -12,9 +12,9 @@ Mod('sea_main',
|
||||
Mod('tt',
|
||||
'frappy_psi.sea.SeaDrivable', '',
|
||||
io='sea_main',
|
||||
meaning=['temperature_regulation', 20],
|
||||
meaning=['temperature_regulation', 27],
|
||||
sea_object='tt',
|
||||
rel_paths=['tm', 'set', 'dblctrl'],
|
||||
rel_paths=['tm', '.', 'set', 'dblctrl'],
|
||||
)
|
||||
|
||||
Mod('cc',
|
||||
|
@ -12,9 +12,9 @@ Mod('sea_main',
|
||||
Mod('tt',
|
||||
'frappy_psi.sea.SeaDrivable', '',
|
||||
io='sea_main',
|
||||
meaning=['temperature_regulation', 20],
|
||||
meaning=['temperature_regulation', 27],
|
||||
sea_object='tt',
|
||||
rel_paths=['tm', 'set', 'dblctrl'],
|
||||
rel_paths=['tm', '.', 'set', 'dblctrl'],
|
||||
)
|
||||
|
||||
Mod('cc',
|
||||
|
@ -12,9 +12,9 @@ Mod('sea_main',
|
||||
Mod('tt',
|
||||
'frappy_psi.sea.SeaDrivable', '',
|
||||
io='sea_main',
|
||||
meaning=['temperature_regulation', 20],
|
||||
meaning=['temperature_regulation', 27],
|
||||
sea_object='tt',
|
||||
rel_paths=['tm', 'set', 'dblctrl'],
|
||||
rel_paths=['tm', '.', 'set', 'dblctrl'],
|
||||
)
|
||||
|
||||
Mod('pauto',
|
||||
|
@ -12,7 +12,7 @@ Mod('sea_main',
|
||||
Mod('tt',
|
||||
'frappy_psi.sea.SeaDrivable', '',
|
||||
io='sea_main',
|
||||
meaning=['temperature_regulation', 20],
|
||||
meaning=['temperature_regulation', 27],
|
||||
sea_object='tt',
|
||||
rel_paths=['tt', 'set'],
|
||||
)
|
||||
|
@ -12,9 +12,9 @@ Mod('sea_main',
|
||||
Mod('tt',
|
||||
'frappy_psi.sea.SeaDrivable', '',
|
||||
io='sea_main',
|
||||
meaning=['temperature_regulation', 20],
|
||||
meaning=['temperature_regulation', 27],
|
||||
sea_object='tt',
|
||||
rel_paths=['tm', 'set', 'dblctrl'],
|
||||
rel_paths=['tm', '.', 'set', 'dblctrl'],
|
||||
)
|
||||
|
||||
Mod('cc',
|
||||
|
@ -12,9 +12,9 @@ Mod('sea_main',
|
||||
Mod('tt',
|
||||
'frappy_psi.sea.SeaDrivable', '',
|
||||
io='sea_main',
|
||||
meaning=['temperature_regulation', 20],
|
||||
meaning=['temperature_regulation', 27],
|
||||
sea_object='tt',
|
||||
rel_paths=['tm', 'set', 'dblctrl'],
|
||||
rel_paths=['tm', '.', 'set', 'dblctrl'],
|
||||
)
|
||||
|
||||
Mod('cc',
|
||||
|
@ -12,7 +12,7 @@ Mod('sea_main',
|
||||
Mod('ts',
|
||||
'frappy_psi.sea.SeaDrivable', '',
|
||||
io='sea_main',
|
||||
meaning=['temperature_regulation', 20],
|
||||
meaning=['temperature_regulation', 27],
|
||||
sea_object='tt',
|
||||
rel_paths=['ts', 'set']
|
||||
)
|
||||
|
@ -13,7 +13,7 @@ Mod('th',
|
||||
'frappy_psi.sea.SeaReadable',
|
||||
'sample heater temperature',
|
||||
io='sea_main',
|
||||
meaning=['temperature_regulation', 20],
|
||||
meaning=['temperature_regulation', 27],
|
||||
sea_object='tt',
|
||||
rel_paths=['ts', 'setsamp']
|
||||
)
|
||||
@ -22,7 +22,7 @@ Mod('tm',
|
||||
'frappy_psi.sea.SeaDrivable', '',
|
||||
io='sea_main',
|
||||
sea_object='tt',
|
||||
rel_paths=['tm', 'set']
|
||||
rel_paths=['tm', '.', 'set']
|
||||
)
|
||||
|
||||
Mod('ts',
|
||||
|
@ -12,9 +12,9 @@ Mod('sea_main',
|
||||
Mod('tt',
|
||||
'frappy_psi.sea.SeaDrivable', '',
|
||||
io='sea_main',
|
||||
meaning=['temperature_regulation', 20],
|
||||
meaning=['temperature_regulation', 27],
|
||||
sea_object='tt',
|
||||
rel_paths=['tm', 'set', 'dblctrl'],
|
||||
rel_paths=['tm', '.', 'set', 'dblctrl'],
|
||||
)
|
||||
|
||||
Mod('cc',
|
||||
|
@ -10,9 +10,9 @@ Mod('sea_main',
|
||||
Mod('tt',
|
||||
'frappy_psi.sea.SeaDrivable', '',
|
||||
io = 'sea_main',
|
||||
meaning=['temperature_regulation', 20],
|
||||
meaning=['temperature_regulation', 27],
|
||||
sea_object = 'tt',
|
||||
rel_paths=['tm', 'set', 'dblctrl'],
|
||||
rel_paths=['tm', '.', 'set', 'dblctrl'],
|
||||
)
|
||||
Mod('cc',
|
||||
'frappy_psi.sea.SeaReadable', '',
|
||||
|
@ -12,9 +12,9 @@ Mod('sea_main',
|
||||
Mod('tt',
|
||||
'frappy_psi.sea.SeaDrivable', '',
|
||||
io='sea_main',
|
||||
meaning=['temperature_regulation', 20],
|
||||
meaning=['temperature_regulation', 27],
|
||||
sea_object='tt',
|
||||
rel_paths=['tm', 'set', 'dblctrl'],
|
||||
rel_paths=['tm', '.', 'set', 'dblctrl'],
|
||||
)
|
||||
|
||||
Mod('cc',
|
||||
|
@ -12,9 +12,9 @@ Mod('sea_main',
|
||||
Mod('tt',
|
||||
'frappy_psi.sea.SeaDrivable', '',
|
||||
io='sea_main',
|
||||
meaning=['temperature_regulation', 20],
|
||||
meaning=['temperature_regulation', 27],
|
||||
sea_object='tt',
|
||||
rel_paths=['tm', 'set', 'dblctrl'],
|
||||
rel_paths=['tm', '.', 'set', 'dblctrl'],
|
||||
)
|
||||
|
||||
Mod('th',
|
||||
|
@ -10,11 +10,12 @@ Mod('sea_main',
|
||||
)
|
||||
|
||||
Mod('tt',
|
||||
'frappy_psi.sea.SeaDrivable', '',
|
||||
'frappy_psi.sea.LscDrivable', '',
|
||||
io='sea_main',
|
||||
meaning=['temperature_regulation', 20],
|
||||
meaning=['temperature_regulation', 27],
|
||||
sea_object='tt',
|
||||
rel_paths=['tm', 'set', 'dblctrl'],
|
||||
sensor_path='tm',
|
||||
set_path='set',
|
||||
)
|
||||
|
||||
Mod('cc',
|
||||
@ -69,15 +70,13 @@ Mod('lev',
|
||||
Mod('tcoil1',
|
||||
'frappy_psi.sea.SeaReadable', '',
|
||||
io='sea_main',
|
||||
sea_object='tcoil',
|
||||
rel_paths=['ta'],
|
||||
sea_path='tcoil/ta',
|
||||
)
|
||||
|
||||
Mod('tcoil2',
|
||||
'frappy_psi.sea.SeaReadable', '',
|
||||
io='sea_main',
|
||||
sea_object='tcoil',
|
||||
rel_paths=['tb'],
|
||||
sea_path='tcoil/tb',
|
||||
)
|
||||
|
||||
Mod('table',
|
||||
|
@ -12,9 +12,9 @@ Mod('sea_main',
|
||||
Mod('tt',
|
||||
'frappy_psi.sea.SeaDrivable', '',
|
||||
io='sea_main',
|
||||
meaning=['temperature_regulation', 20],
|
||||
meaning=['temperature_regulation', 27],
|
||||
sea_object='tt',
|
||||
rel_paths=['tm', 'set', 'dblctrl', 'voltage'],
|
||||
rel_paths=['tm', '.', 'set', 'dblctrl', 'voltage'],
|
||||
extra_modules=['manualpower'],
|
||||
)
|
||||
|
||||
|
@ -12,9 +12,9 @@ Mod('sea_main',
|
||||
Mod('tt',
|
||||
'frappy_psi.sea.SeaDrivable', '',
|
||||
io='sea_main',
|
||||
meaning=['temperature_regulation', 20],
|
||||
meaning=['temperature_regulation', 27],
|
||||
sea_object='tt',
|
||||
rel_paths=['tm', 'set', 'dblctrl'],
|
||||
rel_paths=['tm', '.', 'set', 'dblctrl'],
|
||||
)
|
||||
|
||||
Mod('th',
|
||||
|
@ -16,10 +16,10 @@ Mod('sea_main',
|
||||
|
||||
Mod('tt',
|
||||
'frappy_psi.sea.SeaDrivable', '',
|
||||
meaning=['temperature_regulation', 20],
|
||||
meaning=['temperature_regulation', 27],
|
||||
io='sea_main',
|
||||
sea_object='tt',
|
||||
rel_paths=['tm', 'set', 'dblctrl'],
|
||||
rel_paths=['tm', '.', 'set', 'dblctrl'],
|
||||
)
|
||||
|
||||
Mod('th',
|
||||
|
@ -12,9 +12,9 @@ Mod('sea_main',
|
||||
Mod('tt',
|
||||
'frappy_psi.sea.SeaDrivable', '',
|
||||
io='sea_main',
|
||||
meaning=['temperature_regulation', 20],
|
||||
meaning=['temperature_regulation', 27],
|
||||
sea_object='tt',
|
||||
rel_paths=['tm', 'set', 'dblctrl'],
|
||||
rel_paths=['tm', '.', 'set', 'dblctrl'],
|
||||
)
|
||||
|
||||
Mod('cc',
|
||||
|
@ -25,7 +25,7 @@ Mod('ips',
|
||||
Mod('T_stat',
|
||||
'frappy_psi.mercury.TemperatureAutoFlow',
|
||||
'static heat exchanger temperature',
|
||||
meaning=['temperature_regulation', 20],
|
||||
meaning=['temperature_regulation', 27],
|
||||
output_module='htr_stat',
|
||||
needle_valve='p_stat',
|
||||
slot='DB6.T1',
|
||||
|
@ -23,7 +23,7 @@ Mod('ips',
|
||||
Mod('T_stat',
|
||||
'frappy_psi.mercury.TemperatureAutoFlow',
|
||||
'static heat exchanger temperature',
|
||||
meaning=['temperature_regulation', 20],
|
||||
meaning=['temperature_regulation', 27],
|
||||
output_module='htr_stat',
|
||||
needle_valve='p_stat',
|
||||
slot='DB6.T1',
|
||||
@ -46,6 +46,7 @@ Mod('ts',
|
||||
slot='MB1.T1',
|
||||
io='itc1',
|
||||
tolerance=1.0,
|
||||
visibility='expert',
|
||||
)
|
||||
|
||||
Mod('htr_sample',
|
||||
|
73
cfg/main/ori2_cfg.py
Normal file
73
cfg/main/ori2_cfg.py
Normal file
@ -0,0 +1,73 @@
|
||||
Node('ori3.config.sea.psi.ch',
|
||||
'orange cryostat with 50 mm sample space',
|
||||
)
|
||||
|
||||
Mod('sea_main',
|
||||
'frappy_psi.sea.SeaClient',
|
||||
'main sea connection for ori2.config',
|
||||
config='ori2.config',
|
||||
service='main',
|
||||
)
|
||||
|
||||
Mod('tt',
|
||||
'frappy_psi.sea.SeaDrivable', '',
|
||||
io='sea_main',
|
||||
meaning=['temperature_regulation', 27],
|
||||
sea_object='tt',
|
||||
rel_paths=['tm', '.', 'set', 'dblctrl'],
|
||||
)
|
||||
|
||||
Mod('cc',
|
||||
'frappy_psi.sea.SeaReadable', '',
|
||||
io='sea_main',
|
||||
sea_object='cc',
|
||||
extra_modules=['h'],
|
||||
)
|
||||
|
||||
Mod('lev',
|
||||
'frappy_psi.sea.SeaReadable', '',
|
||||
io='sea_main',
|
||||
single_module='cc.h',
|
||||
)
|
||||
|
||||
Mod('nv',
|
||||
'frappy_psi.sea.SeaWritable', '',
|
||||
io='sea_main',
|
||||
sea_object='nv',
|
||||
)
|
||||
|
||||
Mod('ln2fill',
|
||||
'frappy_psi.sea.SeaWritable', '',
|
||||
io='sea_main',
|
||||
sea_object='ln2fill',
|
||||
)
|
||||
|
||||
Mod('hefill',
|
||||
'frappy_psi.sea.SeaWritable', '',
|
||||
io='sea_main',
|
||||
sea_object='hefill',
|
||||
)
|
||||
|
||||
Mod('hepump',
|
||||
'frappy_psi.sea.SeaWritable', '',
|
||||
io='sea_main',
|
||||
sea_object='hepump',
|
||||
)
|
||||
|
||||
Mod('hemot',
|
||||
'frappy_psi.sea.SeaDrivable', '',
|
||||
io='sea_main',
|
||||
sea_object='hemot',
|
||||
)
|
||||
|
||||
Mod('nvflow',
|
||||
'frappy_psi.sea.SeaReadable', '',
|
||||
io='sea_main',
|
||||
sea_object='nvflow',
|
||||
)
|
||||
|
||||
Mod('table',
|
||||
'frappy_psi.sea.SeaReadable', '',
|
||||
io='sea_main',
|
||||
sea_object='table',
|
||||
)
|
@ -12,9 +12,9 @@ Mod('sea_main',
|
||||
Mod('tt',
|
||||
'frappy_psi.sea.SeaDrivable', '',
|
||||
io='sea_main',
|
||||
meaning=['temperature_regulation', 20],
|
||||
meaning=['temperature_regulation', 27],
|
||||
sea_object='tt',
|
||||
rel_paths=['tm', 'set', 'dblctrl'],
|
||||
rel_paths=['tm', '.', 'set', 'dblctrl'],
|
||||
)
|
||||
|
||||
Mod('cc',
|
||||
|
@ -12,9 +12,9 @@ Mod('sea_main',
|
||||
Mod('tt',
|
||||
'frappy_psi.sea.SeaDrivable', '',
|
||||
io='sea_main',
|
||||
meaning=['temperature_regulation', 20],
|
||||
meaning=['temperature_regulation', 27],
|
||||
sea_object='tt',
|
||||
rel_paths=['tm', 'set', 'dblctrl'],
|
||||
rel_paths=['tm', '.', 'set', 'dblctrl'],
|
||||
)
|
||||
|
||||
Mod('cc',
|
||||
|
@ -12,9 +12,9 @@ Mod('sea_main',
|
||||
Mod('tt',
|
||||
'frappy_psi.sea.SeaDrivable', '',
|
||||
io='sea_main',
|
||||
meaning=['temperature_regulation', 20],
|
||||
meaning=['temperature_regulation', 27],
|
||||
sea_object='tt',
|
||||
rel_paths=['tm', 'set', 'dblctrl'],
|
||||
rel_paths=['tm', '.', 'set', 'dblctrl'],
|
||||
)
|
||||
|
||||
Mod('cc',
|
||||
|
@ -12,9 +12,9 @@ Mod('sea_main',
|
||||
Mod('tt',
|
||||
'frappy_psi.sea.SeaDrivable', '',
|
||||
io = 'sea_main',
|
||||
meaning=['temperature_regulation', 20],
|
||||
meaning=['temperature_regulation', 27],
|
||||
sea_object = 'tt',
|
||||
rel_paths=['tm', 'set', 'dblctrl'],
|
||||
rel_paths=['tm', '.', 'set', 'dblctrl'],
|
||||
)
|
||||
|
||||
Mod('cc',
|
||||
|
17
cfg/main/ori7test_cfg.py
Normal file
17
cfg/main/ori7test_cfg.py
Normal file
@ -0,0 +1,17 @@
|
||||
from frappy_psi.ccracks import Rack
|
||||
|
||||
Node('ori7test.psi.ch',
|
||||
'ORI7 test',
|
||||
'tcp://5000'
|
||||
)
|
||||
|
||||
rack = Rack(Mod)
|
||||
|
||||
with rack.lakeshore() as ls:
|
||||
ls.sensor('Ts', channel='C', calcurve='x186350')
|
||||
ls.loop('T', channel='B', calcurve='x174786')
|
||||
ls.heater('htr', '100W', 100)
|
||||
|
||||
rack.ccu(he=True, n2=True)
|
||||
|
||||
rack.hepump()
|
58
cfg/main/peltier_cfg.py
Normal file
58
cfg/main/peltier_cfg.py
Normal file
@ -0,0 +1,58 @@
|
||||
Node('peltier.psi.ch',
|
||||
'peltier test',
|
||||
'tcp://5000',
|
||||
)
|
||||
|
||||
Mod('tio',
|
||||
'frappy_psi.qnw.QnwIO',
|
||||
'connection for Quantum northwest',
|
||||
uri='tcp://ldm-fi-ts:3001',
|
||||
)
|
||||
|
||||
Mod('T',
|
||||
'frappy_psi.qnw.TemperatureLoopTC1',
|
||||
'holder temperature',
|
||||
channel='CT',
|
||||
io='tio',
|
||||
)
|
||||
|
||||
Mod('Th',
|
||||
'frappy_psi.qnw.SensorTC1',
|
||||
'heat exch. temperature',
|
||||
channel='HT',
|
||||
io='tio',
|
||||
)
|
||||
|
||||
Mod('wio',
|
||||
'frappy_psi.thermofisher.ThermFishIO',
|
||||
'connection for water bath',
|
||||
uri='tcp://ldm-fi-ts:3002',
|
||||
)
|
||||
|
||||
Mod('Tbath',
|
||||
'frappy_psi.thermofisher.TemperatureLoopA10',
|
||||
'water bath',
|
||||
io='wio',
|
||||
target=Param(max=100),
|
||||
tolerance=0.5,
|
||||
settling_time=20,
|
||||
)
|
||||
|
||||
Mod('lio', # the name of the module
|
||||
'frappy_demo.lakeshore.LakeshoreIO', # the class used for communication
|
||||
'communication to main controller', # a description
|
||||
uri="tcp://ldmcc02-ls:7777", # the serial connection
|
||||
)
|
||||
Mod('Ta',
|
||||
'frappy_demo.lakeshore.TemperatureSensor',
|
||||
'Sample Temperature',
|
||||
io='lio',
|
||||
channel='A', # the channel on the LakeShore for this module
|
||||
)
|
||||
Mod('Tb',
|
||||
'frappy_demo.lakeshore.TemperatureSensor',
|
||||
'Sample Temperature',
|
||||
io='lio',
|
||||
channel='B', # the channel on the LakeShore for this module
|
||||
)
|
||||
|
@ -12,9 +12,9 @@ Mod('sea_main',
|
||||
Mod('tt',
|
||||
'frappy_psi.sea.SeaDrivable', '',
|
||||
io='sea_main',
|
||||
meaning=['temperature_regulation', 20],
|
||||
meaning=['temperature_regulation', 27],
|
||||
sea_object='tt',
|
||||
rel_paths=['tm', 'set', 'dblctrl'],
|
||||
rel_paths=['tm', '.', 'set', 'dblctrl'],
|
||||
)
|
||||
|
||||
Mod('cc',
|
||||
|
@ -18,7 +18,7 @@ Mod('itc2',
|
||||
Mod('T_stat',
|
||||
'frappy_psi.mercury.TemperatureAutoFlow',
|
||||
'static heat exchanger temperature',
|
||||
meaning=['temperature_regulation', 20],
|
||||
meaning=['temperature_regulation', 27],
|
||||
output_module='htr_stat',
|
||||
needle_valve='p_stat',
|
||||
slot='DB6.T1',
|
||||
@ -168,6 +168,8 @@ Mod('htr_nvd',
|
||||
io='itc2',
|
||||
)
|
||||
|
||||
# Motor controller is not yet available!
|
||||
#
|
||||
#Mod('om_io',
|
||||
# 'frappy_psi.phytron.PhytronIO',
|
||||
# 'dom motor IO',
|
||||
|
14
cfg/sea/ah2550.addon.json
Normal file
14
cfg/sea/ah2550.addon.json
Normal file
@ -0,0 +1,14 @@
|
||||
{"cap": {"base": "/cap", "params": [{"path": "", "type": "none", "kids": 8},
|
||||
{"path": "send", "type": "text", "readonly": false, "cmd": "cap send", "visibility": 3},
|
||||
{"path": "status", "type": "text", "visibility": 3},
|
||||
{"path": "cap", "type": "float"},
|
||||
{"path": "loss", "type": "float"},
|
||||
{"path": "period", "type": "float", "readonly": false, "cmd": "cap period"},
|
||||
{"path": "V", "type": "float", "readonly": false, "cmd": "cap V"},
|
||||
{"path": "average", "type": "int", "readonly": false, "cmd": "cap average"}]}, "capslope": {"base": "/capslope", "params": [{"path": "", "type": "float", "kids": 6},
|
||||
{"path": "send", "type": "text", "readonly": false, "cmd": "capslope send", "visibility": 3},
|
||||
{"path": "status", "type": "text", "visibility": 3},
|
||||
{"path": "node", "type": "text", "readonly": false, "cmd": "capslope node"},
|
||||
{"path": "unit", "type": "float", "readonly": false, "cmd": "capslope unit", "description": "unit=60: mainunits/minutes, unit=1: mainunits/sec"},
|
||||
{"path": "ref", "type": "float", "readonly": false, "cmd": "capslope ref"},
|
||||
{"path": "buffersize", "type": "float", "readonly": false, "cmd": "capslope buffersize"}]}}
|
@ -18,22 +18,22 @@
|
||||
{"path": "t1", "type": "float", "readonly": false, "cmd": "run tt", "kids": 3},
|
||||
{"path": "t1/raw", "type": "float", "readonly": false, "cmd": "run tt"},
|
||||
{"path": "t1/curve", "type": "text", "readonly": false, "cmd": "tt t1/curve", "kids": 1},
|
||||
{"path": "t1/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt t1/curve/points"},
|
||||
{"path": "t1/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt t1/curve/points", "visibility": 3},
|
||||
{"path": "t1/valid", "type": "bool", "readonly": false, "cmd": "run tt"},
|
||||
{"path": "t2", "type": "float", "readonly": false, "cmd": "run tt", "kids": 3},
|
||||
{"path": "t2/raw", "type": "float", "readonly": false, "cmd": "run tt"},
|
||||
{"path": "t2/curve", "type": "text", "readonly": false, "cmd": "tt t2/curve", "kids": 1},
|
||||
{"path": "t2/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt t2/curve/points"},
|
||||
{"path": "t2/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt t2/curve/points", "visibility": 3},
|
||||
{"path": "t2/valid", "type": "bool", "readonly": false, "cmd": "run tt"},
|
||||
{"path": "t3", "type": "float", "readonly": false, "cmd": "run tt", "kids": 3},
|
||||
{"path": "t3/raw", "type": "float", "readonly": false, "cmd": "run tt"},
|
||||
{"path": "t3/curve", "type": "text", "readonly": false, "cmd": "tt t3/curve", "kids": 1},
|
||||
{"path": "t3/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt t3/curve/points"},
|
||||
{"path": "t3/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt t3/curve/points", "visibility": 3},
|
||||
{"path": "t3/valid", "type": "bool", "readonly": false, "cmd": "run tt"},
|
||||
{"path": "t4", "type": "float", "readonly": false, "cmd": "run tt", "kids": 3},
|
||||
{"path": "t4/raw", "type": "float", "readonly": false, "cmd": "run tt"},
|
||||
{"path": "t4/curve", "type": "text", "readonly": false, "cmd": "tt t4/curve", "kids": 1},
|
||||
{"path": "t4/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt t4/curve/points"},
|
||||
{"path": "t4/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt t4/curve/points", "visibility": 3},
|
||||
{"path": "t4/valid", "type": "bool", "readonly": false, "cmd": "run tt"},
|
||||
{"path": "tref", "type": "float", "readonly": false, "cmd": "run tt"},
|
||||
{"path": "tout", "type": "float", "readonly": false, "cmd": "run tt"},
|
||||
|
@ -269,7 +269,7 @@
|
||||
{"path": "custompar", "type": "float", "readonly": false, "cmd": "hemot custompar"}]},
|
||||
|
||||
"mf": {"base": "/mf", "params": [
|
||||
{"path": "", "type": "float", "kids": 26},
|
||||
{"path": "", "type": "float", "cmd": "run mf", "kids": 26},
|
||||
{"path": "persmode", "type": "int", "readonly": false, "cmd": "mf persmode"},
|
||||
{"path": "perswitch", "type": "int"},
|
||||
{"path": "nowait", "type": "int", "readonly": false, "cmd": "mf nowait"},
|
||||
|
@ -1,4 +1,5 @@
|
||||
{"tt": {"base": "/tt", "params": [{"path": "", "type": "float", "readonly": false, "cmd": "run tt", "description": "tt", "kids": 18},
|
||||
{"tt": {"base": "/tt", "params": [
|
||||
{"path": "", "type": "float", "readonly": false, "cmd": "run tt", "description": "tt", "kids": 18},
|
||||
{"path": "send", "type": "text", "readonly": false, "cmd": "tt send", "visibility": 3},
|
||||
{"path": "status", "type": "text", "visibility": 3},
|
||||
{"path": "is_running", "type": "int", "readonly": false, "cmd": "tt is_running", "visibility": 3},
|
||||
@ -85,7 +86,10 @@
|
||||
{"path": "setsamp/integ", "type": "float", "readonly": false, "cmd": "tt setsamp/integ", "description": "bigger means faster"},
|
||||
{"path": "setsamp/deriv", "type": "float", "readonly": false, "cmd": "tt setsamp/deriv"},
|
||||
{"path": "display", "type": "text", "readonly": false, "cmd": "tt display"},
|
||||
{"path": "remote", "type": "bool"}]}, "cc": {"base": "/cc", "params": [{"path": "", "type": "bool", "kids": 96},
|
||||
{"path": "remote", "type": "bool"}]},
|
||||
|
||||
"cc": {"base": "/cc", "params": [
|
||||
{"path": "", "type": "bool", "kids": 96},
|
||||
{"path": "send", "type": "text", "readonly": false, "cmd": "cc send", "visibility": 3},
|
||||
{"path": "status", "type": "text", "visibility": 3},
|
||||
{"path": "autodevice", "type": "bool", "readonly": false, "cmd": "cc autodevice"},
|
||||
@ -181,7 +185,10 @@
|
||||
{"path": "tm", "type": "float", "visibility": 3},
|
||||
{"path": "tv", "type": "float", "visibility": 3},
|
||||
{"path": "tq", "type": "float", "visibility": 3},
|
||||
{"path": "bdl", "type": "float", "readonly": false, "cmd": "cc bdl"}]}, "nv": {"base": "/nv", "params": [{"path": "", "type": "enum", "enum": {"fixed": 0, "controlled": 1, "automatic": 2, "close": 3, "open": 4}, "readonly": false, "cmd": "nv", "kids": 11},
|
||||
{"path": "bdl", "type": "float", "readonly": false, "cmd": "cc bdl"}]},
|
||||
|
||||
"nv": {"base": "/nv", "params": [
|
||||
{"path": "", "type": "enum", "enum": {"fixed": 0, "controlled": 1, "automatic": 2, "close": 3, "open": 4}, "readonly": false, "cmd": "nv", "kids": 11},
|
||||
{"path": "send", "type": "text", "readonly": false, "cmd": "nv send", "visibility": 3},
|
||||
{"path": "status", "type": "text", "visibility": 3},
|
||||
{"path": "motstat", "type": "enum", "enum": {"idle": 0, "opening": 1, "closing": 2, "opened": 3, "closed": 4, "no_motor": 5}},
|
||||
@ -231,7 +238,11 @@
|
||||
{"path": "autoflow/flowtarget", "type": "float"},
|
||||
{"path": "calib", "type": "none", "kids": 2},
|
||||
{"path": "calib/ln_per_min_per_mbar", "type": "float", "readonly": false, "cmd": "nv calib/ln_per_min_per_mbar"},
|
||||
{"path": "calib/mbar_offset", "type": "float", "readonly": false, "cmd": "nv calib/mbar_offset"}]}, "hepump": {"base": "/hepump", "params": [{"path": "", "type": "enum", "enum": {"neodry": 8, "xds35_auto": 0, "xds35_manual": 1, "sv65": 2, "other": 3, "no": -1}, "readonly": false, "cmd": "hepump", "description": "xds35: scroll pump, sv65: leybold", "kids": 9},
|
||||
{"path": "calib/mbar_offset", "type": "float", "readonly": false, "cmd": "nv calib/mbar_offset"}]},
|
||||
|
||||
|
||||
"hepump": {"base": "/hepump", "params": [
|
||||
{"path": "", "type": "enum", "enum": {"neodry": 8, "xds35_auto": 0, "xds35_manual": 1, "sv65": 2, "other": 3, "no": -1}, "readonly": false, "cmd": "hepump", "description": "xds35: scroll pump, sv65: leybold", "kids": 9},
|
||||
{"path": "send", "type": "text", "readonly": false, "cmd": "hepump send", "visibility": 3},
|
||||
{"path": "status", "type": "text", "visibility": 3},
|
||||
{"path": "running", "type": "bool", "readonly": false, "cmd": "hepump running"},
|
||||
@ -239,7 +250,10 @@
|
||||
{"path": "auto", "type": "bool", "readonly": false, "cmd": "hepump auto"},
|
||||
{"path": "valve", "type": "enum", "enum": {"closed": 0, "closing": 1, "opening": 2, "opened": 3, "undefined": 4}, "readonly": false, "cmd": "hepump valve"},
|
||||
{"path": "eco_t_lim", "type": "float", "readonly": false, "cmd": "hepump eco_t_lim", "description": "switch off eco mode when T_set < eco_t_lim and T < eco_t_lim * 2"},
|
||||
{"path": "calib", "type": "float", "readonly": false, "cmd": "hepump calib", "visibility": 3}]}, "hemot": {"base": "/hepump/hemot", "params": [{"path": "", "type": "float", "readonly": false, "cmd": "run hemot", "kids": 30},
|
||||
{"path": "calib", "type": "float", "readonly": false, "cmd": "hepump calib", "visibility": 3}]},
|
||||
|
||||
"hemot": {"base": "/hepump/hemot", "params": [
|
||||
{"path": "", "type": "float", "readonly": false, "cmd": "run hemot", "kids": 30},
|
||||
{"path": "send", "type": "text", "readonly": false, "cmd": "hemot send", "visibility": 3},
|
||||
{"path": "status", "type": "text", "visibility": 3},
|
||||
{"path": "is_running", "type": "int", "readonly": false, "cmd": "hemot is_running", "visibility": 3},
|
||||
@ -270,7 +284,8 @@
|
||||
{"path": "eeprom", "type": "enum", "enum": {"ok": 0, "dirty": 1, "save": 2, "load": 3}, "readonly": false, "cmd": "hemot eeprom"},
|
||||
{"path": "customadr", "type": "text", "readonly": false, "cmd": "hemot customadr"},
|
||||
{"path": "custompar", "type": "float", "readonly": false, "cmd": "hemot custompar"}]},
|
||||
"ln2fill": {"base": "/ln2fill", "params": [
|
||||
"
|
||||
ln2fill": {"base": "/ln2fill", "params": [
|
||||
{"path": "", "type": "enum", "enum": {"watching": 0, "fill": 1, "inactive": 2}, "readonly": false, "cmd": "ln2fill", "kids": 14},
|
||||
{"path": "send", "type": "text", "readonly": false, "cmd": "ln2fill send", "visibility": 3},
|
||||
{"path": "status", "type": "text", "visibility": 3},
|
||||
@ -285,7 +300,10 @@
|
||||
{"path": "maxholdhours", "type": "float", "readonly": false, "cmd": "ln2fill maxholdhours"},
|
||||
{"path": "tolerance", "type": "float", "readonly": false, "cmd": "ln2fill tolerance"},
|
||||
{"path": "badreadingminutes", "type": "float", "readonly": false, "cmd": "ln2fill badreadingminutes"},
|
||||
{"path": "tubecoolingminutes", "type": "float", "readonly": false, "cmd": "ln2fill tubecoolingminutes"}]}, "hefill": {"base": "/hefill", "params": [{"path": "", "type": "enum", "enum": {"watching": 0, "fill": 1, "inactive": 2}, "readonly": false, "cmd": "hefill", "kids": 16},
|
||||
{"path": "tubecoolingminutes", "type": "float", "readonly": false, "cmd": "ln2fill tubecoolingminutes"}]},
|
||||
|
||||
"hefill": {"base": "/hefill", "params": [
|
||||
{"path": "", "type": "enum", "enum": {"watching": 0, "fill": 1, "inactive": 2}, "readonly": false, "cmd": "hefill", "kids": 16},
|
||||
{"path": "send", "type": "text", "readonly": false, "cmd": "hefill send", "visibility": 3},
|
||||
{"path": "status", "type": "text", "visibility": 3},
|
||||
{"path": "state", "type": "text"},
|
||||
@ -301,11 +319,17 @@
|
||||
{"path": "badreadingminutes", "type": "float", "readonly": false, "cmd": "hefill badreadingminutes"},
|
||||
{"path": "tubecoolingminutes", "type": "float", "readonly": false, "cmd": "hefill tubecoolingminutes"},
|
||||
{"path": "vessellimit", "type": "float", "readonly": false, "cmd": "hefill vessellimit"},
|
||||
{"path": "vext", "type": "float"}]}, "lev": {"base": "/lev", "params": [{"path": "", "type": "float", "kids": 4},
|
||||
{"path": "vext", "type": "float"}]},
|
||||
|
||||
"lev": {"base": "/lev", "params": [
|
||||
{"path": "", "type": "float", "kids": 4},
|
||||
{"path": "send", "type": "text", "readonly": false, "cmd": "lev send", "visibility": 3},
|
||||
{"path": "status", "type": "text", "visibility": 3},
|
||||
{"path": "mode", "type": "enum", "enum": {"slow": 0, "fast (switches to slow automatically after filling)": 1}, "readonly": false, "cmd": "lev mode"},
|
||||
{"path": "n2", "type": "float"}]}, "mf": {"base": "/mf", "params": [{"path": "", "type": "float", "kids": 26},
|
||||
{"path": "n2", "type": "float"}]},
|
||||
|
||||
"mf": {"base": "/mf", "params": [
|
||||
{"path": "", "type": "float", "cmd": "run mf", "kids": 26},
|
||||
{"path": "persmode", "type": "int", "readonly": false, "cmd": "mf persmode"},
|
||||
{"path": "perswitch", "type": "int"},
|
||||
{"path": "nowait", "type": "int", "readonly": false, "cmd": "mf nowait"},
|
||||
@ -330,7 +354,10 @@
|
||||
{"path": "driver", "type": "text", "visibility": 3},
|
||||
{"path": "creationCmd", "type": "text", "visibility": 3},
|
||||
{"path": "targetValue", "type": "float"},
|
||||
{"path": "status", "type": "text", "readonly": false, "cmd": "mf status", "visibility": 3}]}, "tcoil": {"base": "/tcoil", "params": [{"path": "", "type": "float", "kids": 11},
|
||||
{"path": "status", "type": "text", "readonly": false, "cmd": "mf status", "visibility": 3}]},
|
||||
|
||||
"tcoil": {"base": "/tcoil", "params": [
|
||||
{"path": "", "type": "float", "kids": 11},
|
||||
{"path": "send", "type": "text", "readonly": false, "cmd": "tcoil send", "visibility": 3},
|
||||
{"path": "status", "type": "text", "visibility": 3},
|
||||
{"path": "excitation", "type": "float", "readonly": false, "cmd": "tcoil excitation", "visibility": 3},
|
||||
@ -338,40 +365,43 @@
|
||||
{"path": "td/enable", "type": "bool", "readonly": false, "cmd": "tcoil td/enable"},
|
||||
{"path": "td/r", "type": "float"},
|
||||
{"path": "td/curve", "type": "text", "readonly": false, "cmd": "tcoil td/curve", "kids": 3},
|
||||
{"path": "td/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil td/curve/adjust"},
|
||||
{"path": "td/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil td/curve/points"},
|
||||
{"path": "td/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil td/curve/cpoints"},
|
||||
{"path": "td/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil td/curve/adjust", "visibility": 3},
|
||||
{"path": "td/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil td/curve/points", "visibility": 3},
|
||||
{"path": "td/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil td/curve/cpoints", "visibility": 3},
|
||||
{"path": "tc", "type": "float", "visibility": 3, "kids": 3},
|
||||
{"path": "tc/enable", "type": "bool", "readonly": false, "cmd": "tcoil tc/enable"},
|
||||
{"path": "tc/r", "type": "float"},
|
||||
{"path": "tc/curve", "type": "text", "readonly": false, "cmd": "tcoil tc/curve", "kids": 3},
|
||||
{"path": "tc/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil tc/curve/adjust"},
|
||||
{"path": "tc/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil tc/curve/points"},
|
||||
{"path": "tc/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil tc/curve/cpoints"},
|
||||
{"path": "tc/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil tc/curve/adjust", "visibility": 3},
|
||||
{"path": "tc/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil tc/curve/points", "visibility": 3},
|
||||
{"path": "tc/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil tc/curve/cpoints", "visibility": 3},
|
||||
{"path": "tb", "type": "float", "visibility": 3, "kids": 3},
|
||||
{"path": "tb/enable", "type": "bool", "readonly": false, "cmd": "tcoil tb/enable"},
|
||||
{"path": "tb/r", "type": "float"},
|
||||
{"path": "tb/curve", "type": "text", "readonly": false, "cmd": "tcoil tb/curve", "kids": 3},
|
||||
{"path": "tb/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil tb/curve/adjust"},
|
||||
{"path": "tb/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil tb/curve/points"},
|
||||
{"path": "tb/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil tb/curve/cpoints"},
|
||||
{"path": "tb/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil tb/curve/adjust", "visibility": 3},
|
||||
{"path": "tb/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil tb/curve/points", "visibility": 3},
|
||||
{"path": "tb/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil tb/curve/cpoints", "visibility": 3},
|
||||
{"path": "ta", "type": "float", "visibility": 3, "kids": 3},
|
||||
{"path": "ta/enable", "type": "bool", "readonly": false, "cmd": "tcoil ta/enable"},
|
||||
{"path": "ta/r", "type": "float"},
|
||||
{"path": "ta/curve", "type": "text", "readonly": false, "cmd": "tcoil ta/curve", "kids": 3},
|
||||
{"path": "ta/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil ta/curve/adjust"},
|
||||
{"path": "ta/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil ta/curve/points"},
|
||||
{"path": "ta/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil ta/curve/cpoints"},
|
||||
{"path": "ta/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil ta/curve/adjust", "visibility": 3},
|
||||
{"path": "ta/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil ta/curve/points", "visibility": 3},
|
||||
{"path": "ta/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil ta/curve/cpoints", "visibility": 3},
|
||||
{"path": "ref", "type": "float", "visibility": 3, "kids": 3},
|
||||
{"path": "ref/enable", "type": "bool", "readonly": false, "cmd": "tcoil ref/enable"},
|
||||
{"path": "ref/r", "type": "float"},
|
||||
{"path": "ref/curve", "type": "text", "readonly": false, "cmd": "tcoil ref/curve", "kids": 3},
|
||||
{"path": "ref/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil ref/curve/adjust"},
|
||||
{"path": "ref/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil ref/curve/points"},
|
||||
{"path": "ref/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil ref/curve/cpoints"},
|
||||
{"path": "ref/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil ref/curve/adjust", "visibility": 3},
|
||||
{"path": "ref/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil ref/curve/points", "visibility": 3},
|
||||
{"path": "ref/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil ref/curve/cpoints", "visibility": 3},
|
||||
{"path": "ext", "type": "float", "visibility": 3},
|
||||
{"path": "com", "type": "float", "visibility": 3},
|
||||
{"path": "gnd", "type": "float", "visibility": 3}]}, "table": {"base": "/table", "params": [{"path": "", "type": "none", "kids": 17},
|
||||
{"path": "gnd", "type": "float", "visibility": 3}]},
|
||||
|
||||
"table": {"base": "/table", "params": [
|
||||
{"path": "", "type": "none", "kids": 17},
|
||||
{"path": "send", "type": "text", "readonly": false, "cmd": "table send", "visibility": 3},
|
||||
{"path": "status", "type": "text", "visibility": 3},
|
||||
{"path": "fix_tt_set_prop", "type": "bool", "readonly": false, "cmd": "table fix_tt_set_prop"},
|
||||
@ -388,8 +418,14 @@
|
||||
{"path": "tbl_tt_dblctrl_prop_up", "type": "text", "readonly": false, "cmd": "table tbl_tt_dblctrl_prop_up", "description": "enter value pair separated with colon T1:par1 T2:par2 ..."},
|
||||
{"path": "fix_tt_dblctrl_prop_lo", "type": "bool", "readonly": false, "cmd": "table fix_tt_dblctrl_prop_lo"},
|
||||
{"path": "val_tt_dblctrl_prop_lo", "type": "float"},
|
||||
{"path": "tbl_tt_dblctrl_prop_lo", "type": "text", "readonly": false, "cmd": "table tbl_tt_dblctrl_prop_lo", "description": "enter value pair separated with colon T1:par1 T2:par2 ..."}]}, "ccu2": {"base": "/sics/ccu2", "params": [{"path": "", "type": "text", "readonly": false, "cmd": "ccu2", "kids": 23},
|
||||
{"path": "tasks", "type": "none", "visibility": 3}]}, "lnv": {"base": "/lnv", "params": [{"path": "", "type": "enum", "enum": {"off": 5, "fixed": 0, "controlling": 1, "close": 3, "open": 4}, "readonly": false, "cmd": "lnv", "kids": 12},
|
||||
{"path": "tbl_tt_dblctrl_prop_lo", "type": "text", "readonly": false, "cmd": "table tbl_tt_dblctrl_prop_lo", "description": "enter value pair separated with colon T1:par1 T2:par2 ..."}]},
|
||||
|
||||
"ccu2": {"base": "/sics/ccu2", "params": [
|
||||
{"path": "", "type": "text", "readonly": false, "cmd": "ccu2", "kids": 23},
|
||||
{"path": "tasks", "type": "none", "visibility": 3}]},
|
||||
|
||||
"lnv": {"base": "/lnv", "params": [
|
||||
{"path": "", "type": "enum", "enum": {"off": 5, "fixed": 0, "controlling": 1, "close": 3, "open": 4}, "readonly": false, "cmd": "lnv", "kids": 12},
|
||||
{"path": "send", "type": "text", "readonly": false, "cmd": "lnv send", "visibility": 3},
|
||||
{"path": "status", "type": "text", "visibility": 3},
|
||||
{"path": "set", "type": "float", "readonly": false, "cmd": "lnv set"},
|
||||
@ -427,7 +463,10 @@
|
||||
{"path": "autoflow/difmax", "type": "float"},
|
||||
{"path": "autoflow/setmin", "type": "float"},
|
||||
{"path": "autoflow/setmax", "type": "float"},
|
||||
{"path": "autoflow/flowtarget", "type": "float"}]}, "lpr": {"base": "/lpr", "params": [{"path": "", "type": "float", "readonly": false, "cmd": "run lpr", "description": "lpr", "kids": 28},
|
||||
{"path": "autoflow/flowtarget", "type": "float"}]},
|
||||
|
||||
"lpr": {"base": "/lpr", "params": [
|
||||
{"path": "", "type": "float", "readonly": false, "cmd": "run lpr", "description": "lpr", "kids": 28},
|
||||
{"path": "send", "type": "text", "readonly": false, "cmd": "lpr send", "visibility": 3},
|
||||
{"path": "status", "type": "text", "visibility": 3},
|
||||
{"path": "is_running", "type": "int", "readonly": false, "cmd": "lpr is_running", "visibility": 3},
|
||||
@ -455,12 +494,13 @@
|
||||
{"path": "running", "type": "int"},
|
||||
{"path": "tolerance", "type": "float", "readonly": false, "cmd": "lpr tolerance"},
|
||||
{"path": "maxwait", "type": "float", "readonly": false, "cmd": "lpr maxwait"},
|
||||
{"path": "settle", "type": "float", "readonly": false, "cmd": "lpr settle"}]}, "lambdawatch": {"base": "/lambdawatch", "params": [{"path": "", "type": "float", "kids": 6},
|
||||
{"path": "settle", "type": "float", "readonly": false, "cmd": "lpr settle"}]},
|
||||
|
||||
"lambdawatch": {"base": "/lambdawatch", "params": [
|
||||
{"path": "", "type": "float", "kids": 6},
|
||||
{"path": "send", "type": "text", "readonly": false, "cmd": "lambdawatch send", "visibility": 3},
|
||||
{"path": "status", "type": "text", "visibility": 3},
|
||||
{"path": "safefield", "type": "float", "readonly": false, "cmd": "lambdawatch safefield"},
|
||||
{"path": "maxfield", "type": "float", "readonly": false, "cmd": "lambdawatch maxfield"},
|
||||
{"path": "safetemp", "type": "float", "readonly": false, "cmd": "lambdawatch safetemp"},
|
||||
{"path": "coiltemp", "type": "text", "readonly": false, "cmd": "lambdawatch coiltemp"}]}, "prep0": {"base": "/prep0", "params": [{"path": "", "type": "text", "readonly": false, "cmd": "prep0", "kids": 2},
|
||||
{"path": "send", "type": "text", "readonly": false, "cmd": "prep0 send", "visibility": 3},
|
||||
{"path": "status", "type": "text", "visibility": 3}]}}
|
||||
{"path": "coiltemp", "type": "text", "readonly": false, "cmd": "lambdawatch coiltemp"}]}}
|
||||
|
@ -371,37 +371,37 @@
|
||||
{"path": "td/enable", "type": "bool", "readonly": false, "cmd": "tcoil td/enable"},
|
||||
{"path": "td/r", "type": "float"},
|
||||
{"path": "td/curve", "type": "text", "readonly": false, "cmd": "tcoil td/curve", "kids": 3},
|
||||
{"path": "td/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil td/curve/adjust"},
|
||||
{"path": "td/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil td/curve/points"},
|
||||
{"path": "td/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil td/curve/cpoints"},
|
||||
{"path": "td/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil td/curve/adjust", "visibility": 3},
|
||||
{"path": "td/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil td/curve/points", "visibility": 3},
|
||||
{"path": "td/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil td/curve/cpoints", "visibility": 3},
|
||||
{"path": "tb", "type": "float", "visibility": 3, "kids": 3},
|
||||
{"path": "tb/enable", "type": "bool", "readonly": false, "cmd": "tcoil tb/enable"},
|
||||
{"path": "tb/r", "type": "float"},
|
||||
{"path": "tb/curve", "type": "text", "readonly": false, "cmd": "tcoil tb/curve", "kids": 3},
|
||||
{"path": "tb/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil tb/curve/adjust"},
|
||||
{"path": "tb/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil tb/curve/points"},
|
||||
{"path": "tb/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil tb/curve/cpoints"},
|
||||
{"path": "tb/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil tb/curve/adjust", "visibility": 3},
|
||||
{"path": "tb/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil tb/curve/points", "visibility": 3},
|
||||
{"path": "tb/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil tb/curve/cpoints", "visibility": 3},
|
||||
{"path": "tc", "type": "float", "visibility": 3, "kids": 3},
|
||||
{"path": "tc/enable", "type": "bool", "readonly": false, "cmd": "tcoil tc/enable"},
|
||||
{"path": "tc/r", "type": "float"},
|
||||
{"path": "tc/curve", "type": "text", "readonly": false, "cmd": "tcoil tc/curve", "kids": 3},
|
||||
{"path": "tc/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil tc/curve/adjust"},
|
||||
{"path": "tc/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil tc/curve/points"},
|
||||
{"path": "tc/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil tc/curve/cpoints"},
|
||||
{"path": "tc/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil tc/curve/adjust", "visibility": 3},
|
||||
{"path": "tc/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil tc/curve/points", "visibility": 3},
|
||||
{"path": "tc/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil tc/curve/cpoints", "visibility": 3},
|
||||
{"path": "ta", "type": "float", "visibility": 3, "kids": 3},
|
||||
{"path": "ta/enable", "type": "bool", "readonly": false, "cmd": "tcoil ta/enable"},
|
||||
{"path": "ta/r", "type": "float"},
|
||||
{"path": "ta/curve", "type": "text", "readonly": false, "cmd": "tcoil ta/curve", "kids": 3},
|
||||
{"path": "ta/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil ta/curve/adjust"},
|
||||
{"path": "ta/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil ta/curve/points"},
|
||||
{"path": "ta/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil ta/curve/cpoints"},
|
||||
{"path": "ta/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil ta/curve/adjust", "visibility": 3},
|
||||
{"path": "ta/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil ta/curve/points", "visibility": 3},
|
||||
{"path": "ta/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil ta/curve/cpoints", "visibility": 3},
|
||||
{"path": "ref", "type": "float", "visibility": 3, "kids": 3},
|
||||
{"path": "ref/enable", "type": "bool", "readonly": false, "cmd": "tcoil ref/enable"},
|
||||
{"path": "ref/r", "type": "float"},
|
||||
{"path": "ref/curve", "type": "text", "readonly": false, "cmd": "tcoil ref/curve", "kids": 3},
|
||||
{"path": "ref/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil ref/curve/adjust"},
|
||||
{"path": "ref/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil ref/curve/points"},
|
||||
{"path": "ref/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil ref/curve/cpoints"},
|
||||
{"path": "ref/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil ref/curve/adjust", "visibility": 3},
|
||||
{"path": "ref/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil ref/curve/points", "visibility": 3},
|
||||
{"path": "ref/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil ref/curve/cpoints", "visibility": 3},
|
||||
{"path": "ext", "type": "float", "visibility": 3},
|
||||
{"path": "com", "type": "float", "visibility": 3},
|
||||
{"path": "gnd", "type": "float", "visibility": 3}]},
|
||||
|
@ -371,37 +371,37 @@
|
||||
{"path": "ta/enable", "type": "bool", "readonly": false, "cmd": "tcoil ta/enable"},
|
||||
{"path": "ta/r", "type": "float"},
|
||||
{"path": "ta/curve", "type": "text", "readonly": false, "cmd": "tcoil ta/curve", "kids": 3},
|
||||
{"path": "ta/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil ta/curve/adjust"},
|
||||
{"path": "ta/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil ta/curve/points"},
|
||||
{"path": "ta/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil ta/curve/cpoints"},
|
||||
{"path": "ta/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil ta/curve/adjust", "visibility": 3},
|
||||
{"path": "ta/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil ta/curve/points", "visibility": 3},
|
||||
{"path": "ta/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil ta/curve/cpoints", "visibility": 3},
|
||||
{"path": "tb", "type": "float", "kids": 3},
|
||||
{"path": "tb/enable", "type": "bool", "readonly": false, "cmd": "tcoil tb/enable"},
|
||||
{"path": "tb/r", "type": "float"},
|
||||
{"path": "tb/curve", "type": "text", "readonly": false, "cmd": "tcoil tb/curve", "kids": 3},
|
||||
{"path": "tb/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil tb/curve/adjust"},
|
||||
{"path": "tb/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil tb/curve/points"},
|
||||
{"path": "tb/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil tb/curve/cpoints"},
|
||||
{"path": "tb/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil tb/curve/adjust", "visibility": 3},
|
||||
{"path": "tb/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil tb/curve/points", "visibility": 3},
|
||||
{"path": "tb/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil tb/curve/cpoints", "visibility": 3},
|
||||
{"path": "td", "type": "float", "visibility": 3, "kids": 3},
|
||||
{"path": "td/enable", "type": "bool", "readonly": false, "cmd": "tcoil td/enable"},
|
||||
{"path": "td/r", "type": "float"},
|
||||
{"path": "td/curve", "type": "text", "readonly": false, "cmd": "tcoil td/curve", "kids": 3},
|
||||
{"path": "td/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil td/curve/adjust"},
|
||||
{"path": "td/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil td/curve/points"},
|
||||
{"path": "td/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil td/curve/cpoints"},
|
||||
{"path": "td/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil td/curve/adjust", "visibility": 3},
|
||||
{"path": "td/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil td/curve/points", "visibility": 3},
|
||||
{"path": "td/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil td/curve/cpoints", "visibility": 3},
|
||||
{"path": "ref", "type": "float", "visibility": 3, "kids": 3},
|
||||
{"path": "ref/enable", "type": "bool", "readonly": false, "cmd": "tcoil ref/enable"},
|
||||
{"path": "ref/r", "type": "float"},
|
||||
{"path": "ref/curve", "type": "text", "readonly": false, "cmd": "tcoil ref/curve", "kids": 3},
|
||||
{"path": "ref/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil ref/curve/adjust"},
|
||||
{"path": "ref/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil ref/curve/points"},
|
||||
{"path": "ref/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil ref/curve/cpoints"},
|
||||
{"path": "ref/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil ref/curve/adjust", "visibility": 3},
|
||||
{"path": "ref/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil ref/curve/points", "visibility": 3},
|
||||
{"path": "ref/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil ref/curve/cpoints", "visibility": 3},
|
||||
{"path": "tc", "type": "float", "visibility": 3, "kids": 3},
|
||||
{"path": "tc/enable", "type": "bool", "readonly": false, "cmd": "tcoil tc/enable"},
|
||||
{"path": "tc/r", "type": "float"},
|
||||
{"path": "tc/curve", "type": "text", "readonly": false, "cmd": "tcoil tc/curve", "kids": 3},
|
||||
{"path": "tc/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil tc/curve/adjust"},
|
||||
{"path": "tc/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil tc/curve/points"},
|
||||
{"path": "tc/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil tc/curve/cpoints"},
|
||||
{"path": "tc/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil tc/curve/adjust", "visibility": 3},
|
||||
{"path": "tc/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil tc/curve/points", "visibility": 3},
|
||||
{"path": "tc/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil tc/curve/cpoints", "visibility": 3},
|
||||
{"path": "ext", "type": "float", "visibility": 3},
|
||||
{"path": "com", "type": "float", "visibility": 3},
|
||||
{"path": "gnd", "type": "float", "visibility": 3}]},
|
||||
|
@ -370,37 +370,37 @@
|
||||
{"path": "ta/enable", "type": "bool", "readonly": false, "cmd": "tcoil ta/enable"},
|
||||
{"path": "ta/r", "type": "float"},
|
||||
{"path": "ta/curve", "type": "text", "readonly": false, "cmd": "tcoil ta/curve", "kids": 3},
|
||||
{"path": "ta/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil ta/curve/adjust"},
|
||||
{"path": "ta/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil ta/curve/points"},
|
||||
{"path": "ta/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil ta/curve/cpoints"},
|
||||
{"path": "ta/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil ta/curve/adjust", "visibility": 3},
|
||||
{"path": "ta/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil ta/curve/points", "visibility": 3},
|
||||
{"path": "ta/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil ta/curve/cpoints", "visibility": 3},
|
||||
{"path": "tb", "type": "float", "kids": 3},
|
||||
{"path": "tb/enable", "type": "bool", "readonly": false, "cmd": "tcoil tb/enable"},
|
||||
{"path": "tb/r", "type": "float"},
|
||||
{"path": "tb/curve", "type": "text", "readonly": false, "cmd": "tcoil tb/curve", "kids": 3},
|
||||
{"path": "tb/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil tb/curve/adjust"},
|
||||
{"path": "tb/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil tb/curve/points"},
|
||||
{"path": "tb/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil tb/curve/cpoints"},
|
||||
{"path": "tb/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil tb/curve/adjust", "visibility": 3},
|
||||
{"path": "tb/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil tb/curve/points", "visibility": 3},
|
||||
{"path": "tb/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil tb/curve/cpoints", "visibility": 3},
|
||||
{"path": "td", "type": "float", "visibility": 3, "kids": 3},
|
||||
{"path": "td/enable", "type": "bool", "readonly": false, "cmd": "tcoil td/enable"},
|
||||
{"path": "td/r", "type": "float"},
|
||||
{"path": "td/curve", "type": "text", "readonly": false, "cmd": "tcoil td/curve", "kids": 3},
|
||||
{"path": "td/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil td/curve/adjust"},
|
||||
{"path": "td/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil td/curve/points"},
|
||||
{"path": "td/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil td/curve/cpoints"},
|
||||
{"path": "td/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil td/curve/adjust", "visibility": 3},
|
||||
{"path": "td/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil td/curve/points", "visibility": 3},
|
||||
{"path": "td/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil td/curve/cpoints", "visibility": 3},
|
||||
{"path": "ref", "type": "float", "visibility": 3, "kids": 3},
|
||||
{"path": "ref/enable", "type": "bool", "readonly": false, "cmd": "tcoil ref/enable"},
|
||||
{"path": "ref/r", "type": "float"},
|
||||
{"path": "ref/curve", "type": "text", "readonly": false, "cmd": "tcoil ref/curve", "kids": 3},
|
||||
{"path": "ref/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil ref/curve/adjust"},
|
||||
{"path": "ref/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil ref/curve/points"},
|
||||
{"path": "ref/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil ref/curve/cpoints"},
|
||||
{"path": "ref/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil ref/curve/adjust", "visibility": 3},
|
||||
{"path": "ref/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil ref/curve/points", "visibility": 3},
|
||||
{"path": "ref/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil ref/curve/cpoints", "visibility": 3},
|
||||
{"path": "tc", "type": "float", "visibility": 3, "kids": 3},
|
||||
{"path": "tc/enable", "type": "bool", "readonly": false, "cmd": "tcoil tc/enable"},
|
||||
{"path": "tc/r", "type": "float"},
|
||||
{"path": "tc/curve", "type": "text", "readonly": false, "cmd": "tcoil tc/curve", "kids": 3},
|
||||
{"path": "tc/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil tc/curve/adjust"},
|
||||
{"path": "tc/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil tc/curve/points"},
|
||||
{"path": "tc/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil tc/curve/cpoints"},
|
||||
{"path": "tc/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil tc/curve/adjust", "visibility": 3},
|
||||
{"path": "tc/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil tc/curve/points", "visibility": 3},
|
||||
{"path": "tc/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil tc/curve/cpoints", "visibility": 3},
|
||||
{"path": "ext", "type": "float", "visibility": 3},
|
||||
{"path": "com", "type": "float", "visibility": 3},
|
||||
{"path": "gnd", "type": "float", "visibility": 3}]},
|
||||
|
@ -371,37 +371,37 @@
|
||||
{"path": "ta/enable", "type": "bool", "readonly": false, "cmd": "tcoil ta/enable"},
|
||||
{"path": "ta/r", "type": "float"},
|
||||
{"path": "ta/curve", "type": "text", "readonly": false, "cmd": "tcoil ta/curve", "kids": 3},
|
||||
{"path": "ta/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil ta/curve/adjust"},
|
||||
{"path": "ta/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil ta/curve/points"},
|
||||
{"path": "ta/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil ta/curve/cpoints"},
|
||||
{"path": "ta/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil ta/curve/adjust", "visibility": 3},
|
||||
{"path": "ta/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil ta/curve/points", "visibility": 3},
|
||||
{"path": "ta/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil ta/curve/cpoints", "visibility": 3},
|
||||
{"path": "tb", "type": "float", "kids": 3},
|
||||
{"path": "tb/enable", "type": "bool", "readonly": false, "cmd": "tcoil tb/enable"},
|
||||
{"path": "tb/r", "type": "float"},
|
||||
{"path": "tb/curve", "type": "text", "readonly": false, "cmd": "tcoil tb/curve", "kids": 3},
|
||||
{"path": "tb/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil tb/curve/adjust"},
|
||||
{"path": "tb/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil tb/curve/points"},
|
||||
{"path": "tb/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil tb/curve/cpoints"},
|
||||
{"path": "tb/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil tb/curve/adjust", "visibility": 3},
|
||||
{"path": "tb/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil tb/curve/points", "visibility": 3},
|
||||
{"path": "tb/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil tb/curve/cpoints", "visibility": 3},
|
||||
{"path": "td", "type": "float", "visibility": 3, "kids": 3},
|
||||
{"path": "td/enable", "type": "bool", "readonly": false, "cmd": "tcoil td/enable"},
|
||||
{"path": "td/r", "type": "float"},
|
||||
{"path": "td/curve", "type": "text", "readonly": false, "cmd": "tcoil td/curve", "kids": 3},
|
||||
{"path": "td/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil td/curve/adjust"},
|
||||
{"path": "td/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil td/curve/points"},
|
||||
{"path": "td/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil td/curve/cpoints"},
|
||||
{"path": "td/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil td/curve/adjust", "visibility": 3},
|
||||
{"path": "td/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil td/curve/points", "visibility": 3},
|
||||
{"path": "td/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil td/curve/cpoints", "visibility": 3},
|
||||
{"path": "ref", "type": "float", "visibility": 3, "kids": 3},
|
||||
{"path": "ref/enable", "type": "bool", "readonly": false, "cmd": "tcoil ref/enable"},
|
||||
{"path": "ref/r", "type": "float"},
|
||||
{"path": "ref/curve", "type": "text", "readonly": false, "cmd": "tcoil ref/curve", "kids": 3},
|
||||
{"path": "ref/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil ref/curve/adjust"},
|
||||
{"path": "ref/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil ref/curve/points"},
|
||||
{"path": "ref/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil ref/curve/cpoints"},
|
||||
{"path": "ref/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil ref/curve/adjust", "visibility": 3},
|
||||
{"path": "ref/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil ref/curve/points", "visibility": 3},
|
||||
{"path": "ref/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil ref/curve/cpoints", "visibility": 3},
|
||||
{"path": "tc", "type": "float", "visibility": 3, "kids": 3},
|
||||
{"path": "tc/enable", "type": "bool", "readonly": false, "cmd": "tcoil tc/enable"},
|
||||
{"path": "tc/r", "type": "float"},
|
||||
{"path": "tc/curve", "type": "text", "readonly": false, "cmd": "tcoil tc/curve", "kids": 3},
|
||||
{"path": "tc/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil tc/curve/adjust"},
|
||||
{"path": "tc/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil tc/curve/points"},
|
||||
{"path": "tc/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil tc/curve/cpoints"},
|
||||
{"path": "tc/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil tc/curve/adjust", "visibility": 3},
|
||||
{"path": "tc/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil tc/curve/points", "visibility": 3},
|
||||
{"path": "tc/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil tc/curve/cpoints", "visibility": 3},
|
||||
{"path": "ext", "type": "float", "visibility": 3},
|
||||
{"path": "com", "type": "float", "visibility": 3},
|
||||
{"path": "gnd", "type": "float", "visibility": 3}]},
|
||||
|
@ -365,37 +365,37 @@
|
||||
{"path": "ta/enable", "type": "bool", "readonly": false, "cmd": "tcoil ta/enable"},
|
||||
{"path": "ta/r", "type": "float"},
|
||||
{"path": "ta/curve", "type": "text", "readonly": false, "cmd": "tcoil ta/curve", "kids": 3},
|
||||
{"path": "ta/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil ta/curve/adjust"},
|
||||
{"path": "ta/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil ta/curve/points"},
|
||||
{"path": "ta/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil ta/curve/cpoints"},
|
||||
{"path": "ta/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil ta/curve/adjust", "visibility": 3},
|
||||
{"path": "ta/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil ta/curve/points", "visibility": 3},
|
||||
{"path": "ta/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil ta/curve/cpoints", "visibility": 3},
|
||||
{"path": "tb", "type": "float", "visibility": 3, "kids": 3},
|
||||
{"path": "tb/enable", "type": "bool", "readonly": false, "cmd": "tcoil tb/enable"},
|
||||
{"path": "tb/r", "type": "float"},
|
||||
{"path": "tb/curve", "type": "text", "readonly": false, "cmd": "tcoil tb/curve", "kids": 3},
|
||||
{"path": "tb/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil tb/curve/adjust"},
|
||||
{"path": "tb/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil tb/curve/points"},
|
||||
{"path": "tb/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil tb/curve/cpoints"},
|
||||
{"path": "tb/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil tb/curve/adjust", "visibility": 3},
|
||||
{"path": "tb/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil tb/curve/points", "visibility": 3},
|
||||
{"path": "tb/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil tb/curve/cpoints", "visibility": 3},
|
||||
{"path": "td", "type": "float", "visibility": 3, "kids": 3},
|
||||
{"path": "td/enable", "type": "bool", "readonly": false, "cmd": "tcoil td/enable"},
|
||||
{"path": "td/r", "type": "float"},
|
||||
{"path": "td/curve", "type": "text", "readonly": false, "cmd": "tcoil td/curve", "kids": 3},
|
||||
{"path": "td/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil td/curve/adjust"},
|
||||
{"path": "td/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil td/curve/points"},
|
||||
{"path": "td/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil td/curve/cpoints"},
|
||||
{"path": "td/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil td/curve/adjust", "visibility": 3},
|
||||
{"path": "td/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil td/curve/points", "visibility": 3},
|
||||
{"path": "td/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil td/curve/cpoints", "visibility": 3},
|
||||
{"path": "ref", "type": "float", "visibility": 3, "kids": 3},
|
||||
{"path": "ref/enable", "type": "bool", "readonly": false, "cmd": "tcoil ref/enable"},
|
||||
{"path": "ref/r", "type": "float"},
|
||||
{"path": "ref/curve", "type": "text", "readonly": false, "cmd": "tcoil ref/curve", "kids": 3},
|
||||
{"path": "ref/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil ref/curve/adjust"},
|
||||
{"path": "ref/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil ref/curve/points"},
|
||||
{"path": "ref/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil ref/curve/cpoints"},
|
||||
{"path": "ref/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil ref/curve/adjust", "visibility": 3},
|
||||
{"path": "ref/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil ref/curve/points", "visibility": 3},
|
||||
{"path": "ref/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil ref/curve/cpoints", "visibility": 3},
|
||||
{"path": "tc", "type": "float", "visibility": 3, "kids": 3},
|
||||
{"path": "tc/enable", "type": "bool", "readonly": false, "cmd": "tcoil tc/enable"},
|
||||
{"path": "tc/r", "type": "float"},
|
||||
{"path": "tc/curve", "type": "text", "readonly": false, "cmd": "tcoil tc/curve", "kids": 3},
|
||||
{"path": "tc/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil tc/curve/adjust"},
|
||||
{"path": "tc/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil tc/curve/points"},
|
||||
{"path": "tc/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil tc/curve/cpoints"},
|
||||
{"path": "tc/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil tc/curve/adjust", "visibility": 3},
|
||||
{"path": "tc/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil tc/curve/points", "visibility": 3},
|
||||
{"path": "tc/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil tc/curve/cpoints", "visibility": 3},
|
||||
{"path": "ext", "type": "float", "visibility": 3},
|
||||
{"path": "com", "type": "float", "visibility": 3},
|
||||
{"path": "gnd", "type": "float", "visibility": 3}]},
|
||||
|
@ -331,37 +331,37 @@
|
||||
{"path": "ta/enable", "type": "bool", "readonly": false, "cmd": "tcoil ta/enable"},
|
||||
{"path": "ta/r", "type": "float"},
|
||||
{"path": "ta/curve", "type": "text", "readonly": false, "cmd": "tcoil ta/curve", "kids": 3},
|
||||
{"path": "ta/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil ta/curve/adjust"},
|
||||
{"path": "ta/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil ta/curve/points"},
|
||||
{"path": "ta/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil ta/curve/cpoints"},
|
||||
{"path": "ta/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil ta/curve/adjust", "visibility": 3},
|
||||
{"path": "ta/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil ta/curve/points", "visibility": 3},
|
||||
{"path": "ta/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil ta/curve/cpoints", "visibility": 3},
|
||||
{"path": "tb", "type": "float", "visibility": 3, "kids": 3},
|
||||
{"path": "tb/enable", "type": "bool", "readonly": false, "cmd": "tcoil tb/enable"},
|
||||
{"path": "tb/r", "type": "float"},
|
||||
{"path": "tb/curve", "type": "text", "readonly": false, "cmd": "tcoil tb/curve", "kids": 3},
|
||||
{"path": "tb/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil tb/curve/adjust"},
|
||||
{"path": "tb/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil tb/curve/points"},
|
||||
{"path": "tb/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil tb/curve/cpoints"},
|
||||
{"path": "tb/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil tb/curve/adjust", "visibility": 3},
|
||||
{"path": "tb/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil tb/curve/points", "visibility": 3},
|
||||
{"path": "tb/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil tb/curve/cpoints", "visibility": 3},
|
||||
{"path": "td", "type": "float", "visibility": 3, "kids": 3},
|
||||
{"path": "td/enable", "type": "bool", "readonly": false, "cmd": "tcoil td/enable"},
|
||||
{"path": "td/r", "type": "float"},
|
||||
{"path": "td/curve", "type": "text", "readonly": false, "cmd": "tcoil td/curve", "kids": 3},
|
||||
{"path": "td/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil td/curve/adjust"},
|
||||
{"path": "td/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil td/curve/points"},
|
||||
{"path": "td/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil td/curve/cpoints"},
|
||||
{"path": "td/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil td/curve/adjust", "visibility": 3},
|
||||
{"path": "td/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil td/curve/points", "visibility": 3},
|
||||
{"path": "td/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil td/curve/cpoints", "visibility": 3},
|
||||
{"path": "ref", "type": "float", "visibility": 3, "kids": 3},
|
||||
{"path": "ref/enable", "type": "bool", "readonly": false, "cmd": "tcoil ref/enable"},
|
||||
{"path": "ref/r", "type": "float"},
|
||||
{"path": "ref/curve", "type": "text", "readonly": false, "cmd": "tcoil ref/curve", "kids": 3},
|
||||
{"path": "ref/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil ref/curve/adjust"},
|
||||
{"path": "ref/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil ref/curve/points"},
|
||||
{"path": "ref/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil ref/curve/cpoints"},
|
||||
{"path": "ref/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil ref/curve/adjust", "visibility": 3},
|
||||
{"path": "ref/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil ref/curve/points", "visibility": 3},
|
||||
{"path": "ref/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil ref/curve/cpoints", "visibility": 3},
|
||||
{"path": "tc", "type": "float", "visibility": 3, "kids": 3},
|
||||
{"path": "tc/enable", "type": "bool", "readonly": false, "cmd": "tcoil tc/enable"},
|
||||
{"path": "tc/r", "type": "float"},
|
||||
{"path": "tc/curve", "type": "text", "readonly": false, "cmd": "tcoil tc/curve", "kids": 3},
|
||||
{"path": "tc/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil tc/curve/adjust"},
|
||||
{"path": "tc/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil tc/curve/points"},
|
||||
{"path": "tc/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil tc/curve/cpoints"},
|
||||
{"path": "tc/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil tc/curve/adjust", "visibility": 3},
|
||||
{"path": "tc/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil tc/curve/points", "visibility": 3},
|
||||
{"path": "tc/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil tc/curve/cpoints", "visibility": 3},
|
||||
{"path": "ext", "type": "float", "visibility": 3},
|
||||
{"path": "com", "type": "float", "visibility": 3},
|
||||
{"path": "gnd", "type": "float", "visibility": 3}]},
|
||||
|
307
cfg/sea/ori2.config.json
Normal file
307
cfg/sea/ori2.config.json
Normal file
@ -0,0 +1,307 @@
|
||||
{"tt": {"base": "/tt", "params": [
|
||||
{"path": "", "type": "float", "readonly": false, "cmd": "run tt", "description": "tt", "kids": 19},
|
||||
{"path": "send", "type": "text", "readonly": false, "cmd": "tt send", "visibility": 3},
|
||||
{"path": "status", "type": "text", "visibility": 3},
|
||||
{"path": "is_running", "type": "int", "readonly": false, "cmd": "tt is_running", "visibility": 3},
|
||||
{"path": "mainloop", "type": "text", "readonly": false, "cmd": "tt mainloop", "visibility": 3},
|
||||
{"path": "target", "type": "float"},
|
||||
{"path": "running", "type": "int"},
|
||||
{"path": "tolerance", "type": "float", "readonly": false, "cmd": "tt tolerance"},
|
||||
{"path": "maxwait", "type": "float", "readonly": false, "cmd": "tt maxwait"},
|
||||
{"path": "settle", "type": "float", "readonly": false, "cmd": "tt settle"},
|
||||
{"path": "log", "type": "text", "readonly": false, "cmd": "tt log", "visibility": 3, "kids": 4},
|
||||
{"path": "log/mean", "type": "float", "visibility": 3},
|
||||
{"path": "log/m2", "type": "float", "visibility": 3},
|
||||
{"path": "log/stddev", "type": "float", "visibility": 3},
|
||||
{"path": "log/n", "type": "float", "visibility": 3},
|
||||
{"path": "dblctrl", "type": "bool", "readonly": false, "cmd": "tt dblctrl", "kids": 9},
|
||||
{"path": "dblctrl/tshift", "type": "float", "readonly": false, "cmd": "tt dblctrl/tshift"},
|
||||
{"path": "dblctrl/mode", "type": "enum", "enum": {"disabled": -1, "inactive": 0, "stable": 1, "up": 2, "down": 3}, "readonly": false, "cmd": "tt dblctrl/mode"},
|
||||
{"path": "dblctrl/shift_up", "type": "float"},
|
||||
{"path": "dblctrl/shift_lo", "type": "float"},
|
||||
{"path": "dblctrl/t_min", "type": "float"},
|
||||
{"path": "dblctrl/t_max", "type": "float"},
|
||||
{"path": "dblctrl/int2", "type": "float", "readonly": false, "cmd": "tt dblctrl/int2"},
|
||||
{"path": "dblctrl/prop_up", "type": "float", "readonly": false, "cmd": "tt dblctrl/prop_up"},
|
||||
{"path": "dblctrl/prop_lo", "type": "float", "readonly": false, "cmd": "tt dblctrl/prop_lo"},
|
||||
{"path": "tm", "type": "float", "kids": 4},
|
||||
{"path": "tm/curve", "type": "text", "readonly": false, "cmd": "tt tm/curve", "kids": 1},
|
||||
{"path": "tm/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt tm/curve/points", "visibility": 3},
|
||||
{"path": "tm/alarm", "type": "float", "readonly": false, "cmd": "tt tm/alarm"},
|
||||
{"path": "tm/stddev", "type": "float"},
|
||||
{"path": "tm/raw", "type": "float"},
|
||||
{"path": "ts", "type": "float", "kids": 4},
|
||||
{"path": "ts/curve", "type": "text", "readonly": false, "cmd": "tt ts/curve", "kids": 1},
|
||||
{"path": "ts/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt ts/curve/points", "visibility": 3},
|
||||
{"path": "ts/alarm", "type": "float", "readonly": false, "cmd": "tt ts/alarm"},
|
||||
{"path": "ts/stddev", "type": "float"},
|
||||
{"path": "ts/raw", "type": "float"},
|
||||
{"path": "ts_2", "type": "float", "visibility": 3, "kids": 4},
|
||||
{"path": "ts_2/curve", "type": "text", "readonly": false, "cmd": "tt ts_2/curve", "visibility": 3, "kids": 1},
|
||||
{"path": "ts_2/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt ts_2/curve/points", "visibility": 3},
|
||||
{"path": "ts_2/alarm", "type": "float", "readonly": false, "cmd": "tt ts_2/alarm", "visibility": 3},
|
||||
{"path": "ts_2/stddev", "type": "float", "visibility": 3},
|
||||
{"path": "ts_2/raw", "type": "float", "visibility": 3},
|
||||
{"path": "set", "type": "float", "readonly": false, "cmd": "tt set", "kids": 18},
|
||||
{"path": "set/mode", "type": "enum", "enum": {"disabled": -1, "off": 0, "controlling": 1, "manual": 2}, "readonly": false, "cmd": "tt set/mode"},
|
||||
{"path": "set/reg", "type": "float"},
|
||||
{"path": "set/ramp", "type": "float", "readonly": false, "cmd": "tt set/ramp", "description": "maximum ramp in K/min (0: ramp off)"},
|
||||
{"path": "set/wramp", "type": "float", "readonly": false, "cmd": "tt set/wramp"},
|
||||
{"path": "set/smooth", "type": "float", "readonly": false, "cmd": "tt set/smooth", "description": "smooth time (minutes)"},
|
||||
{"path": "set/channel", "type": "text", "readonly": false, "cmd": "tt set/channel"},
|
||||
{"path": "set/limit", "type": "float", "readonly": false, "cmd": "tt set/limit"},
|
||||
{"path": "set/resist", "type": "float", "readonly": false, "cmd": "tt set/resist"},
|
||||
{"path": "set/maxheater", "type": "text", "readonly": false, "cmd": "tt set/maxheater", "description": "maximum heater limit, units should be given without space: W, mW, A, mA"},
|
||||
{"path": "set/linearpower", "type": "float", "readonly": false, "cmd": "tt set/linearpower", "description": "when not 0, it is the maximum effective power, and the power is linear to the heater output"},
|
||||
{"path": "set/maxpowerlim", "type": "float", "description": "the maximum power limit (before any booster or converter)"},
|
||||
{"path": "set/maxpower", "type": "float", "readonly": false, "cmd": "tt set/maxpower", "description": "maximum power [W]"},
|
||||
{"path": "set/maxcurrent", "type": "float", "description": "the maximum current before any booster or converter"},
|
||||
{"path": "set/manualpower", "type": "float", "readonly": false, "cmd": "tt set/manualpower"},
|
||||
{"path": "set/power", "type": "float"},
|
||||
{"path": "set/prop", "type": "float", "readonly": false, "cmd": "tt set/prop", "description": "bigger means more gain"},
|
||||
{"path": "set/integ", "type": "float", "readonly": false, "cmd": "tt set/integ", "description": "bigger means faster"},
|
||||
{"path": "set/deriv", "type": "float", "readonly": false, "cmd": "tt set/deriv"},
|
||||
{"path": "display", "type": "text", "readonly": false, "cmd": "tt display"},
|
||||
{"path": "remote", "type": "bool"}]},
|
||||
|
||||
"cc": {"base": "/cc", "params": [
|
||||
{"path": "", "type": "bool", "kids": 96},
|
||||
{"path": "send", "type": "text", "readonly": false, "cmd": "cc send", "visibility": 3},
|
||||
{"path": "status", "type": "text", "visibility": 3},
|
||||
{"path": "autodevice", "type": "bool", "readonly": false, "cmd": "cc autodevice"},
|
||||
{"path": "fav", "type": "bool", "readonly": false, "cmd": "cc fav"},
|
||||
{"path": "f", "type": "float"},
|
||||
{"path": "fs", "type": "enum", "enum": {"ok": 0, "no_sens": 1}, "readonly": false, "cmd": "cc fs"},
|
||||
{"path": "mav", "type": "bool", "readonly": false, "cmd": "cc mav"},
|
||||
{"path": "fm", "type": "enum", "enum": {"idle": 0, "opening": 1, "closing": 2, "opened": 3, "closed": 4, "no_motor": 5}},
|
||||
{"path": "fa", "type": "enum", "enum": {"fixed": 0, "controlled": 1, "automatic": 2, "offline": 3}, "readonly": false, "cmd": "cc fa"},
|
||||
{"path": "mp", "type": "float", "readonly": false, "cmd": "cc mp"},
|
||||
{"path": "msp", "type": "float"},
|
||||
{"path": "mmp", "type": "float"},
|
||||
{"path": "mc", "type": "float", "readonly": false, "cmd": "cc mc"},
|
||||
{"path": "mfc", "type": "float", "readonly": false, "cmd": "cc mfc"},
|
||||
{"path": "moc", "type": "float", "readonly": false, "cmd": "cc moc"},
|
||||
{"path": "mtc", "type": "float", "readonly": false, "cmd": "cc mtc"},
|
||||
{"path": "mtl", "type": "float"},
|
||||
{"path": "mft", "type": "float", "readonly": false, "cmd": "cc mft"},
|
||||
{"path": "mt", "type": "float"},
|
||||
{"path": "mo", "type": "float"},
|
||||
{"path": "mcr", "type": "float"},
|
||||
{"path": "mot", "type": "float"},
|
||||
{"path": "mw", "type": "float", "readonly": false, "cmd": "cc mw", "description": "correction pulse after automatic open"},
|
||||
{"path": "hav", "type": "enum", "type": "enum", "enum": {"none": 0, "int": 1, "ext": 2}, "readonly": false, "cmd": "cc hav"},
|
||||
{"path": "h", "type": "float"},
|
||||
{"path": "hr", "type": "float"},
|
||||
{"path": "hc", "type": "float"},
|
||||
{"path": "hu", "type": "float"},
|
||||
{"path": "hh", "type": "float", "readonly": false, "cmd": "cc hh"},
|
||||
{"path": "hl", "type": "float", "readonly": false, "cmd": "cc hl"},
|
||||
{"path": "htf", "type": "float", "readonly": false, "cmd": "cc htf", "description": "meas. period in fast mode"},
|
||||
{"path": "hts", "type": "float", "readonly": false, "cmd": "cc hts", "description": "meas. period in slow mode"},
|
||||
{"path": "hd", "type": "float", "readonly": false, "cmd": "cc hd"},
|
||||
{"path": "hwr", "type": "float", "readonly": false, "cmd": "cc hwr"},
|
||||
{"path": "hem", "type": "float", "readonly": false, "cmd": "cc hem", "description": "sensor length in mm from top to empty pos."},
|
||||
{"path": "hfu", "type": "float", "readonly": false, "cmd": "cc hfu", "description": "sensor length in mm from top to full pos."},
|
||||
{"path": "hcd", "type": "enum", "enum": {"stop": 0, "fill": 1, "off": 2, "auto": 3, "manual": 7}, "readonly": false, "cmd": "cc hcd"},
|
||||
{"path": "hv", "type": "enum", "enum": {"fill_valve_off": 0, "filling": 1, "no_fill_valve": 2, "timeout": 3, "timeout1": 4}},
|
||||
{"path": "hsf", "type": "enum", "enum": {"sens_ok": 0, "sens_warm": 1, "no_sens": 2, "timeout": 3, "not_yet_read": 4, "disabled": 5}},
|
||||
{"path": "ha", "type": "bool", "readonly": false, "cmd": "cc ha"},
|
||||
{"path": "hm", "type": "bool"},
|
||||
{"path": "hf", "type": "enum", "enum": {"slow": 0, "fast": 1}, "readonly": false, "cmd": "cc hf"},
|
||||
{"path": "hbe", "type": "bool", "readonly": false, "cmd": "cc hbe"},
|
||||
{"path": "hmf", "type": "float"},
|
||||
{"path": "hms", "type": "float"},
|
||||
{"path": "hit", "type": "float", "readonly": false, "cmd": "cc hit"},
|
||||
{"path": "hft", "type": "int", "readonly": false, "cmd": "cc hft"},
|
||||
{"path": "hea", "type": "enum", "enum": {"0": 0, "1": 1, "6": 2}, "readonly": false, "cmd": "cc hea"},
|
||||
{"path": "hch", "type": "int", "readonly": false, "cmd": "cc hch", "visibility": 3},
|
||||
{"path": "hwr0", "type": "float", "readonly": false, "cmd": "cc hwr0", "visibility": 3},
|
||||
{"path": "hem0", "type": "float", "readonly": false, "cmd": "cc hem0", "description": "sensor length in mm from top to empty pos.", "visibility": 3},
|
||||
{"path": "hfu0", "type": "float", "readonly": false, "cmd": "cc hfu0", "description": "sensor length in mm from top to full pos.", "visibility": 3},
|
||||
{"path": "hd0", "type": "float", "readonly": false, "cmd": "cc hd0", "description": "external sensor drive current (mA)", "visibility": 3},
|
||||
{"path": "h0", "type": "float", "visibility": 3},
|
||||
{"path": "hs0", "type": "enum", "enum": {"sens_ok": 0, "sens_warm": 1, "no_sens": 2, "timeout": 3, "not_yet_read": 4, "disabled": 5}, "visibility": 3},
|
||||
{"path": "h1", "type": "float", "visibility": 3},
|
||||
{"path": "hs1", "type": "enum", "enum": {"sens_ok": 0, "sens_warm": 1, "no_sens": 2, "timeout": 3, "not_yet_read": 4, "disabled": 5}, "visibility": 3},
|
||||
{"path": "h2", "type": "float", "visibility": 3},
|
||||
{"path": "hs2", "type": "enum", "enum": {"sens_ok": 0, "sens_warm": 1, "no_sens": 2, "timeout": 3, "not_yet_read": 4, "disabled": 5}, "visibility": 3},
|
||||
{"path": "h3", "type": "float", "visibility": 3},
|
||||
{"path": "hs3", "type": "enum", "enum": {"sens_ok": 0, "sens_warm": 1, "no_sens": 2, "timeout": 3, "not_yet_read": 4, "disabled": 5}, "visibility": 3},
|
||||
{"path": "h4", "type": "float", "visibility": 3},
|
||||
{"path": "hs4", "type": "enum", "enum": {"sens_ok": 0, "sens_warm": 1, "no_sens": 2, "timeout": 3, "not_yet_read": 4, "disabled": 5}, "visibility": 3},
|
||||
{"path": "h5", "type": "float", "visibility": 3},
|
||||
{"path": "hs5", "type": "enum", "enum": {"sens_ok": 0, "sens_warm": 1, "no_sens": 2, "timeout": 3, "not_yet_read": 4, "disabled": 5}, "visibility": 3},
|
||||
{"path": "hfb", "type": "float"},
|
||||
{"path": "nav", "type": "enum", "type": "enum", "enum": {"none": 0, "int": 1, "ext": 2}, "readonly": false, "cmd": "cc nav"},
|
||||
{"path": "nu", "type": "float"},
|
||||
{"path": "nl", "type": "float"},
|
||||
{"path": "nth", "type": "float", "readonly": false, "cmd": "cc nth"},
|
||||
{"path": "ntc", "type": "float", "readonly": false, "cmd": "cc ntc"},
|
||||
{"path": "ntm", "type": "float", "readonly": false, "cmd": "cc ntm"},
|
||||
{"path": "ns", "type": "enum", "enum": {"sens_ok": 0, "no_sens": 1, "short_circuit": 2, "upside_down": 3, "sens_warm": 4, "empty": 5}},
|
||||
{"path": "na", "type": "bool", "readonly": false, "cmd": "cc na"},
|
||||
{"path": "nv", "type": "enum", "enum": {"fill_valve_off": 0, "filling": 1, "no_fill_valve": 2, "timeout": 3, "timeout1": 4, "boost": 5}},
|
||||
{"path": "nc", "type": "enum", "enum": {"stop": 0, "fill": 1, "off": 2, "auto": 3}, "readonly": false, "cmd": "cc nc"},
|
||||
{"path": "nfb", "type": "float"},
|
||||
{"path": "cda", "type": "float"},
|
||||
{"path": "cdb", "type": "float"},
|
||||
{"path": "cba", "type": "float"},
|
||||
{"path": "cbb", "type": "float"},
|
||||
{"path": "cvs", "type": "int"},
|
||||
{"path": "csp", "type": "int"},
|
||||
{"path": "cdv", "type": "text", "readonly": false, "cmd": "cc cdv"},
|
||||
{"path": "cic", "type": "text", "readonly": false, "cmd": "cc cic"},
|
||||
{"path": "cin", "type": "text"},
|
||||
{"path": "cds", "type": "enum", "enum": {"local": 0, "remote": 1, "loading": 2, "by_code": 3, "by_touch": 4}, "readonly": false, "cmd": "cc cds"},
|
||||
{"path": "timing", "type": "bool", "readonly": false, "cmd": "cc timing"},
|
||||
{"path": "tc", "type": "float", "visibility": 3},
|
||||
{"path": "tn", "type": "float", "visibility": 3},
|
||||
{"path": "th", "type": "float", "visibility": 3},
|
||||
{"path": "tf", "type": "float", "visibility": 3},
|
||||
{"path": "tm", "type": "float", "visibility": 3},
|
||||
{"path": "tv", "type": "float", "visibility": 3},
|
||||
{"path": "tq", "type": "float", "visibility": 3},
|
||||
{"path": "bdl", "type": "float", "readonly": false, "cmd": "cc bdl"}]},
|
||||
|
||||
"nv": {"base": "/nv", "params": [
|
||||
{"path": "", "type": "enum", "enum": {"fixed": 0, "controlled": 1, "automatic": 2, "close": 3, "open": 4}, "readonly": false, "cmd": "nv", "kids": 11},
|
||||
{"path": "send", "type": "text", "readonly": false, "cmd": "nv send", "visibility": 3},
|
||||
{"path": "status", "type": "text", "visibility": 3},
|
||||
{"path": "motstat", "type": "enum", "enum": {"idle": 0, "opening": 1, "closing": 2, "opened": 3, "closed": 4, "no_motor": 5}},
|
||||
{"path": "flow", "type": "float"},
|
||||
{"path": "set", "type": "float", "readonly": false, "cmd": "nv set"},
|
||||
{"path": "flowmax", "type": "float", "readonly": false, "cmd": "nv flowmax"},
|
||||
{"path": "flowp", "type": "float"},
|
||||
{"path": "span", "type": "float"},
|
||||
{"path": "ctrl", "type": "none", "kids": 13},
|
||||
{"path": "ctrl/regtext", "type": "text"},
|
||||
{"path": "ctrl/prop_o", "type": "float", "readonly": false, "cmd": "nv ctrl/prop_o", "description": "prop [sec/mbar] when opening. above 4 mbar a 10 times lower value is used"},
|
||||
{"path": "ctrl/prop_c", "type": "float", "readonly": false, "cmd": "nv ctrl/prop_c", "description": "prop [sec/mbar] when closing. above 4 mbar a 10 times lower value is used"},
|
||||
{"path": "ctrl/deriv_o", "type": "float", "readonly": false, "cmd": "nv ctrl/deriv_o", "description": "convergence target time [sec] when opening"},
|
||||
{"path": "ctrl/deriv_c", "type": "float", "readonly": false, "cmd": "nv ctrl/deriv_c", "description": "convergence target time [sec] when closing"},
|
||||
{"path": "ctrl/minpulse_o", "type": "float", "readonly": false, "cmd": "nv ctrl/minpulse_o", "description": "minimum close pulse [sec]"},
|
||||
{"path": "ctrl/minpulse_c", "type": "float", "readonly": false, "cmd": "nv ctrl/minpulse_c", "description": "standard close pulse [sec]"},
|
||||
{"path": "ctrl/hystpulse_o", "type": "float", "readonly": false, "cmd": "nv ctrl/hystpulse_o", "description": "motor pulse to overcome hysteresis when opening"},
|
||||
{"path": "ctrl/hystpulse_c", "type": "float", "readonly": false, "cmd": "nv ctrl/hystpulse_c", "description": "motor pulse to overcome hysteresis when closing"},
|
||||
{"path": "ctrl/tol", "type": "float", "readonly": false, "cmd": "nv ctrl/tol", "description": "valid below 3 mbar"},
|
||||
{"path": "ctrl/tolhigh", "type": "float", "readonly": false, "cmd": "nv ctrl/tolhigh", "description": "valid above 4 mbar"},
|
||||
{"path": "ctrl/openpulse", "type": "float", "readonly": false, "cmd": "nv ctrl/openpulse", "description": "time to open from completely closed to a significant opening"},
|
||||
{"path": "ctrl/adjust_minpulse", "type": "bool", "readonly": false, "cmd": "nv ctrl/adjust_minpulse", "description": "adjust minpulse automatically"},
|
||||
{"path": "autoflow", "type": "none", "kids": 24},
|
||||
{"path": "autoflow/suspended", "type": "bool", "readonly": false, "cmd": "nv autoflow/suspended"},
|
||||
{"path": "autoflow/prop", "type": "float", "readonly": false, "cmd": "nv autoflow/prop"},
|
||||
{"path": "autoflow/flowstd", "type": "float", "readonly": false, "cmd": "nv autoflow/flowstd"},
|
||||
{"path": "autoflow/flowlim", "type": "float", "readonly": false, "cmd": "nv autoflow/flowlim"},
|
||||
{"path": "autoflow/smooth", "type": "float", "readonly": false, "cmd": "nv autoflow/smooth"},
|
||||
{"path": "autoflow/difSize", "type": "float", "readonly": false, "cmd": "nv autoflow/difSize"},
|
||||
{"path": "autoflow/difRange", "type": "float", "readonly": false, "cmd": "nv autoflow/difRange"},
|
||||
{"path": "autoflow/flowSize", "type": "float", "readonly": false, "cmd": "nv autoflow/flowSize"},
|
||||
{"path": "autoflow/convTime", "type": "float", "readonly": false, "cmd": "nv autoflow/convTime"},
|
||||
{"path": "autoflow/Tmin", "type": "float", "readonly": false, "cmd": "nv autoflow/Tmin"},
|
||||
{"path": "autoflow/script", "type": "text", "readonly": false, "cmd": "nv autoflow/script"},
|
||||
{"path": "autoflow/getTemp", "type": "text", "readonly": false, "cmd": "nv autoflow/getTemp"},
|
||||
{"path": "autoflow/getTset", "type": "text", "readonly": false, "cmd": "nv autoflow/getTset"},
|
||||
{"path": "autoflow/getFlow", "type": "text", "readonly": false, "cmd": "nv autoflow/getFlow"},
|
||||
{"path": "autoflow/difBuf", "type": "text"},
|
||||
{"path": "autoflow/flowBuf", "type": "text"},
|
||||
{"path": "autoflow/flowset", "type": "float"},
|
||||
{"path": "autoflow/flowmin", "type": "float"},
|
||||
{"path": "autoflow/flowmax", "type": "float"},
|
||||
{"path": "autoflow/difmin", "type": "float"},
|
||||
{"path": "autoflow/difmax", "type": "float"},
|
||||
{"path": "autoflow/setmin", "type": "float"},
|
||||
{"path": "autoflow/setmax", "type": "float"},
|
||||
{"path": "autoflow/flowtarget", "type": "float"},
|
||||
{"path": "calib", "type": "none", "kids": 2},
|
||||
{"path": "calib/ln_per_min_per_mbar", "type": "float", "readonly": false, "cmd": "nv calib/ln_per_min_per_mbar"},
|
||||
{"path": "calib/mbar_offset", "type": "float", "readonly": false, "cmd": "nv calib/mbar_offset"}]},
|
||||
|
||||
"ln2fill": {"base": "/ln2fill", "params": [
|
||||
{"path": "", "type": "enum", "enum": {"watching": 0, "fill": 1, "inactive": 2}, "readonly": false, "cmd": "ln2fill", "kids": 3},
|
||||
{"path": "send", "type": "text", "readonly": false, "cmd": "ln2fill send", "visibility": 3},
|
||||
{"path": "status", "type": "text", "visibility": 3},
|
||||
{"path": "state", "type": "text"}]},
|
||||
|
||||
"hefill": {"base": "/hefill", "params": [
|
||||
{"path": "", "type": "enum", "enum": {"watching": 0, "fill": 1, "inactive": 2, "manualfill": 3}, "readonly": false, "cmd": "hefill", "kids": 6},
|
||||
{"path": "send", "type": "text", "readonly": false, "cmd": "hefill send", "visibility": 3},
|
||||
{"path": "status", "type": "text", "visibility": 3},
|
||||
{"path": "fast", "type": "enum", "enum": {"slow": 0, "fast": 1}, "readonly": false, "cmd": "cc hf"},
|
||||
{"path": "state", "type": "text"},
|
||||
{"path": "hefull", "type": "float", "readonly": false, "cmd": "cc hh"},
|
||||
{"path": "helow", "type": "float", "readonly": false, "cmd": "cc hl"}]},
|
||||
|
||||
"hepump": {"base": "/hepump", "params": [
|
||||
{"path": "", "type": "enum", "enum": {"neodry": 8, "xds35_auto": 0, "xds35_manual": 1, "sv65": 2, "other": 3, "no": -1}, "readonly": false, "cmd": "hepump", "description": "xds35: scroll pump, sv65: leybold", "kids": 10},
|
||||
{"path": "send", "type": "text", "readonly": false, "cmd": "hepump send", "visibility": 3},
|
||||
{"path": "status", "type": "text", "visibility": 3},
|
||||
{"path": "running", "type": "bool", "readonly": false, "cmd": "hepump running"},
|
||||
{"path": "eco", "type": "bool", "readonly": false, "cmd": "hepump eco", "visibility": 3},
|
||||
{"path": "auto", "type": "bool", "readonly": false, "cmd": "hepump auto", "visibility": 3},
|
||||
{"path": "valve", "type": "enum", "enum": {"closed": 0, "closing": 1, "opening": 2, "opened": 3, "undefined": 4}, "readonly": false, "cmd": "hepump valve"},
|
||||
{"path": "eco_t_lim", "type": "float", "readonly": false, "cmd": "hepump eco_t_lim", "description": "switch off eco mode when T_set < eco_t_lim and T < eco_t_lim * 2", "visibility": 3},
|
||||
{"path": "calib", "type": "float", "readonly": false, "cmd": "hepump calib", "visibility": 3},
|
||||
{"path": "health", "type": "float"}]},
|
||||
|
||||
"hemot": {"base": "/hepump/hemot", "params": [
|
||||
{"path": "", "type": "float", "readonly": false, "cmd": "run hemot", "visibility": 3, "kids": 30},
|
||||
{"path": "send", "type": "text", "readonly": false, "cmd": "hemot send", "visibility": 3},
|
||||
{"path": "status", "type": "text", "visibility": 3},
|
||||
{"path": "is_running", "type": "int", "readonly": false, "cmd": "hemot is_running", "visibility": 3},
|
||||
{"path": "pos", "type": "float"},
|
||||
{"path": "encoder", "type": "float"},
|
||||
{"path": "zero", "type": "float", "readonly": false, "cmd": "hemot zero"},
|
||||
{"path": "lowerlimit", "type": "float", "readonly": false, "cmd": "hemot lowerlimit"},
|
||||
{"path": "upperlimit", "type": "float", "readonly": false, "cmd": "hemot upperlimit"},
|
||||
{"path": "disablelimits", "type": "bool", "readonly": false, "cmd": "hemot disablelimits"},
|
||||
{"path": "verbose", "type": "bool", "readonly": false, "cmd": "hemot verbose"},
|
||||
{"path": "target", "type": "float"},
|
||||
{"path": "runstate", "type": "enum", "enum": {"idle": 0, "running": 1, "finished": 2, "error": 3}},
|
||||
{"path": "precision", "type": "float", "readonly": false, "cmd": "hemot precision"},
|
||||
{"path": "maxencdif", "type": "float", "readonly": false, "cmd": "hemot maxencdif"},
|
||||
{"path": "id", "type": "float", "readonly": false, "cmd": "hemot id"},
|
||||
{"path": "pump_number", "type": "float", "readonly": false, "cmd": "hemot pump_number"},
|
||||
{"path": "init", "type": "float", "readonly": false, "cmd": "hemot init"},
|
||||
{"path": "maxspeed", "type": "float", "readonly": false, "cmd": "hemot maxspeed"},
|
||||
{"path": "acceleration", "type": "float", "readonly": false, "cmd": "hemot acceleration"},
|
||||
{"path": "maxcurrent", "type": "float", "readonly": false, "cmd": "hemot maxcurrent"},
|
||||
{"path": "standbycurrent", "type": "float", "readonly": false, "cmd": "hemot standbycurrent"},
|
||||
{"path": "freewheeling", "type": "bool", "readonly": false, "cmd": "hemot freewheeling"},
|
||||
{"path": "output0", "type": "bool", "readonly": false, "cmd": "hemot output0"},
|
||||
{"path": "output1", "type": "bool", "readonly": false, "cmd": "hemot output1"},
|
||||
{"path": "input3", "type": "bool"},
|
||||
{"path": "pullup", "type": "float", "readonly": false, "cmd": "hemot pullup"},
|
||||
{"path": "nopumpfeedback", "type": "bool", "readonly": false, "cmd": "hemot nopumpfeedback"},
|
||||
{"path": "eeprom", "type": "enum", "enum": {"ok": 0, "dirty": 1, "save": 2, "load": 3}, "readonly": false, "cmd": "hemot eeprom"},
|
||||
{"path": "customadr", "type": "text", "readonly": false, "cmd": "hemot customadr"},
|
||||
{"path": "custompar", "type": "float", "readonly": false, "cmd": "hemot custompar"}]},
|
||||
|
||||
"nvflow": {"base": "/nvflow", "params": [
|
||||
{"path": "", "type": "float", "kids": 7},
|
||||
{"path": "send", "type": "text", "readonly": false, "cmd": "nvflow send", "visibility": 3},
|
||||
{"path": "status", "type": "text", "visibility": 3},
|
||||
{"path": "stddev", "type": "float"},
|
||||
{"path": "nsamples", "type": "int", "readonly": false, "cmd": "nvflow nsamples"},
|
||||
{"path": "offset", "type": "float", "readonly": false, "cmd": "nvflow offset"},
|
||||
{"path": "scale", "type": "float", "readonly": false, "cmd": "nvflow scale"},
|
||||
{"path": "save", "type": "bool", "readonly": false, "cmd": "nvflow save", "description": "unchecked: current calib is not saved. set checked: save calib"}]},
|
||||
|
||||
"table": {"base": "/table", "params": [
|
||||
{"path": "", "type": "none", "kids": 17},
|
||||
{"path": "send", "type": "text", "readonly": false, "cmd": "table send", "visibility": 3},
|
||||
{"path": "status", "type": "text", "visibility": 3},
|
||||
{"path": "fix_tt_set_prop", "type": "bool", "readonly": false, "cmd": "table fix_tt_set_prop"},
|
||||
{"path": "val_tt_set_prop", "type": "float"},
|
||||
{"path": "tbl_tt_set_prop", "type": "text", "readonly": false, "cmd": "table tbl_tt_set_prop", "description": "enter value pair separated with colon T1:par1 T2:par2 ..."},
|
||||
{"path": "fix_tt_set_integ", "type": "bool", "readonly": false, "cmd": "table fix_tt_set_integ"},
|
||||
{"path": "val_tt_set_integ", "type": "float"},
|
||||
{"path": "tbl_tt_set_integ", "type": "text", "readonly": false, "cmd": "table tbl_tt_set_integ", "description": "enter value pair separated with colon T1:par1 T2:par2 ..."},
|
||||
{"path": "fix_tt_dblctrl_int2", "type": "bool", "readonly": false, "cmd": "table fix_tt_dblctrl_int2"},
|
||||
{"path": "val_tt_dblctrl_int2", "type": "float"},
|
||||
{"path": "tbl_tt_dblctrl_int2", "type": "text", "readonly": false, "cmd": "table tbl_tt_dblctrl_int2", "description": "enter value pair separated with colon T1:par1 T2:par2 ..."},
|
||||
{"path": "fix_tt_dblctrl_prop_up", "type": "bool", "readonly": false, "cmd": "table fix_tt_dblctrl_prop_up"},
|
||||
{"path": "val_tt_dblctrl_prop_up", "type": "float"},
|
||||
{"path": "tbl_tt_dblctrl_prop_up", "type": "text", "readonly": false, "cmd": "table tbl_tt_dblctrl_prop_up", "description": "enter value pair separated with colon T1:par1 T2:par2 ..."},
|
||||
{"path": "fix_tt_dblctrl_prop_lo", "type": "bool", "readonly": false, "cmd": "table fix_tt_dblctrl_prop_lo"},
|
||||
{"path": "val_tt_dblctrl_prop_lo", "type": "float"},
|
||||
{"path": "tbl_tt_dblctrl_prop_lo", "type": "text", "readonly": false, "cmd": "table tbl_tt_dblctrl_prop_lo", "description": "enter value pair separated with colon T1:par1 T2:par2 ..."}]}}
|
47
cfg/stick/PEstick_cfg.py
Normal file
47
cfg/stick/PEstick_cfg.py
Normal file
@ -0,0 +1,47 @@
|
||||
import os
|
||||
port = os.environ.get('FRAPPY_MAIN_PORT')
|
||||
|
||||
Node('pe_stick.sinq.psi.ch',
|
||||
'soft cal for sample T from mercury',
|
||||
'tcp://5001',
|
||||
)
|
||||
|
||||
Mod('itc_proxy',
|
||||
'frappy.core.Proxy',
|
||||
'ITC io proxy',
|
||||
module='itc1',
|
||||
remote_class = 'frappy_psi.mercury.IO',
|
||||
uri = f'tcp://localhost:{port}',
|
||||
)
|
||||
|
||||
Mod('ts_uncal',
|
||||
'frappy_psi.mercury.TemperatureLoop',
|
||||
'sample temperature',
|
||||
output_module='htr_sample',
|
||||
slot='MB1.T1',
|
||||
io='itc_proxy',
|
||||
tolerance=1.0,
|
||||
)
|
||||
|
||||
Mod('htr_sample',
|
||||
'frappy_psi.mercury.HeaterOutput',
|
||||
'sample stick heater power',
|
||||
slot='MB0.H1',
|
||||
io='itc_proxy',
|
||||
)
|
||||
|
||||
Mod('tsraw',
|
||||
'frappy_psi.parmod.Par',
|
||||
'sample T sensor raw value',
|
||||
read='ts_uncal.raw',
|
||||
unit='Ohm',
|
||||
# export = False,
|
||||
)
|
||||
|
||||
Mod('ts',
|
||||
'frappy_psi.softcal.Sensor',
|
||||
'calibrated value for ts',
|
||||
value = Param(unit = 'K'),
|
||||
rawsensor = 'tsraw',
|
||||
calib = 'X133834',
|
||||
)
|
@ -20,12 +20,11 @@ Mod('tsam',
|
||||
|
||||
Mod('ts',
|
||||
'frappy_psi.parmod.Converging',
|
||||
'virtual stick T',
|
||||
meaning=['temperature', 30],
|
||||
'stick T (controlled)',
|
||||
meaning=['temperature', 25],
|
||||
unit='K',
|
||||
read='tsam.value',
|
||||
write='tsam.setsamp',
|
||||
meaning=['temperature', 20],
|
||||
settling_time=20,
|
||||
tolerance=1,
|
||||
)
|
||||
|
@ -16,7 +16,7 @@ Mod('ts',
|
||||
json_file='ma6.config.json',
|
||||
sea_object='tt',
|
||||
rel_paths=['ts', 'setsamp'],
|
||||
meaning=['temperature', 20],
|
||||
meaning=['temperature', 30],
|
||||
)
|
||||
|
||||
|
||||
@ -24,11 +24,10 @@ Mod('ts',
|
||||
Mod('ts',
|
||||
'frappy_psi.parmod.Converging',
|
||||
'drivable stick T using setsamp',
|
||||
meaning=['temperature', 30],
|
||||
meaning=['temperature', 25],
|
||||
unit='K',
|
||||
read='tsam.value',
|
||||
write='tsam.setsamp',
|
||||
meaning=['temperature', 20],
|
||||
settling_time=20,
|
||||
tolerance=1,
|
||||
)
|
||||
|
@ -13,8 +13,6 @@ Mod('ts',
|
||||
'frappy_psi.sea.SeaReadable', '',
|
||||
meaning=['temperature', 30],
|
||||
io='sea_stick',
|
||||
sea_object='tt',
|
||||
sea_path='tt/ts',
|
||||
json_file='ma7.config.json',
|
||||
rel_paths=['ts'],
|
||||
)
|
||||
|
||||
|
@ -25,7 +25,6 @@ Mod('ts',
|
||||
unit='K',
|
||||
read='tsam.value',
|
||||
write='tsam.setsamp',
|
||||
meaning=['temperature', 20],
|
||||
settling_time=20,
|
||||
tolerance=1,
|
||||
)
|
||||
|
75
debian/changelog
vendored
75
debian/changelog
vendored
@ -1,3 +1,78 @@
|
||||
frappy-core (0.20.4) jammy; urgency=medium
|
||||
|
||||
[ Georg Brandl ]
|
||||
* remove unused file
|
||||
|
||||
[ Markus Zolliker ]
|
||||
* frappy.lib.multievent: avoid deadlock
|
||||
|
||||
[ Jens Krüger ]
|
||||
* PSI: Fix import error on ThermoFisher module
|
||||
|
||||
[ Markus Zolliker ]
|
||||
* frappy.client: catch all errors in handleError callback
|
||||
|
||||
[ Jens Krüger ]
|
||||
* Lib/config: Create a list of pathes only for confdir
|
||||
|
||||
-- Georg Brandl <jenkins@frm2.tum.de> Thu, 14 Nov 2024 14:43:54 +0100
|
||||
|
||||
frappy-core (0.20.3) jammy; urgency=medium
|
||||
|
||||
[ Georg Brandl ]
|
||||
* fixup test for cfg_editor utils to run from non-checkout, and fix names, and remove example code
|
||||
|
||||
[ Alexander Zaft ]
|
||||
* add generalConfig to etc
|
||||
|
||||
-- Georg Brandl <jenkins@frm2.tum.de> Thu, 07 Nov 2024 10:57:11 +0100
|
||||
|
||||
frappy-core (0.20.2) jammy; urgency=medium
|
||||
|
||||
[ Georg Brandl ]
|
||||
* pylint: do not try to infer too much
|
||||
|
||||
[ Alexander Zaft ]
|
||||
* test_server: basic description checks
|
||||
* simulation: fix extra_params default, ccidu1 cfg
|
||||
* sim: make amagnet sim cfg startable again
|
||||
* server: fix positional argument lint
|
||||
* server: show interfaces as custom property
|
||||
* core: fix Dispatcher and SECNode opts handling
|
||||
|
||||
[ Markus Zolliker ]
|
||||
* frappy_psi.sea: bugfix: revert change of updateEvent to udpateItem
|
||||
* fix playground
|
||||
|
||||
[ Alexander Zaft ]
|
||||
* config: allow using Prop(...)
|
||||
* config: fix typo
|
||||
* Revert "config: allow using Prop(...)"
|
||||
* generalconfig: streamlined config discovery
|
||||
|
||||
[ Markus Zolliker ]
|
||||
* better order of accessibles: 'value' 'status' and 'target' first
|
||||
|
||||
[ Alexander Zaft ]
|
||||
* server: fix windows ctrl-c
|
||||
|
||||
[ Georg Brandl ]
|
||||
* systemd: enable indication of reloading/stopping
|
||||
|
||||
[ Alexander Zaft ]
|
||||
* server: service discovery over UDP.
|
||||
|
||||
[ Markus Zolliker ]
|
||||
* generalConfig: fix the case when confdir is a list of paths
|
||||
|
||||
[ Georg Brandl ]
|
||||
* server: better handling of cfgfile argument
|
||||
|
||||
[ Alexander Zaft ]
|
||||
* fix frappy-server cfgfiles command
|
||||
|
||||
-- Georg Brandl <jenkins@frm2.tum.de> Wed, 06 Nov 2024 10:40:26 +0100
|
||||
|
||||
frappy-core (0.20.1) jammy; urgency=medium
|
||||
|
||||
* gui: do not add a console logger when there is no sys.stdout
|
||||
|
2
debian/frappy-core.install
vendored
2
debian/frappy-core.install
vendored
@ -1,6 +1,7 @@
|
||||
usr/bin/frappy-cli
|
||||
usr/bin/frappy-server
|
||||
usr/bin/frappy-play
|
||||
usr/bin/frappy-scan
|
||||
usr/lib/python3.*/dist-packages/frappy/*.py
|
||||
usr/lib/python3.*/dist-packages/frappy/__pycache__
|
||||
usr/lib/python3.*/dist-packages/frappy/lib
|
||||
@ -11,3 +12,4 @@ usr/lib/python3.*/dist-packages/frappy/RELEASE-VERSION
|
||||
usr/lib/python3.*/dist-packages/frappy_demo
|
||||
lib/systemd
|
||||
var/log/frappy
|
||||
etc/frappy/generalConfig.cfg
|
||||
|
1
debian/rules
vendored
1
debian/rules
vendored
@ -11,6 +11,7 @@ override_dh_install:
|
||||
rmdir debian/tmp
|
||||
mv debian/python3-frappy debian/tmp
|
||||
|
||||
install -m644 -Dt debian/tmp/etc/frappy etc/generalConfig.cfg
|
||||
dh_install -i -O--buildsystem=pybuild
|
||||
dh_missing --fail-missing
|
||||
|
||||
|
4
etc/generalConfig.cfg
Normal file
4
etc/generalConfig.cfg
Normal file
@ -0,0 +1,4 @@
|
||||
[FRAPPY]
|
||||
logdir = /var/log
|
||||
piddir = /var/run/frappy
|
||||
confdir = /etc/frappy
|
@ -22,8 +22,6 @@
|
||||
# *****************************************************************************
|
||||
"""general SECoP client"""
|
||||
|
||||
# pylint: disable=too-many-positional-arguments
|
||||
|
||||
import json
|
||||
import queue
|
||||
import re
|
||||
@ -481,7 +479,10 @@ class SecopClient(ProxyClient):
|
||||
continue
|
||||
except Exception as e:
|
||||
e.args = (f'error handling SECoP message {reply!r}: {e}',)
|
||||
self.callback(None, 'handleError', e)
|
||||
try:
|
||||
self.callback(None, 'handleError', e)
|
||||
except Exception:
|
||||
pass
|
||||
continue
|
||||
try:
|
||||
key = action, ident
|
||||
|
@ -498,7 +498,7 @@ class Console(code.InteractiveConsole):
|
||||
history = None
|
||||
if readline:
|
||||
try:
|
||||
history = expanduser(f'~/.frappy-{name}-history')
|
||||
history = expanduser(f'~/.config/frappy/{name}-history')
|
||||
readline.read_history_file(history)
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
@ -538,10 +538,10 @@ def init(*nodes):
|
||||
return success
|
||||
|
||||
|
||||
def interact(usage_tail=''):
|
||||
def interact(usage_tail='', appname=None):
|
||||
empty = '_c0' not in clientenv.namespace
|
||||
print(USAGE.format(
|
||||
client_name='cli' if empty else '_c0',
|
||||
client_assign="\ncli = Client('localhost:5000')\n" if empty else '',
|
||||
tail=usage_tail))
|
||||
Console()
|
||||
Console(name=f'cli-{appname}' if appname else 'cli')
|
||||
|
@ -56,10 +56,12 @@ class Param(dict):
|
||||
kwds['value'] = value
|
||||
super().__init__(**kwds)
|
||||
|
||||
|
||||
class Group(tuple):
|
||||
def __new__(cls, *args):
|
||||
return super().__new__(cls, args)
|
||||
|
||||
|
||||
class Mod(dict):
|
||||
def __init__(self, name, cls, description, **kwds):
|
||||
super().__init__(
|
||||
@ -70,7 +72,8 @@ class Mod(dict):
|
||||
|
||||
# matches name from spec
|
||||
if not re.match(r'^[a-zA-Z]\w{0,62}$', name, re.ASCII):
|
||||
raise ConfigError(f'Not a valid SECoP Module name: "{name}". Does it only contain letters, numbers and underscores?')
|
||||
raise ConfigError(f'Not a valid SECoP Module name: "{name}".'
|
||||
' Does it only contain letters, numbers and underscores?')
|
||||
# Make parameters out of all keywords
|
||||
groups = {}
|
||||
for key, val in kwds.items():
|
||||
@ -85,6 +88,7 @@ class Mod(dict):
|
||||
for member in members:
|
||||
self[member]['group'] = group
|
||||
|
||||
|
||||
class Collector:
|
||||
def __init__(self, cls):
|
||||
self.list = []
|
||||
@ -120,12 +124,14 @@ class Config(dict):
|
||||
def merge_modules(self, other):
|
||||
""" merges only the modules from 'other' into 'self'"""
|
||||
self.ambiguous |= self.module_names & other.module_names
|
||||
equipment_id = other['node']['equipment_id']
|
||||
for name, mod in other.items():
|
||||
if name == 'node':
|
||||
continue
|
||||
if name not in self.module_names:
|
||||
self.module_names.add(name)
|
||||
self[name] = mod
|
||||
mod['original_id'] = equipment_id
|
||||
|
||||
|
||||
def process_file(filename, log):
|
||||
@ -172,8 +178,8 @@ def load_config(cfgfiles, log):
|
||||
Only the node-section of the first config file will be returned.
|
||||
The others will be discarded.
|
||||
Arguments
|
||||
- cfgfiles : str
|
||||
Comma separated list of config-files
|
||||
- cfgfiles : list
|
||||
List of config file paths
|
||||
- log : frappy.logging.Mainlogger
|
||||
Logger aquired from frappy.logging
|
||||
Returns
|
||||
@ -181,8 +187,8 @@ def load_config(cfgfiles, log):
|
||||
merged configuration
|
||||
"""
|
||||
config = None
|
||||
for cfgfile in cfgfiles.split(','):
|
||||
filename = to_config_path(cfgfile, log)
|
||||
for cfgfile in cfgfiles:
|
||||
filename = to_config_path(str(cfgfile), log)
|
||||
log.debug('Parsing config file %s...', filename)
|
||||
cfg = process_file(filename, log)
|
||||
if config:
|
||||
|
@ -218,8 +218,9 @@ def write_config(file_name, tree_widget):
|
||||
with open(file_name, 'w', encoding='utf-8') as configfile:
|
||||
configfile.write('\n'.join(lines))
|
||||
|
||||
|
||||
def read_config(file_path, log):
|
||||
config = load_config(file_path, log)
|
||||
config = load_config([file_path], log)
|
||||
node = TreeWidgetItem(NODE)
|
||||
ifs = TreeWidgetItem(name='Interfaces')
|
||||
mods = TreeWidgetItem(name='Modules')
|
||||
|
@ -106,7 +106,8 @@ def get_file_paths(widget, open_file=True):
|
||||
|
||||
def get_modules():
|
||||
modules = {}
|
||||
generalConfig.init()
|
||||
if not generalConfig.initialized:
|
||||
generalConfig.init()
|
||||
base_path = generalConfig.basedir
|
||||
# pylint: disable=too-many-nested-blocks
|
||||
for dirname in listdir(base_path):
|
||||
@ -157,7 +158,8 @@ def get_interface_class_from_name(name):
|
||||
def get_interfaces():
|
||||
# TODO class must be found out like for modules
|
||||
interfaces = []
|
||||
generalConfig.init()
|
||||
if not generalConfig.initialized:
|
||||
generalConfig.init()
|
||||
interface_path = path.join(generalConfig.basedir, 'frappy',
|
||||
'protocol', 'interface')
|
||||
for filename in listdir(interface_path):
|
||||
|
@ -57,11 +57,12 @@ class GeneralConfig:
|
||||
|
||||
:param configfile: if present, keys and values from the [FRAPPY] section are read
|
||||
|
||||
default values for 'piddir', 'logdir' and 'confdir' are guessed from the
|
||||
location of this source file and from sys.executable.
|
||||
|
||||
if configfile is not given, the general config file is determined by
|
||||
the env. variable FRAPPY_CONFIG_FILE or <confdir>/generalConfig.cfg is used
|
||||
The following locations are searched for the generalConfig.cfg file.
|
||||
- command line argument
|
||||
- environment variable FRAPPY_CONFIG_FILE
|
||||
- git location (../cfg)
|
||||
- local location (cwd)
|
||||
- global location (/etc/frappy)
|
||||
|
||||
if a configfile is given, the values from the FRAPPY section are
|
||||
overriding above defaults
|
||||
@ -69,37 +70,12 @@ class GeneralConfig:
|
||||
finally, the env. variables FRAPPY_PIDDIR, FRAPPY_LOGDIR and FRAPPY_CONFDIR
|
||||
are overriding these values when given
|
||||
"""
|
||||
|
||||
configfile = self._get_file_location(configfile)
|
||||
|
||||
cfg = {}
|
||||
mandatory = 'piddir', 'logdir', 'confdir'
|
||||
repodir = Path(__file__).parents[2].expanduser().resolve()
|
||||
# create default paths
|
||||
if (Path(sys.executable).suffix == ".exe"
|
||||
and not Path(sys.executable).name.startswith('python')):
|
||||
# special MS windows environment
|
||||
confdir = Path('./')
|
||||
self.update_defaults(piddir=Path('./'), logdir=Path('./log'))
|
||||
elif path.exists(path.join(repodir, 'cfg')):
|
||||
# running from git repo
|
||||
confdir = repodir / 'cfg'
|
||||
# take logdir and piddir from <repodir>/cfg/generalConfig.cfg
|
||||
else:
|
||||
# running on installed system (typically with systemd)
|
||||
self.update_defaults(
|
||||
piddir=Path('/var/run/frappy'),
|
||||
logdir=Path('/var/log'),
|
||||
)
|
||||
confdir = Path('/etc/frappy')
|
||||
self.set_default('confdir', confdir)
|
||||
if configfile is None:
|
||||
configfile = environ.get('FRAPPY_CONFIG_FILE')
|
||||
if configfile:
|
||||
configfile = Path(configfile).expanduser()
|
||||
if not configfile.exists():
|
||||
raise FileNotFoundError(configfile)
|
||||
else:
|
||||
configfile = confdir / 'generalConfig.cfg'
|
||||
if not configfile.exists():
|
||||
configfile = None
|
||||
if configfile:
|
||||
parser = ConfigParser()
|
||||
parser.optionxform = str
|
||||
@ -113,25 +89,63 @@ class GeneralConfig:
|
||||
cfg[key] = ':'.join(path.expanduser(v) for v in value.split(':'))
|
||||
if cfg.get('confdir') is None:
|
||||
cfg['confdir'] = configfile.parent
|
||||
# environment variables will overwrite the config file
|
||||
missing_keys = []
|
||||
for key in mandatory:
|
||||
env = environ.get(f'FRAPPY_{key.upper()}')
|
||||
if env is not None:
|
||||
cfg[key] = Path(env)
|
||||
missing_keys = [
|
||||
key for key in mandatory
|
||||
if cfg.get(key) is None and self.defaults.get(key) is None
|
||||
]
|
||||
env = environ.get(f'FRAPPY_{key.upper()}') or cfg.get(key)
|
||||
if env is None:
|
||||
if self.defaults.get(key) is None:
|
||||
missing_keys.append(key)
|
||||
else:
|
||||
if not isinstance(env, Path):
|
||||
if key == 'confdir':
|
||||
env = [Path(v) for v in env.split(':')]
|
||||
else:
|
||||
env = Path(env)
|
||||
cfg[key] = env
|
||||
if missing_keys:
|
||||
if configfile:
|
||||
raise KeyError(f"missing value for {' and '.join(missing_keys)} in {configfile}")
|
||||
raise KeyError('missing %s'
|
||||
% ' and '.join('FRAPPY_%s' % k.upper() for k in missing_keys))
|
||||
|
||||
if len(missing_keys) < 3:
|
||||
# user specified at least one env variable already
|
||||
missing = ' (missing %s)' % ', '.join('FRAPPY_%s' % k.upper() for k in missing_keys)
|
||||
else:
|
||||
missing = ''
|
||||
raise FileNotFoundError(
|
||||
'Could not determine config file location for the general frappy config. '
|
||||
f'Provide a config file or all required environment variables{missing}. '
|
||||
'For more information, see frappy-server --help.'
|
||||
)
|
||||
if 'confdir' in cfg and isinstance(cfg['confdir'], Path):
|
||||
cfg['confdir'] = [cfg['confdir']]
|
||||
# this is not customizable
|
||||
cfg['basedir'] = repodir
|
||||
self._config = cfg
|
||||
|
||||
def _get_file_location(self, configfile):
|
||||
"""Determining the defaultConfig.cfg location as documented in init()"""
|
||||
# given as command line arg
|
||||
if configfile and Path(configfile).exists():
|
||||
return configfile
|
||||
# if not given as argument, check different sources
|
||||
# env variable
|
||||
fromenv = environ.get('FRAPPY_CONFIG_FILE')
|
||||
if fromenv and Path(fromenv).exists():
|
||||
return fromenv
|
||||
# from ../cfg (there if running from checkout)
|
||||
repodir = Path(__file__).parents[2].expanduser().resolve()
|
||||
if (repodir / 'cfg' / 'generalConfig.cfg').exists():
|
||||
return repodir / 'cfg' / 'generalConfig.cfg'
|
||||
localfile = Path.cwd() / 'generalConfig.cfg'
|
||||
if localfile.exists():
|
||||
return localfile
|
||||
# TODO: leave this hardcoded?
|
||||
globalfile = Path('/etc/frappy/generalConfig.cfg')
|
||||
if globalfile.exists():
|
||||
return globalfile
|
||||
return None
|
||||
|
||||
def __getitem__(self, key):
|
||||
"""access for keys known to exist
|
||||
|
||||
|
@ -55,7 +55,7 @@ class MultiEvent(threading.Event):
|
||||
|
||||
def __init__(self, default_timeout=None):
|
||||
self.events = set()
|
||||
self._lock = threading.Lock()
|
||||
self._lock = threading.RLock()
|
||||
self.default_timeout = default_timeout or None # treat 0 as None
|
||||
self.name = None # default event name
|
||||
self._actions = [] # actions to be executed on trigger
|
||||
|
50
frappy/lib/units.py
Normal file
50
frappy/lib/units.py
Normal file
@ -0,0 +1,50 @@
|
||||
# *****************************************************************************
|
||||
#
|
||||
# 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>
|
||||
#
|
||||
# *****************************************************************************
|
||||
"""handling of prefixes of physical units"""
|
||||
|
||||
import re
|
||||
import prefixed
|
||||
|
||||
prefixed.SI_MAGNITUDE['u'] = 1e-6 # accept 'u' as replacement for 'µ'
|
||||
|
||||
|
||||
class NumberWithUnit:
|
||||
def __init__(self, *units):
|
||||
pfx = "|".join(prefixed.SI_MAGNITUDE)
|
||||
unt = "|".join(units)
|
||||
self.units = units
|
||||
self.pattern = re.compile(rf'\s*([+-]?\d*\.?\d*(?:[eE][+-]?\d+)?\s*(?:{pfx})?)({unt})\s*$')
|
||||
|
||||
def parse(self, value):
|
||||
"""parse and return number and value"""
|
||||
match = self.pattern.match(value)
|
||||
if not match:
|
||||
raise ValueError(f'{value!r} can not be interpreted as a number with unit {",".join(self.units)}')
|
||||
number, unit = match.groups()
|
||||
return prefixed.Float(number), unit
|
||||
|
||||
def getnum(self, value):
|
||||
"""parse and return value only"""
|
||||
return self.parse(value)[0]
|
||||
|
||||
|
||||
def format_with_unit(value, unit='', digits=3):
|
||||
return f'{prefixed.Float(value):.{digits}H}{unit}'
|
@ -33,7 +33,7 @@ from frappy.datatypes import ArrayOf, BoolType, EnumType, FloatRange, \
|
||||
from frappy.errors import BadValueError, CommunicationFailedError, ConfigError, \
|
||||
ProgrammingError, SECoPError, secop_error, RangeError
|
||||
from frappy.lib import formatException, mkthread, UniqueObject
|
||||
from frappy.params import Accessible, Command, Parameter, Limit
|
||||
from frappy.params import Accessible, Command, Parameter, Limit, PREDEFINED_ACCESSIBLES
|
||||
from frappy.properties import HasProperties, Property
|
||||
from frappy.logging import RemoteLogHandler
|
||||
|
||||
@ -41,6 +41,7 @@ from frappy.logging import RemoteLogHandler
|
||||
# from .interfaces import SECoP_BASE_CLASSES
|
||||
# WORKAROUND:
|
||||
SECoP_BASE_CLASSES = ['Readable', 'Writable', 'Drivable', 'Communicator']
|
||||
PREDEF_ORDER = list(PREDEFINED_ACCESSIBLES)
|
||||
|
||||
Done = UniqueObject('Done')
|
||||
"""a special return value for a read_<param>/write_<param> method
|
||||
@ -77,7 +78,7 @@ class HasAccessibles(HasProperties):
|
||||
for key, value in base.__dict__.items():
|
||||
if isinstance(value, Accessible):
|
||||
value.updateProperties(merged_properties.setdefault(key, {}))
|
||||
if base == cls and key not in accessibles:
|
||||
if base == cls and key not in accessibles and key not in PREDEFINED_ACCESSIBLES:
|
||||
new_names.append(key)
|
||||
accessibles[key] = value
|
||||
override_values.pop(key, None)
|
||||
@ -97,17 +98,15 @@ class HasAccessibles(HasProperties):
|
||||
aobj.merge(merged_properties[aname])
|
||||
accessibles[aname] = aobj
|
||||
|
||||
# rebuild order: (1) inherited items, (2) items from paramOrder, (3) new accessibles
|
||||
# move (2) to the end
|
||||
paramOrder = cls.__dict__.get('paramOrder', ())
|
||||
for aname in paramOrder:
|
||||
if aname in accessibles:
|
||||
accessibles.move_to_end(aname)
|
||||
# ignore unknown names
|
||||
# rebuild order:
|
||||
# (1) predefined accessibles, in a predefined order, (2) inherited custom items, (3) new custom items
|
||||
# move (1) to the beginning
|
||||
for key in reversed(PREDEF_ORDER):
|
||||
if key in accessibles:
|
||||
accessibles.move_to_end(key, last=False)
|
||||
# move (3) to the end
|
||||
for aname in new_names:
|
||||
if aname not in paramOrder:
|
||||
accessibles.move_to_end(aname)
|
||||
accessibles.move_to_end(aname)
|
||||
cls.accessibles = accessibles
|
||||
|
||||
cls.wrappedAttributes = {'isWrapped': True}
|
||||
@ -189,10 +188,8 @@ class HasAccessibles(HasProperties):
|
||||
if new_value is Done: # TODO: to be removed when all code using Done is updated
|
||||
return getattr(self, pname)
|
||||
new_value = value if new_value is None else validate(new_value)
|
||||
except Exception as e:
|
||||
if isinstance(e, SECoPError):
|
||||
e.raising_methods.append(f'{self.name}.write_{pname}')
|
||||
self.announceUpdate(pname, err=e)
|
||||
except SECoPError as e:
|
||||
e.raising_methods.append(f'{self.name}.write_{pname}')
|
||||
raise
|
||||
self.announceUpdate(pname, new_value, validate=False)
|
||||
return new_value
|
||||
@ -322,6 +319,8 @@ class Module(HasAccessibles):
|
||||
slowinterval = Property('poll interval for other parameters', FloatRange(0.1, 120), default=15)
|
||||
omit_unchanged_within = Property('default for minimum time between updates of unchanged values',
|
||||
NoneOr(FloatRange(0)), export=False, default=None)
|
||||
original_id = Property('original equipment_id\n\ngiven only if different from equipment_id of node',
|
||||
NoneOr(StringType()), default=None, export=True) # exported as custom property _original_id
|
||||
enablePoll = True
|
||||
|
||||
pollInfo = None
|
||||
@ -517,12 +516,14 @@ class Module(HasAccessibles):
|
||||
with self.updateLock:
|
||||
pobj = self.parameters[pname]
|
||||
timestamp = timestamp or time.time()
|
||||
changed = False
|
||||
if not err:
|
||||
try:
|
||||
if validate:
|
||||
value = pobj.datatype(value)
|
||||
except Exception as e:
|
||||
err = e
|
||||
# allow to assign an exception to trigger an error_update message
|
||||
err = value if isinstance(value, Exception) else e
|
||||
changed = False
|
||||
else:
|
||||
changed = pobj.value != value or pobj.readerror
|
||||
@ -656,10 +657,13 @@ class Module(HasAccessibles):
|
||||
self.pollInfo.pending_errors.discard(rfunc.__name__)
|
||||
except Exception as e:
|
||||
if getattr(e, 'report_error', True):
|
||||
self.log.debug('error in %r', rfunc)
|
||||
name = rfunc.__name__
|
||||
self.pollInfo.pending_errors.add(name) # trigger o.k. message after error is resolved
|
||||
if isinstance(e, SECoPError):
|
||||
e.raising_methods.append(name)
|
||||
if name == 'doPoll':
|
||||
# otherwise the method is already appended in rfunc
|
||||
e.raising_methods.append(f'{self.name}.{name}')
|
||||
if e.silent:
|
||||
self.log.debug('%s', e.format(False))
|
||||
else:
|
||||
@ -667,7 +671,7 @@ class Module(HasAccessibles):
|
||||
if raise_com_failed and isinstance(e, CommunicationFailedError):
|
||||
raise
|
||||
else:
|
||||
# not a SECoPError: this is proabably a programming error
|
||||
# not a SECoPError: this is probably a programming error
|
||||
# we want to log the traceback
|
||||
self.log.error('%s', formatException())
|
||||
|
||||
|
@ -259,8 +259,16 @@ class Parameter(Accessible):
|
||||
merged_properties.update(self.ownProperties)
|
||||
|
||||
def create_from_value(self, properties, value):
|
||||
"""return a clone with given value and inherited properties"""
|
||||
return self.clone(properties, value=self.datatype(value))
|
||||
"""return a clone with given value and inherited properties
|
||||
|
||||
called when a Parameter is overridden with a bare value
|
||||
"""
|
||||
try:
|
||||
value = self.datatype(value)
|
||||
except Exception as e:
|
||||
raise ProgrammingError(f'{self.name} must be assigned to a Parameter '
|
||||
f'or a value compatible with {type(self.datatype).__name__}') from e
|
||||
return self.clone(properties, value=value)
|
||||
|
||||
def merge(self, merged_properties):
|
||||
"""merge with inherited properties
|
||||
@ -462,7 +470,8 @@ class Command(Accessible):
|
||||
def create_from_value(self, properties, value):
|
||||
"""return a clone with given value and inherited properties
|
||||
|
||||
this is needed when the @Command is missing on a method overriding a command"""
|
||||
called when the @Command is missing on a method overriding a command
|
||||
"""
|
||||
if not callable(value):
|
||||
raise ProgrammingError(f'{self.name} = {value!r} is overriding a Command')
|
||||
return self.clone(properties)(value)
|
||||
@ -564,15 +573,18 @@ class Limit(Parameter):
|
||||
|
||||
|
||||
# list of predefined accessibles with their type
|
||||
# the order of this list affects the parameter order
|
||||
PREDEFINED_ACCESSIBLES = {
|
||||
'value': Parameter,
|
||||
'status': Parameter,
|
||||
'target': Parameter,
|
||||
'pollinterval': Parameter,
|
||||
'ramp': Parameter,
|
||||
'user_ramp': Parameter,
|
||||
'use_ramp': Parameter,
|
||||
'setpoint': Parameter,
|
||||
'time_to_target': Parameter,
|
||||
'controlled_by': Parameter,
|
||||
'control_active': Parameter,
|
||||
'unit': Parameter, # reserved name
|
||||
'loglevel': Parameter, # reserved name
|
||||
'mode': Parameter, # reserved name
|
||||
|
@ -97,7 +97,9 @@ class Playground(Server):
|
||||
for modname, cfg in kwds.items():
|
||||
cfg.setdefault('description', modname)
|
||||
self.log = logger.log
|
||||
self.node_cfg = {'cls': 'frappy.playground.Dispatcher', 'name': 'playground'}
|
||||
self.node_cfg = {'cls': 'frappy.playground.Dispatcher',
|
||||
'name': 'playground',
|
||||
'description': 'playground'}
|
||||
self._testonly = True # stops before calling startModule
|
||||
self._cfgfiles = 'main'
|
||||
self.module_cfg = {}
|
||||
@ -106,6 +108,7 @@ class Playground(Server):
|
||||
if cfgfiles:
|
||||
if not generalConfig.initialized:
|
||||
generalConfig.init()
|
||||
cfgfiles = [s.strip() for s in cfgfiles.split(',')]
|
||||
merged_cfg = load_config(cfgfiles, self.log)
|
||||
merged_cfg.pop('node', None)
|
||||
self.module_cfg = merged_cfg
|
||||
|
@ -143,6 +143,7 @@ class HasProperties(HasDescriptors):
|
||||
try:
|
||||
# try to apply bare value to Property
|
||||
po.value = po.datatype.validate(value)
|
||||
setattr(cls, pn, po) # replace bare value by updated Property
|
||||
except BadValueError:
|
||||
if callable(value):
|
||||
raise ProgrammingError(f'method {cls.__name__}.{pn} collides with property of {base.__name__}') from None
|
||||
|
108
frappy/protocol/discovery.py
Normal file
108
frappy/protocol/discovery.py
Normal file
@ -0,0 +1,108 @@
|
||||
# *****************************************************************************
|
||||
#
|
||||
# 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:
|
||||
# Alexander Zaft <a.zaft@fz-juelich.de>
|
||||
#
|
||||
# *****************************************************************************
|
||||
"""Discovery via UDP broadcasts."""
|
||||
|
||||
import os
|
||||
import json
|
||||
import socket
|
||||
|
||||
from frappy.lib import closeSocket
|
||||
from frappy.protocol.interface.tcp import format_address
|
||||
from frappy.version import get_version
|
||||
|
||||
UDP_PORT = 10767
|
||||
MAX_MESSAGE_LEN = 508
|
||||
|
||||
|
||||
class UDPListener:
|
||||
def __init__(self, equipment_id, description, ifaces, logger, *,
|
||||
startup_broadcast=True):
|
||||
self.equipment_id = equipment_id
|
||||
self.log = logger
|
||||
self.description = description or ''
|
||||
self.firmware = 'FRAPPY ' + get_version()
|
||||
self.ports = [int(iface.split('://')[1])
|
||||
for iface in ifaces if iface.startswith('tcp')]
|
||||
self.running = False
|
||||
self.is_enabled = True
|
||||
self.startup_broadcast = startup_broadcast
|
||||
|
||||
self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
if os.name == 'nt':
|
||||
self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
else:
|
||||
self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
|
||||
if startup_broadcast:
|
||||
self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
|
||||
self.sock.bind(('0.0.0.0', UDP_PORT))
|
||||
|
||||
available = MAX_MESSAGE_LEN - len(self._getMessage(2**16-1))
|
||||
if available < 0:
|
||||
desc_length = len(self.description.encode('utf-8'))
|
||||
if available + desc_length < 0:
|
||||
self.log.warn('Equipment id and firmware name exceed 430 byte '
|
||||
'limit, not answering to udp discovery')
|
||||
self.is_enabled = False
|
||||
else:
|
||||
self.log.debug('truncating description for udp discovery')
|
||||
# with errors='ignore', cutting insite a utf-8 glyph will not
|
||||
# report an error but remove the rest of the glyph from the
|
||||
# output.
|
||||
self.description = self.description \
|
||||
.encode('utf-8')[:available] \
|
||||
.decode('utf-8', errors='ignore')
|
||||
|
||||
def _getMessage(self, port):
|
||||
return json.dumps({
|
||||
'SECoP': 'node',
|
||||
'port': port,
|
||||
'equipment_id': self.equipment_id,
|
||||
'firmware': self.firmware,
|
||||
'description': self.description,
|
||||
}, ensure_ascii=False, separators=(',', ':')).encode('utf-8')
|
||||
|
||||
def run(self):
|
||||
if self.startup_broadcast:
|
||||
self.log.debug('Sending startup UDP broadcast.')
|
||||
for port in self.ports:
|
||||
self.sock.sendto(self._getMessage(port),
|
||||
('255.255.255.255', UDP_PORT))
|
||||
self.running = True
|
||||
while self.running and self.is_enabled:
|
||||
try:
|
||||
msg, addr = self.sock.recvfrom(1024)
|
||||
except socket.error:
|
||||
return
|
||||
try:
|
||||
request = json.loads(msg.decode('utf-8'))
|
||||
except json.JSONDecodeError:
|
||||
continue
|
||||
if 'SECoP' not in request or request['SECoP'] != 'discover':
|
||||
continue
|
||||
self.log.debug('Answering UDP broadcast from: %s',
|
||||
format_address(addr))
|
||||
for port in self.ports:
|
||||
self.sock.sendto(self._getMessage(port), addr)
|
||||
|
||||
def shutdown(self):
|
||||
self.log.debug('shut down of discovery listener')
|
||||
self.running = False
|
||||
closeSocket(self.sock)
|
@ -77,26 +77,30 @@ class SecopClient(frappy.client.SecopClient):
|
||||
|
||||
|
||||
class Router(frappy.protocol.dispatcher.Dispatcher):
|
||||
singlenode = None
|
||||
|
||||
def __init__(self, name, logger, options, srv):
|
||||
"""initialize router
|
||||
|
||||
Use the option node = <uri> for a single node or
|
||||
nodes = ["<uri1>", "<uri2>" ...] for multiple nodes.
|
||||
If a single node is given, the node properties are forwarded transparently,
|
||||
If a single node is given, and no more additional modules are given,
|
||||
the node properties are forwarded transparently,
|
||||
else the description property is a merge from all client node properties.
|
||||
"""
|
||||
uri = options.pop('node', None)
|
||||
uris = options.pop('nodes', None)
|
||||
if uri and uris:
|
||||
raise frappy.errors.ConfigError('can not specify node _and_ nodes')
|
||||
try:
|
||||
if uris is not None:
|
||||
if isinstance(uris, str) or not all(isinstance(v, str) for v in uris) or uri:
|
||||
raise TypeError()
|
||||
elif isinstance(uri, str):
|
||||
uris = [uri]
|
||||
else:
|
||||
raise TypeError()
|
||||
except Exception as e:
|
||||
raise frappy.errors.ConfigError("a router needs either 'node' as a string'"
|
||||
"' or 'nodes' as a list of strings") from e
|
||||
super().__init__(name, logger, options, srv)
|
||||
if uri:
|
||||
self.nodes = [SecopClient(uri, logger.getChild('routed'), self)]
|
||||
self.singlenode = self.nodes[0]
|
||||
else:
|
||||
self.nodes = [SecopClient(uri, logger.getChild(f'routed{i}'), self) for i, uri in enumerate(uris)]
|
||||
self.nodes = [SecopClient(uri, logger.getChild(f'routed{i}'), self) for i, uri in enumerate(uris)]
|
||||
# register callbacks
|
||||
for node in self.nodes:
|
||||
node.register_callback(None, node.updateEvent, node.descriptiveDataChange, node.nodeStateChange)
|
||||
@ -127,8 +131,8 @@ class Router(frappy.protocol.dispatcher.Dispatcher):
|
||||
logger.warning('can not connect to node %r', node.nodename)
|
||||
|
||||
def handle_describe(self, conn, specifier, data):
|
||||
if self.singlenode:
|
||||
return DESCRIPTIONREPLY, specifier, self.singlenode.descriptive_data
|
||||
if len(self.nodes) == 1 and not self.secnode.modules:
|
||||
return DESCRIPTIONREPLY, specifier, self.nodes[0].descriptive_data
|
||||
reply = super().handle_describe(conn, specifier, data)
|
||||
result = reply[2]
|
||||
allmodules = result.get('modules', {})
|
||||
@ -144,6 +148,7 @@ class Router(frappy.protocol.dispatcher.Dispatcher):
|
||||
self.log.info('module %r is already present', modname)
|
||||
else:
|
||||
allmodules[modname] = moddesc
|
||||
moddesc.setdefault('original_id', equipment_id)
|
||||
result['modules'] = allmodules
|
||||
result['description'] = '\n\n'.join(node_description)
|
||||
return DESCRIPTIONREPLY, specifier, result
|
||||
|
@ -54,8 +54,17 @@ class SecNode:
|
||||
self.name = name
|
||||
|
||||
def add_secnode_property(self, prop, value):
|
||||
"""Add SECNode property. If starting with an underscore, it is exported
|
||||
in the description."""
|
||||
self.nodeprops[prop] = value
|
||||
|
||||
def get_secnode_property(self, prop):
|
||||
"""Get SECNode property.
|
||||
|
||||
Returns None if not present.
|
||||
"""
|
||||
return self.nodeprops.get(prop)
|
||||
|
||||
def get_module(self, modulename):
|
||||
""" Returns a fully initialized module. Or None, if something went
|
||||
wrong during instatiating/initializing the module."""
|
||||
|
103
frappy/server.py
103
frappy/server.py
@ -26,6 +26,7 @@ import os
|
||||
import signal
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
|
||||
import mlzlog
|
||||
|
||||
@ -36,6 +37,7 @@ from frappy.lib.multievent import MultiEvent
|
||||
from frappy.logging import init_remote_logging
|
||||
from frappy.params import PREDEFINED_ACCESSIBLES
|
||||
from frappy.secnode import SecNode
|
||||
from frappy.protocol.discovery import UDPListener
|
||||
|
||||
try:
|
||||
from daemon import DaemonContext
|
||||
@ -67,9 +69,9 @@ class Server:
|
||||
- name: the node name
|
||||
- parent_logger: the logger to inherit from. a handler is installed by
|
||||
the server to provide remote logging
|
||||
- cfgfiles: if not given, defaults to name
|
||||
may be a comma separated list of cfg files
|
||||
items ending with .cfg are taken as paths, else .cfg is appended and
|
||||
- cfgfiles: if not given, defaults to [name]
|
||||
may be a list of cfg files
|
||||
items ending with .py are taken as paths, else _cfg.py is appended and
|
||||
files are looked up in the config path retrieved from the general config
|
||||
- interface: an uri of the from tcp://<port> or a bare port number for tcp
|
||||
if not given, the interface is taken from the config file. In case of
|
||||
@ -93,9 +95,9 @@ class Server:
|
||||
self._testonly = testonly
|
||||
|
||||
if not cfgfiles:
|
||||
cfgfiles = name
|
||||
cfgfiles = [name]
|
||||
# sanitize name (in case it is a cfgfile)
|
||||
name = os.path.splitext(os.path.basename(name))[0]
|
||||
self.name = name = os.path.splitext(os.path.basename(name))[0]
|
||||
if isinstance(parent_logger, mlzlog.MLZLogger):
|
||||
self.log = parent_logger.getChild(name, True)
|
||||
else:
|
||||
@ -106,7 +108,6 @@ class Server:
|
||||
self.node_cfg = merged_cfg.pop('node')
|
||||
self.module_cfg = merged_cfg
|
||||
if interface:
|
||||
self.node_cfg['equipment_id'] = name
|
||||
self.node_cfg['interface'] = str(interface)
|
||||
elif not self.node_cfg.get('interface'):
|
||||
raise ConfigError('No interface specified in configuration or arguments!')
|
||||
@ -116,6 +117,8 @@ class Server:
|
||||
signal.signal(signal.SIGINT, self.signal_handler)
|
||||
signal.signal(signal.SIGTERM, self.signal_handler)
|
||||
|
||||
self.discovery = None
|
||||
|
||||
def signal_handler(self, num, frame):
|
||||
if hasattr(self, 'interfaces') and self.interfaces:
|
||||
self.shutdown()
|
||||
@ -164,6 +167,7 @@ class Server:
|
||||
print(formatException(verbose=True))
|
||||
raise
|
||||
|
||||
# client interfaces
|
||||
self.interfaces = {}
|
||||
iface_threads = []
|
||||
# default_timeout 12 sec: TCPServer might need up to 10 sec to wait for Address no longer in use
|
||||
@ -171,17 +175,20 @@ class Server:
|
||||
lock = threading.Lock()
|
||||
failed = {}
|
||||
interfaces = [self.node_cfg['interface']] + self.node_cfg.get('secondary', [])
|
||||
# TODO: check if only one interface of each type is open?
|
||||
for interface in interfaces:
|
||||
opts = {'uri': interface}
|
||||
t = mkthread(
|
||||
self._interfaceThread,
|
||||
opts,
|
||||
lock,
|
||||
failed,
|
||||
interfaces_started.get_trigger(),
|
||||
)
|
||||
iface_threads.append(t)
|
||||
# allow missing "tcp://"
|
||||
interfaces = [iface if '://' in iface else f'tcp://{iface}' for iface in interfaces]
|
||||
with lock:
|
||||
for interface in interfaces:
|
||||
opts = {'uri': interface}
|
||||
t = mkthread(
|
||||
self._interfaceThread,
|
||||
opts,
|
||||
lock,
|
||||
failed,
|
||||
interfaces,
|
||||
interfaces_started.get_trigger(),
|
||||
)
|
||||
iface_threads.append(t)
|
||||
if not interfaces_started.wait():
|
||||
for iface in interfaces:
|
||||
if iface not in failed and iface not in self.interfaces:
|
||||
@ -192,15 +199,33 @@ class Server:
|
||||
if not self.interfaces:
|
||||
self.log.error('no interface started')
|
||||
return
|
||||
self.secnode.add_secnode_property('_interfaces', list(self.interfaces.keys()))
|
||||
self.log.info('startup done with interface(s) %s' % ', '.join(self.interfaces))
|
||||
self.secnode.add_secnode_property('_interfaces', list(self.interfaces))
|
||||
self.log.info('startup done with interface(s) %s',
|
||||
', '.join(self.interfaces))
|
||||
|
||||
# start discovery interface when we know where we listen
|
||||
self.discovery = UDPListener(
|
||||
self.secnode.equipment_id,
|
||||
self.secnode.get_secnode_property('description'),
|
||||
list(self.interfaces),
|
||||
self.log.getChild('discovery')
|
||||
)
|
||||
mkthread(self.discovery.run)
|
||||
|
||||
if systemd:
|
||||
systemd.daemon.notify("READY=1\nSTATUS=accepting requests")
|
||||
|
||||
# we wait here on the thread finishing, which means we got a
|
||||
# signal to shut down or an exception was raised
|
||||
for t in iface_threads:
|
||||
t.join()
|
||||
if os.name == 'nt':
|
||||
# workaround: thread.join() on Windows blocks and is not
|
||||
# interruptible by the signal handler, so loop and check
|
||||
# periodically whether the interfaces are still running.
|
||||
while True:
|
||||
time.sleep(1)
|
||||
if not interfaces:
|
||||
break
|
||||
else:
|
||||
for t in iface_threads:
|
||||
t.join()
|
||||
|
||||
while failed:
|
||||
iface, err = failed.popitem()
|
||||
@ -208,11 +233,11 @@ class Server:
|
||||
|
||||
self.log.info('stopped listening, cleaning up %d modules',
|
||||
len(self.secnode.modules))
|
||||
# if systemd:
|
||||
# if self._restart:
|
||||
# systemd.daemon.notify('RELOADING=1')
|
||||
# else:
|
||||
# systemd.daemon.notify('STOPPING=1')
|
||||
if systemd:
|
||||
if self._restart:
|
||||
systemd.daemon.notify('RELOADING=1')
|
||||
else:
|
||||
systemd.daemon.notify('STOPPING=1')
|
||||
self.secnode.shutdown_modules()
|
||||
if self._restart:
|
||||
self.restart_hook()
|
||||
@ -227,13 +252,14 @@ class Server:
|
||||
|
||||
def shutdown(self):
|
||||
self._restart = False
|
||||
if self.discovery:
|
||||
self.discovery.shutdown()
|
||||
for iface in self.interfaces.values():
|
||||
iface.shutdown()
|
||||
|
||||
def _interfaceThread(self, opts, lock, failed, start_cb):
|
||||
def _interfaceThread(self, opts, lock, failed, interfaces, start_cb):
|
||||
iface = opts['uri']
|
||||
scheme, _, _ = iface.rpartition('://')
|
||||
scheme = scheme or 'tcp'
|
||||
scheme = iface.split('://')[0]
|
||||
cls = get_class(self.INTERFACES[scheme])
|
||||
try:
|
||||
with cls(scheme, self.log.getChild(scheme), opts, self) as interface:
|
||||
@ -247,9 +273,12 @@ class Server:
|
||||
except Exception as e:
|
||||
with lock:
|
||||
failed[iface] = e
|
||||
start_cb()
|
||||
return
|
||||
self.log.info(f'stopped {iface}')
|
||||
interfaces.remove(iface)
|
||||
start_cb() # callback should also be called on failure
|
||||
else:
|
||||
with lock:
|
||||
interfaces.remove(iface)
|
||||
self.log.info(f'stopped {iface}')
|
||||
|
||||
def _processCfg(self):
|
||||
"""Processes the module configuration.
|
||||
@ -263,10 +292,8 @@ class Server:
|
||||
errors = []
|
||||
opts = dict(self.node_cfg)
|
||||
cls = get_class(opts.pop('cls'))
|
||||
name = opts.pop('name', self._cfgfiles)
|
||||
# TODO: opts not in both
|
||||
self.secnode = SecNode(name, self.log.getChild('secnode'), opts, self)
|
||||
self.dispatcher = cls(name, self.log.getChild('dispatcher'), opts, self)
|
||||
self.secnode = SecNode(self.name, self.log.getChild('secnode'), opts, self)
|
||||
self.dispatcher = cls(self.name, self.log.getChild('dispatcher'), opts, self)
|
||||
|
||||
# add other options as SECNode properties, those with '_' prefixed will
|
||||
# get exported
|
||||
|
@ -69,14 +69,16 @@ class TemperatureLoop(TemperatureSensor, Drivable):
|
||||
# lakeshore loop number to be used for this module
|
||||
loop = Property('lakeshore loop', IntRange(1, 2), default=1)
|
||||
target = Parameter(datatype=FloatRange(unit='K', min=0, max=1500))
|
||||
heater_range = Property('heater power range', IntRange(0, 5)) # max. 3 on LakeShore 336
|
||||
heater_range = Property('heater power range', IntRange(0, 3), readonly=False)
|
||||
tolerance = Parameter('convergence criterion', FloatRange(0), default=0.1, readonly=False)
|
||||
_driving = False
|
||||
|
||||
def write_heater_range(self, value):
|
||||
self.communicate(f'RANGE {self.loop},{value};RANGE?{self.loop}')
|
||||
|
||||
def write_target(self, target):
|
||||
# reactivate heater in case it was switched off
|
||||
# the command has to be changed in case of model 340 to f'RANGE {self.heater_range};RANGE?'
|
||||
self.communicate(f'RANGE {self.loop},{self.heater_range};RANGE?{self.loop}')
|
||||
self.write_heater_range(self.heater_range)
|
||||
self.communicate(f'SETP {self.loop},{target};*OPC?')
|
||||
self._driving = True
|
||||
# Setting the status attribute triggers an update message for the SECoP status
|
||||
@ -85,23 +87,21 @@ class TemperatureLoop(TemperatureSensor, Drivable):
|
||||
return target
|
||||
|
||||
def read_status(self):
|
||||
code = int(self.communicate(f'RDGST?{self.channel}'))
|
||||
if code >= 128:
|
||||
text = 'units overrange'
|
||||
elif code >= 64:
|
||||
text = 'units zero'
|
||||
elif code >= 32:
|
||||
text = 'temperature overrange'
|
||||
elif code >= 16:
|
||||
text = 'temperature underrange'
|
||||
elif code % 2:
|
||||
# ignore 'old reading', as this may happen in normal operation
|
||||
text = 'invalid reading'
|
||||
elif abs(self.target - self.value) > self.tolerance:
|
||||
status = super().read_status()
|
||||
if status[0] == ERROR:
|
||||
return status
|
||||
if abs(self.target - self.value) > self.tolerance:
|
||||
if self._driving:
|
||||
return BUSY, 'approaching setpoint'
|
||||
return WARN, 'temperature out of tolerance'
|
||||
else: # within tolerance: simple convergence criterion
|
||||
self._driving = False
|
||||
return IDLE, ''
|
||||
return ERROR, text
|
||||
# within tolerance: simple convergence criterion
|
||||
self._driving = False
|
||||
return IDLE, ''
|
||||
|
||||
|
||||
class TemperatureLoop340(TemperatureLoop):
|
||||
# slightly different behaviour for model 340
|
||||
heater_range = Property('heater power range', IntRange(0, 5))
|
||||
|
||||
def write_heater_range(self, value):
|
||||
self.communicate(f'RANGE {value};RANGE?')
|
||||
|
@ -26,7 +26,7 @@ from frappy.datatypes import FloatRange, StringType, ValueType, TupleOf, StructO
|
||||
from frappy.modules import Communicator, Drivable, Parameter, Property, Readable, Module, Attached
|
||||
from frappy.params import Command
|
||||
from frappy.dynamic import Pinata
|
||||
from frappy.errors import RangeError
|
||||
from frappy.errors import RangeError, HardwareError
|
||||
|
||||
class Pin(Pinata):
|
||||
def scanModules(self):
|
||||
@ -72,8 +72,12 @@ class Heater(Drivable):
|
||||
maxheaterpower = Parameter('maximum allowed heater power',
|
||||
datatype=FloatRange(0, 100), unit='W',
|
||||
)
|
||||
error_message = Parameter('simulated error message', StringType(),
|
||||
default='', readonly=False)
|
||||
|
||||
def read_value(self):
|
||||
if self.error_message:
|
||||
raise HardwareError(self.error_message)
|
||||
return round(100 * random.random(), 1)
|
||||
|
||||
def write_target(self, target):
|
||||
|
@ -1,25 +1,37 @@
|
||||
"""
|
||||
Created on Tue Nov 26 15:42:43 2019
|
||||
|
||||
@author: tartarotti_d-adm
|
||||
"""
|
||||
|
||||
|
||||
# *****************************************************************************
|
||||
# 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:
|
||||
# Damaris Tartarotti Maimone
|
||||
# Markus Zolliker <markus.zolliker@psi.ch>
|
||||
# *****************************************************************************
|
||||
"""Wrapper for the ADQ data acquisition card for ultrasound"""
|
||||
import sys
|
||||
import atexit
|
||||
import signal
|
||||
import time
|
||||
import numpy as np
|
||||
import ctypes as ct
|
||||
import time
|
||||
from numpy import sqrt, arctan2, sin, cos
|
||||
from scipy.signal import butter, filtfilt
|
||||
|
||||
#from pylab import *
|
||||
|
||||
from scipy import signal
|
||||
|
||||
#ADQAPI = ct.cdll.LoadLibrary("ADQAPI.dll")
|
||||
ADQAPI = ct.cdll.LoadLibrary("libadq.so.0")
|
||||
|
||||
#For different trigger modes
|
||||
# For different trigger modes
|
||||
SW_TRIG = 1
|
||||
EXT_TRIG_1 = 2 #This external trigger does not work if the level of the trigger is very close to 0.5V. Now we have it close to 3V, and it works
|
||||
# The following external trigger does not work if the level of the trigger is very close to 0.5V.
|
||||
# Now we have it close to 3V, and it works
|
||||
EXT_TRIG_1 = 2
|
||||
EXT_TRIG_2 = 7
|
||||
EXT_TRIG_3 = 8
|
||||
LVL_TRIG = 3
|
||||
@ -27,29 +39,36 @@ INT_TRIG = 4
|
||||
LVL_FALLING = 0
|
||||
LVL_RISING = 1
|
||||
|
||||
#samples_per_record=16384
|
||||
ADQ_CLOCK_INT_INTREF = 0 # internal clock source
|
||||
ADQ_CLOCK_EXT_REF = 1 # internal clock source, external reference
|
||||
ADQ_CLOCK_EXT_CLOCK = 2 # External clock source
|
||||
|
||||
ADQ_TRANSFER_MODE_NORMAL = 0x00
|
||||
ADQ_CHANNELS_MASK = 0x3
|
||||
|
||||
#f_LO = 40
|
||||
|
||||
def butter_lowpass(cutoff, sr, order=5):
|
||||
nyq = 0.5 * sr
|
||||
normal_cutoff = cutoff / nyq
|
||||
b, a = signal.butter(order, normal_cutoff, btype = 'low', analog = False)
|
||||
return b, a
|
||||
GHz = 1e9
|
||||
|
||||
|
||||
class Adq(object):
|
||||
class Adq:
|
||||
sample_rate = 2 * GHz
|
||||
max_number_of_channels = 2
|
||||
samp_freq = 2
|
||||
#ndecimate = 50 # decimation ratio (2GHz / 40 MHz)
|
||||
ndecimate = 50
|
||||
ndecimate = 50 # decimation ratio (2GHz / 40 MHz)
|
||||
number_of_records = 1
|
||||
samples_per_record = 16384
|
||||
bw_cutoff = 10E6
|
||||
trigger = EXT_TRIG_1
|
||||
adq_num = 1
|
||||
UNDEFINED = -1
|
||||
IDLE = 0
|
||||
BUSY = 1
|
||||
READY = 2
|
||||
status = UNDEFINED
|
||||
data = None
|
||||
|
||||
def __init__(self):
|
||||
global ADQAPI
|
||||
ADQAPI = ct.cdll.LoadLibrary("libadq.so.0")
|
||||
|
||||
def __init__(self, number_of_records, samples_per_record, bw_cutoff):
|
||||
self.number_of_records = number_of_records
|
||||
self.samples_per_record = samples_per_record
|
||||
self.bw_cutoff = bw_cutoff
|
||||
ADQAPI.ADQAPI_GetRevision()
|
||||
|
||||
# Manually set return type from some ADQAPI functions
|
||||
@ -60,89 +79,79 @@ class Adq(object):
|
||||
# Create ADQControlUnit
|
||||
self.adq_cu = ct.c_void_p(ADQAPI.CreateADQControlUnit())
|
||||
ADQAPI.ADQControlUnit_EnableErrorTrace(self.adq_cu, 3, '.')
|
||||
self.adq_num = 1
|
||||
|
||||
# Find ADQ devices
|
||||
ADQAPI.ADQControlUnit_FindDevices(self.adq_cu)
|
||||
n_of_ADQ = ADQAPI.ADQControlUnit_NofADQ(self.adq_cu)
|
||||
if n_of_ADQ != 1:
|
||||
raise ValueError('number of ADQs must be 1, not %d' % n_of_ADQ)
|
||||
n_of_adq = ADQAPI.ADQControlUnit_NofADQ(self.adq_cu)
|
||||
if n_of_adq != 1:
|
||||
raise RuntimeError('number of ADQs must be 1, not %d' % n_of_adq)
|
||||
|
||||
rev = ADQAPI.ADQ_GetRevision(self.adq_cu, self.adq_num)
|
||||
revision = ct.cast(rev,ct.POINTER(ct.c_int))
|
||||
revision = ct.cast(rev, ct.POINTER(ct.c_int))
|
||||
print('\nConnected to ADQ #1')
|
||||
# Print revision information
|
||||
print('FPGA Revision: {}'.format(revision[0]))
|
||||
if (revision[1]):
|
||||
if revision[1]:
|
||||
print('Local copy')
|
||||
else :
|
||||
else:
|
||||
print('SVN Managed')
|
||||
if (revision[2]):
|
||||
if revision[2]:
|
||||
print('Mixed Revision')
|
||||
else :
|
||||
else:
|
||||
print('SVN Updated')
|
||||
print('')
|
||||
|
||||
ADQ_CLOCK_INT_INTREF = 0 #internal clock source
|
||||
ADQ_CLOCK_EXT_REF = 1 #internal clock source, external reference
|
||||
ADQ_CLOCK_EXT_CLOCK = 2 #External clock source
|
||||
ADQAPI.ADQ_SetClockSource(self.adq_cu, self.adq_num, ADQ_CLOCK_EXT_REF);
|
||||
ADQAPI.ADQ_SetClockSource(self.adq_cu, self.adq_num, ADQ_CLOCK_EXT_REF)
|
||||
|
||||
##########################
|
||||
# Test pattern
|
||||
#ADQAPI.ADQ_SetTestPatternMode(self.adq_cu, self.adq_num, 4)
|
||||
# ADQAPI.ADQ_SetTestPatternMode(self.adq_cu, self.adq_num, 4)
|
||||
##########################
|
||||
# Sample skip
|
||||
#ADQAPI.ADQ_SetSampleSkip(self.adq_cu, self.adq_num, 1)
|
||||
# ADQAPI.ADQ_SetSampleSkip(self.adq_cu, self.adq_num, 1)
|
||||
##########################
|
||||
|
||||
# Set trig mode
|
||||
self.trigger = EXT_TRIG_1
|
||||
#trigger = LVL_TRIG
|
||||
success = ADQAPI.ADQ_SetTriggerMode(self.adq_cu, self.adq_num, self.trigger)
|
||||
if (success == 0):
|
||||
print('ADQ_SetTriggerMode failed.')
|
||||
if (self.trigger == LVL_TRIG):
|
||||
success = ADQAPI.ADQ_SetLvlTrigLevel(self.adq_cu, self.adq_num, -100)
|
||||
if (success == 0):
|
||||
print('ADQ_SetLvlTrigLevel failed.')
|
||||
success = ADQAPI.ADQ_SetTrigLevelResetValue(self.adq_cu, self.adq_num, 1000)
|
||||
if (success == 0):
|
||||
print('ADQ_SetTrigLevelResetValue failed.')
|
||||
success = ADQAPI.ADQ_SetLvlTrigChannel(self.adq_cu, self.adq_num, 1)
|
||||
if (success == 0):
|
||||
print('ADQ_SetLvlTrigChannel failed.')
|
||||
success = ADQAPI.ADQ_SetLvlTrigEdge(self.adq_cu, self.adq_num, LVL_RISING)
|
||||
if (success == 0):
|
||||
print('ADQ_SetLvlTrigEdge failed.')
|
||||
elif (self.trigger == EXT_TRIG_1) :
|
||||
success = ADQAPI.ADQ_SetExternTrigEdge(self.adq_cu, self.adq_num,2)
|
||||
if (success == 0):
|
||||
print('ADQ_SetLvlTrigEdge failed.')
|
||||
# success = ADQAPI.ADQ_SetTriggerThresholdVoltage(self.adq_cu, self.adq_num, trigger, ct.c_double(0.2))
|
||||
# if (success == 0):
|
||||
# print('SetTriggerThresholdVoltage failed.')
|
||||
# set trigger mode
|
||||
if not ADQAPI.ADQ_SetTriggerMode(self.adq_cu, self.adq_num, self.trigger):
|
||||
raise RuntimeError('ADQ_SetTriggerMode failed.')
|
||||
if self.trigger == LVL_TRIG:
|
||||
if not ADQAPI.ADQ_SetLvlTrigLevel(self.adq_cu, self.adq_num, -100):
|
||||
raise RuntimeError('ADQ_SetLvlTrigLevel failed.')
|
||||
if not ADQAPI.ADQ_SetTrigLevelResetValue(self.adq_cu, self.adq_num, 1000):
|
||||
raise RuntimeError('ADQ_SetTrigLevelResetValue failed.')
|
||||
if not ADQAPI.ADQ_SetLvlTrigChannel(self.adq_cu, self.adq_num, 1):
|
||||
raise RuntimeError('ADQ_SetLvlTrigChannel failed.')
|
||||
if not ADQAPI.ADQ_SetLvlTrigEdge(self.adq_cu, self.adq_num, LVL_RISING):
|
||||
raise RuntimeError('ADQ_SetLvlTrigEdge failed.')
|
||||
elif self.trigger == EXT_TRIG_1:
|
||||
if not ADQAPI.ADQ_SetExternTrigEdge(self.adq_cu, self.adq_num, 2):
|
||||
raise RuntimeError('ADQ_SetLvlTrigEdge failed.')
|
||||
# if not ADQAPI.ADQ_SetTriggerThresholdVoltage(self.adq_cu, self.adq_num, trigger, ct.c_double(0.2)):
|
||||
# raise RuntimeError('SetTriggerThresholdVoltage failed.')
|
||||
print("CHANNEL:"+str(ct.c_int(ADQAPI.ADQ_GetLvlTrigChannel(self.adq_cu, self.adq_num))))
|
||||
self.setup_target_buffers()
|
||||
atexit.register(self.deletecu)
|
||||
signal.signal(signal.SIGTERM, lambda *_: sys.exit(0))
|
||||
|
||||
def setup_target_buffers(self):
|
||||
def init(self, samples_per_record=None, number_of_records=None):
|
||||
"""initialize dimensions"""
|
||||
if samples_per_record:
|
||||
self.samples_per_record = samples_per_record
|
||||
if number_of_records:
|
||||
self.number_of_records = number_of_records
|
||||
# Setup target buffers for data
|
||||
self.target_buffers=(ct.POINTER(ct.c_int16 * self.samples_per_record * self.number_of_records)
|
||||
* self.max_number_of_channels)()
|
||||
self.target_buffers = (ct.POINTER(ct.c_int16 * self.samples_per_record * self.number_of_records)
|
||||
* self.max_number_of_channels)()
|
||||
for bufp in self.target_buffers:
|
||||
bufp.contents = (ct.c_int16 * self.samples_per_record * self.number_of_records)()
|
||||
|
||||
def deletecu(self):
|
||||
# Only disarm trigger after data is collected
|
||||
# Only disarm trigger after data is collected
|
||||
ADQAPI.ADQ_DisarmTrigger(self.adq_cu, self.adq_num)
|
||||
ADQAPI.ADQ_MultiRecordClose(self.adq_cu, self.adq_num);
|
||||
ADQAPI.ADQ_MultiRecordClose(self.adq_cu, self.adq_num)
|
||||
# Delete ADQControlunit
|
||||
ADQAPI.DeleteADQControlUnit(self.adq_cu)
|
||||
|
||||
def start(self):
|
||||
"""start datat acquisition"""
|
||||
# samples_per_records = samples_per_record/number_of_records
|
||||
# Change number of pulses to be acquired acording to how many records are taken
|
||||
# Start acquisition
|
||||
ADQAPI.ADQ_MultiRecordSetup(self.adq_cu, self.adq_num,
|
||||
self.number_of_records,
|
||||
@ -150,79 +159,50 @@ class Adq(object):
|
||||
|
||||
ADQAPI.ADQ_DisarmTrigger(self.adq_cu, self.adq_num)
|
||||
ADQAPI.ADQ_ArmTrigger(self.adq_cu, self.adq_num)
|
||||
self.status = self.BUSY
|
||||
|
||||
def getdata(self):
|
||||
"""wait for aquisition to be finished and get data"""
|
||||
#start = time.time()
|
||||
while(ADQAPI.ADQ_GetAcquiredAll(self.adq_cu,self.adq_num) == 0):
|
||||
time.sleep(0.001)
|
||||
#if (self.trigger == SW_TRIG):
|
||||
# ADQAPI.ADQ_SWTrig(self.adq_cu, self.adq_num)
|
||||
#mid = time.time()
|
||||
status = ADQAPI.ADQ_GetData(self.adq_cu, self.adq_num, self.target_buffers,
|
||||
self.samples_per_record * self.number_of_records, 2,
|
||||
0, self.number_of_records, ADQ_CHANNELS_MASK,
|
||||
0, self.samples_per_record, ADQ_TRANSFER_MODE_NORMAL);
|
||||
#print(time.time()-mid,mid-start)
|
||||
if not status:
|
||||
raise ValueError('no succesS from ADQ_GetDATA')
|
||||
# Now this is an array with all records, but the time is artificial
|
||||
def get_status(self):
|
||||
"""check if ADQ card is busy"""
|
||||
if self.status == self.BUSY:
|
||||
if ADQAPI.ADQ_GetAcquiredAll(self.adq_cu, self.adq_num):
|
||||
self.status = self.READY
|
||||
else:
|
||||
if self.trigger == SW_TRIG:
|
||||
ADQAPI.ADQ_SWTrig(self.adq_cu, self.adq_num)
|
||||
return self.status
|
||||
|
||||
def get_data(self, dataclass, **kwds):
|
||||
"""when ready, get raw data from card, else return cached data
|
||||
|
||||
return
|
||||
"""
|
||||
if self.get_status() == self.READY:
|
||||
# Get data from ADQ
|
||||
if not ADQAPI.ADQ_GetData(self.adq_cu, self.adq_num, self.target_buffers,
|
||||
self.samples_per_record * self.number_of_records, 2,
|
||||
0, self.number_of_records, ADQ_CHANNELS_MASK,
|
||||
0, self.samples_per_record, ADQ_TRANSFER_MODE_NORMAL):
|
||||
raise RuntimeError('no success from ADQ_GetDATA')
|
||||
self.data = dataclass(self, **kwds)
|
||||
self.status = self.IDLE
|
||||
if self.status == self.UNDEFINED:
|
||||
raise RuntimeError('no data available yet')
|
||||
return self.data
|
||||
|
||||
|
||||
class PEdata:
|
||||
def __init__(self, adq):
|
||||
self.sample_rate = adq.sample_rate
|
||||
self.samp_freq = self.sample_rate / GHz
|
||||
self.number_of_records = adq.number_of_records
|
||||
data = []
|
||||
for ch in range(2):
|
||||
onedim = np.frombuffer(self.target_buffers[ch].contents, dtype=np.int16)
|
||||
data.append(onedim.reshape(self.number_of_records, self.samples_per_record) / float(2**14)) # 14 bits ADC
|
||||
return data
|
||||
onedim = np.frombuffer(adq.target_buffers[ch].contents, dtype=np.int16)
|
||||
data.append(onedim.reshape(adq.number_of_records, adq.samples_per_record) / float(2**14)) # 14 bits ADC
|
||||
# Now this is an array with all records, but the time is artificial
|
||||
self.data = data
|
||||
|
||||
def acquire(self):
|
||||
self.start()
|
||||
return self.getdata()
|
||||
'''
|
||||
def average(self, data):
|
||||
#Average over records
|
||||
return [data[ch].sum(axis=0) / self.number_of_records for ch in range(2)]
|
||||
|
||||
def iq(self, channel, f_LO):
|
||||
newx = np.linspace(0, self.samples_per_record /2, self.samples_per_record)
|
||||
s0 = channel /((2**16)/2)*0.5*np.exp(1j*2*np.pi*f_LO/(1e3)*newx)
|
||||
I0 = s0.real
|
||||
Q0 = s0.imag
|
||||
return I0, Q0
|
||||
|
||||
|
||||
def fitting(self, data, f_LO, ti, tf):
|
||||
# As long as data[0] is the pulse
|
||||
si = 2*ti #Those are for fitting the pulse
|
||||
sf = 2*tf
|
||||
phase = np.zeros(self.number_of_records)
|
||||
amplitude = np.zeros(self.number_of_records)
|
||||
offset = np.zeros(self.number_of_records)
|
||||
|
||||
for i in range(self.number_of_records):
|
||||
phase[i], amplitude[i] = sineW(data[0][i][si:sf],f_LO*1e-9,ti,tf)
|
||||
offset[i] = np.average(data[0][i][si:sf])
|
||||
return phase, amplitude, offset
|
||||
|
||||
|
||||
def waveIQ(self, channel,ti,f_LO):
|
||||
#channel is not the sample data
|
||||
t = np.linspace(0, self.samples_per_record /2, self.samples_per_record + 1)[:-1]
|
||||
si = 2*ti # Again that is where the wave pulse starts
|
||||
cwi = np.zeros((self.number_of_records,self.samples_per_record))
|
||||
cwq = np.zeros((self.number_of_records,self.samples_per_record))
|
||||
iq = np.zeros((self.number_of_records,self.samples_per_record))
|
||||
q = np.zeros((self.number_of_records,self.samples_per_record))
|
||||
for i in range(self.number_of_records):
|
||||
cwi[i] = np.zeros(self.samples_per_record)
|
||||
cwq[i] = np.zeros(self.samples_per_record)
|
||||
cwi[i] = amplitude[i]*sin(t[si:]*f_LO*1e-9*2*np.pi+phase[i]*np.pi/180)+bias[i]
|
||||
cwq[i] = amplitude[i]*sin(t[si:]*f_LO*1e-9*(2*np.pi+(phase[i]+90)*np.pi/180))+bias[i]
|
||||
|
||||
iq[i] = channel[i]*cwi[i]
|
||||
q[i] = channel[i]*cwq[i]
|
||||
|
||||
return iq,q
|
||||
'''
|
||||
def sinW(self,sig,freq,ti,tf):
|
||||
def sinW(self, sig, freq, ti, tf):
|
||||
# sig: signal array
|
||||
# freq
|
||||
# ti, tf: initial and end time
|
||||
@ -241,30 +221,28 @@ class Adq(object):
|
||||
def mix(self, sigin, sigout, freq, ti, tf):
|
||||
# sigin, sigout: signal array, incomping, output
|
||||
# freq
|
||||
# ti, tf: initial and end time if sigin
|
||||
# ti, tf: initial and end time of sigin
|
||||
a, b = self.sinW(sigin, freq, ti, tf)
|
||||
phase = arctan2(a,b) * 180 / np.pi
|
||||
amp = sqrt(a**2 + b**2)
|
||||
amp = np.sqrt(a**2 + b**2)
|
||||
a, b = a/amp, b/amp
|
||||
#si = int(ti * self.samp_freq)
|
||||
# si = int(ti * self.samp_freq)
|
||||
t = np.arange(len(sigout)) / self.samp_freq
|
||||
wave1 = sigout * (a * cos(2*np.pi*freq*t) + b * sin(2*np.pi*freq*t))
|
||||
wave2 = sigout * (a * sin(2*np.pi*freq*t) - b * cos(2*np.pi*freq*t))
|
||||
wave1 = sigout * (a * np.cos(2*np.pi*freq*t) + b * np.sin(2*np.pi*freq*t))
|
||||
wave2 = sigout * (a * np.sin(2*np.pi*freq*t) - b * np.cos(2*np.pi*freq*t))
|
||||
return wave1, wave2
|
||||
|
||||
def averageiq(self, data, freq, ti, tf):
|
||||
'''Average over records'''
|
||||
"""Average over records"""
|
||||
iorq = np.array([self.mix(data[0][i], data[1][i], freq, ti, tf) for i in range(self.number_of_records)])
|
||||
# iorq = np.array([self.mix(data[0][:], data[1][:], freq, ti, tf)])
|
||||
return iorq.sum(axis=0) / self.number_of_records
|
||||
|
||||
def filtro(self, iorq, cutoff):
|
||||
b, a = butter_lowpass(cutoff, self.samp_freq*1e9)
|
||||
|
||||
#ifi = np.array(signal.filtfilt(b,a,iorq[0]))
|
||||
#qf = np.array(signal.filtfilt(b,a,iorq[1]))
|
||||
iqf = [signal.filtfilt(b,a,iorq[i]) for i in np.arange(len(iorq))]
|
||||
|
||||
# butter lowpass
|
||||
nyq = 0.5 * self.sample_rate
|
||||
normal_cutoff = cutoff / nyq
|
||||
order = 5
|
||||
b, a = butter(order, normal_cutoff, btype='low', analog=False)
|
||||
iqf = [filtfilt(b, a, iorq[i]) for i in np.arange(len(iorq))]
|
||||
return iqf
|
||||
|
||||
def box(self, iorq, ti, tf):
|
||||
@ -273,21 +251,63 @@ class Adq(object):
|
||||
bxa = [sum(iorq[i][si:sf])/(sf-si) for i in np.arange(len(iorq))]
|
||||
return bxa
|
||||
|
||||
def gates_and_curves(self, data, freq, pulse, roi):
|
||||
def gates_and_curves(self, freq, pulse, roi, bw_cutoff):
|
||||
"""return iq values of rois and prepare plottable curves for iq"""
|
||||
times = []
|
||||
times.append(('aviq', time.time()))
|
||||
iq = self.averageiq(data,freq*1e-9,*pulse)
|
||||
times.append(('filtro', time.time()))
|
||||
iqf = self.filtro(iq,self.bw_cutoff)
|
||||
self.ndecimate = int(round(self.sample_rate / freq))
|
||||
# times = []
|
||||
# times.append(('aviq', time.time()))
|
||||
iq = self.averageiq(self.data, freq / GHz, *pulse)
|
||||
# times.append(('filtro', time.time()))
|
||||
iqf = self.filtro(iq, bw_cutoff)
|
||||
m = len(iqf[0]) // self.ndecimate
|
||||
times.append(('iqdec', time.time()))
|
||||
ll = m * self.ndecimate
|
||||
iqf = [iqfx[0:ll] for iqfx in iqf]
|
||||
# times.append(('iqdec', time.time()))
|
||||
iqd = np.average(np.resize(iqf, (2, m, self.ndecimate)), axis=2)
|
||||
t_axis = np.arange(m) * self.ndecimate / self.samp_freq
|
||||
pulsig = np.abs(data[0][0])
|
||||
times.append(('pulsig', time.time()))
|
||||
pulsig = np.abs(self.data[0][0])
|
||||
# times.append(('pulsig', time.time()))
|
||||
pulsig = np.average(np.resize(pulsig, (m, self.ndecimate)), axis=1)
|
||||
self.curves = (t_axis, iqd[0], iqd[1], pulsig)
|
||||
#print(times)
|
||||
return [self.box(iqf,*r) for r in roi]
|
||||
# print(times)
|
||||
return [self.box(iqf, *r) for r in roi]
|
||||
|
||||
|
||||
class RUSdata:
|
||||
def __init__(self, adq, freq, periods):
|
||||
self.sample_rate = adq.sample_rate
|
||||
self.freq = freq
|
||||
self.periods = periods
|
||||
self.samples_per_record = adq.samples_per_record
|
||||
input_signal = np.frombuffer(adq.target_buffers[0].contents, dtype=np.int16)
|
||||
output_signal = np.frombuffer(adq.target_buffers[1].contents, dtype=np.int16)
|
||||
complex_sinusoid = np.exp(1j * 2 * np.pi * self.freq / self.sample_rate * np.arange(len(input_signal)))
|
||||
self.input_mixed = input_signal * complex_sinusoid
|
||||
self.output_mixed = output_signal * complex_sinusoid
|
||||
self.input_mean = self.input_mixed.mean()
|
||||
self.output_mean = self.output_mixed.mean()
|
||||
self.iq = self.output_mean / self.input_mean
|
||||
|
||||
def get_reduced(self, mixed):
|
||||
"""get reduced array and normalize"""
|
||||
nper = self.samples_per_record // self.periods
|
||||
mean = mixed.mean()
|
||||
return mixed[:self.period * nper].reshape((-1, nper)).mean(axis=0) / mean
|
||||
|
||||
def calc_quality(self):
|
||||
"""get signal quality info
|
||||
|
||||
quality info (small values indicate good quality):
|
||||
- input_std and output_std:
|
||||
the imaginary part indicates deviations in phase
|
||||
the real part indicates deviations in amplitude
|
||||
- input_slope and output_slope:
|
||||
the imaginary part indicates a turning phase (rad/sec)
|
||||
the real part indicates changes in amplitude (0.01 ~= 1%/sec)
|
||||
"""
|
||||
reduced = self.get_reduced(self.input_mixed)
|
||||
self.input_stdev = reduced.std()
|
||||
|
||||
reduced = self.get_reduced(self.output_mixed)
|
||||
timeaxis = np.arange(len(reduced)) * self.sample_rate / self.freq
|
||||
self.output_slope = np.polyfit(timeaxis, reduced, 1)[0]
|
||||
|
@ -17,10 +17,39 @@
|
||||
# Markus Zolliker <markus.zolliker@psi.ch>
|
||||
# Jael Celia Lorenzana <jael-celia.lorenzana@psi.ch>
|
||||
# *****************************************************************************
|
||||
"""Powersupply B&K Precision BK168xB"""
|
||||
"""Powersupply B&K Precision BK168xB
|
||||
|
||||
The following lines are part of a config file for the frappy-server process
|
||||
The frappy server creates the following modules and refreshes the values with a refresh rate of 1 sec.
|
||||
The communication with the powersupply is established via serial over USB.
|
||||
The manual can be found here https://www.vectortechnologies.gr/images/products/2022/02/168xB_programming_manual.pdf
|
||||
|
||||
Mod('htr_io',
|
||||
'frappy_psi.bkpower.IO',
|
||||
'powersupply communicator',
|
||||
uri = 'serial:///dev/ttyUSBupper',
|
||||
)
|
||||
|
||||
Mod('htr',
|
||||
'frappy_psi.bkpower.Power',
|
||||
'heater power',
|
||||
io= 'htr_io',
|
||||
)
|
||||
|
||||
Mod('out',
|
||||
'frappy_psi.bkpower.Output',
|
||||
'heater output',
|
||||
io = 'htr_io',
|
||||
maxvolt = 50,
|
||||
maxcurrent = 2,
|
||||
)
|
||||
"""
|
||||
|
||||
|
||||
from frappy.core import StringIO, Readable, Parameter, FloatRange, Writable, HasIO, BoolType
|
||||
|
||||
|
||||
# define the IO class
|
||||
class IO(StringIO):
|
||||
end_of_line = ('OK\r', '\r')
|
||||
default_settings = {'baudrate': 9600}
|
||||
|
@ -22,6 +22,7 @@
|
||||
|
||||
import os
|
||||
import re
|
||||
from pathlib import Path
|
||||
from os.path import basename, dirname, exists, join
|
||||
|
||||
import numpy as np
|
||||
@ -31,13 +32,22 @@ from scipy.interpolate import PchipInterpolator, CubicSpline, PPoly # pylint: d
|
||||
from frappy.errors import ProgrammingError, RangeError
|
||||
from frappy.lib import clamp
|
||||
|
||||
|
||||
def identity(x):
|
||||
return x
|
||||
|
||||
|
||||
def exp10(x):
|
||||
return 10 ** np.array(x)
|
||||
|
||||
|
||||
to_scale = {
|
||||
'lin': lambda x: x,
|
||||
'log': lambda x: np.log10(x),
|
||||
'lin': identity,
|
||||
'log': np.log10,
|
||||
}
|
||||
from_scale = {
|
||||
'lin': lambda x: x,
|
||||
'log': lambda x: 10 ** np.array(x),
|
||||
'lin': identity,
|
||||
'log': exp10,
|
||||
}
|
||||
TYPES = [ # lakeshore type, inp-type, loglog
|
||||
('DT', 'si', False), # Si diode
|
||||
@ -55,7 +65,7 @@ TYPES = [ # lakeshore type, inp-type, loglog
|
||||
|
||||
OPTION_TYPE = {
|
||||
'loglog': 0, # boolean
|
||||
'extrange': 2, # tuple(min T, max T for extrapolation
|
||||
'extrange': 2, # tuple(min T, max T) for extrapolation
|
||||
'calibrange': 2, # tuple(min T, max T)
|
||||
}
|
||||
|
||||
@ -222,14 +232,6 @@ PARSERS = {
|
||||
}
|
||||
|
||||
|
||||
def check(x, y, islog):
|
||||
# check interpolation error
|
||||
yi = y[:-2] + (x[1:-1] - x[:-2]) * (y[2:] - y[:-2]) / (x[2:] - x[:-2])
|
||||
if islog:
|
||||
return sum((yi - y[1:-1]) ** 2)
|
||||
return sum((np.log10(yi) - np.log10(y[1:-1])) ** 2)
|
||||
|
||||
|
||||
def get_curve(newscale, curves):
|
||||
"""get curve from curve cache (converts not existing ones)
|
||||
|
||||
@ -247,6 +249,7 @@ def get_curve(newscale, curves):
|
||||
class CalCurve(HasOptions):
|
||||
EXTRAPOLATION_AMOUNT = 0.1
|
||||
MAX_EXTRAPOLATION_FACTOR = 2
|
||||
filename = None # calibration file
|
||||
|
||||
def __init__(self, calibspec=None, *, x=None, y=None, cubic_spline=True, **options):
|
||||
"""calibration curve
|
||||
@ -257,7 +260,7 @@ class CalCurve(HasOptions):
|
||||
[<full path> | <name>][,<key>=<value> ...]
|
||||
for <key>/<value> as in parser arguments
|
||||
:param x, y: x and y arrays (given instead of calibspec)
|
||||
:param cubic_split: set to False for always using Pchip interpolation
|
||||
:param cubic_spline: set to False for always using Pchip interpolation
|
||||
:param options: options for parsers
|
||||
"""
|
||||
self.options = options
|
||||
@ -265,26 +268,31 @@ class CalCurve(HasOptions):
|
||||
parser = StdParser()
|
||||
parser.xdata = x
|
||||
parser.ydata = y
|
||||
self.calibname = 'custom'
|
||||
else:
|
||||
if x or y:
|
||||
raise ProgrammingError('can not give both calibspec and x,y ')
|
||||
sensopt = calibspec.split(',')
|
||||
calibname = sensopt.pop(0)
|
||||
_, dot, ext = basename(calibname).rpartition('.')
|
||||
self.calibname = basename(calibname)
|
||||
head, dot, ext = self.calibname.rpartition('.')
|
||||
if dot:
|
||||
self.calibname = head
|
||||
kind = None
|
||||
pathlist = os.environ.get('FRAPPY_CALIB_PATH', '').split(':')
|
||||
pathlist.append(join(dirname(__file__), 'calcurves'))
|
||||
pathlist = [Path(p.strip()) for p in os.environ.get('FRAPPY_CALIB_PATH', '').split(':')]
|
||||
pathlist.append(Path(dirname(__file__)) / 'calcurves')
|
||||
for path in pathlist:
|
||||
# first try without adding kind
|
||||
filename = join(path.strip(), calibname)
|
||||
if exists(filename):
|
||||
filename = path / calibname
|
||||
if filename.exists():
|
||||
kind = ext if dot else None
|
||||
break
|
||||
# then try adding all kinds as extension
|
||||
for nam in calibname, calibname.upper(), calibname.lower():
|
||||
for kind in PARSERS:
|
||||
filename = join(path.strip(), '%s.%s' % (nam, kind))
|
||||
filename = path / f'{nam}.{kind}'
|
||||
if exists(filename):
|
||||
self.filename = filename
|
||||
break
|
||||
else:
|
||||
continue
|
||||
@ -328,6 +336,7 @@ class CalCurve(HasOptions):
|
||||
not_incr_idx = np.argwhere(x[1:] <= x[:-1])
|
||||
if len(not_incr_idx):
|
||||
raise RangeError('x not monotonic at x=%.4g' % x[not_incr_idx[0]])
|
||||
self.ptc = y[-1] > y[0]
|
||||
|
||||
self.x = {parser.xscale: x}
|
||||
self.y = {parser.yscale: y}
|
||||
@ -344,8 +353,7 @@ class CalCurve(HasOptions):
|
||||
self.convert_x = to_scale[newscale]
|
||||
self.convert_y = from_scale[newscale]
|
||||
self.calibrange = self.options.get('calibrange')
|
||||
dirty = set()
|
||||
self.extra_points = False
|
||||
self.extra_points = (0, 0)
|
||||
self.cutted = False
|
||||
if self.calibrange:
|
||||
self.calibrange = sorted(self.calibrange)
|
||||
@ -371,7 +379,6 @@ class CalCurve(HasOptions):
|
||||
self.y = {newscale: y}
|
||||
ibeg = 0
|
||||
iend = len(x)
|
||||
dirty.add('xy')
|
||||
else:
|
||||
self.extra_points = ibeg, len(x) - iend
|
||||
else:
|
||||
@ -493,13 +500,48 @@ class CalCurve(HasOptions):
|
||||
except IndexError:
|
||||
return defaultx
|
||||
|
||||
def export(self, logformat=False, nmax=199, yrange=None, extrapolate=True, xlimits=None):
|
||||
def interpolation_error(self, x0, x1, y0, y1, funx, funy, relerror, return_tuple=False):
|
||||
"""calcualte interpoaltion error
|
||||
|
||||
:param x0: start of interval
|
||||
:param x1: end of interval
|
||||
:param y0: y at start of interval
|
||||
:param y1: y at end of interval
|
||||
:param funx: function to convert x from exported scale to internal scale
|
||||
:param funy: function to convert y from internal scale to exported scale
|
||||
:param relerror: True when the exported y scale is linear
|
||||
:param return_tuple: True: return interpolation error as a tuple with two values
|
||||
(without and with 3 additional points)
|
||||
False: return one value without additional points
|
||||
:return: relative deviation
|
||||
"""
|
||||
xspace = np.linspace(x0, x1, 9)
|
||||
x = funx(xspace)
|
||||
yr = self.spline(x)
|
||||
yspline = funy(yr)
|
||||
yinterp = y0 + np.linspace(0.0, y1 - y0, 9)
|
||||
# difference between spline (at m points) and liner interpolation
|
||||
diff = np.abs(yspline - yinterp)
|
||||
# estimate of interpolation error with 4 sections:
|
||||
# difference between spline (at m points) and linear interpolation between neighboring points
|
||||
|
||||
if relerror:
|
||||
fact = 2 / (np.abs(y0) + np.abs(y1)) # division by zero can not happen, as y0 and y1 can not both be zero
|
||||
else:
|
||||
fact = 2.3 # difference is in log10 -> multiply by 1 / log10(e)
|
||||
result = np.max(diff, axis=0) * fact
|
||||
if return_tuple:
|
||||
diff2 = np.abs(0.5 * (yspline[:-2:2] + yspline[2::2]) - funy(yr[1:-1:2]))
|
||||
return result, np.max(diff2, axis=0) * fact
|
||||
return result
|
||||
|
||||
def export(self, logformat=False, nmax=199, yrange=None, extrapolate=True, xlimits=None, nmin=199):
|
||||
"""export curve for downloading to hardware
|
||||
|
||||
:param nmax: max number of points. if the number of given points is bigger,
|
||||
the points with the lowest interpolation error are omitted
|
||||
:param logformat: a list with two elements of None, True or False
|
||||
True: use log, False: use line, None: use log if self.loglog
|
||||
:param logformat: a list with two elements of None, True or False for x and y
|
||||
True: use log, False: use lin, None: use log if self.loglog
|
||||
values None are replaced with the effectively used format
|
||||
False / True are replaced by [False, False] / [True, True]
|
||||
default is False
|
||||
@ -507,25 +549,26 @@ class CalCurve(HasOptions):
|
||||
:param extrapolate: a flag indicating whether the curves should be extrapolated
|
||||
to the preset extrapolation range
|
||||
:param xlimits: max x range
|
||||
:param nmin: minimum number of points
|
||||
:return: numpy array with 2 dimensions returning the curve
|
||||
"""
|
||||
|
||||
if logformat in (True, False):
|
||||
logformat = [logformat, logformat]
|
||||
logformat = (logformat, logformat)
|
||||
self.logformat = list(logformat)
|
||||
try:
|
||||
scales = []
|
||||
for idx, logfmt in enumerate(logformat):
|
||||
if logfmt and self.lin_forced[idx]:
|
||||
raise ValueError('%s must contain positive values only' % 'xy'[idx])
|
||||
logformat[idx] = linlog = self.loglog if logfmt is None else logfmt
|
||||
self.logformat[idx] = linlog = self.loglog if logfmt is None else logfmt
|
||||
scales.append('log' if linlog else 'lin')
|
||||
xscale, yscale = scales
|
||||
except (TypeError, AssertionError):
|
||||
raise ValueError('logformat must be a 2 element list or a boolean')
|
||||
raise ValueError('logformat must be a 2 element sequence or a boolean')
|
||||
|
||||
x = self.spline.x[1:-1] # raw units, excluding extrapolated points
|
||||
x1, x2 = xmin, xmax = x[0], x[-1]
|
||||
y1, y2 = sorted(self.spline([x1, x2]))
|
||||
xr = self.spline.x[1:-1] # raw units, excluding extrapolated points
|
||||
x1, x2 = xmin, xmax = xr[0], xr[-1]
|
||||
|
||||
if extrapolate and not yrange:
|
||||
yrange = self.exty
|
||||
@ -535,42 +578,100 @@ class CalCurve(HasOptions):
|
||||
lim = to_scale[self.scale](xlimits)
|
||||
xmin = clamp(xmin, *lim)
|
||||
xmax = clamp(xmax, *lim)
|
||||
# start and end index of calibrated range
|
||||
ibeg, iend = self.extra_points[0], len(xr) - self.extra_points[1]
|
||||
if xmin != x1 or xmax != x2:
|
||||
ibeg, iend = np.searchsorted(x, (xmin, xmax))
|
||||
if abs(x[ibeg] - xmin) < 0.1 * (x[ibeg + 1] - x[ibeg]):
|
||||
i, j = np.searchsorted(xr, (xmin, xmax))
|
||||
if abs(xr[i] - xmin) < 0.1 * (xr[i + 1] - xr[i]):
|
||||
# remove first point, if close
|
||||
ibeg += 1
|
||||
if abs(x[iend - 1] - xmax) < 0.1 * (x[iend - 1] - x[iend - 2]):
|
||||
i += 1
|
||||
if abs(xr[j - 1] - xmax) < 0.1 * (xr[j - 1] - xr[j - 2]):
|
||||
# remove last point, if close
|
||||
iend -= 1
|
||||
x = np.concatenate(([xmin], x[ibeg:iend], [xmax]))
|
||||
y = self.spline(x)
|
||||
j -= 1
|
||||
offset = i - 1
|
||||
xr = np.concatenate(([xmin], xr[i:j], [xmax]))
|
||||
ibeg = max(0, ibeg - offset)
|
||||
iend = min(len(xr), iend - offset)
|
||||
|
||||
yr = self.spline(xr)
|
||||
|
||||
# convert to exported scale
|
||||
if xscale != self.scale:
|
||||
x = to_scale[xscale](from_scale[self.scale](x))
|
||||
if yscale != self.scale:
|
||||
y = to_scale[yscale](from_scale[self.scale](y))
|
||||
|
||||
# reduce number of points, if needed
|
||||
n = len(x)
|
||||
i, j = 1, n - 1 # index range for calculating interpolation deviation
|
||||
deviation = np.zeros(n)
|
||||
while True:
|
||||
# calculate interpolation error when a single point is omitted
|
||||
ym = y[i-1:j-1] + (x[i:j] - x[i-1:j-1]) * (y[i+1:j+1] - y[i-1:j-1]) / (x[i+1:j+1] - x[i-1:j-1])
|
||||
if yscale == 'log':
|
||||
deviation[i:j] = np.abs(ym - y[i:j])
|
||||
if xscale == self.scale:
|
||||
xbwd = identity
|
||||
x = xr
|
||||
else:
|
||||
if self.scale == 'log':
|
||||
xfwd, xbwd = from_scale[self.scale], to_scale[self.scale]
|
||||
else:
|
||||
deviation[i:j] = np.abs(ym - y[i:j]) / (np.abs(ym + y[i:j]) + 1e-10)
|
||||
if n <= nmax:
|
||||
break
|
||||
idx = np.argmin(deviation[1:-1]) + 1 # find index of the smallest error
|
||||
y = np.delete(y, idx)
|
||||
x = np.delete(x, idx)
|
||||
deviation = np.delete(deviation, idx)
|
||||
n -= 1
|
||||
# index range to recalculate
|
||||
i, j = max(1, idx - 1), min(n - 1, idx + 1)
|
||||
self.deviation = deviation # for debugging purposes
|
||||
xfwd, xbwd = to_scale[xscale], from_scale[xscale]
|
||||
x = xfwd(xr)
|
||||
if yscale == self.scale:
|
||||
yfwd = identity
|
||||
y = yr
|
||||
else:
|
||||
if self.scale == 'log':
|
||||
yfwd = from_scale[self.scale]
|
||||
else:
|
||||
yfwd = to_scale[yscale]
|
||||
y = yfwd(yr)
|
||||
|
||||
self.deviation = None
|
||||
nmin = min(nmin, nmax)
|
||||
n = len(x)
|
||||
relerror = yscale == 'lin'
|
||||
if len(x) > nmax:
|
||||
# reduce number of points, if needed
|
||||
i, j = 1, n - 1 # index range for calculating interpolation deviation
|
||||
deviation = np.zeros(n)
|
||||
while True:
|
||||
deviation[i:j] = self.interpolation_error(
|
||||
x[i-1:j-1], x[i+1:j+1], y[i-1:j-1], y[i+1:j+1],
|
||||
xbwd, yfwd, relerror)
|
||||
# calculate interpolation error when a single point is omitted
|
||||
if n <= nmax:
|
||||
break
|
||||
idx = np.argmin(deviation[1:-1]) + 1 # find index of the smallest error
|
||||
y = np.delete(y, idx)
|
||||
x = np.delete(x, idx)
|
||||
deviation = np.delete(deviation, idx)
|
||||
n = len(x)
|
||||
# index range to recalculate
|
||||
i, j = max(1, idx - 1), min(n - 1, idx + 1)
|
||||
self.deviation = deviation # for debugging purposes
|
||||
elif n < nmin:
|
||||
if ibeg + 1 < iend:
|
||||
diff1, diff4 = self.interpolation_error(
|
||||
x[ibeg:iend - 1], x[ibeg + 1:iend], y[ibeg:iend - 1], y[ibeg + 1:iend],
|
||||
xbwd, yfwd, relerror, return_tuple=True)
|
||||
dif_target = 1e-4
|
||||
sq4 = np.sqrt(diff4) * 4
|
||||
sq1 = np.sqrt(diff1)
|
||||
offset = 0.49
|
||||
n_mid = nmax - len(x) + iend - ibeg - 1
|
||||
# iteration to find a dif target resulting in no more than nmax points
|
||||
while True:
|
||||
scale = 1 / np.sqrt(dif_target)
|
||||
# estimate number of intermediate points (float!) needed to reach dif_target
|
||||
# number of points estimated from the result of the interpolation error with 4 sections
|
||||
n4 = np.maximum(1, sq4 * scale)
|
||||
# number of points estimated from the result of the interpolation error with 1 section
|
||||
n1 = np.maximum(1, sq1 * scale)
|
||||
# use n4 where n4 > 4, n1, where n1 < 1 and a weighted average in between
|
||||
nn = np.select([n4 > 4, n1 > 1],
|
||||
[n4, (n4 * (n1 - 1) + n1 * (4 - n4)) / (3 + n1 - n4)], n1)
|
||||
n_tot = np.sum(np.rint(nn + offset))
|
||||
extra = n_tot - n_mid
|
||||
if extra <= 0:
|
||||
break
|
||||
dif_target *= (n_tot / n_mid) ** 2
|
||||
|
||||
xnew = [x[:ibeg]]
|
||||
for x0, x1, ni in zip(x[ibeg:iend-1], x[ibeg+1:iend], np.rint(nn + offset)):
|
||||
xnew.append(np.linspace(x0, x1, int(ni) + 1)[:-1])
|
||||
xnew.append(x[iend-1:])
|
||||
x = np.concatenate(xnew)
|
||||
y = yfwd(self.spline(xbwd(x)))
|
||||
# for debugging purposes:
|
||||
self.deviation = self.interpolation_error(x[:-1], x[1:], y[:-1], y[1:], xbwd, yfwd, relerror)
|
||||
|
||||
return np.stack([x, y], axis=1)
|
||||
|
125
frappy_psi/ccracks.py
Normal file
125
frappy_psi/ccracks.py
Normal file
@ -0,0 +1,125 @@
|
||||
import os
|
||||
from configparser import ConfigParser
|
||||
|
||||
|
||||
class Lsc:
|
||||
def __init__(self, modfactory, ls_uri, ls_ioname='lsio', ls_devname='ls', ls_model='336', **kwds):
|
||||
self.modfactory = Mod = modfactory
|
||||
self.model = ls_model
|
||||
self.ioname = ls_ioname
|
||||
self.devname = ls_devname
|
||||
self.io = Mod(self.ioname, cls=f'frappy_psi.lakeshore.IO{self.model}',
|
||||
description='comm. to lakeshore in cc rack',
|
||||
uri=ls_uri)
|
||||
self.dev = Mod(self.devname, cls=f'frappy_psi.lakeshore.Device{self.model}',
|
||||
description='lakeshore in cc rack', io=self.ioname, curve_handling=True)
|
||||
self.loops = {}
|
||||
self.outputs = {}
|
||||
|
||||
def sensor(self, name, channel, calcurve, **kwds):
|
||||
Mod = self.modfactory
|
||||
kwds.setdefault('cls', f'frappy_psi.lakeshore.Sensor{self.model}')
|
||||
kwds.setdefault('description', f'T sensor {name}')
|
||||
return Mod(name, channel=channel, calcurve=calcurve,
|
||||
io=self.ioname, device=self.devname, **kwds)
|
||||
|
||||
def loop(self, name, channel, calcurve, **kwds):
|
||||
Mod = self.modfactory
|
||||
kwds.setdefault('cls', f'frappy_psi.lakeshore.Loop{self.model}')
|
||||
kwds.setdefault('description', f'T loop {name}')
|
||||
mod = Mod(name, channel=channel, calcurve=calcurve,
|
||||
io=self.ioname, device=self.devname, **kwds)
|
||||
self.loops[name] = mod
|
||||
return mod
|
||||
|
||||
def heater(self, name, max_heater, resistance, output_no=1, **kwds):
|
||||
Mod = self.modfactory
|
||||
if output_no == 1:
|
||||
kwds.setdefault('cls', f'frappy_psi.lakeshore.MainOutput{self.model}')
|
||||
elif output_no == 2:
|
||||
kwds.setdefault('cls', f'frappy_psi.lakeshore.SecondaryOutput{self.model}')
|
||||
else:
|
||||
return
|
||||
kwds.setdefault('description', '')
|
||||
mod = Mod(name, max_heater=max_heater, resistance=resistance,
|
||||
io=self.ioname, device=self.devname, **kwds)
|
||||
self.outputs[name] = mod
|
||||
return mod
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
outmodules = dict(self.outputs)
|
||||
for name, loop in self.loops.items():
|
||||
outname = loop.get('output_module')
|
||||
if outname:
|
||||
out = outmodules.pop(outname, None)
|
||||
if not out:
|
||||
raise KeyError(f'{outname} is not a output module in this lakeshore')
|
||||
else:
|
||||
if not outmodules:
|
||||
raise KeyError(f'{name} needs an output module on this lakeshore')
|
||||
outname = list(outmodules)[0]
|
||||
out = outmodules.pop(outname)
|
||||
loop['output_module'] = outname
|
||||
if not out['description']:
|
||||
out['description'] = f'heater for {outname}'
|
||||
|
||||
|
||||
class CCU:
|
||||
def __init__(self, Mod, ccu_uri, ccu_ioname='ccuio', ccu_devname='ccu', he=None, n2=None, **kwds):
|
||||
self.ioname = ccu_ioname
|
||||
self.devname = ccu_devname
|
||||
Mod(self.ioname, 'frappy_psi.ccu4.CCU4IO',
|
||||
'comm. to CCU4', uri=ccu_uri)
|
||||
if he:
|
||||
if not isinstance(he, str): # e.g. True
|
||||
he = 'He_lev'
|
||||
Mod(he, cls='frappy_psi.ccu4.HeLevel',
|
||||
description='the He Level', io=self.ioname)
|
||||
if n2:
|
||||
if isinstance(n2, str):
|
||||
n2 = n2.split(',')
|
||||
else: # e.g. True
|
||||
n2 = []
|
||||
n2, valve, upper, lower = n2 + ['N2_lev', 'N2_valve', 'N2_upper', 'N2_lower'][len(n2):]
|
||||
print(n2, valve, upper, lower)
|
||||
Mod(n2, cls='frappy_psi.ccu4.N2Level',
|
||||
description='the N2 Level', io=self.ioname,
|
||||
valve=valve, upper=upper, lower=lower)
|
||||
Mod(valve, cls='frappy_psi.ccu4.N2FillValve',
|
||||
description='LN2 fill valve', io=self.ioname)
|
||||
Mod(upper, cls='frappy_psi.ccu4.N2TempSensor',
|
||||
description='upper LN2 sensor')
|
||||
Mod(lower, cls='frappy_psi.ccu4.N2TempSensor',
|
||||
description='lower LN2 sensor')
|
||||
|
||||
|
||||
class HePump:
|
||||
def __init__(self, Mod, hepump_uri, hepump_io='hepump_io', hemotname='hepump_mot', **kwds):
|
||||
Mod(hepump_io, 'frappy_psi.trinamic.BytesIO', 'He pump connection', uri=hepump_uri)
|
||||
Mod(hemotname, 'frappy_psi.trinamic.Motor', 'He pump valve motor', io=hepump_io)
|
||||
|
||||
|
||||
class Rack:
|
||||
rackfile = '/home/l_samenv/.config/racks.ini'
|
||||
|
||||
def __init__(self, modfactory, **kwds):
|
||||
self.modfactory = modfactory
|
||||
parser = ConfigParser()
|
||||
parser.optionxform = str
|
||||
parser.read([self.rackfile])
|
||||
kwds.update(parser.items(os.environ['Instrument']))
|
||||
self.kwds = kwds
|
||||
|
||||
def lakeshore(self):
|
||||
return Lsc(self.modfactory, **self.kwds)
|
||||
|
||||
def ccu(self, **kwds):
|
||||
kwds.update(self.kwds)
|
||||
return CCU(self.modfactory, **kwds)
|
||||
|
||||
def hepump(self):
|
||||
return HePump(self.modfactory, **self.kwds)
|
||||
|
@ -22,46 +22,59 @@
|
||||
"""drivers for CCU4, the cryostat control unit at SINQ"""
|
||||
import time
|
||||
import math
|
||||
from frappy.lib.enum import Enum
|
||||
# the most common Frappy classes can be imported from frappy.core
|
||||
from frappy.core import HasIO, Parameter, Command, Readable, Writable, Drivable, \
|
||||
Property, StringIO, BUSY, IDLE, WARN, ERROR, DISABLED, Attached
|
||||
from frappy.datatypes import BoolType, EnumType, FloatRange, StructOf, \
|
||||
StatusType, IntRange, StringType, TupleOf
|
||||
from frappy.dynamic import Pinata
|
||||
from frappy.errors import CommunicationFailedError
|
||||
from frappy.states import HasStates, status_code, Retry
|
||||
|
||||
|
||||
M = Enum(idle=0, opening=1, closing=2, opened=3, closed=5, no_motor=6)
|
||||
A = Enum(disabled=0, manual=1, auto=2)
|
||||
|
||||
|
||||
class CCU4IO(StringIO):
|
||||
"""communication with CCU4"""
|
||||
# for completeness: (not needed, as it is the default)
|
||||
end_of_line = '\n'
|
||||
# on connect, we send 'cid' and expect a reply starting with 'CCU4'
|
||||
identification = [('cid', r'CCU4.*')]
|
||||
identification = [('cid', r'cid=CCU4.*')]
|
||||
|
||||
|
||||
class CCU4Base(HasIO):
|
||||
ioClass = CCU4IO
|
||||
|
||||
def command(self, *args, **kwds):
|
||||
def command(self, **kwds):
|
||||
"""send a command and get the response
|
||||
|
||||
:param args: the name of the parameters to query
|
||||
:param kwds: <parameter>=<value> for changing a parameter
|
||||
:param kwds: <parameter>=<value> for changing a parameter <parameter>=<type> for querying a parameter
|
||||
:returns: the (new) values of the parameters
|
||||
"""
|
||||
cmds = [f'{k}={v:g}' for k, v in kwds.items()] + list(args)
|
||||
types = {}
|
||||
cmds = []
|
||||
for key, value in kwds.items():
|
||||
if isinstance(value, type):
|
||||
types[key] = value
|
||||
cmds.append(key)
|
||||
elif isinstance(value, str):
|
||||
types[key] = str
|
||||
cmds.append(f'{key}={value}')
|
||||
else:
|
||||
types[key] = float
|
||||
cmds.append(f'{key}={value:g}')
|
||||
reply = self.io.communicate(' '.join(cmds)).split()
|
||||
names = list(args) + list(kwds)
|
||||
if len(reply) != len(names):
|
||||
if len(reply) != len(types):
|
||||
raise CommunicationFailedError('number of reply items does not match')
|
||||
result = []
|
||||
for given, item in zip(names, reply):
|
||||
name, txtvalue = item.split('=')
|
||||
for (given, typ), res in zip(types.items(), reply):
|
||||
name, txtvalue = res.split('=')
|
||||
if given != name:
|
||||
raise CommunicationFailedError('result keys do not match given keys')
|
||||
result.append(float(txtvalue))
|
||||
if len(result) == 1:
|
||||
result.append(typ(txtvalue))
|
||||
if len(kwds) == 1:
|
||||
return result[0]
|
||||
return result
|
||||
|
||||
@ -89,13 +102,13 @@ class HeLevel(CCU4Base, Readable):
|
||||
}
|
||||
|
||||
def read_value(self):
|
||||
return self.command('h')
|
||||
return self.command(h=float)
|
||||
|
||||
def read_status(self):
|
||||
return self.STATUS_MAP[int(self.command('hsf'))]
|
||||
return self.STATUS_MAP[int(self.command(hsf=int))]
|
||||
|
||||
def read_sample_rate(self):
|
||||
value, self.empty_length, self.full_length = self.command('hf', 'hem', 'hfu')
|
||||
value, self.empty_length, self.full_length = self.command(hf=int, hem=float, hfu=float)
|
||||
return value
|
||||
|
||||
def write_sample_rate(self, value):
|
||||
@ -130,7 +143,7 @@ class Valve(CCU4Base, Writable):
|
||||
self.command(**self._close_command)
|
||||
|
||||
def read_status(self):
|
||||
state = self.command(self._query_state)
|
||||
state = int(self.command(**self._query_state))
|
||||
self.value, status = self.STATE_MAP[state]
|
||||
return status
|
||||
|
||||
@ -138,13 +151,13 @@ class Valve(CCU4Base, Writable):
|
||||
class HeFillValve(Valve):
|
||||
_open_command = {'hcd': 1, 'hf': 1}
|
||||
_close_command = {'hcd': 0, 'hf': 0}
|
||||
_query_state = 'hv'
|
||||
_query_state = {'hv': int}
|
||||
|
||||
|
||||
class N2FillValve(Valve):
|
||||
_open_command = {'nc': 1}
|
||||
_close_command = {'nc': 0}
|
||||
_query_state = 'nv'
|
||||
_query_state = {'nv': int}
|
||||
|
||||
|
||||
class AuxValve(Valve):
|
||||
@ -153,21 +166,21 @@ class AuxValve(Valve):
|
||||
def initModule(self):
|
||||
self._open_command = {f'vc{self.channel}': 1}
|
||||
self._close_command = {f'vc{self.channel}': 0}
|
||||
self._query_state = f'v{self.channel}'
|
||||
self._query_state = {f'v{self.channel}': int}
|
||||
|
||||
|
||||
class N2TempSensor(Readable):
|
||||
value = Parameter('LN2 T sensor', FloatRange(unit='K'), default=0)
|
||||
|
||||
|
||||
class N2Level(CCU4Base, Pinata, Readable):
|
||||
class N2Level(CCU4Base, Readable):
|
||||
valve = Attached(Writable, mandatory=False)
|
||||
lower = Attached(Readable, mandatory=False)
|
||||
upper = Attached(Readable, mandatory=False)
|
||||
|
||||
value = Parameter('vessel state', EnumType(empty=0, ok=1, full=2))
|
||||
status = Parameter(datatype=StatusType(Readable, 'BUSY'))
|
||||
mode = Parameter('auto mode', EnumType(disabled=0, manual=1, auto=2), readonly=False)
|
||||
status = Parameter(datatype=StatusType(Readable, 'DISABLED', 'BUSY'))
|
||||
mode = Parameter('auto mode', EnumType(A), readonly=False, default=A.manual)
|
||||
|
||||
threshold = Parameter('threshold triggering start/stop filling',
|
||||
FloatRange(unit='K'), readonly=False)
|
||||
@ -192,31 +205,22 @@ class N2Level(CCU4Base, Pinata, Readable):
|
||||
5: (WARN, 'empty'),
|
||||
}
|
||||
|
||||
def scanModules(self):
|
||||
for modname, name in self.names.items():
|
||||
if name:
|
||||
sensor_name = name.replace('$', self.name)
|
||||
self.setProperty(modname, sensor_name)
|
||||
yield sensor_name, {
|
||||
'cls': N2FillValve if modname == 'valve' else N2TempSensor,
|
||||
'description': f'LN2 {modname} T sensor'}
|
||||
|
||||
def initialReads(self):
|
||||
self.command(nav=1) # tell CCU4 to activate LN2 sensor readings
|
||||
super().initialReads()
|
||||
|
||||
def read_status(self):
|
||||
auto, nstate = self.command('na', 'ns')
|
||||
auto, nstate = self.command(na=int, ns=int)
|
||||
if not self.valve or not auto:
|
||||
if self.mode == 'auto':
|
||||
if self.mode == A.auto:
|
||||
# no valve assigned
|
||||
self.mode = 'manual'
|
||||
if self.mode == 'disabled':
|
||||
self.mode = A.manual
|
||||
if self.mode == A.disabled:
|
||||
return DISABLED, ''
|
||||
status = self.STATUS_MAP[nstate]
|
||||
if status[0] // 100 != IDLE // 100:
|
||||
return status
|
||||
if self.mode == 'manual':
|
||||
if self.mode == A.manual:
|
||||
return IDLE, ''
|
||||
vstatus = self.valve.status
|
||||
if vstatus[0] // 100 == WARN // 100:
|
||||
@ -229,7 +233,7 @@ class N2Level(CCU4Base, Pinata, Readable):
|
||||
|
||||
def read_value(self):
|
||||
# read sensors
|
||||
lower, upper = self.command('nl', 'nu')
|
||||
lower, upper = self.command(nl=float, nu=float)
|
||||
if self.lower:
|
||||
self.lower.value = lower
|
||||
if self.upper:
|
||||
@ -241,7 +245,7 @@ class N2Level(CCU4Base, Pinata, Readable):
|
||||
return 'empty'
|
||||
|
||||
def write_mode(self, mode):
|
||||
if mode == 'auto':
|
||||
if mode == A.auto:
|
||||
if self.isBusy():
|
||||
return mode
|
||||
# set to watching
|
||||
@ -252,7 +256,7 @@ class N2Level(CCU4Base, Pinata, Readable):
|
||||
return mode
|
||||
|
||||
def read_threshold(self):
|
||||
value, self.cool_delay, self.fill_timeout = self.command('nth', 'ntc', 'ntm')
|
||||
value, self.cool_delay, self.fill_timeout = self.command(nth=float, ntc=float, ntm=float)
|
||||
return value
|
||||
|
||||
def write_threshold(self, value):
|
||||
@ -266,17 +270,19 @@ class N2Level(CCU4Base, Pinata, Readable):
|
||||
|
||||
@Command()
|
||||
def fill(self):
|
||||
self.mode = 'auto'
|
||||
self.io.write(nc=1)
|
||||
"""start filling"""
|
||||
self.mode = A.auto
|
||||
self.command(nc=1)
|
||||
|
||||
@Command()
|
||||
def stop(self):
|
||||
if self.mode == 'auto':
|
||||
"""stop filling"""
|
||||
if self.mode == A.auto:
|
||||
# set to watching
|
||||
self.command(nc=3)
|
||||
else:
|
||||
# set to off
|
||||
self.io.write(nc=0)
|
||||
self.command(nc=0)
|
||||
|
||||
|
||||
class FlowPressure(CCU4Base, Readable):
|
||||
@ -285,7 +291,7 @@ class FlowPressure(CCU4Base, Readable):
|
||||
pollinterval = Parameter(default=0.25)
|
||||
|
||||
def read_value(self):
|
||||
return self.filter(self.command('f')) - self.mbar_offset
|
||||
return self.filter(self.command(f=float)) - self.mbar_offset
|
||||
|
||||
|
||||
class NeedleValve(HasStates, CCU4Base, Drivable):
|
||||
@ -298,9 +304,7 @@ class NeedleValve(HasStates, CCU4Base, Drivable):
|
||||
lnm_per_mbar = Parameter(unit='ln/min/mbar', default=0.6, readonly=False)
|
||||
use_pressure = Parameter('use flow from pressure', BoolType(),
|
||||
default=False, readonly=False)
|
||||
motor_state = Parameter('motor_state',
|
||||
EnumType(idle=0, opening=1, closing=2,
|
||||
opened=3, closed=5, no_motor=6))
|
||||
motor_state = Parameter('motor_state', EnumType(M))
|
||||
tolerance = Parameter('tolerance', FloatRange(0), value=0.25, readonly=False)
|
||||
tolerance2 = Parameter('tolerance limit above 2 lnm', FloatRange(0), value=0.5, readonly=False)
|
||||
prop = Parameter('proportional term', FloatRange(unit='s/lnm'), readonly=False)
|
||||
@ -341,12 +345,12 @@ class NeedleValve(HasStates, CCU4Base, Drivable):
|
||||
def update_flow(self, value):
|
||||
if not self.use_pressure:
|
||||
self.value = value
|
||||
self.doPoll()
|
||||
self.cycle_machine()
|
||||
|
||||
def update_flow_pressure(self, value):
|
||||
if self.use_pressure:
|
||||
self.value = value * self.lnm_per_mbar
|
||||
self.doPoll()
|
||||
self.cycle_machine()
|
||||
|
||||
def write_target(self, value):
|
||||
self.start_machine(self.controlling, in_tol_time=0,
|
||||
@ -354,7 +358,7 @@ class NeedleValve(HasStates, CCU4Base, Drivable):
|
||||
|
||||
@status_code(BUSY)
|
||||
def unblock_from_open(self, state):
|
||||
self.motor_state = self.command('fm')
|
||||
self.motor_state = self.command(fm=int)
|
||||
if self.motor_state == 'opened':
|
||||
self.command(mp=-60)
|
||||
return Retry
|
||||
@ -372,7 +376,7 @@ class NeedleValve(HasStates, CCU4Base, Drivable):
|
||||
|
||||
@status_code(BUSY)
|
||||
def unblock_open(self, state):
|
||||
self.motor_state = self.command('fm')
|
||||
self.motor_state = self.command(fm=int)
|
||||
if self.value < state.flow_before:
|
||||
state.flow_before_open = self.value
|
||||
elif self.value > state.flow_before + 1:
|
||||
@ -393,7 +397,7 @@ class NeedleValve(HasStates, CCU4Base, Drivable):
|
||||
|
||||
@status_code(BUSY)
|
||||
def unblock_close(self, state):
|
||||
self.motor_state = self.command('fm')
|
||||
self.motor_state = self.command(fm=int)
|
||||
if self.value > state.flow_before:
|
||||
state.flow_before_open = self.value
|
||||
elif self.value < state.flow_before - 1:
|
||||
@ -435,7 +439,7 @@ class NeedleValve(HasStates, CCU4Base, Drivable):
|
||||
dif = self.target - self.value
|
||||
difdif = dif - state.prev_dif
|
||||
state.prev_dif = dif
|
||||
self.motor_state = self.command('fm')
|
||||
self.motor_state = self.command(fm=int)
|
||||
if self.motor_state == 'closed':
|
||||
if dif < 0 or difdif < 0:
|
||||
return Retry
|
||||
@ -467,5 +471,3 @@ class NeedleValve(HasStates, CCU4Base, Drivable):
|
||||
return Retry
|
||||
self.command(mp=state.step)
|
||||
return Retry
|
||||
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -63,7 +63,12 @@ def parse_result(reply):
|
||||
|
||||
class LakeShoreIO(HasIO):
|
||||
def set_param(self, cmd, *args):
|
||||
head = ','.join([cmd] + [f'{a:g}' for a in args])
|
||||
args = [f'{a:g}' for a in args]
|
||||
if ' ' in cmd.strip():
|
||||
args.insert(0, cmd)
|
||||
else:
|
||||
args[0] = cmd + args[0]
|
||||
head = ','.join(args)
|
||||
tail = cmd.replace(' ', '?')
|
||||
reply = self.io.communicate(f'{head};{tail}')
|
||||
return parse_result(reply)
|
||||
@ -99,7 +104,7 @@ class Switcher(LakeShoreIO, ChannelSwitcher):
|
||||
if channelno is None:
|
||||
self.status = 'ERROR', 'no enabled channel'
|
||||
return
|
||||
self.set_param(f'SCAN {channelno},0')
|
||||
self.set_param('SCAN ', channelno, 0)
|
||||
|
||||
def doPoll(self):
|
||||
"""poll buttons
|
||||
@ -160,7 +165,7 @@ class Switcher(LakeShoreIO, ChannelSwitcher):
|
||||
self.measure_delay = chan.dwell
|
||||
|
||||
def set_active_channel(self, chan):
|
||||
self.set_param(f'SCAN {chan.channel},0')
|
||||
self.set_param('SCAN ', chan.channel, 0)
|
||||
chan._last_range_change = time.monotonic()
|
||||
self.set_delays(chan)
|
||||
|
||||
@ -227,7 +232,7 @@ class ResChannel(LakeShoreIO, Channel):
|
||||
now = time.monotonic()
|
||||
if now + 0.5 < max(self._last_range_change, self.switcher._start_switch) + self.pause:
|
||||
return None
|
||||
result = self.get_param(f'RDGR{self.channel}')
|
||||
result = self.get_param(f'RDGR?{self.channel}')
|
||||
if self.autorange:
|
||||
self.fix_autorange()
|
||||
if now + 0.5 > self._last_range_change + self.pause:
|
||||
@ -251,7 +256,7 @@ class ResChannel(LakeShoreIO, Channel):
|
||||
|
||||
def read_value(self):
|
||||
if self.channel == self.switcher.value == self.switcher.target:
|
||||
value = self._read_value()
|
||||
value = self.get_value()
|
||||
if value is not None:
|
||||
return value
|
||||
return self.value # return previous value
|
||||
@ -264,7 +269,7 @@ class ResChannel(LakeShoreIO, Channel):
|
||||
|
||||
@CommonReadHandler(rdgrng_params)
|
||||
def read_rdgrng(self):
|
||||
iscur, exc, rng, autorange, excoff = self.get_param(f'RDGRNG{self.channel}')
|
||||
iscur, exc, rng, autorange, excoff = self.get_param(f'RDGRNG?{self.channel}')
|
||||
self._prev_rdgrng = iscur, exc
|
||||
if autorange: # pressed autorange button
|
||||
if not self._toggle_autorange:
|
||||
@ -293,8 +298,7 @@ class ResChannel(LakeShoreIO, Channel):
|
||||
excoff = 1
|
||||
rng = change['range']
|
||||
if self.autorange:
|
||||
if rng < self.minrange:
|
||||
rng = self.minrange
|
||||
rng = max(rng, self.minrange)
|
||||
self.set_param(f'RDGRNG {self.channel}', iscur, exc, rng, 0, excoff)
|
||||
self.read_range()
|
||||
|
||||
|
@ -16,84 +16,38 @@
|
||||
# Module authors:
|
||||
# Markus Zolliker <markus.zolliker@psi.ch>
|
||||
# *****************************************************************************
|
||||
"""a very simple simulator for a LakeShore Model 370"""
|
||||
"""a very simple simulator for LakeShore Models 370 and 372
|
||||
|
||||
reduced to the functionality actually used in e.g. frappy_psi.ls370res
|
||||
"""
|
||||
|
||||
import time
|
||||
from frappy.modules import Communicator
|
||||
|
||||
|
||||
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'),
|
||||
('*OPC?', '1'),
|
||||
]
|
||||
|
||||
def earlyInit(self):
|
||||
super().earlyInit()
|
||||
self._data = dict(self.OTHER_COMMANDS)
|
||||
for fmt, v in self.CHANNEL_COMMANDS:
|
||||
for chan in range(1,17):
|
||||
self._data[fmt % chan] = v
|
||||
|
||||
def communicate(self, command):
|
||||
self.comLog('> %s' % 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] = '6'
|
||||
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
|
||||
reply = ';'.join(reply)
|
||||
self.comLog('< %s' % reply)
|
||||
return reply
|
||||
|
||||
|
||||
class Ls372Sim(Communicator):
|
||||
class _Ls37xSim(Communicator):
|
||||
# commands containing %d for the channel number
|
||||
CHANNEL_COMMANDS = [
|
||||
('RDGR?%d', '1.0'),
|
||||
('RDGK?%d', '1.5'),
|
||||
('RDGST?%d', '0'),
|
||||
('RDGRNG?%d', '0,5,5,0,0'),
|
||||
('INSET?%d', '1,5,5,0,0'),
|
||||
('INSET?%d', '1,3,3,0,0'),
|
||||
('FILTER?%d', '1,5,80'),
|
||||
]
|
||||
# commands not related to a channel
|
||||
OTHER_COMMANDS = [
|
||||
('*IDN?', 'LSCI,MODEL372,372184,05302003'),
|
||||
('SCAN?', '3,1'),
|
||||
('PID?1', '10,10,0'),
|
||||
('*OPC?', '1'),
|
||||
]
|
||||
|
||||
def earlyInit(self):
|
||||
super().earlyInit()
|
||||
self._res = {}
|
||||
self._start = time.time()
|
||||
self._data = dict(self.OTHER_COMMANDS)
|
||||
for fmt, v in self.CHANNEL_COMMANDS:
|
||||
for chan in range(1,17):
|
||||
for chan in range(1, 17):
|
||||
self._data[fmt % chan] = v
|
||||
|
||||
def communicate(self, command):
|
||||
@ -105,6 +59,10 @@ class Ls372Sim(Communicator):
|
||||
self._data['RDGST?%d' % channel] = '6'
|
||||
else:
|
||||
self._data['RDGST?%d' % channel] = '0'
|
||||
channel = int(self._data['SCAN?'].split(',', 1)[0])
|
||||
self._res[channel] = channel + (time.time() - self._start) / 3600
|
||||
strvalue = f'{self._res[channel]:g}'
|
||||
self._data['RDGR?%d' % channel] = self._data['RDGK?%d' % channel] = strvalue
|
||||
|
||||
chunks = command.split(';')
|
||||
reply = []
|
||||
@ -112,7 +70,7 @@ class Ls372Sim(Communicator):
|
||||
if '?' in chunk:
|
||||
reply.append(self._data[chunk])
|
||||
else:
|
||||
for nqarg in (1,0):
|
||||
for nqarg in (1, 0):
|
||||
if nqarg == 0:
|
||||
qcmd, arg = chunk.split(' ', 1)
|
||||
qcmd += '?'
|
||||
@ -125,3 +83,16 @@ class Ls372Sim(Communicator):
|
||||
reply = ';'.join(reply)
|
||||
self.comLog('< %s' % reply)
|
||||
return reply
|
||||
|
||||
|
||||
class Ls370Sim(_Ls37xSim):
|
||||
OTHER_COMMANDS = _Ls37xSim.OTHER_COMMANDS + [
|
||||
('*IDN?', 'LSCI,MODEL370,370184,05302003'),
|
||||
]
|
||||
|
||||
|
||||
class Ls372Sim(_Ls37xSim):
|
||||
OTHER_COMMANDS = _Ls37xSim.OTHER_COMMANDS + [
|
||||
('*IDN?', 'LSCI,MODEL372,372184,05302003'),
|
||||
('PID?1', '10,10,0'),
|
||||
]
|
||||
|
@ -140,6 +140,15 @@ class SimpleMagfield(HasStates, Drivable):
|
||||
self.setFastPoll(True, 1.0)
|
||||
return self.start_ramp_to_target
|
||||
|
||||
@status_code(Status.RAMPING)
|
||||
def start_ramp_to_target(self, sm):
|
||||
"""start ramping current to target field
|
||||
|
||||
initiate ramp to target
|
||||
the implementation should return ramp_to_target
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
@status_code(BUSY, 'ramping field')
|
||||
def ramp_to_target(self, sm):
|
||||
if sm.init:
|
||||
@ -324,15 +333,6 @@ class Magfield(SimpleMagfield):
|
||||
self._last_target = sm.target
|
||||
return self.start_ramp_to_target
|
||||
|
||||
@status_code(Status.RAMPING)
|
||||
def start_ramp_to_target(self, sm):
|
||||
"""start ramping current to target field
|
||||
|
||||
initiate ramp to target
|
||||
the implementation should return ramp_to_target
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
@status_code(Status.RAMPING)
|
||||
def ramp_to_target(self, sm):
|
||||
dif = abs(self.value - sm.target)
|
||||
|
276
frappy_psi/oxinst.py
Normal file
276
frappy_psi/oxinst.py
Normal file
@ -0,0 +1,276 @@
|
||||
# *****************************************************************************
|
||||
# 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>
|
||||
# *****************************************************************************
|
||||
"""oxford instruments old devices (ILM, IGH, IPS)"""
|
||||
|
||||
import re
|
||||
import time
|
||||
from frappy.core import StringIO, HasIO, Readable, Parameter, ERROR, IDLE, PREPARING
|
||||
from frappy.datatypes import FloatRange, BoolType, EnumType
|
||||
from frappy.errors import HardwareError, RangeError
|
||||
from frappy.lib import formatStatusBits, clamp
|
||||
from frappy.lib.enum import Enum
|
||||
from frappy_psi.magfield import Magfield
|
||||
from frappy.states import Retry
|
||||
|
||||
|
||||
class IlmIO(StringIO):
|
||||
end_of_line = '\r'
|
||||
identification = [('V', r'ILM200.*')]
|
||||
timeout = 5
|
||||
|
||||
|
||||
class OxiBase(HasIO):
|
||||
def query(self, cmd, dig=0):
|
||||
reply = self.communicate(cmd)
|
||||
if reply[0] == cmd[0]:
|
||||
if '.' not in reply and dig > 0:
|
||||
# add decimal point if not already there (for older systems)
|
||||
reply = f'{reply[1:-dig]}.{reply[-dig:]}'
|
||||
try:
|
||||
value = float(reply)
|
||||
return value
|
||||
except Exception:
|
||||
pass
|
||||
raise HardwareError(f'bad reply {reply!r} to {cmd!r}')
|
||||
|
||||
def command(self, *cmds):
|
||||
try:
|
||||
self.communicate('C3')
|
||||
for cmd in cmds:
|
||||
self.communicate(cmd)
|
||||
finally:
|
||||
self.communicate('C0')
|
||||
|
||||
def change(self, cmd, query, value):
|
||||
try:
|
||||
self.communicate('C3')
|
||||
self.communicate(f'{cmd}{value:g}')
|
||||
return self.query(query)
|
||||
finally:
|
||||
self.communicate('C0')
|
||||
|
||||
|
||||
class Level(OxiBase, Readable):
|
||||
ioClass = IlmIO
|
||||
value = Parameter(datatype=FloatRange(unit='%'))
|
||||
CHANNEL = None
|
||||
XPAT = re.compile(r'X(\d)(\d)(\d)S([0-9A-F]{2}){3}R\d\d$')
|
||||
FLUID = None
|
||||
_statusbits = None
|
||||
|
||||
def read_value(self):
|
||||
return self.query(f'R{self.CHANNEL}', 1)
|
||||
|
||||
def write_fast(self, fast):
|
||||
self.command(f'T{self.CHANNEL}' if fast else f'S{self.CHANNEL}')
|
||||
|
||||
def get_status(self):
|
||||
reply = self.communicate('X')
|
||||
match = self.XPAT.match(reply)
|
||||
if match:
|
||||
statuslist = match.groups()
|
||||
if statuslist[self.CHANNEL] == '9':
|
||||
return ERROR, f'error on {self.FLUID} level channel (not connected?)'
|
||||
if statuslist[self.CHANNEL] != '2':
|
||||
return ERROR, f'{self.FLUID} level channel not configured properly'
|
||||
self._statusbits = int(statuslist[self.CHANNEL + 3], 16)
|
||||
return None
|
||||
return ERROR, f'bad status message {reply}'
|
||||
|
||||
|
||||
class HeLevel:
|
||||
CHANNEL = 1
|
||||
FLUID = 'He'
|
||||
fast = Parameter('measuring mode: True is fast', BoolType())
|
||||
|
||||
def read_status(self):
|
||||
status = self.get_status()
|
||||
if status is not None:
|
||||
return status
|
||||
return IDLE, formatStatusBits(self._statusbits, ['meas', 'fast', 'slow'])
|
||||
|
||||
|
||||
class N2Level:
|
||||
CHANNEL = 2
|
||||
MEDIUM = 'N2'
|
||||
|
||||
def read_status(self):
|
||||
status = self.get_status()
|
||||
if status is not None:
|
||||
return status
|
||||
return IDLE, ''
|
||||
|
||||
|
||||
A = Enum(hold=0, run_to_set=1, run_to_zero=2, clamped=4)
|
||||
|
||||
|
||||
class Field(OxiBase, Magfield):
|
||||
action = Parameter('action', EnumType(A), readonly=False)
|
||||
setpoint = Parameter('field setpoint', FloatRange(unit='T'), default=0)
|
||||
voltage = Parameter('leads voltage', FloatRange(unit='V'), default=0)
|
||||
atob = Parameter('field to amp', FloatRange(0, unit='A/T'), default=0)
|
||||
working_ramp = Parameter('effective ramp', FloatRange(0, unit='T/min'), default=0)
|
||||
persistent_field = Parameter(
|
||||
'persistent field at last switch off', FloatRange(unit='$'), readonly=False)
|
||||
wait_switch_on = Parameter(default=60)
|
||||
wait_switch_off = Parameter(default=60)
|
||||
forced_persistent_field = Parameter(
|
||||
'manual indication that persistent field is bad', BoolType(), readonly=False, default=False)
|
||||
|
||||
XPAT = re.compile(r'X(\d)(\d)A(\d)C\dH(\d)M(\d\d)P\d\d$')
|
||||
|
||||
def startModule(self, start_events):
|
||||
# on restart, assume switch is changed long time ago, if not, the mercury
|
||||
# will complain and this will be handled in start_ramp_to_field
|
||||
self.switch_on_time = 0
|
||||
self.switch_off_time = 0
|
||||
super().startModule(start_events)
|
||||
|
||||
def read_value(self):
|
||||
current = self.query('R7')
|
||||
if self.switch_heater == self.switch_heater.on:
|
||||
self.__persistent_field = current
|
||||
self.forced_persistent_field = False
|
||||
self._field_mismatch = False
|
||||
return current
|
||||
pf = self.query('R18')
|
||||
if self.__persistent_field is None:
|
||||
self.__persistent_field = pf
|
||||
self._field_mismatch = False
|
||||
else:
|
||||
self._field_mismatch = abs(self.__persistent_field - pf) > self.tolerance * 10
|
||||
self.persistent_field = self.__persistent_field
|
||||
return self.__persistent_field
|
||||
|
||||
def read_current(self):
|
||||
current = self.query('R2')
|
||||
return current / self.atob
|
||||
|
||||
def write_persistent_field(self, value):
|
||||
if self.forced_persistent_field or abs(self.__persistent_field - value) <= self.tolerance * 10:
|
||||
self._field_mismatch = False
|
||||
self.__persistent_field = value
|
||||
return value
|
||||
raise RangeError('changing persistent field needs forced_persistent_field=True')
|
||||
|
||||
def write_target(self, target):
|
||||
if self._field_mismatch:
|
||||
self.forced_persistent_field = True
|
||||
raise RangeError('persistent field does not match - set persistent field to guessed value first')
|
||||
return super().write_target(target)
|
||||
|
||||
def read_status(self):
|
||||
status = super().read_status() # from HasStates
|
||||
reply = self.communicate('X')
|
||||
match = self.XPAT.match(reply)
|
||||
statuslist = match.group()
|
||||
if statuslist[0] != '0':
|
||||
return ERROR, formatStatusBits(int(statuslist[0]),
|
||||
['quenched', 'overheated', 'warming up', 'fault'])
|
||||
# TODO: statuslist[1]: voltage / current limit status
|
||||
self.action = int(statuslist[2])
|
||||
if statuslist[3] >= '4':
|
||||
return ERROR, 'auto run-down'
|
||||
self.switch_heater = statuslist[3] == '1'
|
||||
if statuslist[3] == '5':
|
||||
return ERROR, 'switch heater failure'
|
||||
# TODO: sweep mode (fast, slow), sweep limits
|
||||
return status
|
||||
|
||||
def read_ramp(self):
|
||||
return self.query('R9')
|
||||
|
||||
def write_ramp(self, value):
|
||||
return self.change('T', 'R9', value)
|
||||
|
||||
def write_action(self, value):
|
||||
return self.change(f'A{int(value)}')
|
||||
|
||||
def read_voltage(self):
|
||||
return self.query('R1')
|
||||
|
||||
def read_working_ramp(self):
|
||||
return self.query('R6')
|
||||
|
||||
def read_setpoint(self):
|
||||
return self.query('R8')
|
||||
|
||||
def write_setpoint(self, value):
|
||||
return self.change('J', 'R8', value)
|
||||
|
||||
def write_switch_heater(self, value):
|
||||
self.read_status()
|
||||
if value == self.switch_heater:
|
||||
self.log.info('switch heater already %r', value)
|
||||
# we do not want to restart the timer
|
||||
return value
|
||||
self.command('H1')
|
||||
|
||||
# inherit Magfield.start_field_change
|
||||
|
||||
def start_ramp_to_field(self, sm):
|
||||
if abs(self.current - self.__persistent_field) <= self.tolerance:
|
||||
self.log.info('leads %g are already at %g', self.current, self.__persistent_field)
|
||||
return self.ramp_to_field
|
||||
self.read_value()
|
||||
self.write_setpoint(self.__persistent_field)
|
||||
self.write_action(A.run_to_set)
|
||||
return self.ramp_to_field
|
||||
|
||||
# inherit from Magfield: ramp_to_field, stabilize_current, start_switch_on
|
||||
|
||||
def wait_for_switch_on(self, sm):
|
||||
self.read_status()
|
||||
if self.switch_heater == self.switch_heater.off:
|
||||
if sm.init: # avoid too many states chained
|
||||
return Retry
|
||||
self.log.warning('switch turned off manually?')
|
||||
return self.start_switch_on
|
||||
return super().wait_for_switch_on(sm) # will eventually return start_ramp_to_target
|
||||
|
||||
def start_ramp_to_target(self, sm):
|
||||
self.write_action(A.run_to_set)
|
||||
return self.ramp_to_target
|
||||
|
||||
def ramp_to_target(self, sm):
|
||||
step = self.ramp / 4 # step to be done in 15 seconds
|
||||
# change the setpoint only gradually, ramping stoppes soon after connection is lost
|
||||
self.write_setpoint(clamp(self.target, self.setpoint + step, self.setpoint - step))
|
||||
return super().ramp_to_target() # will eventually return stabilize_field
|
||||
|
||||
# inherit from Magfield: stabilize_field, check_switch_off, start_switch_off
|
||||
|
||||
def wait_for_switch_off(self, sm):
|
||||
self.read_status()
|
||||
if self.switch_heater == self.switch_heater.on:
|
||||
if sm.init: # avoid too many states chained
|
||||
return Retry
|
||||
self.log.warning('switch turned on manually?')
|
||||
return self.start_switch_off
|
||||
return super().wait_for_switch_off(sm) # will eventually return start_ramp_to_zero
|
||||
|
||||
def start_ramp_to_zero(self, sm):
|
||||
self.write_action(A.run_to_zero)
|
||||
return self.ramp_to_zero
|
||||
|
||||
# inherit from Magfield: ramp_to_zero
|
||||
|
||||
def final_status(self, *args, **kwds):
|
||||
self.write_action(A.hold)
|
||||
return super().final_status(*args, **kwds)
|
@ -48,7 +48,7 @@ class Base(HasIO):
|
||||
return self.communicate(f'smua.measure.{cmd} = {val}')
|
||||
|
||||
|
||||
class Create_Pulse(Base, Readable, Writable):
|
||||
class Create_Pulse(Base, Writable):
|
||||
target = Parameter('source target', FloatRange, unit='A', readonly=False)
|
||||
width = Parameter('pulse width', FloatRange, unit="s", readonly=False)
|
||||
resistance = Parameter('resistance', FloatRange)
|
||||
@ -72,3 +72,4 @@ class Create_Pulse(Base, Readable, Writable):
|
||||
|
||||
|
||||
class Script(Create_Pulse):
|
||||
pass
|
||||
|
@ -36,11 +36,12 @@ import time
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
from frappy.client import ProxyClient
|
||||
from frappy.client import ProxyClient, CacheItem
|
||||
from frappy.datatypes import ArrayOf, BoolType, \
|
||||
EnumType, FloatRange, IntRange, StringType, StatusType
|
||||
from frappy.core import IDLE, BUSY, WARN, ERROR, DISABLED
|
||||
from frappy.errors import ConfigError, HardwareError, ReadFailedError, CommunicationFailedError
|
||||
from frappy.errors import ConfigError, HardwareError, ReadFailedError, \
|
||||
CommunicationFailedError, ProgrammingError
|
||||
from frappy.lib import generalConfig, mkthread, lazy_property
|
||||
from frappy.lib.asynconn import AsynConn, ConnectionClosed
|
||||
from frappy.modulebase import Done
|
||||
@ -161,7 +162,7 @@ class SeaClient(ProxyClient, Module):
|
||||
self.objects.add(obj)
|
||||
for k, v in module.path2param.items():
|
||||
self.path2param.setdefault(k, []).extend(v)
|
||||
self.register_callback(module.name, module.updateItem)
|
||||
self.register_callback(module.name, module.updateEvent)
|
||||
|
||||
def _connect(self):
|
||||
try:
|
||||
@ -332,9 +333,10 @@ class SeaClient(ProxyClient, Module):
|
||||
self.secNode.srv.shutdown()
|
||||
else:
|
||||
for module, param in mplist:
|
||||
oldv, oldt, oldr = self.cache.get((module, param), [None, None, None])
|
||||
oldv, oldt, oldr = self.cache[module, param]
|
||||
if value is None:
|
||||
value = oldv
|
||||
self.cache[module, param] = CacheItem(value, now, readerror)
|
||||
if value != oldv or str(readerror) != str(oldr) or abs(now - oldt) > 60:
|
||||
# do not update unchanged values within 60 sec
|
||||
self.updateValue(module, param, value, now, readerror)
|
||||
@ -417,11 +419,24 @@ class SeaConfigCreator(SeaClient):
|
||||
return reply
|
||||
|
||||
|
||||
class SeaBool(BoolType):
|
||||
"""some sea enum nodes used as boolean have text type -> accept '<integer>' also"""
|
||||
def copy(self):
|
||||
return SeaBool()
|
||||
|
||||
def __call__(self, value):
|
||||
try:
|
||||
value = int(value)
|
||||
return super().__call__(value)
|
||||
except Exception as e:
|
||||
raise ReadFailedError(e) from e
|
||||
|
||||
|
||||
SEA_TO_SECOPTYPE = {
|
||||
'float': FloatRange(),
|
||||
'text': StringType(),
|
||||
'int': IntRange(-1 << 63, 1 << 63 - 1),
|
||||
'bool': BoolType(),
|
||||
'bool': SeaBool(),
|
||||
'none': None,
|
||||
'floatvarar': ArrayOf(FloatRange(), 0, 400), # 400 is the current limit for proper notify events in SEA
|
||||
}
|
||||
@ -451,13 +466,19 @@ def get_datatype(paramdesc):
|
||||
raise ValueError('unknown SEA type %r' % typ)
|
||||
|
||||
|
||||
def get_cfg(cfgdict, *args):
|
||||
result = cfgdict.get(*args)
|
||||
return result['value'] if isinstance(result, dict) else result
|
||||
|
||||
|
||||
def pop_cfg(cfgdict, *args):
|
||||
result = cfgdict.pop(*args)
|
||||
return result['value'] if isinstance(result, dict) else result
|
||||
|
||||
|
||||
class SeaModule(Module):
|
||||
io = Attached()
|
||||
|
||||
path2param = None
|
||||
sea_object = None
|
||||
hdbpath = None # hdbpath for main writable
|
||||
|
||||
# pylint: disable=too-many-statements,arguments-differ,too-many-branches
|
||||
def __new__(cls, name, logger, cfgdict, srv):
|
||||
if hasattr(srv, 'extra_sea_modules'):
|
||||
@ -465,90 +486,100 @@ class SeaModule(Module):
|
||||
else:
|
||||
extra_modules = {}
|
||||
srv.extra_sea_modules = extra_modules
|
||||
for k, v in cfgdict.items():
|
||||
try:
|
||||
cfgdict[k] = v['value']
|
||||
except (KeyError, TypeError):
|
||||
pass
|
||||
json_file = cfgdict.pop('json_file', None) or SeaClient.default_json_file[cfgdict['io']]
|
||||
visibility_level = cfgdict.pop('visibility_level', 2)
|
||||
|
||||
single_module = cfgdict.pop('single_module', None)
|
||||
json_file = pop_cfg(cfgdict, 'json_file', None) or SeaClient.default_json_file[get_cfg(cfgdict, 'io')]
|
||||
visibility_level = pop_cfg(cfgdict, 'visibility_level', 2)
|
||||
|
||||
single_module = pop_cfg(cfgdict, 'single_module', None)
|
||||
if single_module:
|
||||
sea_object, base, paramdesc = extra_modules[single_module]
|
||||
params = [paramdesc]
|
||||
paramdesc['key'] = 'value'
|
||||
if issubclass(cls, SeaWritable):
|
||||
if issubclass(cls, SeaWritable): # and not SeaDrivable!
|
||||
if paramdesc.get('readonly', True):
|
||||
raise ConfigError(f"{sea_object}/{paramdesc['path']} is not writable")
|
||||
params.insert(0, paramdesc.copy()) # copy value
|
||||
paramdesc['key'] = 'target'
|
||||
paramdesc['readonly'] = False
|
||||
extra_module_set = ()
|
||||
if 'description' not in cfgdict:
|
||||
if not get_cfg(cfgdict, 'description'):
|
||||
cfgdict['description'] = f'{single_module}@{json_file}'
|
||||
else:
|
||||
sea_object = cfgdict.pop('sea_object')
|
||||
rel_paths = cfgdict.pop('rel_paths', '.')
|
||||
if 'description' not in cfgdict:
|
||||
sea_object = pop_cfg(cfgdict, 'sea_object', None)
|
||||
sea_path = pop_cfg(cfgdict, 'sea_path', None)
|
||||
if sea_object:
|
||||
if sea_path:
|
||||
raise ConfigError(f'module {name}: superfluous sea_object property (sea_path is given)')
|
||||
sea_path = sea_object
|
||||
rel_paths = get_cfg(cfgdict, 'rel_paths', None)
|
||||
if rel_paths is None:
|
||||
sea_object, *rel_paths = sea_path.split('/', 1)
|
||||
if not rel_paths:
|
||||
rel_paths = None
|
||||
else:
|
||||
if '/' in sea_path:
|
||||
raise ConfigError(f'module {name}: superfluous rel_paths property (sea_path is given)')
|
||||
sea_object = sea_path
|
||||
# rel_paths:
|
||||
# a list of sub nodes to look for parameters
|
||||
# '.' denotes the main path
|
||||
# Readable: the main value is taken from the first subpath
|
||||
# Writable:
|
||||
# - read the target value: <sicsobj> target
|
||||
# - write target value: command from first subpath
|
||||
# Drivable:
|
||||
# - write target value: run <sea_object> <target>
|
||||
if not get_cfg(cfgdict, 'description'):
|
||||
cfgdict['description'] = '%s@%s%s' % (
|
||||
name, json_file, '' if rel_paths == '.' else f' (rel_paths={rel_paths})')
|
||||
name, json_file, '' if rel_paths is None else f' (rel_paths={rel_paths})')
|
||||
|
||||
with (seaconfig.dir / json_file).open(encoding='utf-8') as fp:
|
||||
content = json.load(fp)
|
||||
descr = content[sea_object]
|
||||
if rel_paths == '*' or not rel_paths:
|
||||
# take all
|
||||
main = descr['params'][0]
|
||||
if issubclass(cls, Readable):
|
||||
# assert main['path'] == '' # TODO: check cases where this fails
|
||||
main['key'] = 'value'
|
||||
else:
|
||||
descr['params'].pop(0)
|
||||
|
||||
# filter by relative paths
|
||||
if rel_paths:
|
||||
result = {k: [] for k in rel_paths}
|
||||
else:
|
||||
# filter by relative paths
|
||||
result = []
|
||||
is_running = None
|
||||
for rpath in rel_paths:
|
||||
include = True
|
||||
for paramdesc in descr['params']:
|
||||
path = paramdesc['path']
|
||||
if path.endswith('is_running') and issubclass(cls, Drivable):
|
||||
# take this independent of visibility
|
||||
is_running = paramdesc
|
||||
continue
|
||||
if paramdesc.get('visibility', 1) > visibility_level:
|
||||
continue
|
||||
sub = path.split('/', 1)
|
||||
if rpath == '.': # take all except subpaths with readonly node at top
|
||||
if len(sub) == 1:
|
||||
include = paramdesc.get('kids', 0) == 0 or not paramdesc.get('readonly', True)
|
||||
if include or path == '':
|
||||
result.append(paramdesc)
|
||||
elif sub[0] == rpath:
|
||||
result.append(paramdesc)
|
||||
if is_running: # take this at end
|
||||
result.append(is_running)
|
||||
descr['params'] = result
|
||||
rel0 = '' if rel_paths[0] == '.' else rel_paths[0]
|
||||
if result[0]['path'] == rel0:
|
||||
if issubclass(cls, Readable):
|
||||
result[0]['key'] = 'value'
|
||||
else:
|
||||
result.pop(0)
|
||||
result = {True: []}
|
||||
is_running = None
|
||||
for paramdesc in descr['params']:
|
||||
path = paramdesc['path']
|
||||
pathlist = path.split('/')
|
||||
if pathlist[-1] == 'is_running' and issubclass(cls, Drivable):
|
||||
# take this independent of visibility
|
||||
is_running = paramdesc
|
||||
continue
|
||||
if paramdesc.get('visibility', 1) > visibility_level:
|
||||
continue
|
||||
if rel_paths is None:
|
||||
result[True].append(paramdesc)
|
||||
else:
|
||||
logger.error('%s: no value found', name)
|
||||
cls.paramFilter(result, paramdesc)
|
||||
cfgdict.pop('rel_paths', None)
|
||||
params = sum(result.values(), [])
|
||||
if is_running: # take this at end
|
||||
params.append(is_running)
|
||||
|
||||
main_value = params[0]
|
||||
if issubclass(cls, Readable):
|
||||
if 'key' in main_value:
|
||||
raise ProgrammingError(f'main_value {main_value!r}')
|
||||
main_value['key'] = 'value'
|
||||
else:
|
||||
params.pop(0)
|
||||
base = descr['base']
|
||||
params = descr['params']
|
||||
if issubclass(cls, SeaWritable):
|
||||
if issubclass(cls, SeaWritable): # and not SeaDrivable!
|
||||
paramdesc = params[0]
|
||||
assert paramdesc['key'] == 'value'
|
||||
if paramdesc.get('key') != 'value':
|
||||
raise ProgrammingError(f"key of first parameter of {name} must be 'value'")
|
||||
params.append(paramdesc.copy()) # copy value?
|
||||
if paramdesc.get('readonly', True):
|
||||
raise ConfigError(f"{sea_object}/{paramdesc['path']} is not writable")
|
||||
paramdesc['key'] = 'target'
|
||||
paramdesc['readonly'] = False
|
||||
extra_module_set = set(cfgdict.pop('extra_modules', ()))
|
||||
|
||||
extra_module_set = set(pop_cfg(cfgdict, 'extra_modules', ()))
|
||||
path2param = {}
|
||||
attributes = {'sea_object': sea_object, 'path2param': path2param}
|
||||
|
||||
@ -559,14 +590,14 @@ class SeaModule(Module):
|
||||
attributes['visibility'] = 2
|
||||
# check for ambiguous names. candidates are either the last item
|
||||
# of the path or the full path (underscore separated)
|
||||
simple_names = {k: 1 for k in cls.accessibles}
|
||||
duplicates = {k: [k] for k in cls.accessibles}
|
||||
for paramdesc in params:
|
||||
path = paramdesc['path']
|
||||
if path:
|
||||
pathlist = path.split('/')
|
||||
if 'key' not in paramdesc:
|
||||
pname = pathlist[-1]
|
||||
simple_names[pname] = simple_names.get(pname, 0) + 1
|
||||
duplicates.setdefault(pname, pathlist)
|
||||
for paramdesc in params:
|
||||
path = paramdesc['path']
|
||||
readonly = paramdesc.get('readonly', True)
|
||||
@ -583,11 +614,11 @@ class SeaModule(Module):
|
||||
if len(pathlist) > 0:
|
||||
if len(pathlist) == 1:
|
||||
if issubclass(cls, Readable):
|
||||
kwds['group'] = 'more'
|
||||
kwds['group'] = 'more_'
|
||||
else:
|
||||
kwds['group'] = pathlist[-2]
|
||||
kwds['group'] = pathlist[-2] + '_'
|
||||
# take short name if unique
|
||||
if simple_names[pathlist[-1]] == 1:
|
||||
if duplicates[pathlist[-1]] == pathlist:
|
||||
key = pathlist[-1]
|
||||
else:
|
||||
key = '_'.join(pathlist)
|
||||
@ -595,31 +626,41 @@ class SeaModule(Module):
|
||||
kwds['export'] = False
|
||||
if key == 'target' and kwds.get('group') == 'more':
|
||||
kwds.pop('group')
|
||||
prev = cls.accessibles.get(key)
|
||||
if key in cls.accessibles:
|
||||
if key == 'target':
|
||||
kwds['readonly'] = False
|
||||
prev = cls.accessibles[key]
|
||||
if key == 'status':
|
||||
# special case: status from sea is a string, not the status tuple
|
||||
pobj = prev.copy()
|
||||
else:
|
||||
pobj = Parameter(**kwds)
|
||||
merged_properties = prev.propertyValues.copy()
|
||||
pobj.updateProperties(merged_properties)
|
||||
pobj.merge(merged_properties)
|
||||
else:
|
||||
pobj = Parameter(**kwds)
|
||||
datatype = pobj.datatype
|
||||
if issubclass(cls, SeaWritable) and key == 'target':
|
||||
kwds['readonly'] = False
|
||||
attributes['value'] = Parameter(**kwds)
|
||||
attributes['target'] = pobj = Parameter(**kwds)
|
||||
if prev:
|
||||
merged_properties = prev.propertyValues.copy()
|
||||
pobj.updateProperties(merged_properties)
|
||||
pobj.merge(merged_properties)
|
||||
|
||||
if key in ('value', 'target'):
|
||||
unit = get_cfg(cfgdict, 'unit')
|
||||
if unit is not None:
|
||||
pcfg = cfgdict.get(key, None)
|
||||
if not isinstance(pcfg, dict):
|
||||
cfgdict[key] = pcfg = {} if pcfg is None else {'value': pcfg}
|
||||
pcfg['unit'] = unit
|
||||
|
||||
hdbpath = '/'.join([base] + pathlist)
|
||||
if key in extra_module_set:
|
||||
extra_modules[name + '.' + key] = sea_object, base, paramdesc
|
||||
continue # skip this parameter
|
||||
path2param.setdefault(hdbpath, []).append((name, key))
|
||||
attributes[key] = pobj
|
||||
if key is not None:
|
||||
path2param.setdefault(hdbpath, []).append((name, key))
|
||||
attributes[key] = pobj
|
||||
|
||||
def rfunc(self, cmd=f'hval {base}/{path}'):
|
||||
reply = self.io.query(cmd, True)
|
||||
@ -631,7 +672,7 @@ class SeaModule(Module):
|
||||
return reply
|
||||
|
||||
rfunc.poll = False
|
||||
if key != 'status':
|
||||
if key != 'status' and key is not None:
|
||||
attributes['read_' + key] = rfunc
|
||||
|
||||
if not readonly:
|
||||
@ -645,7 +686,7 @@ class SeaModule(Module):
|
||||
self.io.query(cmd)
|
||||
return Done
|
||||
|
||||
attributes['write_' + key] = wfunc
|
||||
attributes['write_' + (key or 'target')] = wfunc
|
||||
|
||||
# create standard parameters like value and status, if not yet there
|
||||
for pname, pobj in cls.accessibles.items():
|
||||
@ -659,16 +700,29 @@ class SeaModule(Module):
|
||||
pobj.__set_name__(cls, pname)
|
||||
|
||||
classname = f'{cls.__name__}_{name}'
|
||||
newcls = type(classname, (cls,), attributes)
|
||||
try:
|
||||
newcls = type(classname, (cls,), attributes)
|
||||
except Exception as e:
|
||||
raise
|
||||
# newcls = type(classname, (cls,), attributes)
|
||||
result = Module.__new__(newcls)
|
||||
return result
|
||||
|
||||
def updateItem(self, module, parameter, item):
|
||||
@classmethod
|
||||
def paramFilter(cls, result, paramdesc):
|
||||
sub = paramdesc['path'].split('/', 1)
|
||||
sublist = result.get(sub[0])
|
||||
if sublist is None:
|
||||
return False
|
||||
sublist.append(paramdesc)
|
||||
return True
|
||||
|
||||
def updateEvent(self, module, parameter, value, timestamp, readerror):
|
||||
upd = getattr(self, 'update_' + parameter, None)
|
||||
if upd:
|
||||
upd(*item)
|
||||
upd(value, timestamp, readerror)
|
||||
return
|
||||
self.announceUpdate(parameter, *item)
|
||||
self.announceUpdate(parameter, value, readerror, timestamp)
|
||||
|
||||
def initModule(self):
|
||||
self.io.register_obj(self, self.sea_object)
|
||||
@ -679,6 +733,7 @@ class SeaReadable(SeaModule, Readable):
|
||||
_readerror = None
|
||||
_status = IDLE, ''
|
||||
|
||||
unit = Property('physical unit', StringType(isUTF8=True), default='')
|
||||
status = Parameter(datatype=StatusType(Readable, 'DISABLED'))
|
||||
|
||||
def update_value(self, value, timestamp, readerror):
|
||||
@ -778,11 +833,6 @@ class SeaDrivable(SeaReadable, Drivable):
|
||||
return IDLE, f'started, but not running'
|
||||
return IDLE, ''
|
||||
|
||||
def update_target(self, module, parameter, item):
|
||||
# TODO: check if this is needed
|
||||
if item.value is not None:
|
||||
self.target = item.value
|
||||
|
||||
@Command()
|
||||
def stop(self):
|
||||
"""propagate to SEA
|
||||
@ -791,3 +841,20 @@ class SeaDrivable(SeaReadable, Drivable):
|
||||
- on EaseDriv this will set the stopped state
|
||||
"""
|
||||
self.io.query(f'{self.sea_object} is_running 0')
|
||||
|
||||
|
||||
class LscDrivable(SeaDrivable):
|
||||
def __new__(cls, name, logger, cfgdict, srv):
|
||||
cfgdict['rel_paths'] = [pop_cfg(cfgdict, 'sensor_path', 'tm'), '.',
|
||||
pop_cfg(cfgdict, 'set_path', 'set'), 'dblctrl']
|
||||
return super().__new__(cls, name, logger, cfgdict, srv)
|
||||
|
||||
@classmethod
|
||||
def paramFilter(cls, result, paramdesc):
|
||||
if super().paramFilter(result, paramdesc):
|
||||
return True
|
||||
pathlist = paramdesc['path'].split('/')
|
||||
if len(pathlist) == 1 and paramdesc.get('kids', 0) == 0:
|
||||
result['.'].append(paramdesc)
|
||||
return True
|
||||
return False
|
||||
|
@ -21,7 +21,7 @@
|
||||
|
||||
from frappy.core import Command, StringIO, Parameter, HasIO, \
|
||||
Drivable, FloatRange, IDLE, BUSY, ERROR, WARN, BoolType
|
||||
from frappy.structparam import StructParam
|
||||
from frappy.extparams import StructParam
|
||||
from frappy_psi.convergence import HasConvergence
|
||||
|
||||
|
||||
|
@ -19,17 +19,19 @@
|
||||
"""frappy support for ultrasound"""
|
||||
|
||||
import math
|
||||
#import serial
|
||||
import os
|
||||
import time
|
||||
|
||||
import numpy as np
|
||||
|
||||
import frappy_psi.iqplot as iqplot
|
||||
from frappy_psi.adq_mr import Adq
|
||||
from frappy_psi.adq_mr import Adq, PEdata, RUSdata
|
||||
from frappy.core import Attached, BoolType, Done, FloatRange, HasIO, \
|
||||
IntRange, Module, Parameter, Readable, StringIO, StringType
|
||||
IntRange, Module, Parameter, Readable, Writable, Drivable, StringIO, StringType, \
|
||||
IDLE, BUSY, DISABLED, ERROR, TupleOf, ArrayOf, Command
|
||||
from frappy.properties import Property
|
||||
#from frappy.modules import Collector
|
||||
|
||||
Collector = Readable
|
||||
|
||||
|
||||
def fname_from_time(t, extension):
|
||||
@ -52,10 +54,9 @@ class Roi(Readable):
|
||||
time = Parameter('start time', FloatRange(unit='nsec'), readonly=False)
|
||||
size = Parameter('interval (symmetric around time)', FloatRange(unit='nsec'), readonly=False)
|
||||
enable = Parameter('calculate this roi', BoolType(), readonly=False, default=True)
|
||||
#status = Parameter(export=False)
|
||||
pollinterval = Parameter(export=False)
|
||||
|
||||
interval = (0,0)
|
||||
interval = (0, 0)
|
||||
|
||||
def initModule(self):
|
||||
super().initModule()
|
||||
@ -65,6 +66,9 @@ class Roi(Readable):
|
||||
def calc_interval(self):
|
||||
self.interval = (self.time - 0.5 * self.size, self.time + 0.5 * self.size)
|
||||
|
||||
def read_status(self):
|
||||
return (IDLE, '') if self.enable else (DISABLED, 'disabled')
|
||||
|
||||
def write_time(self, value):
|
||||
self.time = value
|
||||
self.calc_interval()
|
||||
@ -82,81 +86,29 @@ class Pars(Module):
|
||||
timestamp = Parameter('unix timestamp', StringType(), default='0', readonly=False)
|
||||
temperature = Parameter('T', FloatRange(unit='K'), default=0, readonly=False)
|
||||
mf = Parameter('field', FloatRange(unit='T'), default=0, readonly=False)
|
||||
sr = Parameter('rotaion angle', FloatRange(unit='deg'), default=0, readonly=False)
|
||||
sr = Parameter('rotation angle', FloatRange(unit='deg'), default=0, readonly=False)
|
||||
|
||||
|
||||
class FreqStringIO(StringIO):
|
||||
end_of_line = '\r'
|
||||
|
||||
|
||||
class Frequency(HasIO, Readable):
|
||||
pars = Attached()
|
||||
sr = Property('samples per record', datatype=IntRange(), default=16384)
|
||||
maxy = Property('plot y scale', datatype=FloatRange(), default=0.5)
|
||||
|
||||
value = Parameter('frequency@I,q', datatype=FloatRange(unit='Hz'), default=0)
|
||||
basefreq = Parameter('base frequency', FloatRange(unit='Hz'), readonly=False)
|
||||
nr = Parameter('number of records', datatype=IntRange(1,10000), default=500)
|
||||
freq = Parameter('target frequency', FloatRange(unit='Hz'), readonly=False)
|
||||
bw = Parameter('bandwidth lowpassfilter', datatype=FloatRange(unit='Hz'),default=10E6)
|
||||
class Frequency(HasIO, Writable):
|
||||
value = Parameter('frequency', unit='Hz')
|
||||
amp = Parameter('amplitude', FloatRange(unit='dBm'), readonly=False)
|
||||
control = Parameter('control loop on?', BoolType(), readonly=False, default=True)
|
||||
time = Parameter('pulse start time', FloatRange(unit='nsec'),
|
||||
readonly=False)
|
||||
size = Parameter('pulse length (starting from time)', FloatRange(unit='nsec'),
|
||||
readonly=False)
|
||||
pulselen = Parameter('adjusted pulse length (integer number of periods)', FloatRange(unit='nsec'), default=1)
|
||||
maxstep = Parameter('max frequency step', FloatRange(unit='Hz'), readonly=False,
|
||||
default=10000)
|
||||
minstep = Parameter('min frequency step for slope calculation', FloatRange(unit='Hz'),
|
||||
readonly=False, default=4000)
|
||||
slope = Parameter('inphase/frequency slope', FloatRange(), readonly=False,
|
||||
default=1e6)
|
||||
plot = Parameter('create plot images', BoolType(), readonly=False, default=True)
|
||||
save = Parameter('save data', BoolType(), readonly=False, default=True)
|
||||
pollinterval = Parameter(datatype=FloatRange(0,120))
|
||||
|
||||
last_change = 0
|
||||
ioClass = FreqStringIO
|
||||
dif = None
|
||||
|
||||
lastfreq = None
|
||||
old = None
|
||||
starttime = None
|
||||
interval = (0,0)
|
||||
def register_dif(self, dif):
|
||||
self.dif = dif
|
||||
|
||||
def earlyInit(self):
|
||||
super().earlyInit()
|
||||
self.adq = Adq(self.nr, self.sr, self.bw)
|
||||
self.roilist = []
|
||||
self.write_nr(self.nr)
|
||||
self.skipctrl = 0
|
||||
self.plotter = iqplot.Plot(self.maxy)
|
||||
self.calc_interval()
|
||||
|
||||
def calc_interval(self):
|
||||
self.interval = (self.time, self.time + self.size)
|
||||
|
||||
def write_time(self, value):
|
||||
self.time = value
|
||||
self.calc_interval()
|
||||
return Done
|
||||
|
||||
def write_size(self, value):
|
||||
self.size = value
|
||||
self.calc_interval()
|
||||
return Done
|
||||
|
||||
def write_nr(self, value):
|
||||
# self.pollinterval = value * 0.0001
|
||||
return value
|
||||
|
||||
def register_roi(self, roi):
|
||||
self.roilist.append(roi)
|
||||
|
||||
def set_freq(self):
|
||||
freq = self.freq + self.basefreq
|
||||
self.communicate('FREQ %.15g;FREQ?' % freq)
|
||||
#self._iodev.readline().decode('ascii')
|
||||
return freq
|
||||
def write_target(self, value):
|
||||
self.communicate('FREQ %.15g;FREQ?' % value)
|
||||
self.last_change = time.time()
|
||||
if self.dif:
|
||||
self.dif.read_value()
|
||||
|
||||
def write_amp(self, amp):
|
||||
reply = self.communicate('AMPR %g;AMPR?' % amp)
|
||||
@ -166,94 +118,196 @@ class Frequency(HasIO, Readable):
|
||||
reply = self.communicate('AMPR?')
|
||||
return float(reply)
|
||||
|
||||
def write_freq(self, value):
|
||||
self.skipctrl = 2 # suppress control for the 2 next steps
|
||||
return value
|
||||
|
||||
def doPoll(self):
|
||||
"""main poll loop body"""
|
||||
if self.lastfreq is None:
|
||||
self.lastfreq = self.set_freq()
|
||||
self.adq.start()
|
||||
if self.starttime is None:
|
||||
self.starttime = time.time()
|
||||
times = []
|
||||
times.append(('init', time.time()))
|
||||
seadata = {p: float(getattr(self.pars, p)) for p in self.pars.parameters}
|
||||
data = self.adq.getdata() # this will wait, if not yet finished
|
||||
#save sample
|
||||
#np.save('sample.dat',data)
|
||||
times.append(('wait',time.time()))
|
||||
if self.control:
|
||||
freq = self.lastfreq # data was acquired at this freq
|
||||
else:
|
||||
freq = self.set_freq()
|
||||
seadata['frequency'] = freq
|
||||
if self.control:
|
||||
self.lastfreq = self.set_freq()
|
||||
times.append(('setf',time.time()))
|
||||
self.adq.start() # start next acq
|
||||
times.append(('start',time.time()))
|
||||
roilist = [r for r in self.roilist if r.enable]
|
||||
class FrequencyDif(Readable):
|
||||
freq = Attached(Frequency)
|
||||
base = Parameter('base frequency', FloatRange(unit='Hz'), default=0)
|
||||
value = Parameter('difference to base frequency', FloatRange(unit='Hz'), default=0)
|
||||
|
||||
gates = self.adq.gates_and_curves(data, freq, self.interval,
|
||||
[r.interval for r in roilist])
|
||||
if self.save:
|
||||
times.append(('save',time.time()))
|
||||
tdata, idata, qdata, pdata = self.adq.curves
|
||||
seadata['timestep'] = tdata[1] - tdata[0]
|
||||
iqdata = np.array((idata, qdata, pdata), dtype='f4')
|
||||
ts = seadata['timestamp']
|
||||
if ts:
|
||||
filename = fname_from_time(ts, '.npz')
|
||||
seanp = np.array(list(seadata.items()), dtype=[('name', 'U16'), ('value', 'f8')])
|
||||
np.savez(filename, seadata=seanp, iqdata=iqdata)
|
||||
# can be load back via
|
||||
# content = np.load(filename)
|
||||
# content['seadata'], content['iqdata']
|
||||
self.pulselen = self.adq.pulselen
|
||||
times.append(('ana',time.time()))
|
||||
if self.plot:
|
||||
# get reduced interval from adq.sinW
|
||||
pulseint = (self.interval[0], self.interval[0] + self.pulselen)
|
||||
try:
|
||||
self.plotter.plot(
|
||||
self.adq.curves,
|
||||
rois=[pulseint] + [r.interval for r in roilist],
|
||||
average=([r.time for r in roilist],
|
||||
[r.i for r in roilist],
|
||||
[r.q for r in roilist]))
|
||||
except Exception as e:
|
||||
self.log.warning('can not plot %r' % e)
|
||||
else:
|
||||
self.plotter.close()
|
||||
now = time.time()
|
||||
times.append(('plot',now))
|
||||
# print(' '.join('%s %5.3f' % (label, t - self.starttime) for label, t in times))
|
||||
self.starttime = now
|
||||
self.value = freq - self.basefreq
|
||||
for i, roi in enumerate(roilist):
|
||||
roi.i = a = gates[i][0]
|
||||
roi.q = b = gates[i][1]
|
||||
roi.value = math.sqrt(a ** 2 + b ** 2)
|
||||
roi.phase = math.atan2(a,b) * 180 / math.pi
|
||||
inphase = self.roilist[0].i
|
||||
if self.control:
|
||||
newfreq = freq + inphase * self.slope - self.basefreq
|
||||
# step = sorted((-self.maxstep, inphase * self.slope, self.maxstep))[1]
|
||||
if self.old:
|
||||
fdif = freq - self.old[0]
|
||||
idif = inphase - self.old[1]
|
||||
if abs(fdif) >= self.minstep:
|
||||
self.slope = - fdif / idif
|
||||
else:
|
||||
fdif = 0
|
||||
idif = 0
|
||||
newfreq = freq + self.minstep
|
||||
self.old = (freq, inphase)
|
||||
if self.skipctrl > 0: # do no control for some time after changing frequency
|
||||
self.skipctrl -= 1
|
||||
elif self.control:
|
||||
self.freq = sorted((self.freq - self.maxstep, newfreq, self.freq + self.maxstep))[1]
|
||||
#print(times)
|
||||
return Done
|
||||
def initModule(self):
|
||||
super().initModule()
|
||||
self.freq.register_dif(self)
|
||||
|
||||
def read_value(self):
|
||||
return self.freq - self.base
|
||||
|
||||
|
||||
class Base(Collector):
|
||||
freq = Attached()
|
||||
adq = Attached(Adq)
|
||||
sr = Parameter('samples per record', datatype=IntRange(1, 1E9), default=16384)
|
||||
pollinterval = Parameter(datatype=FloatRange(0, 120)) # allow pollinterval = 0
|
||||
_data = None
|
||||
_data_args = None
|
||||
|
||||
def read_status(self):
|
||||
adqstate = self.adq.get_status()
|
||||
if adqstate == Adq.BUSY:
|
||||
return BUSY, 'acquiring'
|
||||
if adqstate == Adq.UNDEFINED:
|
||||
return ERROR, 'no data yet'
|
||||
if adqstate == Adq.READY:
|
||||
return IDLE, 'new data available'
|
||||
return IDLE, ''
|
||||
|
||||
def get_data(self):
|
||||
data = self.adq.get_data(*self._data_args)
|
||||
if id(data) != id(self._data):
|
||||
self._data = data
|
||||
return data
|
||||
return None
|
||||
|
||||
|
||||
class PulseEcho(Base):
|
||||
value = Parameter("t, i, q, pulse curves",
|
||||
TupleOf(*[ArrayOf(FloatRange(), 0, 16283) for _ in range(4)]), default=[[]] * 4)
|
||||
nr = Parameter('number of records', datatype=IntRange(1, 9999), default=500)
|
||||
bw = Parameter('bandwidth lowpassfilter', datatype=FloatRange(unit='Hz'), default=10E6)
|
||||
control = Parameter('control loop on?', BoolType(), readonly=False, default=True)
|
||||
time = Parameter('pulse start time', FloatRange(unit='nsec'),
|
||||
readonly=False)
|
||||
size = Parameter('pulse length (starting from time)', FloatRange(unit='nsec'),
|
||||
readonly=False)
|
||||
pulselen = Parameter('adjusted pulse length (integer number of periods)', FloatRange(unit='nsec'), default=1)
|
||||
|
||||
starttime = None
|
||||
|
||||
def initModule(self):
|
||||
super().initModule()
|
||||
self.adq = Adq()
|
||||
self.adq.init(self.sr, self.nr)
|
||||
self.roilist = []
|
||||
|
||||
def write_nr(self, value):
|
||||
self.adq.init(self.sr, value)
|
||||
|
||||
def write_sr(self, value):
|
||||
self.adq.init(value, self.nr)
|
||||
|
||||
def write_bw(self, value):
|
||||
self.adq.bw_cutoff = value
|
||||
|
||||
def register_roi(self, roi):
|
||||
self.roilist.append(roi)
|
||||
|
||||
def go(self):
|
||||
self.starttime = time.time()
|
||||
self.adq.start()
|
||||
|
||||
def read_value(self):
|
||||
if self.get_rawdata(): # new data available
|
||||
roilist = [r for r in self.roilist if r.enable]
|
||||
freq = self.freq.value
|
||||
gates = self.adq.gates_and_curves(self._data, freq,
|
||||
(self.time, self.time + self.size),
|
||||
[r.interval for r in roilist])
|
||||
for i, roi in enumerate(roilist):
|
||||
roi.i = a = gates[i][0]
|
||||
roi.q = b = gates[i][1]
|
||||
roi.value = math.sqrt(a ** 2 + b ** 2)
|
||||
roi.phase = math.atan2(a, b) * 180 / math.pi
|
||||
return self.adq.curves
|
||||
|
||||
# TODO: CONTROL
|
||||
# inphase = self.roilist[0].i
|
||||
# if self.control:
|
||||
# newfreq = freq + inphase * self.slope - self.basefreq
|
||||
# # step = sorted((-self.maxstep, inphase * self.slope, self.maxstep))[1]
|
||||
# if self.old:
|
||||
# fdif = freq - self.old[0]
|
||||
# idif = inphase - self.old[1]
|
||||
# if abs(fdif) >= self.minstep:
|
||||
# self.slope = - fdif / idif
|
||||
# else:
|
||||
# fdif = 0
|
||||
# idif = 0
|
||||
# newfreq = freq + self.minstep
|
||||
# self.old = (freq, inphase)
|
||||
# if self.skipctrl > 0: # do no control for some time after changing frequency
|
||||
# self.skipctrl -= 1
|
||||
# elif self.control:
|
||||
# self.freq = sorted((self.freq - self.maxstep, newfreq, self.freq + self.maxstep))[1]
|
||||
|
||||
|
||||
class RUS(Base):
|
||||
value = Parameter('averaged (I, Q) tuple', TupleOf(FloatRange(), FloatRange()))
|
||||
periods = Parameter('number of periods', IntRange(1, 9999), default=12)
|
||||
scale = Parameter('scale,taking into account input attenuation', FloatRange(), default=0.1)
|
||||
input_phase_stddev = Parameter('input signal quality', FloatRange(unit='rad'))
|
||||
output_phase_slope = Parameter('output signal phase slope', FloatRange(unit='rad/sec'))
|
||||
output_amp_slope = Parameter('output signal amplitude change', FloatRange(unit='1/sec'))
|
||||
phase = Parameter('phase', FloatRange(unit='deg'))
|
||||
amp = Parameter('amplitude', FloatRange())
|
||||
|
||||
starttime = None
|
||||
_data_args = None
|
||||
|
||||
def initModule(self):
|
||||
super().initModule()
|
||||
self.adq = Adq()
|
||||
# self.write_periods(self.periods)
|
||||
|
||||
def read_value(self):
|
||||
if self._data_args is None:
|
||||
return self.value # or may we raise as no value is defined yet?
|
||||
data = self.get_data(RUSdata, *self._data_args)
|
||||
if data:
|
||||
# data available
|
||||
data.calc_quality()
|
||||
self.input_phase_stddev = data.input_stddev.imag
|
||||
self.output_phase_slope = data.output_slope.imag
|
||||
self.output_amp_slope = data.output_slope.real
|
||||
|
||||
iq = data.iq * self.scale
|
||||
self.phase = np.arctan2(iq.imag, iq.real) * 180 / np.pi
|
||||
self.amp = np.abs(iq.imag, iq.real)
|
||||
return iq.real, iq.imag
|
||||
return self.value
|
||||
|
||||
def go(self):
|
||||
self.starttime = time.time()
|
||||
freq = self.freq.value
|
||||
self._data_args = (RUSdata, freq, self.periods)
|
||||
self.sr = round(self.periods * self.adq.sample_rate / freq)
|
||||
self.adq.init(self.sr, 1)
|
||||
self.adq.start()
|
||||
self.read_status()
|
||||
|
||||
|
||||
class ControlLoop:
|
||||
maxstep = Parameter('max frequency step', FloatRange(unit='Hz'), readonly=False,
|
||||
default=10000)
|
||||
minstep = Parameter('min frequency step for slope calculation', FloatRange(unit='Hz'),
|
||||
readonly=False, default=4000)
|
||||
slope = Parameter('inphase/frequency slope', FloatRange(), readonly=False,
|
||||
default=1e6)
|
||||
|
||||
|
||||
# class Frequency(HasIO, Readable):
|
||||
# pars = Attached()
|
||||
# curves = Attached(mandatory=False)
|
||||
# maxy = Property('plot y scale', datatype=FloatRange(), default=0.5)
|
||||
#
|
||||
# value = Parameter('frequency@I,q', datatype=FloatRange(unit='Hz'), default=0)
|
||||
# basefreq = Parameter('base frequency', FloatRange(unit='Hz'), readonly=False)
|
||||
# nr = Parameter('number of records', datatype=IntRange(1,10000), default=500)
|
||||
# sr = Parameter('samples per record', datatype=IntRange(1,1E9), default=16384)
|
||||
# freq = Parameter('target frequency', FloatRange(unit='Hz'), readonly=False)
|
||||
# bw = Parameter('bandwidth lowpassfilter', datatype=FloatRange(unit='Hz'),default=10E6)
|
||||
# amp = Parameter('amplitude', FloatRange(unit='dBm'), readonly=False)
|
||||
# control = Parameter('control loop on?', BoolType(), readonly=False, default=True)
|
||||
# rusmode = Parameter('RUS mode on?', BoolType(), readonly=False, default=False)
|
||||
# time = Parameter('pulse start time', FloatRange(unit='nsec'),
|
||||
# readonly=False)
|
||||
# size = Parameter('pulse length (starting from time)', FloatRange(unit='nsec'),
|
||||
# readonly=False)
|
||||
# pulselen = Parameter('adjusted pulse length (integer number of periods)', FloatRange(unit='nsec'), default=1)
|
||||
# maxstep = Parameter('max frequency step', FloatRange(unit='Hz'), readonly=False,
|
||||
# default=10000)
|
||||
# minstep = Parameter('min frequency step for slope calculation', FloatRange(unit='Hz'),
|
||||
# readonly=False, default=4000)
|
||||
# slope = Parameter('inphase/frequency slope', FloatRange(), readonly=False,
|
||||
# default=1e6)
|
||||
# plot = Parameter('create plot images', BoolType(), readonly=False, default=True)
|
||||
# save = Parameter('save data', BoolType(), readonly=False, default=True)
|
||||
# pollinterval = Parameter(datatype=FloatRange(0,120))
|
||||
|
@ -96,12 +96,14 @@ def print_commit(line):
|
||||
print(' '.join(output), title)
|
||||
cnt[0] += 1
|
||||
if cnt[0] % 50 == 0:
|
||||
input(f' {br0:11s} {br1:11s}')
|
||||
if input(f' {br0:11s} {br1:11s}'):
|
||||
raise StopIteration()
|
||||
|
||||
|
||||
(br0, log0), (br1, log1) = list(log_no.items())
|
||||
no1 = 0
|
||||
for no0, line0 in enumerate(log0):
|
||||
try:
|
||||
for no0, line0 in enumerate(log0):
|
||||
if line0[1]: # line not yet printed
|
||||
infodict = commits[line0[1]]
|
||||
if len(infodict) > 1: # found a match
|
||||
@ -114,3 +116,5 @@ for no0, line0 in enumerate(log0):
|
||||
print_commit(line1)
|
||||
no1 = no1end
|
||||
print_commit(line0)
|
||||
except StopIteration:
|
||||
pass
|
||||
|
@ -1,2 +1,3 @@
|
||||
2024-01-29 wip develop
|
||||
2024-01-29 wip mlz
|
||||
2024-10-01 wip sinq
|
||||
|
@ -1,21 +0,0 @@
|
||||
# content of conftest.py
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def constants():
|
||||
# setup
|
||||
class Constants:
|
||||
ONE = 1
|
||||
TWO = 2
|
||||
c = Constants()
|
||||
yield c
|
||||
# teardown
|
||||
del c
|
||||
|
||||
|
||||
# pylint: disable=redefined-builtin
|
||||
@pytest.fixture(scope="session")
|
||||
def globals():
|
||||
return {}
|
13
test/test_cfg_editor.py
Normal file
13
test/test_cfg_editor.py
Normal file
@ -0,0 +1,13 @@
|
||||
from pathlib import Path
|
||||
|
||||
from frappy.gui.cfg_editor.utils import get_modules, get_interfaces
|
||||
from frappy.lib import generalConfig
|
||||
|
||||
basedir = Path(__file__).parent.parent.absolute()
|
||||
|
||||
|
||||
def test_imports():
|
||||
generalConfig.testinit(basedir=basedir)
|
||||
|
||||
get_modules()
|
||||
get_interfaces()
|
@ -137,5 +137,5 @@ def test_process_file(direc, log):
|
||||
|
||||
|
||||
def test_full(direc, log):
|
||||
ret = load_config('pyfile_cfg.py', log)
|
||||
ret = load_config(['pyfile_cfg.py'], log)
|
||||
do_asserts(ret)
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user