Compare commits
84 Commits
Author | SHA1 | Date | |
---|---|---|---|
421eb67b93 | |||
3048b8cb7d | |||
0ef484e082 | |||
8560384529 | |||
![]() |
16d419c0f3 | ||
![]() |
8c548da2e0 | ||
![]() |
d9f340dce6 | ||
![]() |
1325c8924d | ||
![]() |
f8e3bd9ad2 | ||
6f547f0781 | |||
![]() |
322cd39e0a | ||
![]() |
41b51b35fd | ||
19571ab83d | |||
b35c97f311 | |||
5d175b89ca | |||
f8c52af3ac | |||
bf9c946b1d | |||
09e596f847 | |||
![]() |
7e2ccd214e | ||
907a52ccdb | |||
51dba895a5 | |||
![]() |
d86718b81e | ||
![]() |
42a6bfb5d2 | ||
895f66f713 | |||
3663c62b46 | |||
![]() |
8c2588a5ed | ||
![]() |
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 |
@ -204,6 +204,9 @@ max-statements=150
|
|||||||
# Maximum number of parents for a class (see R0901).
|
# Maximum number of parents for a class (see R0901).
|
||||||
max-parents=20
|
max-parents=20
|
||||||
|
|
||||||
|
# Maximum number of positional arguments
|
||||||
|
max-positional-arguments=10
|
||||||
|
|
||||||
# Maximum number of attributes for a class (see R0902).
|
# Maximum number of attributes for a class (see R0902).
|
||||||
max-attributes=50
|
max-attributes=50
|
||||||
|
|
||||||
|
@ -69,7 +69,7 @@ def main(argv=None):
|
|||||||
console.setLevel(loglevel)
|
console.setLevel(loglevel)
|
||||||
logger.addHandler(console)
|
logger.addHandler(console)
|
||||||
|
|
||||||
app = QApplication(argv)
|
app = QApplication(argv, organizationName='frappy', applicationName='frappy_gui')
|
||||||
|
|
||||||
win = MainWindow(args, logger)
|
win = MainWindow(args, logger)
|
||||||
app.aboutToQuit.connect(win._onQuit)
|
app.aboutToQuit.connect(win._onQuit)
|
||||||
|
@ -23,6 +23,8 @@
|
|||||||
|
|
||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from frappy.lib import generalConfig
|
||||||
|
from frappy.logging import logger
|
||||||
|
|
||||||
# Add import path for inplace usage
|
# Add import path for inplace usage
|
||||||
sys.path.insert(0, str(Path(__file__).absolute().parents[1]))
|
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.client.interactive import Console
|
||||||
from frappy.playground import play, USAGE
|
from frappy.playground import play, USAGE
|
||||||
|
|
||||||
|
generalConfig.init()
|
||||||
|
logger.init()
|
||||||
if len(sys.argv) > 1:
|
if len(sys.argv) > 1:
|
||||||
play(sys.argv[1])
|
play(sys.argv[1])
|
||||||
else:
|
else:
|
||||||
|
139
bin/frappy-scan
Executable file
139
bin/frappy-scan
Executable file
@ -0,0 +1,139 @@
|
|||||||
|
#!/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):
|
||||||
|
try:
|
||||||
|
hostname = socket.gethostbyaddr(answer.address)[0]
|
||||||
|
address = hostname
|
||||||
|
numeric = f' ({answer.address})'
|
||||||
|
except Exception:
|
||||||
|
address = answer.address
|
||||||
|
numeric = ''
|
||||||
|
if short:
|
||||||
|
# NOTE: keep this easily parseable!
|
||||||
|
print(f'{answer.equipment_id} {address}:{answer.port}')
|
||||||
|
return
|
||||||
|
print(f'Found {answer.equipment_id} at {address}{numeric}:')
|
||||||
|
print(f' Port: {answer.port}')
|
||||||
|
print(f' Firmware: {answer.firmware}')
|
||||||
|
desc = answer.description.replace('\n', '\n ')
|
||||||
|
print(f' Node description: {desc}')
|
||||||
|
print('-' * 80)
|
||||||
|
|
||||||
|
|
||||||
|
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='Keep listening after the broadcast.')
|
||||||
|
parser.add_argument('-s', '--short', action='store_true',
|
||||||
|
help='Print short info (always on when listen).')
|
||||||
|
args = parser.parse_args(sys.argv[1:])
|
||||||
|
short = args.listen or args.short
|
||||||
|
if not short:
|
||||||
|
print('-' * 80)
|
||||||
|
for answer in scan():
|
||||||
|
print_answer(answer, short=short)
|
||||||
|
if args.listen:
|
||||||
|
listen(short=short)
|
@ -35,7 +35,15 @@ from frappy.server import Server
|
|||||||
|
|
||||||
|
|
||||||
def parseArgv(argv):
|
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 = parser.add_mutually_exclusive_group()
|
||||||
loggroup.add_argument("-v", "--verbose",
|
loggroup.add_argument("-v", "--verbose",
|
||||||
help="Output lots of diagnostic information",
|
help="Output lots of diagnostic information",
|
||||||
@ -60,9 +68,9 @@ def parseArgv(argv):
|
|||||||
action='store',
|
action='store',
|
||||||
help="comma separated list of cfg files,\n"
|
help="comma separated list of cfg files,\n"
|
||||||
"defaults to <name_of_the_instance>.\n"
|
"defaults to <name_of_the_instance>.\n"
|
||||||
"cfgfiles given without '.cfg' extension are searched"
|
"If a config file contains a slash, it is treated as a"
|
||||||
" in the configuration directory,"
|
"path, otherwise the file is searched for in the "
|
||||||
" else they are treated as path names",
|
"configuration directory.",
|
||||||
default=None)
|
default=None)
|
||||||
parser.add_argument('-g',
|
parser.add_argument('-g',
|
||||||
'--gencfg',
|
'--gencfg',
|
||||||
@ -96,7 +104,9 @@ def main(argv=None):
|
|||||||
generalConfig.init(args.gencfg)
|
generalConfig.init(args.gencfg)
|
||||||
logger.init(loglevel)
|
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)
|
interface=args.port, testonly=args.test)
|
||||||
|
|
||||||
if args.daemonize:
|
if args.daemonize:
|
||||||
|
50
bin/peus-plot
Executable file
50
bin/peus-plot
Executable file
@ -0,0 +1,50 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
# Add import path for inplace usage
|
||||||
|
sys.path.insert(0, str(Path(__file__).absolute().parents[1]))
|
||||||
|
|
||||||
|
from frappy.client.interactive import Client
|
||||||
|
from frappy_psi.iqplot import Plot
|
||||||
|
import numpy as np
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
|
||||||
|
|
||||||
|
if len(sys.argv) < 2:
|
||||||
|
print('Usage: peus-plot <maxY>')
|
||||||
|
|
||||||
|
|
||||||
|
def get_modules(name):
|
||||||
|
return list(filter(None, (globals().get(name % i) for i in range(10))))
|
||||||
|
|
||||||
|
|
||||||
|
secnode = Client('pc13252:5000')
|
||||||
|
time_size = {'time', 'size'}
|
||||||
|
int_mods = [u] + get_modules('roi%d')
|
||||||
|
t_rois = get_modules('roi%d')
|
||||||
|
i_rois = get_modules('roi%di')
|
||||||
|
q_rois = get_modules('roi%dq')
|
||||||
|
|
||||||
|
if len(sys.argv) > 1:
|
||||||
|
maxy = float(sys.argv[1])
|
||||||
|
else:
|
||||||
|
maxy = 0.02
|
||||||
|
|
||||||
|
|
||||||
|
iqplot = Plot(maxy)
|
||||||
|
|
||||||
|
for i in range(99):
|
||||||
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
while True:
|
||||||
|
curves = np.array(u.get_curves())
|
||||||
|
iqplot.plot(curves,
|
||||||
|
rois=[(r.time - r.size * 0.5, r.time + r.size * 0.5) for r in int_mods],
|
||||||
|
average=([r.time for r in t_rois],
|
||||||
|
[r.value for r in i_rois],
|
||||||
|
[r.value for r in q_rois]))
|
||||||
|
if not iqplot.pause(0.5):
|
||||||
|
break
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
iqplot.close()
|
226
bin/sim-server
226
bin/sim-server
@ -22,22 +22,35 @@
|
|||||||
|
|
||||||
Usage:
|
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.
|
open a server on <server port> to communicate with the string based <communicator> over TCP/IP.
|
||||||
|
|
||||||
Use cases, mainly for test purposes:
|
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
|
- 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 sys
|
||||||
import argparse
|
import argparse
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import asyncore
|
|
||||||
import socket
|
import socket
|
||||||
import time
|
import time
|
||||||
|
import os
|
||||||
|
from ast import literal_eval
|
||||||
|
from socketserver import BaseRequestHandler, ThreadingTCPServer
|
||||||
|
|
||||||
# Add import path for inplace usage
|
# Add import path for inplace usage
|
||||||
sys.path.insert(0, str(Path(__file__).absolute().parents[1]))
|
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
|
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:
|
class Logger:
|
||||||
def debug(self, *args):
|
def debug(self, *args):
|
||||||
pass
|
pass
|
||||||
@ -144,43 +71,126 @@ class Logger:
|
|||||||
exception = error = warn = info
|
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):
|
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",
|
parser.add_argument("-v", "--verbose",
|
||||||
help="output full communication",
|
help="output full communication",
|
||||||
action='store_true', default=False)
|
action='store_true', default=False)
|
||||||
parser.add_argument("cls",
|
parser.add_argument("cls",
|
||||||
type=str,
|
type=str,
|
||||||
help="simulator class.\n",)
|
help="communicator class.\n",)
|
||||||
parser.add_argument('-p',
|
parser.add_argument('-p',
|
||||||
'--port',
|
'--port',
|
||||||
action='store',
|
action='store',
|
||||||
help='server port or uri',
|
help='server port or uri',
|
||||||
default=2089)
|
default=2089)
|
||||||
|
parser.add_argument('-o',
|
||||||
|
'--options',
|
||||||
|
action='store',
|
||||||
|
nargs='*',
|
||||||
|
help='options in the form key=value',
|
||||||
|
default=None)
|
||||||
return parser.parse_args(argv)
|
return parser.parse_args(argv)
|
||||||
|
|
||||||
|
|
||||||
def poller(pollfunc):
|
|
||||||
while True:
|
|
||||||
time.sleep(1.0)
|
|
||||||
pollfunc()
|
|
||||||
|
|
||||||
|
|
||||||
def main(argv=None):
|
def main(argv=None):
|
||||||
if argv is None:
|
if argv is None:
|
||||||
argv = sys.argv
|
argv = sys.argv
|
||||||
|
|
||||||
args = parse_argv(argv[1:])
|
args = parse_argv(argv[1:])
|
||||||
|
options = {'description': ''}
|
||||||
opts = {'description': 'simulator'}
|
for item in args.options or ():
|
||||||
|
key, eq, value = item.partition('=')
|
||||||
handler_args = {'verbose': args.verbose}
|
if not eq:
|
||||||
srv = Server(int(args.port), Handler, handler_args)
|
raise ValueError(f"missing '=' in {item}")
|
||||||
module = get_class(args.cls)(args.cls, Logger(), opts, srv)
|
try:
|
||||||
handler_args['module'] = module
|
value = literal_eval(value)
|
||||||
module.earlyInit()
|
except Exception:
|
||||||
mkthread(poller, module.doPoll)
|
pass
|
||||||
srv.loop()
|
options[key] = value
|
||||||
|
srv = Server(int(args.port), args.cls, options, args.verbose)
|
||||||
|
srv.serve_forever()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
65
bin/us-plot
Executable file
65
bin/us-plot
Executable file
@ -0,0 +1,65 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
# Add import path for inplace usage
|
||||||
|
sys.path.insert(0, str(Path(__file__).absolute().parents[1]))
|
||||||
|
|
||||||
|
from frappy.client.interactive import Client
|
||||||
|
import numpy as np
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
from frappy_psi.iqplot import Pause
|
||||||
|
|
||||||
|
|
||||||
|
if len(sys.argv) < 2:
|
||||||
|
print("""
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
us-plot <end> [<start> [<npoints>]]
|
||||||
|
|
||||||
|
end: end of window [ns]
|
||||||
|
start: start of window [n2], default: 0
|
||||||
|
npoints: number fo points (default 1000)
|
||||||
|
""")
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
Client('pc13252:5000')
|
||||||
|
|
||||||
|
|
||||||
|
def plot(array, ax, style, xs):
|
||||||
|
xaxis = np.arange(len(array)) * xs
|
||||||
|
return ax.plot(xaxis, array, style)[0]
|
||||||
|
|
||||||
|
|
||||||
|
def update(array, line, xs):
|
||||||
|
xaxis = np.arange(len(array)) * xs
|
||||||
|
line.set_data(np.array([xaxis, array]))
|
||||||
|
|
||||||
|
def on_close(event):
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
start = 0
|
||||||
|
end = float(sys.argv[1])
|
||||||
|
npoints = 1000
|
||||||
|
if len(sys.argv) > 2:
|
||||||
|
start = float(sys.argv[2])
|
||||||
|
if len(sys.argv) > 3:
|
||||||
|
npoints = float(sys.argv[3])
|
||||||
|
|
||||||
|
fig, ax = plt.subplots(figsize=(15,3))
|
||||||
|
pause = Pause(fig)
|
||||||
|
try:
|
||||||
|
get_signal = iq.get_signal
|
||||||
|
print('plotting RUS signal')
|
||||||
|
except NameError:
|
||||||
|
get_signal = u.get_signal
|
||||||
|
print('plotting PE signal')
|
||||||
|
|
||||||
|
xs, signal = get_signal(start, end, npoints)
|
||||||
|
|
||||||
|
lines = [plot(s, ax, '-', xs) for s in signal]
|
||||||
|
|
||||||
|
while pause(0.5):
|
||||||
|
plt.draw()
|
||||||
|
xs, signal = get_signal(start, end, npoints)
|
||||||
|
for line, sig in zip(lines, signal):
|
||||||
|
update(sig, line, xs)
|
87
cfg/PEUS_cfg.py
Normal file
87
cfg/PEUS_cfg.py
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
Node('PEUS.psi.ch',
|
||||||
|
'ultrasound, pulse_echo configuration',
|
||||||
|
interface='5000',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('u',
|
||||||
|
'frappy_psi.ultrasound.PulseEcho',
|
||||||
|
'ultrasound acquisition loop',
|
||||||
|
freq='f',
|
||||||
|
# pollinterval=0.1,
|
||||||
|
time=900.0,
|
||||||
|
size=5000.0,
|
||||||
|
nr=500,
|
||||||
|
sr=32768,
|
||||||
|
bw=1e7,
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('fio',
|
||||||
|
'frappy_psi.ultrasound.FreqStringIO', '',
|
||||||
|
uri='serial:///dev/ttyS1?baudrate=57600',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('f',
|
||||||
|
'frappy_psi.ultrasound.Frequency',
|
||||||
|
'writable for frequency',
|
||||||
|
output='R', # L for LF (bnc), R for RF (type N)
|
||||||
|
io='fio',
|
||||||
|
amp=0.5, # VPP
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('fdif',
|
||||||
|
'frappy_psi.ultrasound.FrequencyDif',
|
||||||
|
'writable for frequency minus base frequency',
|
||||||
|
freq='f',
|
||||||
|
base=41490200.0,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Mod('curves',
|
||||||
|
# 'frappy_psi.ultrasound.Curves',
|
||||||
|
# 't, I, Q and pulse arrays for plot',
|
||||||
|
# )
|
||||||
|
|
||||||
|
def roi(name, time, size, components='iqpa', enable=True, control=False, freq=None, **kwds):
|
||||||
|
description = 'I/Q of region {name}'
|
||||||
|
if freq:
|
||||||
|
kwds.update(cls='frappy_psi.ultrasound.ControlRoi',
|
||||||
|
description=f'{description} as control loop',
|
||||||
|
freq=freq, **kwds)
|
||||||
|
else:
|
||||||
|
kwds.update(cls='frappy_psi.ultrasound.Roi',
|
||||||
|
description=description, **kwds)
|
||||||
|
kwds.update({c: name + c for c in components})
|
||||||
|
Mod(name,
|
||||||
|
main='u',
|
||||||
|
time=time,
|
||||||
|
size=size,
|
||||||
|
enable=enable,
|
||||||
|
**kwds,
|
||||||
|
)
|
||||||
|
for c in components:
|
||||||
|
Mod(name + c,
|
||||||
|
'frappy.modules.Readable',
|
||||||
|
f'{name}{c} component',
|
||||||
|
)
|
||||||
|
|
||||||
|
# control loop
|
||||||
|
roi('roi0', 2450, 300, freq='f', maxstep=100000, minstep=4000)
|
||||||
|
# other rois
|
||||||
|
roi('roi1', 5950, 300)
|
||||||
|
roi('roi2', 9475, 300)
|
||||||
|
roi('roi3', 12900, 300)
|
||||||
|
#roi('roi4', 400, 30, False)
|
||||||
|
#roi('roi5', 400, 30, False)
|
||||||
|
#roi('roi6', 400, 30, False)
|
||||||
|
#roi('roi7', 400, 30, False)
|
||||||
|
#roi('roi8', 400, 30, False)
|
||||||
|
#roi('roi9', 400, 30, False)
|
||||||
|
|
||||||
|
Mod('delay',
|
||||||
|
'frappy_psi.dg645.Delay',
|
||||||
|
'delay line with 2 channels',
|
||||||
|
uri='serial:///dev/ttyS2',
|
||||||
|
on1=1e-09,
|
||||||
|
on2=1e-09,
|
||||||
|
off1=4e-07,
|
||||||
|
off2=6e-07,
|
||||||
|
)
|
39
cfg/RUS_cfg.py
Normal file
39
cfg/RUS_cfg.py
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
Node(equipment_id = 'r_ultrasound.psi.ch',
|
||||||
|
description = 'resonant ultra sound setup',
|
||||||
|
interface = 'tcp://5000',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('iq',
|
||||||
|
cls = 'frappy_psi.ultrasound.RUS',
|
||||||
|
description = 'ultrasound iq mesurement',
|
||||||
|
imod = 'i',
|
||||||
|
qmod = 'q',
|
||||||
|
freq='f',
|
||||||
|
input_range=10, # VPP
|
||||||
|
input_delay = 0,
|
||||||
|
periods = 163,
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('freqio',
|
||||||
|
'frappy_psi.ultrasound.FreqStringIO',
|
||||||
|
' ',
|
||||||
|
uri = 'serial:///dev/ttyS1?baudrate=57600',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('f',
|
||||||
|
cls = 'frappy_psi.ultrasound.Frequency',
|
||||||
|
description = 'ultrasound frequency',
|
||||||
|
io='freqio',
|
||||||
|
output='L', # L for LF (bnc), R for RF (type N)
|
||||||
|
target=10000,
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('i',
|
||||||
|
cls='frappy.modules.Readable',
|
||||||
|
description='I component',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('q',
|
||||||
|
cls='frappy.modules.Readable',
|
||||||
|
description='Q component',
|
||||||
|
)
|
15
cfg/addons/ah2700_cfg.py
Normal file → Executable file
15
cfg/addons/ah2700_cfg.py
Normal file → Executable file
@ -2,8 +2,21 @@ Node('ah2700.frappy.psi.ch',
|
|||||||
'Andeen Hagerlin 2700 Capacitance Bridge',
|
'Andeen Hagerlin 2700 Capacitance Bridge',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Mod('cap_io',
|
||||||
|
'frappy_psi.ah2700.Ah2700IO',
|
||||||
|
'',
|
||||||
|
uri='linse-976d-ts:3006',
|
||||||
|
)
|
||||||
|
|
||||||
Mod('cap',
|
Mod('cap',
|
||||||
'frappy_psi.ah2700.Capacitance',
|
'frappy_psi.ah2700.Capacitance',
|
||||||
'capacitance',
|
'capacitance',
|
||||||
uri='dil4-ts.psi.ch:3008',
|
io = 'cap_io',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('loss',
|
||||||
|
'frappy_psi.parmod.Par',
|
||||||
|
'loss parameter',
|
||||||
|
read='cap.loss',
|
||||||
|
unit='deg',
|
||||||
)
|
)
|
||||||
|
@ -4,33 +4,22 @@ Node('ls340test.psi.ch',
|
|||||||
)
|
)
|
||||||
|
|
||||||
Mod('io',
|
Mod('io',
|
||||||
'frappy_psi.lakeshore.Ls340IO',
|
'frappy_psi.lakeshore.IO340',
|
||||||
'communication to ls340',
|
'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',
|
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',
|
'frappy_psi.lakeshore.Sensor340',
|
||||||
'cold finger temperature',
|
'sample temperature',
|
||||||
io='io',
|
# output_module='Heater',
|
||||||
channel='A'
|
device='dev',
|
||||||
)
|
channel='A',
|
||||||
|
calcurve='x29746',
|
||||||
Mod('Heater',
|
|
||||||
'frappy_psi.lakeshore.HeaterOutput',
|
|
||||||
'heater output',
|
|
||||||
channel='B',
|
|
||||||
io='io',
|
|
||||||
resistance=25,
|
|
||||||
max_power=50,
|
|
||||||
current=1
|
|
||||||
)
|
)
|
||||||
|
@ -12,9 +12,9 @@ Mod('sea_main',
|
|||||||
Mod('tt',
|
Mod('tt',
|
||||||
'frappy_psi.sea.SeaDrivable', '',
|
'frappy_psi.sea.SeaDrivable', '',
|
||||||
io='sea_main',
|
io='sea_main',
|
||||||
meaning=['temperature_regulation', 20],
|
meaning=['temperature_regulation', 27],
|
||||||
sea_object='tt',
|
sea_object='tt',
|
||||||
rel_paths=['tm', 'set', 'dblctrl'],
|
rel_paths=['tm', '.', 'set', 'dblctrl'],
|
||||||
)
|
)
|
||||||
|
|
||||||
Mod('cc',
|
Mod('cc',
|
||||||
|
@ -12,9 +12,9 @@ Mod('sea_main',
|
|||||||
Mod('tt',
|
Mod('tt',
|
||||||
'frappy_psi.sea.SeaDrivable', '',
|
'frappy_psi.sea.SeaDrivable', '',
|
||||||
io='sea_main',
|
io='sea_main',
|
||||||
meaning=['temperature_regulation', 20],
|
meaning=['temperature_regulation', 27],
|
||||||
sea_object='tt',
|
sea_object='tt',
|
||||||
rel_paths=['tm', 'set', 'dblctrl'],
|
rel_paths=['tm', '.', 'set', 'dblctrl'],
|
||||||
)
|
)
|
||||||
|
|
||||||
Mod('cc',
|
Mod('cc',
|
||||||
|
@ -12,9 +12,9 @@ Mod('sea_main',
|
|||||||
Mod('tt',
|
Mod('tt',
|
||||||
'frappy_psi.sea.SeaDrivable', '',
|
'frappy_psi.sea.SeaDrivable', '',
|
||||||
io='sea_main',
|
io='sea_main',
|
||||||
meaning=['temperature_regulation', 20],
|
meaning=['temperature_regulation', 27],
|
||||||
sea_object='tt',
|
sea_object='tt',
|
||||||
rel_paths=['tm', 'set', 'dblctrl'],
|
rel_paths=['tm', '.', 'set', 'dblctrl'],
|
||||||
)
|
)
|
||||||
|
|
||||||
Mod('cc',
|
Mod('cc',
|
||||||
|
@ -12,9 +12,9 @@ Mod('sea_main',
|
|||||||
Mod('tt',
|
Mod('tt',
|
||||||
'frappy_psi.sea.SeaDrivable', '',
|
'frappy_psi.sea.SeaDrivable', '',
|
||||||
io='sea_main',
|
io='sea_main',
|
||||||
meaning=['temperature_regulation', 20],
|
meaning=['temperature_regulation', 27],
|
||||||
sea_object='tt',
|
sea_object='tt',
|
||||||
rel_paths=['tm', 'set', 'dblctrl'],
|
rel_paths=['tm', '.', 'set', 'dblctrl'],
|
||||||
)
|
)
|
||||||
|
|
||||||
Mod('cc',
|
Mod('cc',
|
||||||
|
@ -12,9 +12,9 @@ Mod('sea_main',
|
|||||||
Mod('tt',
|
Mod('tt',
|
||||||
'frappy_psi.sea.SeaDrivable', '',
|
'frappy_psi.sea.SeaDrivable', '',
|
||||||
io='sea_main',
|
io='sea_main',
|
||||||
meaning=['temperature_regulation', 20],
|
meaning=['temperature_regulation', 27],
|
||||||
sea_object='tt',
|
sea_object='tt',
|
||||||
rel_paths=['tm', 'set', 'dblctrl'],
|
rel_paths=['tm', '.', 'set', 'dblctrl'],
|
||||||
)
|
)
|
||||||
|
|
||||||
Mod('cc',
|
Mod('cc',
|
||||||
|
@ -12,9 +12,9 @@ Mod('sea_main',
|
|||||||
Mod('tt',
|
Mod('tt',
|
||||||
'frappy_psi.sea.SeaDrivable', '',
|
'frappy_psi.sea.SeaDrivable', '',
|
||||||
io='sea_main',
|
io='sea_main',
|
||||||
meaning=['temperature_regulation', 20],
|
meaning=['temperature_regulation', 27],
|
||||||
sea_object='tt',
|
sea_object='tt',
|
||||||
rel_paths=['tm', 'set', 'dblctrl'],
|
rel_paths=['tm', '.', 'set', 'dblctrl'],
|
||||||
)
|
)
|
||||||
|
|
||||||
Mod('pauto',
|
Mod('pauto',
|
||||||
|
@ -12,7 +12,7 @@ Mod('sea_main',
|
|||||||
Mod('tt',
|
Mod('tt',
|
||||||
'frappy_psi.sea.SeaDrivable', '',
|
'frappy_psi.sea.SeaDrivable', '',
|
||||||
io='sea_main',
|
io='sea_main',
|
||||||
meaning=['temperature_regulation', 20],
|
meaning=['temperature_regulation', 27],
|
||||||
sea_object='tt',
|
sea_object='tt',
|
||||||
rel_paths=['tt', 'set'],
|
rel_paths=['tt', 'set'],
|
||||||
)
|
)
|
||||||
|
@ -12,9 +12,9 @@ Mod('sea_main',
|
|||||||
Mod('tt',
|
Mod('tt',
|
||||||
'frappy_psi.sea.SeaDrivable', '',
|
'frappy_psi.sea.SeaDrivable', '',
|
||||||
io='sea_main',
|
io='sea_main',
|
||||||
meaning=['temperature_regulation', 20],
|
meaning=['temperature_regulation', 27],
|
||||||
sea_object='tt',
|
sea_object='tt',
|
||||||
rel_paths=['tm', 'set', 'dblctrl'],
|
rel_paths=['tm', '.', 'set', 'dblctrl'],
|
||||||
)
|
)
|
||||||
|
|
||||||
Mod('cc',
|
Mod('cc',
|
||||||
|
@ -12,9 +12,9 @@ Mod('sea_main',
|
|||||||
Mod('tt',
|
Mod('tt',
|
||||||
'frappy_psi.sea.SeaDrivable', '',
|
'frappy_psi.sea.SeaDrivable', '',
|
||||||
io='sea_main',
|
io='sea_main',
|
||||||
meaning=['temperature_regulation', 20],
|
meaning=['temperature_regulation', 27],
|
||||||
sea_object='tt',
|
sea_object='tt',
|
||||||
rel_paths=['tm', 'set', 'dblctrl'],
|
rel_paths=['tm', '.', 'set', 'dblctrl'],
|
||||||
)
|
)
|
||||||
|
|
||||||
Mod('cc',
|
Mod('cc',
|
||||||
|
@ -12,7 +12,7 @@ Mod('sea_main',
|
|||||||
Mod('ts',
|
Mod('ts',
|
||||||
'frappy_psi.sea.SeaDrivable', '',
|
'frappy_psi.sea.SeaDrivable', '',
|
||||||
io='sea_main',
|
io='sea_main',
|
||||||
meaning=['temperature_regulation', 20],
|
meaning=['temperature_regulation', 27],
|
||||||
sea_object='tt',
|
sea_object='tt',
|
||||||
rel_paths=['ts', 'set']
|
rel_paths=['ts', 'set']
|
||||||
)
|
)
|
||||||
|
@ -13,7 +13,7 @@ Mod('th',
|
|||||||
'frappy_psi.sea.SeaReadable',
|
'frappy_psi.sea.SeaReadable',
|
||||||
'sample heater temperature',
|
'sample heater temperature',
|
||||||
io='sea_main',
|
io='sea_main',
|
||||||
meaning=['temperature_regulation', 20],
|
meaning=['temperature_regulation', 27],
|
||||||
sea_object='tt',
|
sea_object='tt',
|
||||||
rel_paths=['ts', 'setsamp']
|
rel_paths=['ts', 'setsamp']
|
||||||
)
|
)
|
||||||
@ -22,7 +22,7 @@ Mod('tm',
|
|||||||
'frappy_psi.sea.SeaDrivable', '',
|
'frappy_psi.sea.SeaDrivable', '',
|
||||||
io='sea_main',
|
io='sea_main',
|
||||||
sea_object='tt',
|
sea_object='tt',
|
||||||
rel_paths=['tm', 'set']
|
rel_paths=['tm', '.', 'set']
|
||||||
)
|
)
|
||||||
|
|
||||||
Mod('ts',
|
Mod('ts',
|
||||||
|
@ -12,9 +12,9 @@ Mod('sea_main',
|
|||||||
Mod('tt',
|
Mod('tt',
|
||||||
'frappy_psi.sea.SeaDrivable', '',
|
'frappy_psi.sea.SeaDrivable', '',
|
||||||
io='sea_main',
|
io='sea_main',
|
||||||
meaning=['temperature_regulation', 20],
|
meaning=['temperature_regulation', 27],
|
||||||
sea_object='tt',
|
sea_object='tt',
|
||||||
rel_paths=['tm', 'set', 'dblctrl'],
|
rel_paths=['tm', '.', 'set', 'dblctrl'],
|
||||||
)
|
)
|
||||||
|
|
||||||
Mod('cc',
|
Mod('cc',
|
||||||
|
@ -10,9 +10,9 @@ Mod('sea_main',
|
|||||||
Mod('tt',
|
Mod('tt',
|
||||||
'frappy_psi.sea.SeaDrivable', '',
|
'frappy_psi.sea.SeaDrivable', '',
|
||||||
io = 'sea_main',
|
io = 'sea_main',
|
||||||
meaning=['temperature_regulation', 20],
|
meaning=['temperature_regulation', 27],
|
||||||
sea_object = 'tt',
|
sea_object = 'tt',
|
||||||
rel_paths=['tm', 'set', 'dblctrl'],
|
rel_paths=['tm', '.', 'set', 'dblctrl'],
|
||||||
)
|
)
|
||||||
Mod('cc',
|
Mod('cc',
|
||||||
'frappy_psi.sea.SeaReadable', '',
|
'frappy_psi.sea.SeaReadable', '',
|
||||||
|
@ -12,9 +12,9 @@ Mod('sea_main',
|
|||||||
Mod('tt',
|
Mod('tt',
|
||||||
'frappy_psi.sea.SeaDrivable', '',
|
'frappy_psi.sea.SeaDrivable', '',
|
||||||
io='sea_main',
|
io='sea_main',
|
||||||
meaning=['temperature_regulation', 20],
|
meaning=['temperature_regulation', 27],
|
||||||
sea_object='tt',
|
sea_object='tt',
|
||||||
rel_paths=['tm', 'set', 'dblctrl'],
|
rel_paths=['tm', '.', 'set', 'dblctrl'],
|
||||||
)
|
)
|
||||||
|
|
||||||
Mod('cc',
|
Mod('cc',
|
||||||
|
@ -12,9 +12,9 @@ Mod('sea_main',
|
|||||||
Mod('tt',
|
Mod('tt',
|
||||||
'frappy_psi.sea.SeaDrivable', '',
|
'frappy_psi.sea.SeaDrivable', '',
|
||||||
io='sea_main',
|
io='sea_main',
|
||||||
meaning=['temperature_regulation', 20],
|
meaning=['temperature_regulation', 27],
|
||||||
sea_object='tt',
|
sea_object='tt',
|
||||||
rel_paths=['tm', 'set', 'dblctrl'],
|
rel_paths=['tm', '.', 'set', 'dblctrl'],
|
||||||
)
|
)
|
||||||
|
|
||||||
Mod('th',
|
Mod('th',
|
||||||
|
@ -10,11 +10,12 @@ Mod('sea_main',
|
|||||||
)
|
)
|
||||||
|
|
||||||
Mod('tt',
|
Mod('tt',
|
||||||
'frappy_psi.sea.SeaDrivable', '',
|
'frappy_psi.sea.LscDrivable', '',
|
||||||
io='sea_main',
|
io='sea_main',
|
||||||
meaning=['temperature_regulation', 20],
|
meaning=['temperature_regulation', 27],
|
||||||
sea_object='tt',
|
sea_object='tt',
|
||||||
rel_paths=['tm', 'set', 'dblctrl'],
|
sensor_path='tm',
|
||||||
|
set_path='set',
|
||||||
)
|
)
|
||||||
|
|
||||||
Mod('cc',
|
Mod('cc',
|
||||||
@ -69,15 +70,13 @@ Mod('lev',
|
|||||||
Mod('tcoil1',
|
Mod('tcoil1',
|
||||||
'frappy_psi.sea.SeaReadable', '',
|
'frappy_psi.sea.SeaReadable', '',
|
||||||
io='sea_main',
|
io='sea_main',
|
||||||
sea_object='tcoil',
|
sea_path='tcoil/ta',
|
||||||
rel_paths=['ta'],
|
|
||||||
)
|
)
|
||||||
|
|
||||||
Mod('tcoil2',
|
Mod('tcoil2',
|
||||||
'frappy_psi.sea.SeaReadable', '',
|
'frappy_psi.sea.SeaReadable', '',
|
||||||
io='sea_main',
|
io='sea_main',
|
||||||
sea_object='tcoil',
|
sea_path='tcoil/tb',
|
||||||
rel_paths=['tb'],
|
|
||||||
)
|
)
|
||||||
|
|
||||||
Mod('table',
|
Mod('table',
|
||||||
|
@ -12,9 +12,9 @@ Mod('sea_main',
|
|||||||
Mod('tt',
|
Mod('tt',
|
||||||
'frappy_psi.sea.SeaDrivable', '',
|
'frappy_psi.sea.SeaDrivable', '',
|
||||||
io='sea_main',
|
io='sea_main',
|
||||||
meaning=['temperature_regulation', 20],
|
meaning=['temperature_regulation', 27],
|
||||||
sea_object='tt',
|
sea_object='tt',
|
||||||
rel_paths=['tm', 'set', 'dblctrl', 'voltage'],
|
rel_paths=['tm', '.', 'set', 'dblctrl', 'voltage'],
|
||||||
extra_modules=['manualpower'],
|
extra_modules=['manualpower'],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -12,9 +12,9 @@ Mod('sea_main',
|
|||||||
Mod('tt',
|
Mod('tt',
|
||||||
'frappy_psi.sea.SeaDrivable', '',
|
'frappy_psi.sea.SeaDrivable', '',
|
||||||
io='sea_main',
|
io='sea_main',
|
||||||
meaning=['temperature_regulation', 20],
|
meaning=['temperature_regulation', 27],
|
||||||
sea_object='tt',
|
sea_object='tt',
|
||||||
rel_paths=['tm', 'set', 'dblctrl'],
|
rel_paths=['tm', '.', 'set', 'dblctrl'],
|
||||||
)
|
)
|
||||||
|
|
||||||
Mod('th',
|
Mod('th',
|
||||||
|
@ -16,10 +16,10 @@ Mod('sea_main',
|
|||||||
|
|
||||||
Mod('tt',
|
Mod('tt',
|
||||||
'frappy_psi.sea.SeaDrivable', '',
|
'frappy_psi.sea.SeaDrivable', '',
|
||||||
meaning=['temperature_regulation', 20],
|
meaning=['temperature_regulation', 27],
|
||||||
io='sea_main',
|
io='sea_main',
|
||||||
sea_object='tt',
|
sea_object='tt',
|
||||||
rel_paths=['tm', 'set', 'dblctrl'],
|
rel_paths=['tm', '.', 'set', 'dblctrl'],
|
||||||
)
|
)
|
||||||
|
|
||||||
Mod('th',
|
Mod('th',
|
||||||
|
@ -12,9 +12,9 @@ Mod('sea_main',
|
|||||||
Mod('tt',
|
Mod('tt',
|
||||||
'frappy_psi.sea.SeaDrivable', '',
|
'frappy_psi.sea.SeaDrivable', '',
|
||||||
io='sea_main',
|
io='sea_main',
|
||||||
meaning=['temperature_regulation', 20],
|
meaning=['temperature_regulation', 27],
|
||||||
sea_object='tt',
|
sea_object='tt',
|
||||||
rel_paths=['tm', 'set', 'dblctrl'],
|
rel_paths=['tm', '.', 'set', 'dblctrl'],
|
||||||
)
|
)
|
||||||
|
|
||||||
Mod('cc',
|
Mod('cc',
|
||||||
|
@ -25,7 +25,7 @@ Mod('ips',
|
|||||||
Mod('T_stat',
|
Mod('T_stat',
|
||||||
'frappy_psi.mercury.TemperatureAutoFlow',
|
'frappy_psi.mercury.TemperatureAutoFlow',
|
||||||
'static heat exchanger temperature',
|
'static heat exchanger temperature',
|
||||||
meaning=['temperature_regulation', 20],
|
meaning=['temperature_regulation', 27],
|
||||||
output_module='htr_stat',
|
output_module='htr_stat',
|
||||||
needle_valve='p_stat',
|
needle_valve='p_stat',
|
||||||
slot='DB6.T1',
|
slot='DB6.T1',
|
||||||
|
@ -23,7 +23,7 @@ Mod('ips',
|
|||||||
Mod('T_stat',
|
Mod('T_stat',
|
||||||
'frappy_psi.mercury.TemperatureAutoFlow',
|
'frappy_psi.mercury.TemperatureAutoFlow',
|
||||||
'static heat exchanger temperature',
|
'static heat exchanger temperature',
|
||||||
meaning=['temperature_regulation', 20],
|
meaning=['temperature_regulation', 27],
|
||||||
output_module='htr_stat',
|
output_module='htr_stat',
|
||||||
needle_valve='p_stat',
|
needle_valve='p_stat',
|
||||||
slot='DB6.T1',
|
slot='DB6.T1',
|
||||||
|
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',
|
Mod('tt',
|
||||||
'frappy_psi.sea.SeaDrivable', '',
|
'frappy_psi.sea.SeaDrivable', '',
|
||||||
io='sea_main',
|
io='sea_main',
|
||||||
meaning=['temperature_regulation', 20],
|
meaning=['temperature_regulation', 27],
|
||||||
sea_object='tt',
|
sea_object='tt',
|
||||||
rel_paths=['tm', 'set', 'dblctrl'],
|
rel_paths=['tm', '.', 'set', 'dblctrl'],
|
||||||
)
|
)
|
||||||
|
|
||||||
Mod('cc',
|
Mod('cc',
|
||||||
|
@ -12,9 +12,9 @@ Mod('sea_main',
|
|||||||
Mod('tt',
|
Mod('tt',
|
||||||
'frappy_psi.sea.SeaDrivable', '',
|
'frappy_psi.sea.SeaDrivable', '',
|
||||||
io='sea_main',
|
io='sea_main',
|
||||||
meaning=['temperature_regulation', 20],
|
meaning=['temperature_regulation', 27],
|
||||||
sea_object='tt',
|
sea_object='tt',
|
||||||
rel_paths=['tm', 'set', 'dblctrl'],
|
rel_paths=['tm', '.', 'set', 'dblctrl'],
|
||||||
)
|
)
|
||||||
|
|
||||||
Mod('cc',
|
Mod('cc',
|
||||||
|
@ -12,9 +12,9 @@ Mod('sea_main',
|
|||||||
Mod('tt',
|
Mod('tt',
|
||||||
'frappy_psi.sea.SeaDrivable', '',
|
'frappy_psi.sea.SeaDrivable', '',
|
||||||
io='sea_main',
|
io='sea_main',
|
||||||
meaning=['temperature_regulation', 20],
|
meaning=['temperature_regulation', 27],
|
||||||
sea_object='tt',
|
sea_object='tt',
|
||||||
rel_paths=['tm', 'set', 'dblctrl'],
|
rel_paths=['tm', '.', 'set', 'dblctrl'],
|
||||||
)
|
)
|
||||||
|
|
||||||
Mod('cc',
|
Mod('cc',
|
||||||
|
@ -12,9 +12,9 @@ Mod('sea_main',
|
|||||||
Mod('tt',
|
Mod('tt',
|
||||||
'frappy_psi.sea.SeaDrivable', '',
|
'frappy_psi.sea.SeaDrivable', '',
|
||||||
io = 'sea_main',
|
io = 'sea_main',
|
||||||
meaning=['temperature_regulation', 20],
|
meaning=['temperature_regulation', 27],
|
||||||
sea_object = 'tt',
|
sea_object = 'tt',
|
||||||
rel_paths=['tm', 'set', 'dblctrl'],
|
rel_paths=['tm', '.', 'set', 'dblctrl'],
|
||||||
)
|
)
|
||||||
|
|
||||||
Mod('cc',
|
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)
|
||||||
|
|
||||||
|
rack.lakeshore()
|
||||||
|
rack.sensor('Ts', channel='C', calcurve='x186350')
|
||||||
|
rack.loop('T', channel='B', calcurve='x174786', output_module='htr', target=10)
|
||||||
|
rack.heater('htr', 1, '100W', 25)
|
||||||
|
|
||||||
|
rack.ccu(he=True, n2=True)
|
||||||
|
|
||||||
|
rack.hepump()
|
@ -12,9 +12,9 @@ Mod('sea_main',
|
|||||||
Mod('tt',
|
Mod('tt',
|
||||||
'frappy_psi.sea.SeaDrivable', '',
|
'frappy_psi.sea.SeaDrivable', '',
|
||||||
io='sea_main',
|
io='sea_main',
|
||||||
meaning=['temperature_regulation', 20],
|
meaning=['temperature_regulation', 27],
|
||||||
sea_object='tt',
|
sea_object='tt',
|
||||||
rel_paths=['tm', 'set', 'dblctrl'],
|
rel_paths=['tm', '.', 'set', 'dblctrl'],
|
||||||
)
|
)
|
||||||
|
|
||||||
Mod('cc',
|
Mod('cc',
|
||||||
|
@ -18,7 +18,7 @@ Mod('itc2',
|
|||||||
Mod('T_stat',
|
Mod('T_stat',
|
||||||
'frappy_psi.mercury.TemperatureAutoFlow',
|
'frappy_psi.mercury.TemperatureAutoFlow',
|
||||||
'static heat exchanger temperature',
|
'static heat exchanger temperature',
|
||||||
meaning=['temperature_regulation', 20],
|
meaning=['temperature_regulation', 27],
|
||||||
output_module='htr_stat',
|
output_module='htr_stat',
|
||||||
needle_valve='p_stat',
|
needle_valve='p_stat',
|
||||||
slot='DB6.T1',
|
slot='DB6.T1',
|
||||||
@ -168,6 +168,8 @@ Mod('htr_nvd',
|
|||||||
io='itc2',
|
io='itc2',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Motor controller is not yet available!
|
||||||
|
#
|
||||||
#Mod('om_io',
|
#Mod('om_io',
|
||||||
# 'frappy_psi.phytron.PhytronIO',
|
# 'frappy_psi.phytron.PhytronIO',
|
||||||
# 'dom motor IO',
|
# 'dom motor IO',
|
||||||
|
@ -18,22 +18,22 @@
|
|||||||
{"path": "t1", "type": "float", "readonly": false, "cmd": "run tt", "kids": 3},
|
{"path": "t1", "type": "float", "readonly": false, "cmd": "run tt", "kids": 3},
|
||||||
{"path": "t1/raw", "type": "float", "readonly": false, "cmd": "run tt"},
|
{"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", "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": "t1/valid", "type": "bool", "readonly": false, "cmd": "run tt"},
|
||||||
{"path": "t2", "type": "float", "readonly": false, "cmd": "run tt", "kids": 3},
|
{"path": "t2", "type": "float", "readonly": false, "cmd": "run tt", "kids": 3},
|
||||||
{"path": "t2/raw", "type": "float", "readonly": false, "cmd": "run tt"},
|
{"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", "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": "t2/valid", "type": "bool", "readonly": false, "cmd": "run tt"},
|
||||||
{"path": "t3", "type": "float", "readonly": false, "cmd": "run tt", "kids": 3},
|
{"path": "t3", "type": "float", "readonly": false, "cmd": "run tt", "kids": 3},
|
||||||
{"path": "t3/raw", "type": "float", "readonly": false, "cmd": "run tt"},
|
{"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", "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": "t3/valid", "type": "bool", "readonly": false, "cmd": "run tt"},
|
||||||
{"path": "t4", "type": "float", "readonly": false, "cmd": "run tt", "kids": 3},
|
{"path": "t4", "type": "float", "readonly": false, "cmd": "run tt", "kids": 3},
|
||||||
{"path": "t4/raw", "type": "float", "readonly": false, "cmd": "run tt"},
|
{"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", "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": "t4/valid", "type": "bool", "readonly": false, "cmd": "run tt"},
|
||||||
{"path": "tref", "type": "float", "readonly": false, "cmd": "run tt"},
|
{"path": "tref", "type": "float", "readonly": false, "cmd": "run tt"},
|
||||||
{"path": "tout", "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"}]},
|
{"path": "custompar", "type": "float", "readonly": false, "cmd": "hemot custompar"}]},
|
||||||
|
|
||||||
"mf": {"base": "/mf", "params": [
|
"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": "persmode", "type": "int", "readonly": false, "cmd": "mf persmode"},
|
||||||
{"path": "perswitch", "type": "int"},
|
{"path": "perswitch", "type": "int"},
|
||||||
{"path": "nowait", "type": "int", "readonly": false, "cmd": "mf nowait"},
|
{"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": "send", "type": "text", "readonly": false, "cmd": "tt send", "visibility": 3},
|
||||||
{"path": "status", "type": "text", "visibility": 3},
|
{"path": "status", "type": "text", "visibility": 3},
|
||||||
{"path": "is_running", "type": "int", "readonly": false, "cmd": "tt is_running", "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/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": "setsamp/deriv", "type": "float", "readonly": false, "cmd": "tt setsamp/deriv"},
|
||||||
{"path": "display", "type": "text", "readonly": false, "cmd": "tt display"},
|
{"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": "send", "type": "text", "readonly": false, "cmd": "cc send", "visibility": 3},
|
||||||
{"path": "status", "type": "text", "visibility": 3},
|
{"path": "status", "type": "text", "visibility": 3},
|
||||||
{"path": "autodevice", "type": "bool", "readonly": false, "cmd": "cc autodevice"},
|
{"path": "autodevice", "type": "bool", "readonly": false, "cmd": "cc autodevice"},
|
||||||
@ -181,7 +185,10 @@
|
|||||||
{"path": "tm", "type": "float", "visibility": 3},
|
{"path": "tm", "type": "float", "visibility": 3},
|
||||||
{"path": "tv", "type": "float", "visibility": 3},
|
{"path": "tv", "type": "float", "visibility": 3},
|
||||||
{"path": "tq", "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": "send", "type": "text", "readonly": false, "cmd": "nv send", "visibility": 3},
|
||||||
{"path": "status", "type": "text", "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": "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": "autoflow/flowtarget", "type": "float"},
|
||||||
{"path": "calib", "type": "none", "kids": 2},
|
{"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/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": "send", "type": "text", "readonly": false, "cmd": "hepump send", "visibility": 3},
|
||||||
{"path": "status", "type": "text", "visibility": 3},
|
{"path": "status", "type": "text", "visibility": 3},
|
||||||
{"path": "running", "type": "bool", "readonly": false, "cmd": "hepump running"},
|
{"path": "running", "type": "bool", "readonly": false, "cmd": "hepump running"},
|
||||||
@ -239,7 +250,10 @@
|
|||||||
{"path": "auto", "type": "bool", "readonly": false, "cmd": "hepump auto"},
|
{"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": "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": "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": "send", "type": "text", "readonly": false, "cmd": "hemot send", "visibility": 3},
|
||||||
{"path": "status", "type": "text", "visibility": 3},
|
{"path": "status", "type": "text", "visibility": 3},
|
||||||
{"path": "is_running", "type": "int", "readonly": false, "cmd": "hemot is_running", "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": "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": "customadr", "type": "text", "readonly": false, "cmd": "hemot customadr"},
|
||||||
{"path": "custompar", "type": "float", "readonly": false, "cmd": "hemot custompar"}]},
|
{"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": "", "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": "send", "type": "text", "readonly": false, "cmd": "ln2fill send", "visibility": 3},
|
||||||
{"path": "status", "type": "text", "visibility": 3},
|
{"path": "status", "type": "text", "visibility": 3},
|
||||||
@ -285,7 +300,10 @@
|
|||||||
{"path": "maxholdhours", "type": "float", "readonly": false, "cmd": "ln2fill maxholdhours"},
|
{"path": "maxholdhours", "type": "float", "readonly": false, "cmd": "ln2fill maxholdhours"},
|
||||||
{"path": "tolerance", "type": "float", "readonly": false, "cmd": "ln2fill tolerance"},
|
{"path": "tolerance", "type": "float", "readonly": false, "cmd": "ln2fill tolerance"},
|
||||||
{"path": "badreadingminutes", "type": "float", "readonly": false, "cmd": "ln2fill badreadingminutes"},
|
{"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": "send", "type": "text", "readonly": false, "cmd": "hefill send", "visibility": 3},
|
||||||
{"path": "status", "type": "text", "visibility": 3},
|
{"path": "status", "type": "text", "visibility": 3},
|
||||||
{"path": "state", "type": "text"},
|
{"path": "state", "type": "text"},
|
||||||
@ -301,11 +319,17 @@
|
|||||||
{"path": "badreadingminutes", "type": "float", "readonly": false, "cmd": "hefill badreadingminutes"},
|
{"path": "badreadingminutes", "type": "float", "readonly": false, "cmd": "hefill badreadingminutes"},
|
||||||
{"path": "tubecoolingminutes", "type": "float", "readonly": false, "cmd": "hefill tubecoolingminutes"},
|
{"path": "tubecoolingminutes", "type": "float", "readonly": false, "cmd": "hefill tubecoolingminutes"},
|
||||||
{"path": "vessellimit", "type": "float", "readonly": false, "cmd": "hefill vessellimit"},
|
{"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": "send", "type": "text", "readonly": false, "cmd": "lev send", "visibility": 3},
|
||||||
{"path": "status", "type": "text", "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": "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": "persmode", "type": "int", "readonly": false, "cmd": "mf persmode"},
|
||||||
{"path": "perswitch", "type": "int"},
|
{"path": "perswitch", "type": "int"},
|
||||||
{"path": "nowait", "type": "int", "readonly": false, "cmd": "mf nowait"},
|
{"path": "nowait", "type": "int", "readonly": false, "cmd": "mf nowait"},
|
||||||
@ -330,7 +354,10 @@
|
|||||||
{"path": "driver", "type": "text", "visibility": 3},
|
{"path": "driver", "type": "text", "visibility": 3},
|
||||||
{"path": "creationCmd", "type": "text", "visibility": 3},
|
{"path": "creationCmd", "type": "text", "visibility": 3},
|
||||||
{"path": "targetValue", "type": "float"},
|
{"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": "send", "type": "text", "readonly": false, "cmd": "tcoil send", "visibility": 3},
|
||||||
{"path": "status", "type": "text", "visibility": 3},
|
{"path": "status", "type": "text", "visibility": 3},
|
||||||
{"path": "excitation", "type": "float", "readonly": false, "cmd": "tcoil excitation", "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/enable", "type": "bool", "readonly": false, "cmd": "tcoil td/enable"},
|
||||||
{"path": "td/r", "type": "float"},
|
{"path": "td/r", "type": "float"},
|
||||||
{"path": "td/curve", "type": "text", "readonly": false, "cmd": "tcoil td/curve", "kids": 3},
|
{"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/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"},
|
{"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"},
|
{"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", "type": "float", "visibility": 3, "kids": 3},
|
||||||
{"path": "tc/enable", "type": "bool", "readonly": false, "cmd": "tcoil tc/enable"},
|
{"path": "tc/enable", "type": "bool", "readonly": false, "cmd": "tcoil tc/enable"},
|
||||||
{"path": "tc/r", "type": "float"},
|
{"path": "tc/r", "type": "float"},
|
||||||
{"path": "tc/curve", "type": "text", "readonly": false, "cmd": "tcoil tc/curve", "kids": 3},
|
{"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/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"},
|
{"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"},
|
{"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", "type": "float", "visibility": 3, "kids": 3},
|
||||||
{"path": "tb/enable", "type": "bool", "readonly": false, "cmd": "tcoil tb/enable"},
|
{"path": "tb/enable", "type": "bool", "readonly": false, "cmd": "tcoil tb/enable"},
|
||||||
{"path": "tb/r", "type": "float"},
|
{"path": "tb/r", "type": "float"},
|
||||||
{"path": "tb/curve", "type": "text", "readonly": false, "cmd": "tcoil tb/curve", "kids": 3},
|
{"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/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"},
|
{"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"},
|
{"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", "type": "float", "visibility": 3, "kids": 3},
|
||||||
{"path": "ta/enable", "type": "bool", "readonly": false, "cmd": "tcoil ta/enable"},
|
{"path": "ta/enable", "type": "bool", "readonly": false, "cmd": "tcoil ta/enable"},
|
||||||
{"path": "ta/r", "type": "float"},
|
{"path": "ta/r", "type": "float"},
|
||||||
{"path": "ta/curve", "type": "text", "readonly": false, "cmd": "tcoil ta/curve", "kids": 3},
|
{"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/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"},
|
{"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"},
|
{"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", "type": "float", "visibility": 3, "kids": 3},
|
||||||
{"path": "ref/enable", "type": "bool", "readonly": false, "cmd": "tcoil ref/enable"},
|
{"path": "ref/enable", "type": "bool", "readonly": false, "cmd": "tcoil ref/enable"},
|
||||||
{"path": "ref/r", "type": "float"},
|
{"path": "ref/r", "type": "float"},
|
||||||
{"path": "ref/curve", "type": "text", "readonly": false, "cmd": "tcoil ref/curve", "kids": 3},
|
{"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/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"},
|
{"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"},
|
{"path": "ref/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil ref/curve/cpoints", "visibility": 3},
|
||||||
{"path": "ext", "type": "float", "visibility": 3},
|
{"path": "ext", "type": "float", "visibility": 3},
|
||||||
{"path": "com", "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": "send", "type": "text", "readonly": false, "cmd": "table send", "visibility": 3},
|
||||||
{"path": "status", "type": "text", "visibility": 3},
|
{"path": "status", "type": "text", "visibility": 3},
|
||||||
{"path": "fix_tt_set_prop", "type": "bool", "readonly": false, "cmd": "table fix_tt_set_prop"},
|
{"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": "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": "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": "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": "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 ..."}]},
|
||||||
{"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},
|
|
||||||
|
"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": "send", "type": "text", "readonly": false, "cmd": "lnv send", "visibility": 3},
|
||||||
{"path": "status", "type": "text", "visibility": 3},
|
{"path": "status", "type": "text", "visibility": 3},
|
||||||
{"path": "set", "type": "float", "readonly": false, "cmd": "lnv set"},
|
{"path": "set", "type": "float", "readonly": false, "cmd": "lnv set"},
|
||||||
@ -427,7 +463,10 @@
|
|||||||
{"path": "autoflow/difmax", "type": "float"},
|
{"path": "autoflow/difmax", "type": "float"},
|
||||||
{"path": "autoflow/setmin", "type": "float"},
|
{"path": "autoflow/setmin", "type": "float"},
|
||||||
{"path": "autoflow/setmax", "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": "send", "type": "text", "readonly": false, "cmd": "lpr send", "visibility": 3},
|
||||||
{"path": "status", "type": "text", "visibility": 3},
|
{"path": "status", "type": "text", "visibility": 3},
|
||||||
{"path": "is_running", "type": "int", "readonly": false, "cmd": "lpr is_running", "visibility": 3},
|
{"path": "is_running", "type": "int", "readonly": false, "cmd": "lpr is_running", "visibility": 3},
|
||||||
@ -455,12 +494,13 @@
|
|||||||
{"path": "running", "type": "int"},
|
{"path": "running", "type": "int"},
|
||||||
{"path": "tolerance", "type": "float", "readonly": false, "cmd": "lpr tolerance"},
|
{"path": "tolerance", "type": "float", "readonly": false, "cmd": "lpr tolerance"},
|
||||||
{"path": "maxwait", "type": "float", "readonly": false, "cmd": "lpr maxwait"},
|
{"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": "send", "type": "text", "readonly": false, "cmd": "lambdawatch send", "visibility": 3},
|
||||||
{"path": "status", "type": "text", "visibility": 3},
|
{"path": "status", "type": "text", "visibility": 3},
|
||||||
{"path": "safefield", "type": "float", "readonly": false, "cmd": "lambdawatch safefield"},
|
{"path": "safefield", "type": "float", "readonly": false, "cmd": "lambdawatch safefield"},
|
||||||
{"path": "maxfield", "type": "float", "readonly": false, "cmd": "lambdawatch maxfield"},
|
{"path": "maxfield", "type": "float", "readonly": false, "cmd": "lambdawatch maxfield"},
|
||||||
{"path": "safetemp", "type": "float", "readonly": false, "cmd": "lambdawatch safetemp"},
|
{"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": "coiltemp", "type": "text", "readonly": false, "cmd": "lambdawatch coiltemp"}]}}
|
||||||
{"path": "send", "type": "text", "readonly": false, "cmd": "prep0 send", "visibility": 3},
|
|
||||||
{"path": "status", "type": "text", "visibility": 3}]}}
|
|
||||||
|
@ -371,37 +371,37 @@
|
|||||||
{"path": "td/enable", "type": "bool", "readonly": false, "cmd": "tcoil td/enable"},
|
{"path": "td/enable", "type": "bool", "readonly": false, "cmd": "tcoil td/enable"},
|
||||||
{"path": "td/r", "type": "float"},
|
{"path": "td/r", "type": "float"},
|
||||||
{"path": "td/curve", "type": "text", "readonly": false, "cmd": "tcoil td/curve", "kids": 3},
|
{"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/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"},
|
{"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"},
|
{"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", "type": "float", "visibility": 3, "kids": 3},
|
||||||
{"path": "tb/enable", "type": "bool", "readonly": false, "cmd": "tcoil tb/enable"},
|
{"path": "tb/enable", "type": "bool", "readonly": false, "cmd": "tcoil tb/enable"},
|
||||||
{"path": "tb/r", "type": "float"},
|
{"path": "tb/r", "type": "float"},
|
||||||
{"path": "tb/curve", "type": "text", "readonly": false, "cmd": "tcoil tb/curve", "kids": 3},
|
{"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/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"},
|
{"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"},
|
{"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", "type": "float", "visibility": 3, "kids": 3},
|
||||||
{"path": "tc/enable", "type": "bool", "readonly": false, "cmd": "tcoil tc/enable"},
|
{"path": "tc/enable", "type": "bool", "readonly": false, "cmd": "tcoil tc/enable"},
|
||||||
{"path": "tc/r", "type": "float"},
|
{"path": "tc/r", "type": "float"},
|
||||||
{"path": "tc/curve", "type": "text", "readonly": false, "cmd": "tcoil tc/curve", "kids": 3},
|
{"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/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"},
|
{"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"},
|
{"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", "type": "float", "visibility": 3, "kids": 3},
|
||||||
{"path": "ta/enable", "type": "bool", "readonly": false, "cmd": "tcoil ta/enable"},
|
{"path": "ta/enable", "type": "bool", "readonly": false, "cmd": "tcoil ta/enable"},
|
||||||
{"path": "ta/r", "type": "float"},
|
{"path": "ta/r", "type": "float"},
|
||||||
{"path": "ta/curve", "type": "text", "readonly": false, "cmd": "tcoil ta/curve", "kids": 3},
|
{"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/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"},
|
{"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"},
|
{"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", "type": "float", "visibility": 3, "kids": 3},
|
||||||
{"path": "ref/enable", "type": "bool", "readonly": false, "cmd": "tcoil ref/enable"},
|
{"path": "ref/enable", "type": "bool", "readonly": false, "cmd": "tcoil ref/enable"},
|
||||||
{"path": "ref/r", "type": "float"},
|
{"path": "ref/r", "type": "float"},
|
||||||
{"path": "ref/curve", "type": "text", "readonly": false, "cmd": "tcoil ref/curve", "kids": 3},
|
{"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/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"},
|
{"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"},
|
{"path": "ref/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil ref/curve/cpoints", "visibility": 3},
|
||||||
{"path": "ext", "type": "float", "visibility": 3},
|
{"path": "ext", "type": "float", "visibility": 3},
|
||||||
{"path": "com", "type": "float", "visibility": 3},
|
{"path": "com", "type": "float", "visibility": 3},
|
||||||
{"path": "gnd", "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/enable", "type": "bool", "readonly": false, "cmd": "tcoil ta/enable"},
|
||||||
{"path": "ta/r", "type": "float"},
|
{"path": "ta/r", "type": "float"},
|
||||||
{"path": "ta/curve", "type": "text", "readonly": false, "cmd": "tcoil ta/curve", "kids": 3},
|
{"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/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"},
|
{"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"},
|
{"path": "ta/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil ta/curve/cpoints", "visibility": 3},
|
||||||
{"path": "tb", "type": "float", "kids": 3},
|
{"path": "tb", "type": "float", "kids": 3},
|
||||||
{"path": "tb/enable", "type": "bool", "readonly": false, "cmd": "tcoil tb/enable"},
|
{"path": "tb/enable", "type": "bool", "readonly": false, "cmd": "tcoil tb/enable"},
|
||||||
{"path": "tb/r", "type": "float"},
|
{"path": "tb/r", "type": "float"},
|
||||||
{"path": "tb/curve", "type": "text", "readonly": false, "cmd": "tcoil tb/curve", "kids": 3},
|
{"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/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"},
|
{"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"},
|
{"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", "type": "float", "visibility": 3, "kids": 3},
|
||||||
{"path": "td/enable", "type": "bool", "readonly": false, "cmd": "tcoil td/enable"},
|
{"path": "td/enable", "type": "bool", "readonly": false, "cmd": "tcoil td/enable"},
|
||||||
{"path": "td/r", "type": "float"},
|
{"path": "td/r", "type": "float"},
|
||||||
{"path": "td/curve", "type": "text", "readonly": false, "cmd": "tcoil td/curve", "kids": 3},
|
{"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/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"},
|
{"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"},
|
{"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", "type": "float", "visibility": 3, "kids": 3},
|
||||||
{"path": "ref/enable", "type": "bool", "readonly": false, "cmd": "tcoil ref/enable"},
|
{"path": "ref/enable", "type": "bool", "readonly": false, "cmd": "tcoil ref/enable"},
|
||||||
{"path": "ref/r", "type": "float"},
|
{"path": "ref/r", "type": "float"},
|
||||||
{"path": "ref/curve", "type": "text", "readonly": false, "cmd": "tcoil ref/curve", "kids": 3},
|
{"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/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"},
|
{"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"},
|
{"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", "type": "float", "visibility": 3, "kids": 3},
|
||||||
{"path": "tc/enable", "type": "bool", "readonly": false, "cmd": "tcoil tc/enable"},
|
{"path": "tc/enable", "type": "bool", "readonly": false, "cmd": "tcoil tc/enable"},
|
||||||
{"path": "tc/r", "type": "float"},
|
{"path": "tc/r", "type": "float"},
|
||||||
{"path": "tc/curve", "type": "text", "readonly": false, "cmd": "tcoil tc/curve", "kids": 3},
|
{"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/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"},
|
{"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"},
|
{"path": "tc/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil tc/curve/cpoints", "visibility": 3},
|
||||||
{"path": "ext", "type": "float", "visibility": 3},
|
{"path": "ext", "type": "float", "visibility": 3},
|
||||||
{"path": "com", "type": "float", "visibility": 3},
|
{"path": "com", "type": "float", "visibility": 3},
|
||||||
{"path": "gnd", "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/enable", "type": "bool", "readonly": false, "cmd": "tcoil ta/enable"},
|
||||||
{"path": "ta/r", "type": "float"},
|
{"path": "ta/r", "type": "float"},
|
||||||
{"path": "ta/curve", "type": "text", "readonly": false, "cmd": "tcoil ta/curve", "kids": 3},
|
{"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/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"},
|
{"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"},
|
{"path": "ta/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil ta/curve/cpoints", "visibility": 3},
|
||||||
{"path": "tb", "type": "float", "kids": 3},
|
{"path": "tb", "type": "float", "kids": 3},
|
||||||
{"path": "tb/enable", "type": "bool", "readonly": false, "cmd": "tcoil tb/enable"},
|
{"path": "tb/enable", "type": "bool", "readonly": false, "cmd": "tcoil tb/enable"},
|
||||||
{"path": "tb/r", "type": "float"},
|
{"path": "tb/r", "type": "float"},
|
||||||
{"path": "tb/curve", "type": "text", "readonly": false, "cmd": "tcoil tb/curve", "kids": 3},
|
{"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/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"},
|
{"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"},
|
{"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", "type": "float", "visibility": 3, "kids": 3},
|
||||||
{"path": "td/enable", "type": "bool", "readonly": false, "cmd": "tcoil td/enable"},
|
{"path": "td/enable", "type": "bool", "readonly": false, "cmd": "tcoil td/enable"},
|
||||||
{"path": "td/r", "type": "float"},
|
{"path": "td/r", "type": "float"},
|
||||||
{"path": "td/curve", "type": "text", "readonly": false, "cmd": "tcoil td/curve", "kids": 3},
|
{"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/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"},
|
{"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"},
|
{"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", "type": "float", "visibility": 3, "kids": 3},
|
||||||
{"path": "ref/enable", "type": "bool", "readonly": false, "cmd": "tcoil ref/enable"},
|
{"path": "ref/enable", "type": "bool", "readonly": false, "cmd": "tcoil ref/enable"},
|
||||||
{"path": "ref/r", "type": "float"},
|
{"path": "ref/r", "type": "float"},
|
||||||
{"path": "ref/curve", "type": "text", "readonly": false, "cmd": "tcoil ref/curve", "kids": 3},
|
{"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/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"},
|
{"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"},
|
{"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", "type": "float", "visibility": 3, "kids": 3},
|
||||||
{"path": "tc/enable", "type": "bool", "readonly": false, "cmd": "tcoil tc/enable"},
|
{"path": "tc/enable", "type": "bool", "readonly": false, "cmd": "tcoil tc/enable"},
|
||||||
{"path": "tc/r", "type": "float"},
|
{"path": "tc/r", "type": "float"},
|
||||||
{"path": "tc/curve", "type": "text", "readonly": false, "cmd": "tcoil tc/curve", "kids": 3},
|
{"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/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"},
|
{"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"},
|
{"path": "tc/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil tc/curve/cpoints", "visibility": 3},
|
||||||
{"path": "ext", "type": "float", "visibility": 3},
|
{"path": "ext", "type": "float", "visibility": 3},
|
||||||
{"path": "com", "type": "float", "visibility": 3},
|
{"path": "com", "type": "float", "visibility": 3},
|
||||||
{"path": "gnd", "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/enable", "type": "bool", "readonly": false, "cmd": "tcoil ta/enable"},
|
||||||
{"path": "ta/r", "type": "float"},
|
{"path": "ta/r", "type": "float"},
|
||||||
{"path": "ta/curve", "type": "text", "readonly": false, "cmd": "tcoil ta/curve", "kids": 3},
|
{"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/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"},
|
{"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"},
|
{"path": "ta/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil ta/curve/cpoints", "visibility": 3},
|
||||||
{"path": "tb", "type": "float", "kids": 3},
|
{"path": "tb", "type": "float", "kids": 3},
|
||||||
{"path": "tb/enable", "type": "bool", "readonly": false, "cmd": "tcoil tb/enable"},
|
{"path": "tb/enable", "type": "bool", "readonly": false, "cmd": "tcoil tb/enable"},
|
||||||
{"path": "tb/r", "type": "float"},
|
{"path": "tb/r", "type": "float"},
|
||||||
{"path": "tb/curve", "type": "text", "readonly": false, "cmd": "tcoil tb/curve", "kids": 3},
|
{"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/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"},
|
{"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"},
|
{"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", "type": "float", "visibility": 3, "kids": 3},
|
||||||
{"path": "td/enable", "type": "bool", "readonly": false, "cmd": "tcoil td/enable"},
|
{"path": "td/enable", "type": "bool", "readonly": false, "cmd": "tcoil td/enable"},
|
||||||
{"path": "td/r", "type": "float"},
|
{"path": "td/r", "type": "float"},
|
||||||
{"path": "td/curve", "type": "text", "readonly": false, "cmd": "tcoil td/curve", "kids": 3},
|
{"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/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"},
|
{"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"},
|
{"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", "type": "float", "visibility": 3, "kids": 3},
|
||||||
{"path": "ref/enable", "type": "bool", "readonly": false, "cmd": "tcoil ref/enable"},
|
{"path": "ref/enable", "type": "bool", "readonly": false, "cmd": "tcoil ref/enable"},
|
||||||
{"path": "ref/r", "type": "float"},
|
{"path": "ref/r", "type": "float"},
|
||||||
{"path": "ref/curve", "type": "text", "readonly": false, "cmd": "tcoil ref/curve", "kids": 3},
|
{"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/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"},
|
{"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"},
|
{"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", "type": "float", "visibility": 3, "kids": 3},
|
||||||
{"path": "tc/enable", "type": "bool", "readonly": false, "cmd": "tcoil tc/enable"},
|
{"path": "tc/enable", "type": "bool", "readonly": false, "cmd": "tcoil tc/enable"},
|
||||||
{"path": "tc/r", "type": "float"},
|
{"path": "tc/r", "type": "float"},
|
||||||
{"path": "tc/curve", "type": "text", "readonly": false, "cmd": "tcoil tc/curve", "kids": 3},
|
{"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/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"},
|
{"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"},
|
{"path": "tc/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil tc/curve/cpoints", "visibility": 3},
|
||||||
{"path": "ext", "type": "float", "visibility": 3},
|
{"path": "ext", "type": "float", "visibility": 3},
|
||||||
{"path": "com", "type": "float", "visibility": 3},
|
{"path": "com", "type": "float", "visibility": 3},
|
||||||
{"path": "gnd", "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/enable", "type": "bool", "readonly": false, "cmd": "tcoil ta/enable"},
|
||||||
{"path": "ta/r", "type": "float"},
|
{"path": "ta/r", "type": "float"},
|
||||||
{"path": "ta/curve", "type": "text", "readonly": false, "cmd": "tcoil ta/curve", "kids": 3},
|
{"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/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"},
|
{"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"},
|
{"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", "type": "float", "visibility": 3, "kids": 3},
|
||||||
{"path": "tb/enable", "type": "bool", "readonly": false, "cmd": "tcoil tb/enable"},
|
{"path": "tb/enable", "type": "bool", "readonly": false, "cmd": "tcoil tb/enable"},
|
||||||
{"path": "tb/r", "type": "float"},
|
{"path": "tb/r", "type": "float"},
|
||||||
{"path": "tb/curve", "type": "text", "readonly": false, "cmd": "tcoil tb/curve", "kids": 3},
|
{"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/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"},
|
{"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"},
|
{"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", "type": "float", "visibility": 3, "kids": 3},
|
||||||
{"path": "td/enable", "type": "bool", "readonly": false, "cmd": "tcoil td/enable"},
|
{"path": "td/enable", "type": "bool", "readonly": false, "cmd": "tcoil td/enable"},
|
||||||
{"path": "td/r", "type": "float"},
|
{"path": "td/r", "type": "float"},
|
||||||
{"path": "td/curve", "type": "text", "readonly": false, "cmd": "tcoil td/curve", "kids": 3},
|
{"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/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"},
|
{"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"},
|
{"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", "type": "float", "visibility": 3, "kids": 3},
|
||||||
{"path": "ref/enable", "type": "bool", "readonly": false, "cmd": "tcoil ref/enable"},
|
{"path": "ref/enable", "type": "bool", "readonly": false, "cmd": "tcoil ref/enable"},
|
||||||
{"path": "ref/r", "type": "float"},
|
{"path": "ref/r", "type": "float"},
|
||||||
{"path": "ref/curve", "type": "text", "readonly": false, "cmd": "tcoil ref/curve", "kids": 3},
|
{"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/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"},
|
{"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"},
|
{"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", "type": "float", "visibility": 3, "kids": 3},
|
||||||
{"path": "tc/enable", "type": "bool", "readonly": false, "cmd": "tcoil tc/enable"},
|
{"path": "tc/enable", "type": "bool", "readonly": false, "cmd": "tcoil tc/enable"},
|
||||||
{"path": "tc/r", "type": "float"},
|
{"path": "tc/r", "type": "float"},
|
||||||
{"path": "tc/curve", "type": "text", "readonly": false, "cmd": "tcoil tc/curve", "kids": 3},
|
{"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/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"},
|
{"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"},
|
{"path": "tc/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil tc/curve/cpoints", "visibility": 3},
|
||||||
{"path": "ext", "type": "float", "visibility": 3},
|
{"path": "ext", "type": "float", "visibility": 3},
|
||||||
{"path": "com", "type": "float", "visibility": 3},
|
{"path": "com", "type": "float", "visibility": 3},
|
||||||
{"path": "gnd", "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/enable", "type": "bool", "readonly": false, "cmd": "tcoil ta/enable"},
|
||||||
{"path": "ta/r", "type": "float"},
|
{"path": "ta/r", "type": "float"},
|
||||||
{"path": "ta/curve", "type": "text", "readonly": false, "cmd": "tcoil ta/curve", "kids": 3},
|
{"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/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"},
|
{"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"},
|
{"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", "type": "float", "visibility": 3, "kids": 3},
|
||||||
{"path": "tb/enable", "type": "bool", "readonly": false, "cmd": "tcoil tb/enable"},
|
{"path": "tb/enable", "type": "bool", "readonly": false, "cmd": "tcoil tb/enable"},
|
||||||
{"path": "tb/r", "type": "float"},
|
{"path": "tb/r", "type": "float"},
|
||||||
{"path": "tb/curve", "type": "text", "readonly": false, "cmd": "tcoil tb/curve", "kids": 3},
|
{"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/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"},
|
{"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"},
|
{"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", "type": "float", "visibility": 3, "kids": 3},
|
||||||
{"path": "td/enable", "type": "bool", "readonly": false, "cmd": "tcoil td/enable"},
|
{"path": "td/enable", "type": "bool", "readonly": false, "cmd": "tcoil td/enable"},
|
||||||
{"path": "td/r", "type": "float"},
|
{"path": "td/r", "type": "float"},
|
||||||
{"path": "td/curve", "type": "text", "readonly": false, "cmd": "tcoil td/curve", "kids": 3},
|
{"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/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"},
|
{"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"},
|
{"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", "type": "float", "visibility": 3, "kids": 3},
|
||||||
{"path": "ref/enable", "type": "bool", "readonly": false, "cmd": "tcoil ref/enable"},
|
{"path": "ref/enable", "type": "bool", "readonly": false, "cmd": "tcoil ref/enable"},
|
||||||
{"path": "ref/r", "type": "float"},
|
{"path": "ref/r", "type": "float"},
|
||||||
{"path": "ref/curve", "type": "text", "readonly": false, "cmd": "tcoil ref/curve", "kids": 3},
|
{"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/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"},
|
{"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"},
|
{"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", "type": "float", "visibility": 3, "kids": 3},
|
||||||
{"path": "tc/enable", "type": "bool", "readonly": false, "cmd": "tcoil tc/enable"},
|
{"path": "tc/enable", "type": "bool", "readonly": false, "cmd": "tcoil tc/enable"},
|
||||||
{"path": "tc/r", "type": "float"},
|
{"path": "tc/r", "type": "float"},
|
||||||
{"path": "tc/curve", "type": "text", "readonly": false, "cmd": "tcoil tc/curve", "kids": 3},
|
{"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/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"},
|
{"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"},
|
{"path": "tc/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil tc/curve/cpoints", "visibility": 3},
|
||||||
{"path": "ext", "type": "float", "visibility": 3},
|
{"path": "ext", "type": "float", "visibility": 3},
|
||||||
{"path": "com", "type": "float", "visibility": 3},
|
{"path": "com", "type": "float", "visibility": 3},
|
||||||
{"path": "gnd", "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 ..."}]}}
|
@ -20,8 +20,8 @@ Mod('tsam',
|
|||||||
|
|
||||||
Mod('ts',
|
Mod('ts',
|
||||||
'frappy_psi.parmod.Converging',
|
'frappy_psi.parmod.Converging',
|
||||||
'virtual stick T',
|
'stick T (controlled)',
|
||||||
meaning=['temperature', 30],
|
meaning=['temperature', 25],
|
||||||
unit='K',
|
unit='K',
|
||||||
read='tsam.value',
|
read='tsam.value',
|
||||||
write='tsam.setsamp',
|
write='tsam.setsamp',
|
||||||
|
@ -16,7 +16,7 @@ Mod('ts',
|
|||||||
json_file='ma6.config.json',
|
json_file='ma6.config.json',
|
||||||
sea_object='tt',
|
sea_object='tt',
|
||||||
rel_paths=['ts', 'setsamp'],
|
rel_paths=['ts', 'setsamp'],
|
||||||
meaning=['temperature', 20],
|
meaning=['temperature', 30],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -24,11 +24,10 @@ Mod('ts',
|
|||||||
Mod('ts',
|
Mod('ts',
|
||||||
'frappy_psi.parmod.Converging',
|
'frappy_psi.parmod.Converging',
|
||||||
'drivable stick T using setsamp',
|
'drivable stick T using setsamp',
|
||||||
meaning=['temperature', 30],
|
meaning=['temperature', 25],
|
||||||
unit='K',
|
unit='K',
|
||||||
read='tsam.value',
|
read='tsam.value',
|
||||||
write='tsam.setsamp',
|
write='tsam.setsamp',
|
||||||
meaning=['temperature', 20],
|
|
||||||
settling_time=20,
|
settling_time=20,
|
||||||
tolerance=1,
|
tolerance=1,
|
||||||
)
|
)
|
||||||
|
@ -13,8 +13,6 @@ Mod('ts',
|
|||||||
'frappy_psi.sea.SeaReadable', '',
|
'frappy_psi.sea.SeaReadable', '',
|
||||||
meaning=['temperature', 30],
|
meaning=['temperature', 30],
|
||||||
io='sea_stick',
|
io='sea_stick',
|
||||||
sea_object='tt',
|
sea_path='tt/ts',
|
||||||
json_file='ma7.config.json',
|
json_file='ma7.config.json',
|
||||||
rel_paths=['ts'],
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
169
debian/changelog
vendored
169
debian/changelog
vendored
@ -1,4 +1,79 @@
|
|||||||
frappy-core (0.20.1) jammy; urgency=medium
|
frappy-core (0.20.4) stable; 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) stable; 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) stable; 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) stable; urgency=medium
|
||||||
|
|
||||||
* gui: do not add a console logger when there is no sys.stdout
|
* gui: do not add a console logger when there is no sys.stdout
|
||||||
* remove unused test class
|
* remove unused test class
|
||||||
@ -8,7 +83,7 @@ frappy-core (0.20.1) jammy; urgency=medium
|
|||||||
|
|
||||||
-- Georg Brandl <jenkins@frm2.tum.de> Thu, 17 Oct 2024 16:31:27 +0200
|
-- Georg Brandl <jenkins@frm2.tum.de> Thu, 17 Oct 2024 16:31:27 +0200
|
||||||
|
|
||||||
frappy-core (0.20.0) jammy; urgency=medium
|
frappy-core (0.20.0) stable; urgency=medium
|
||||||
|
|
||||||
[ Alexander Zaft ]
|
[ Alexander Zaft ]
|
||||||
* bin: remove make_doc
|
* bin: remove make_doc
|
||||||
@ -53,7 +128,7 @@ frappy-core (0.20.0) jammy; urgency=medium
|
|||||||
|
|
||||||
-- Alexander Zaft <jenkins@frm2.tum.de> Thu, 17 Oct 2024 14:24:29 +0200
|
-- Alexander Zaft <jenkins@frm2.tum.de> Thu, 17 Oct 2024 14:24:29 +0200
|
||||||
|
|
||||||
frappy-core (0.19.10) jammy; urgency=medium
|
frappy-core (0.19.10) stable; urgency=medium
|
||||||
|
|
||||||
[ Alexander Zaft ]
|
[ Alexander Zaft ]
|
||||||
* debian: let frappy-core replace frappy-demo
|
* debian: let frappy-core replace frappy-demo
|
||||||
@ -63,25 +138,25 @@ frappy-core (0.19.10) jammy; urgency=medium
|
|||||||
|
|
||||||
-- Alexander Zaft <jenkins@frm2.tum.de> Wed, 07 Aug 2024 17:00:06 +0200
|
-- Alexander Zaft <jenkins@frm2.tum.de> Wed, 07 Aug 2024 17:00:06 +0200
|
||||||
|
|
||||||
frappy-core (0.19.9) jammy; urgency=medium
|
frappy-core (0.19.9) stable; urgency=medium
|
||||||
|
|
||||||
* debian: fix missing install dir
|
* debian: fix missing install dir
|
||||||
|
|
||||||
-- Georg Brandl <jenkins@frm2.tum.de> Tue, 06 Aug 2024 16:02:50 +0200
|
-- Georg Brandl <jenkins@frm2.tum.de> Tue, 06 Aug 2024 16:02:50 +0200
|
||||||
|
|
||||||
frappy-core (0.19.8) jammy; urgency=medium
|
frappy-core (0.19.8) stable; urgency=medium
|
||||||
|
|
||||||
* debian: move demo into core
|
* debian: move demo into core
|
||||||
|
|
||||||
-- Georg Brandl <jenkins@frm2.tum.de> Tue, 06 Aug 2024 15:58:20 +0200
|
-- Georg Brandl <jenkins@frm2.tum.de> Tue, 06 Aug 2024 15:58:20 +0200
|
||||||
|
|
||||||
frappy-core (0.19.7) jammy; urgency=medium
|
frappy-core (0.19.7) stable; urgency=medium
|
||||||
|
|
||||||
* lib: GeneralConfig fix missing keys logic
|
* lib: GeneralConfig fix missing keys logic
|
||||||
|
|
||||||
-- Alexander Zaft <jenkins@frm2.tum.de> Tue, 06 Aug 2024 15:04:07 +0200
|
-- Alexander Zaft <jenkins@frm2.tum.de> Tue, 06 Aug 2024 15:04:07 +0200
|
||||||
|
|
||||||
frappy-core (0.19.6) jammy; urgency=medium
|
frappy-core (0.19.6) stable; urgency=medium
|
||||||
|
|
||||||
[ Jens Krüger ]
|
[ Jens Krüger ]
|
||||||
* SINQ/SEA: Fix import error due to None value
|
* SINQ/SEA: Fix import error due to None value
|
||||||
@ -95,7 +170,7 @@ frappy-core (0.19.6) jammy; urgency=medium
|
|||||||
|
|
||||||
-- Jens Krüger <jenkins@frm2.tum.de> Tue, 06 Aug 2024 13:56:51 +0200
|
-- Jens Krüger <jenkins@frm2.tum.de> Tue, 06 Aug 2024 13:56:51 +0200
|
||||||
|
|
||||||
frappy-core (0.19.5) jammy; urgency=medium
|
frappy-core (0.19.5) stable; urgency=medium
|
||||||
|
|
||||||
* client: fix how to raise error on wrong ident
|
* client: fix how to raise error on wrong ident
|
||||||
* add missing requirements to setup.py
|
* add missing requirements to setup.py
|
||||||
@ -104,13 +179,13 @@ frappy-core (0.19.5) jammy; urgency=medium
|
|||||||
|
|
||||||
-- Alexander Zaft <jenkins@frm2.tum.de> Mon, 05 Aug 2024 09:30:53 +0200
|
-- Alexander Zaft <jenkins@frm2.tum.de> Mon, 05 Aug 2024 09:30:53 +0200
|
||||||
|
|
||||||
frappy-core (0.19.4) jammy; urgency=medium
|
frappy-core (0.19.4) stable; urgency=medium
|
||||||
|
|
||||||
* actually exclude cfg-editor
|
* actually exclude cfg-editor
|
||||||
|
|
||||||
-- Georg Brandl <jenkins@frm2.tum.de> Fri, 26 Jul 2024 11:46:10 +0200
|
-- Georg Brandl <jenkins@frm2.tum.de> Fri, 26 Jul 2024 11:46:10 +0200
|
||||||
|
|
||||||
frappy-core (0.19.3) jammy; urgency=medium
|
frappy-core (0.19.3) stable; urgency=medium
|
||||||
|
|
||||||
[ Markus Zolliker ]
|
[ Markus Zolliker ]
|
||||||
* frappy_psi.extparams.StructParam: fix doc + simplify
|
* frappy_psi.extparams.StructParam: fix doc + simplify
|
||||||
@ -130,7 +205,7 @@ frappy-core (0.19.3) jammy; urgency=medium
|
|||||||
|
|
||||||
-- Markus Zolliker <jenkins@frm2.tum.de> Fri, 26 Jul 2024 08:36:43 +0200
|
-- Markus Zolliker <jenkins@frm2.tum.de> Fri, 26 Jul 2024 08:36:43 +0200
|
||||||
|
|
||||||
frappy-core (0.19.2) jammy; urgency=medium
|
frappy-core (0.19.2) stable; urgency=medium
|
||||||
|
|
||||||
[ l_samenv ]
|
[ l_samenv ]
|
||||||
* fix missing update after error on parameter
|
* fix missing update after error on parameter
|
||||||
@ -155,7 +230,7 @@ frappy-core (0.19.2) jammy; urgency=medium
|
|||||||
|
|
||||||
-- l_samenv <jenkins@frm2.tum.de> Tue, 18 Jun 2024 15:21:43 +0200
|
-- l_samenv <jenkins@frm2.tum.de> Tue, 18 Jun 2024 15:21:43 +0200
|
||||||
|
|
||||||
frappy-core (0.19.1) jammy; urgency=medium
|
frappy-core (0.19.1) stable; urgency=medium
|
||||||
|
|
||||||
[ Markus Zolliker ]
|
[ Markus Zolliker ]
|
||||||
* SecopClient.online must be True while activating
|
* SecopClient.online must be True while activating
|
||||||
@ -167,7 +242,7 @@ frappy-core (0.19.1) jammy; urgency=medium
|
|||||||
|
|
||||||
-- Markus Zolliker <jenkins@frm2.tum.de> Fri, 07 Jun 2024 16:50:33 +0200
|
-- Markus Zolliker <jenkins@frm2.tum.de> Fri, 07 Jun 2024 16:50:33 +0200
|
||||||
|
|
||||||
frappy-core (0.19.0) jammy; urgency=medium
|
frappy-core (0.19.0) stable; urgency=medium
|
||||||
|
|
||||||
[ Markus Zolliker ]
|
[ Markus Zolliker ]
|
||||||
* simulation: extra_params might be a list
|
* simulation: extra_params might be a list
|
||||||
@ -223,14 +298,14 @@ frappy-core (0.19.0) jammy; urgency=medium
|
|||||||
|
|
||||||
-- Markus Zolliker <jenkins@frm2.tum.de> Thu, 16 May 2024 11:31:25 +0200
|
-- Markus Zolliker <jenkins@frm2.tum.de> Thu, 16 May 2024 11:31:25 +0200
|
||||||
|
|
||||||
frappy-core (0.18.1) focal; urgency=medium
|
frappy-core (0.18.1) stable; urgency=medium
|
||||||
|
|
||||||
* mlz: Zapf fix unit handling and small errors
|
* mlz: Zapf fix unit handling and small errors
|
||||||
* mlz: entangle fix limit check
|
* mlz: entangle fix limit check
|
||||||
|
|
||||||
-- Alexander Zaft <jenkins@frm2.tum.de> Wed, 24 Jan 2024 14:59:21 +0100
|
-- Alexander Zaft <jenkins@frm2.tum.de> Wed, 24 Jan 2024 14:59:21 +0100
|
||||||
|
|
||||||
frappy-core (0.18.0) focal; urgency=medium
|
frappy-core (0.18.0) stable; urgency=medium
|
||||||
|
|
||||||
[ Alexander Zaft ]
|
[ Alexander Zaft ]
|
||||||
* Add shutdownModule function
|
* Add shutdownModule function
|
||||||
@ -341,7 +416,7 @@ frappy-core (0.18.0) focal; urgency=medium
|
|||||||
|
|
||||||
-- Alexander Zaft <jenkins@frm2.tum.de> Wed, 17 Jan 2024 12:35:00 +0100
|
-- Alexander Zaft <jenkins@frm2.tum.de> Wed, 17 Jan 2024 12:35:00 +0100
|
||||||
|
|
||||||
frappy-core (0.17.13) focal; urgency=medium
|
frappy-core (0.17.13) stable; urgency=medium
|
||||||
|
|
||||||
[ Alexander Zaft ]
|
[ Alexander Zaft ]
|
||||||
* add egg-info to gitignore
|
* add egg-info to gitignore
|
||||||
@ -362,7 +437,7 @@ frappy-core (0.17.13) focal; urgency=medium
|
|||||||
|
|
||||||
-- Alexander Zaft <jenkins@frm2.tum.de> Tue, 20 Jun 2023 14:38:00 +0200
|
-- Alexander Zaft <jenkins@frm2.tum.de> Tue, 20 Jun 2023 14:38:00 +0200
|
||||||
|
|
||||||
frappy-core (0.17.12) focal; urgency=medium
|
frappy-core (0.17.12) stable; urgency=medium
|
||||||
|
|
||||||
[ Alexander Zaft ]
|
[ Alexander Zaft ]
|
||||||
* Warn about duplicate module definitions in a file
|
* Warn about duplicate module definitions in a file
|
||||||
@ -387,7 +462,7 @@ frappy-core (0.17.12) focal; urgency=medium
|
|||||||
|
|
||||||
-- Alexander Zaft <jenkins@frm2.tum.de> Tue, 13 Jun 2023 06:51:27 +0200
|
-- Alexander Zaft <jenkins@frm2.tum.de> Tue, 13 Jun 2023 06:51:27 +0200
|
||||||
|
|
||||||
frappy-core (0.17.11) focal; urgency=medium
|
frappy-core (0.17.11) stable; urgency=medium
|
||||||
|
|
||||||
[ Alexander Zaft ]
|
[ Alexander Zaft ]
|
||||||
* Add __format__ to EnumMember
|
* Add __format__ to EnumMember
|
||||||
@ -460,7 +535,7 @@ frappy-core (0.17.11) focal; urgency=medium
|
|||||||
|
|
||||||
-- Alexander Zaft <jenkins@frm2.tum.de> Thu, 25 May 2023 09:38:24 +0200
|
-- Alexander Zaft <jenkins@frm2.tum.de> Thu, 25 May 2023 09:38:24 +0200
|
||||||
|
|
||||||
frappy-core (0.17.10) focal; urgency=medium
|
frappy-core (0.17.10) stable; urgency=medium
|
||||||
|
|
||||||
* Change leftover %-logging calls to lazy
|
* Change leftover %-logging calls to lazy
|
||||||
* Convert formatting automatically to f-strings
|
* Convert formatting automatically to f-strings
|
||||||
@ -472,25 +547,25 @@ frappy-core (0.17.10) focal; urgency=medium
|
|||||||
|
|
||||||
-- Alexander Zaft <jenkins@frm2.tum.de> Wed, 19 Apr 2023 14:32:52 +0200
|
-- Alexander Zaft <jenkins@frm2.tum.de> Wed, 19 Apr 2023 14:32:52 +0200
|
||||||
|
|
||||||
frappy-core (0.17.9) focal; urgency=medium
|
frappy-core (0.17.9) stable; urgency=medium
|
||||||
|
|
||||||
* interactive client: avoid messing up the input line
|
* interactive client: avoid messing up the input line
|
||||||
|
|
||||||
-- Markus Zolliker <jenkins@frm2.tum.de> Tue, 11 Apr 2023 16:09:03 +0200
|
-- Markus Zolliker <jenkins@frm2.tum.de> Tue, 11 Apr 2023 16:09:03 +0200
|
||||||
|
|
||||||
frappy-core (0.17.8) focal; urgency=medium
|
frappy-core (0.17.8) stable; urgency=medium
|
||||||
|
|
||||||
* Debian: Fix typo
|
* Debian: Fix typo
|
||||||
|
|
||||||
-- Jens Krüger <jenkins@frm2.tum.de> Wed, 05 Apr 2023 07:20:25 +0200
|
-- Jens Krüger <jenkins@frm2.tum.de> Wed, 05 Apr 2023 07:20:25 +0200
|
||||||
|
|
||||||
frappy-core (0.17.7) focal; urgency=medium
|
frappy-core (0.17.7) stable; urgency=medium
|
||||||
|
|
||||||
* Debian: add pyqtgraph dependency
|
* Debian: add pyqtgraph dependency
|
||||||
|
|
||||||
-- Jens Krüger <jenkins@frm2.tum.de> Wed, 05 Apr 2023 07:07:24 +0200
|
-- Jens Krüger <jenkins@frm2.tum.de> Wed, 05 Apr 2023 07:07:24 +0200
|
||||||
|
|
||||||
frappy-core (0.17.6) focal; urgency=medium
|
frappy-core (0.17.6) stable; urgency=medium
|
||||||
|
|
||||||
[ Alexander Zaft ]
|
[ Alexander Zaft ]
|
||||||
* gui: show parameter properties again
|
* gui: show parameter properties again
|
||||||
@ -510,25 +585,25 @@ frappy-core (0.17.6) focal; urgency=medium
|
|||||||
|
|
||||||
-- Alexander Zaft <jenkins@frm2.tum.de> Tue, 04 Apr 2023 08:42:26 +0200
|
-- Alexander Zaft <jenkins@frm2.tum.de> Tue, 04 Apr 2023 08:42:26 +0200
|
||||||
|
|
||||||
frappy-core (0.17.5) focal; urgency=medium
|
frappy-core (0.17.5) stable; urgency=medium
|
||||||
|
|
||||||
* Fix generator
|
* Fix generator
|
||||||
|
|
||||||
-- Alexander Zaft <jenkins@frm2.tum.de> Wed, 22 Mar 2023 12:32:06 +0100
|
-- Alexander Zaft <jenkins@frm2.tum.de> Wed, 22 Mar 2023 12:32:06 +0100
|
||||||
|
|
||||||
frappy-core (0.17.4) focal; urgency=medium
|
frappy-core (0.17.4) stable; urgency=medium
|
||||||
|
|
||||||
* Fix entangle integration bugs
|
* Fix entangle integration bugs
|
||||||
|
|
||||||
-- Alexander Zaft <jenkins@frm2.tum.de> Wed, 22 Mar 2023 11:44:34 +0100
|
-- Alexander Zaft <jenkins@frm2.tum.de> Wed, 22 Mar 2023 11:44:34 +0100
|
||||||
|
|
||||||
frappy-core (0.17.3) focal; urgency=medium
|
frappy-core (0.17.3) stable; urgency=medium
|
||||||
|
|
||||||
* UNRELEASED
|
* UNRELEASED
|
||||||
|
|
||||||
-- Alexander Zaft <jenkins@frm2.tum.de> Tue, 21 Mar 2023 15:55:09 +0100
|
-- Alexander Zaft <jenkins@frm2.tum.de> Tue, 21 Mar 2023 15:55:09 +0100
|
||||||
|
|
||||||
frappy-core (0.17.2) focal; urgency=medium
|
frappy-core (0.17.2) stable; urgency=medium
|
||||||
|
|
||||||
[ Alexander Zaft ]
|
[ Alexander Zaft ]
|
||||||
* Fix Simulation and Proxy
|
* Fix Simulation and Proxy
|
||||||
@ -665,7 +740,7 @@ frappy-core (0.17.2) focal; urgency=medium
|
|||||||
|
|
||||||
-- Alexander Zaft <jenkins@frm2.tum.de> Tue, 21 Mar 2023 15:49:06 +0100
|
-- Alexander Zaft <jenkins@frm2.tum.de> Tue, 21 Mar 2023 15:49:06 +0100
|
||||||
|
|
||||||
frappy-core (0.17.1) focal; urgency=medium
|
frappy-core (0.17.1) stable; urgency=medium
|
||||||
|
|
||||||
[ Georg Brandl ]
|
[ Georg Brandl ]
|
||||||
* gitignore: ignore demo PID file
|
* gitignore: ignore demo PID file
|
||||||
@ -684,7 +759,7 @@ frappy-core (0.17.1) focal; urgency=medium
|
|||||||
|
|
||||||
-- Georg Brandl <jenkins@frm2.tum.de> Tue, 21 Feb 2023 17:44:56 +0100
|
-- Georg Brandl <jenkins@frm2.tum.de> Tue, 21 Feb 2023 17:44:56 +0100
|
||||||
|
|
||||||
frappy-core (0.17.0) focal; urgency=medium
|
frappy-core (0.17.0) stable; urgency=medium
|
||||||
|
|
||||||
[ Alexander Zaft ]
|
[ Alexander Zaft ]
|
||||||
* Rework GUI.
|
* Rework GUI.
|
||||||
@ -695,37 +770,37 @@ frappy-core (0.17.0) focal; urgency=medium
|
|||||||
|
|
||||||
-- Alexander Zaft <jenkins@frm2.tum.de> Tue, 21 Feb 2023 13:52:17 +0100
|
-- Alexander Zaft <jenkins@frm2.tum.de> Tue, 21 Feb 2023 13:52:17 +0100
|
||||||
|
|
||||||
frappy-core (0.16.1) focal; urgency=medium
|
frappy-core (0.16.1) stable; urgency=medium
|
||||||
|
|
||||||
* UNRELEASED
|
* UNRELEASED
|
||||||
|
|
||||||
-- Georg Brandl <jenkins@frm2.tum.de> Tue, 21 Feb 2023 08:44:28 +0100
|
-- Georg Brandl <jenkins@frm2.tum.de> Tue, 21 Feb 2023 08:44:28 +0100
|
||||||
|
|
||||||
frappy-core (0.16.4) focal; urgency=medium
|
frappy-core (0.16.4) stable; urgency=medium
|
||||||
|
|
||||||
* UNRELEASED
|
* UNRELEASED
|
||||||
|
|
||||||
-- Georg Brandl <jenkins@frm2.tum.de> Tue, 21 Feb 2023 08:09:20 +0100
|
-- Georg Brandl <jenkins@frm2.tum.de> Tue, 21 Feb 2023 08:09:20 +0100
|
||||||
|
|
||||||
frappy-core (0.16.3) focal; urgency=medium
|
frappy-core (0.16.3) stable; urgency=medium
|
||||||
|
|
||||||
* UNRELEASED
|
* UNRELEASED
|
||||||
|
|
||||||
-- Georg Brandl <jenkins@frm2.tum.de> Tue, 21 Feb 2023 08:00:15 +0100
|
-- Georg Brandl <jenkins@frm2.tum.de> Tue, 21 Feb 2023 08:00:15 +0100
|
||||||
|
|
||||||
frappy-core (0.16.2) focal; urgency=medium
|
frappy-core (0.16.2) stable; urgency=medium
|
||||||
|
|
||||||
* gui: move icon resources for the cfg editor to its subdirectory
|
* gui: move icon resources for the cfg editor to its subdirectory
|
||||||
|
|
||||||
-- Georg Brandl <jenkins@frm2.tum.de> Tue, 21 Feb 2023 07:50:13 +0100
|
-- Georg Brandl <jenkins@frm2.tum.de> Tue, 21 Feb 2023 07:50:13 +0100
|
||||||
|
|
||||||
frappy-core (0.16.1) focal; urgency=medium
|
frappy-core (0.16.1) stable; urgency=medium
|
||||||
|
|
||||||
* add frappy-cli to package
|
* add frappy-cli to package
|
||||||
|
|
||||||
-- Enrico Faulhaber <jenkins@frm2.tum.de> Mon, 20 Feb 2023 17:17:23 +0100
|
-- Enrico Faulhaber <jenkins@frm2.tum.de> Mon, 20 Feb 2023 17:17:23 +0100
|
||||||
|
|
||||||
frappy-core (0.16.0) focal; urgency=medium
|
frappy-core (0.16.0) stable; urgency=medium
|
||||||
|
|
||||||
[ Enrico Faulhaber ]
|
[ Enrico Faulhaber ]
|
||||||
* fix sorce package name
|
* fix sorce package name
|
||||||
@ -787,7 +862,7 @@ frappy-core (0.16.0) focal; urgency=medium
|
|||||||
|
|
||||||
-- Enrico Faulhaber <jenkins@frm2.tum.de> Mon, 20 Feb 2023 16:15:10 +0100
|
-- Enrico Faulhaber <jenkins@frm2.tum.de> Mon, 20 Feb 2023 16:15:10 +0100
|
||||||
|
|
||||||
frappy-core (0.15.0) focal; urgency=medium
|
frappy-core (0.15.0) stable; urgency=medium
|
||||||
|
|
||||||
[ Björn Pedersen ]
|
[ Björn Pedersen ]
|
||||||
* Remove iohandler left-overs from docs
|
* Remove iohandler left-overs from docs
|
||||||
@ -817,7 +892,7 @@ frappy-core (0.15.0) focal; urgency=medium
|
|||||||
|
|
||||||
-- Björn Pedersen <jenkins@frm2.tum.de> Thu, 10 Nov 2022 14:46:01 +0100
|
-- Björn Pedersen <jenkins@frm2.tum.de> Thu, 10 Nov 2022 14:46:01 +0100
|
||||||
|
|
||||||
secop-core (0.14.3) focal; urgency=medium
|
secop-core (0.14.3) stable; urgency=medium
|
||||||
|
|
||||||
[ Enrico Faulhaber ]
|
[ Enrico Faulhaber ]
|
||||||
* change repo to secop/frappy
|
* change repo to secop/frappy
|
||||||
@ -833,13 +908,13 @@ secop-core (0.14.3) focal; urgency=medium
|
|||||||
|
|
||||||
-- Enrico Faulhaber <jenkins@frm2.tum.de> Thu, 03 Nov 2022 13:51:52 +0100
|
-- Enrico Faulhaber <jenkins@frm2.tum.de> Thu, 03 Nov 2022 13:51:52 +0100
|
||||||
|
|
||||||
secop-core (0.14.2) focal; urgency=medium
|
secop-core (0.14.2) stable; urgency=medium
|
||||||
|
|
||||||
* systemd generator: adapt to changed config API
|
* systemd generator: adapt to changed config API
|
||||||
|
|
||||||
-- Georg Brandl <jenkins@frm2.tum.de> Thu, 20 Oct 2022 15:38:45 +0200
|
-- Georg Brandl <jenkins@frm2.tum.de> Thu, 20 Oct 2022 15:38:45 +0200
|
||||||
|
|
||||||
secop-core (0.14.1) focal; urgency=medium
|
secop-core (0.14.1) stable; urgency=medium
|
||||||
|
|
||||||
[ Markus Zolliker ]
|
[ Markus Zolliker ]
|
||||||
* secop_psi.entangle.AnalogInput: fix main value
|
* secop_psi.entangle.AnalogInput: fix main value
|
||||||
@ -851,7 +926,7 @@ secop-core (0.14.1) focal; urgency=medium
|
|||||||
|
|
||||||
-- Markus Zolliker <jenkins@frm2.tum.de> Thu, 20 Oct 2022 14:04:07 +0200
|
-- Markus Zolliker <jenkins@frm2.tum.de> Thu, 20 Oct 2022 14:04:07 +0200
|
||||||
|
|
||||||
secop-core (0.14.0) focal; urgency=medium
|
secop-core (0.14.0) stable; urgency=medium
|
||||||
|
|
||||||
* add simple interactive python client
|
* add simple interactive python client
|
||||||
* fix undefined status in softcal
|
* fix undefined status in softcal
|
||||||
@ -865,7 +940,7 @@ secop-core (0.14.0) focal; urgency=medium
|
|||||||
|
|
||||||
-- Markus Zolliker <jenkins@frm2.tum.de> Wed, 19 Oct 2022 11:31:50 +0200
|
-- Markus Zolliker <jenkins@frm2.tum.de> Wed, 19 Oct 2022 11:31:50 +0200
|
||||||
|
|
||||||
secop-core (0.13.1) focal; urgency=medium
|
secop-core (0.13.1) stable; urgency=medium
|
||||||
|
|
||||||
[ Markus Zolliker ]
|
[ Markus Zolliker ]
|
||||||
* an enum with value 0 should be interpreted as False
|
* an enum with value 0 should be interpreted as False
|
||||||
@ -876,7 +951,7 @@ secop-core (0.13.1) focal; urgency=medium
|
|||||||
|
|
||||||
-- Markus Zolliker <jenkins@jenkins02.admin.frm2.tum.de> Tue, 02 Aug 2022 15:31:52 +0200
|
-- Markus Zolliker <jenkins@jenkins02.admin.frm2.tum.de> Tue, 02 Aug 2022 15:31:52 +0200
|
||||||
|
|
||||||
secop-core (0.13.0) focal; urgency=medium
|
secop-core (0.13.0) stable; urgency=medium
|
||||||
|
|
||||||
[ Georg Brandl ]
|
[ Georg Brandl ]
|
||||||
* debian: fix email addresses in changelog
|
* debian: fix email addresses in changelog
|
||||||
@ -939,13 +1014,13 @@ secop-core (0.13.0) focal; urgency=medium
|
|||||||
|
|
||||||
-- Georg Brandl <jenkins@frm2.tum.de> Tue, 02 Aug 2022 09:47:06 +0200
|
-- Georg Brandl <jenkins@frm2.tum.de> Tue, 02 Aug 2022 09:47:06 +0200
|
||||||
|
|
||||||
secop-core (0.12.4) focal; urgency=medium
|
secop-core (0.12.4) stable; urgency=medium
|
||||||
|
|
||||||
* fix command inheritance
|
* fix command inheritance
|
||||||
|
|
||||||
-- Markus Zolliker <jenkins@jenkins01.admin.frm2.tum.de> Thu, 11 Nov 2021 16:21:19 +0100
|
-- Markus Zolliker <jenkins@jenkins01.admin.frm2.tum.de> Thu, 11 Nov 2021 16:21:19 +0100
|
||||||
|
|
||||||
secop-core (0.12.3) focal; urgency=medium
|
secop-core (0.12.3) stable; urgency=medium
|
||||||
|
|
||||||
[ Georg Brandl ]
|
[ Georg Brandl ]
|
||||||
* Makefile: fix docker image
|
* Makefile: fix docker image
|
||||||
@ -968,7 +1043,7 @@ secop-core (0.12.3) focal; urgency=medium
|
|||||||
|
|
||||||
-- Georg Brandl <jenkins@jenkins01.admin.frm2.tum.de> Wed, 10 Nov 2021 16:33:19 +0100
|
-- Georg Brandl <jenkins@jenkins01.admin.frm2.tum.de> Wed, 10 Nov 2021 16:33:19 +0100
|
||||||
|
|
||||||
secop-core (0.12.2) focal; urgency=medium
|
secop-core (0.12.2) stable; urgency=medium
|
||||||
|
|
||||||
[ Markus Zolliker ]
|
[ Markus Zolliker ]
|
||||||
* fix issue with new syntax in simulation
|
* fix issue with new syntax in simulation
|
||||||
@ -980,13 +1055,13 @@ secop-core (0.12.2) focal; urgency=medium
|
|||||||
|
|
||||||
-- Markus Zolliker <jenkins@jenkins01.admin.frm2.tum.de> Tue, 18 May 2021 10:29:17 +0200
|
-- Markus Zolliker <jenkins@jenkins01.admin.frm2.tum.de> Tue, 18 May 2021 10:29:17 +0200
|
||||||
|
|
||||||
secop-core (0.12.1) focal; urgency=medium
|
secop-core (0.12.1) stable; urgency=medium
|
||||||
|
|
||||||
* remove secop-console from debian *.install file
|
* remove secop-console from debian *.install file
|
||||||
|
|
||||||
-- Enrico Faulhaber <jenkins@jenkins02.admin.frm2.tum.de> Tue, 04 May 2021 09:42:53 +0200
|
-- Enrico Faulhaber <jenkins@jenkins02.admin.frm2.tum.de> Tue, 04 May 2021 09:42:53 +0200
|
||||||
|
|
||||||
secop-core (0.12.0) focal; urgency=medium
|
secop-core (0.12.0) stable; urgency=medium
|
||||||
|
|
||||||
[ Markus Zolliker ]
|
[ Markus Zolliker ]
|
||||||
* make datatypes immutable
|
* make datatypes immutable
|
||||||
|
1
debian/compat
vendored
1
debian/compat
vendored
@ -1 +0,0 @@
|
|||||||
11
|
|
4
debian/control
vendored
4
debian/control
vendored
@ -2,7 +2,7 @@ Source: frappy-core
|
|||||||
Section: contrib/misc
|
Section: contrib/misc
|
||||||
Priority: optional
|
Priority: optional
|
||||||
Maintainer: Enrico Faulhaber <enrico.faulhaber@frm2.tum.de>
|
Maintainer: Enrico Faulhaber <enrico.faulhaber@frm2.tum.de>
|
||||||
Build-Depends: debhelper (>= 11~),
|
Build-Depends: debhelper-compat (= 13),
|
||||||
dh-python,
|
dh-python,
|
||||||
python3 (>=3.6),
|
python3 (>=3.6),
|
||||||
python3-all,
|
python3-all,
|
||||||
@ -20,7 +20,7 @@ Build-Depends: debhelper (>= 11~),
|
|||||||
git,
|
git,
|
||||||
markdown,
|
markdown,
|
||||||
python3-daemon
|
python3-daemon
|
||||||
Standards-Version: 4.1.4
|
Standards-Version: 4.6.2
|
||||||
X-Python3-Version: >= 3.6
|
X-Python3-Version: >= 3.6
|
||||||
|
|
||||||
Package: frappy-core
|
Package: frappy-core
|
||||||
|
2
debian/frappy-core.install
vendored
2
debian/frappy-core.install
vendored
@ -1,6 +1,7 @@
|
|||||||
usr/bin/frappy-cli
|
usr/bin/frappy-cli
|
||||||
usr/bin/frappy-server
|
usr/bin/frappy-server
|
||||||
usr/bin/frappy-play
|
usr/bin/frappy-play
|
||||||
|
usr/bin/frappy-scan
|
||||||
usr/lib/python3.*/dist-packages/frappy/*.py
|
usr/lib/python3.*/dist-packages/frappy/*.py
|
||||||
usr/lib/python3.*/dist-packages/frappy/__pycache__
|
usr/lib/python3.*/dist-packages/frappy/__pycache__
|
||||||
usr/lib/python3.*/dist-packages/frappy/lib
|
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
|
usr/lib/python3.*/dist-packages/frappy_demo
|
||||||
lib/systemd
|
lib/systemd
|
||||||
var/log/frappy
|
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
|
rmdir debian/tmp
|
||||||
mv debian/python3-frappy 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_install -i -O--buildsystem=pybuild
|
||||||
dh_missing --fail-missing
|
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"""
|
"""general SECoP client"""
|
||||||
|
|
||||||
# pylint: disable=too-many-positional-arguments
|
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import queue
|
import queue
|
||||||
import re
|
import re
|
||||||
@ -481,7 +479,10 @@ class SecopClient(ProxyClient):
|
|||||||
continue
|
continue
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
e.args = (f'error handling SECoP message {reply!r}: {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
|
continue
|
||||||
try:
|
try:
|
||||||
key = action, ident
|
key = action, ident
|
||||||
|
@ -498,7 +498,7 @@ class Console(code.InteractiveConsole):
|
|||||||
history = None
|
history = None
|
||||||
if readline:
|
if readline:
|
||||||
try:
|
try:
|
||||||
history = expanduser(f'~/.frappy-{name}-history')
|
history = expanduser(f'~/.config/frappy/{name}-history')
|
||||||
readline.read_history_file(history)
|
readline.read_history_file(history)
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
pass
|
pass
|
||||||
@ -538,10 +538,10 @@ def init(*nodes):
|
|||||||
return success
|
return success
|
||||||
|
|
||||||
|
|
||||||
def interact(usage_tail=''):
|
def interact(usage_tail='', appname=None):
|
||||||
empty = '_c0' not in clientenv.namespace
|
empty = '_c0' not in clientenv.namespace
|
||||||
print(USAGE.format(
|
print(USAGE.format(
|
||||||
client_name='cli' if empty else '_c0',
|
client_name='cli' if empty else '_c0',
|
||||||
client_assign="\ncli = Client('localhost:5000')\n" if empty else '',
|
client_assign="\ncli = Client('localhost:5000')\n" if empty else '',
|
||||||
tail=usage_tail))
|
tail=usage_tail))
|
||||||
Console()
|
Console(name=f'cli-{appname}' if appname else 'cli')
|
||||||
|
@ -56,10 +56,12 @@ class Param(dict):
|
|||||||
kwds['value'] = value
|
kwds['value'] = value
|
||||||
super().__init__(**kwds)
|
super().__init__(**kwds)
|
||||||
|
|
||||||
|
|
||||||
class Group(tuple):
|
class Group(tuple):
|
||||||
def __new__(cls, *args):
|
def __new__(cls, *args):
|
||||||
return super().__new__(cls, args)
|
return super().__new__(cls, args)
|
||||||
|
|
||||||
|
|
||||||
class Mod(dict):
|
class Mod(dict):
|
||||||
def __init__(self, name, cls, description, **kwds):
|
def __init__(self, name, cls, description, **kwds):
|
||||||
super().__init__(
|
super().__init__(
|
||||||
@ -70,7 +72,8 @@ class Mod(dict):
|
|||||||
|
|
||||||
# matches name from spec
|
# matches name from spec
|
||||||
if not re.match(r'^[a-zA-Z]\w{0,62}$', name, re.ASCII):
|
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
|
# Make parameters out of all keywords
|
||||||
groups = {}
|
groups = {}
|
||||||
for key, val in kwds.items():
|
for key, val in kwds.items():
|
||||||
@ -85,13 +88,16 @@ class Mod(dict):
|
|||||||
for member in members:
|
for member in members:
|
||||||
self[member]['group'] = group
|
self[member]['group'] = group
|
||||||
|
|
||||||
|
|
||||||
class Collector:
|
class Collector:
|
||||||
def __init__(self, cls):
|
def __init__(self, cls):
|
||||||
self.list = []
|
self.list = []
|
||||||
self.cls = cls
|
self.cls = cls
|
||||||
|
|
||||||
def add(self, *args, **kwds):
|
def add(self, *args, **kwds):
|
||||||
self.list.append(self.cls(*args, **kwds))
|
result = self.cls(*args, **kwds)
|
||||||
|
self.list.append(result)
|
||||||
|
return result
|
||||||
|
|
||||||
def append(self, mod):
|
def append(self, mod):
|
||||||
self.list.append(mod)
|
self.list.append(mod)
|
||||||
@ -120,12 +126,14 @@ class Config(dict):
|
|||||||
def merge_modules(self, other):
|
def merge_modules(self, other):
|
||||||
""" merges only the modules from 'other' into 'self'"""
|
""" merges only the modules from 'other' into 'self'"""
|
||||||
self.ambiguous |= self.module_names & other.module_names
|
self.ambiguous |= self.module_names & other.module_names
|
||||||
|
equipment_id = other['node']['equipment_id']
|
||||||
for name, mod in other.items():
|
for name, mod in other.items():
|
||||||
if name == 'node':
|
if name == 'node':
|
||||||
continue
|
continue
|
||||||
if name not in self.module_names:
|
if name not in self.module_names:
|
||||||
self.module_names.add(name)
|
self.module_names.add(name)
|
||||||
self[name] = mod
|
self[name] = mod
|
||||||
|
mod['original_id'] = equipment_id
|
||||||
|
|
||||||
|
|
||||||
def process_file(filename, log):
|
def process_file(filename, log):
|
||||||
@ -172,8 +180,8 @@ def load_config(cfgfiles, log):
|
|||||||
Only the node-section of the first config file will be returned.
|
Only the node-section of the first config file will be returned.
|
||||||
The others will be discarded.
|
The others will be discarded.
|
||||||
Arguments
|
Arguments
|
||||||
- cfgfiles : str
|
- cfgfiles : list
|
||||||
Comma separated list of config-files
|
List of config file paths
|
||||||
- log : frappy.logging.Mainlogger
|
- log : frappy.logging.Mainlogger
|
||||||
Logger aquired from frappy.logging
|
Logger aquired from frappy.logging
|
||||||
Returns
|
Returns
|
||||||
@ -181,8 +189,8 @@ def load_config(cfgfiles, log):
|
|||||||
merged configuration
|
merged configuration
|
||||||
"""
|
"""
|
||||||
config = None
|
config = None
|
||||||
for cfgfile in cfgfiles.split(','):
|
for cfgfile in cfgfiles:
|
||||||
filename = to_config_path(cfgfile, log)
|
filename = to_config_path(str(cfgfile), log)
|
||||||
log.debug('Parsing config file %s...', filename)
|
log.debug('Parsing config file %s...', filename)
|
||||||
cfg = process_file(filename, log)
|
cfg = process_file(filename, log)
|
||||||
if config:
|
if config:
|
||||||
|
@ -218,8 +218,9 @@ def write_config(file_name, tree_widget):
|
|||||||
with open(file_name, 'w', encoding='utf-8') as configfile:
|
with open(file_name, 'w', encoding='utf-8') as configfile:
|
||||||
configfile.write('\n'.join(lines))
|
configfile.write('\n'.join(lines))
|
||||||
|
|
||||||
|
|
||||||
def read_config(file_path, log):
|
def read_config(file_path, log):
|
||||||
config = load_config(file_path, log)
|
config = load_config([file_path], log)
|
||||||
node = TreeWidgetItem(NODE)
|
node = TreeWidgetItem(NODE)
|
||||||
ifs = TreeWidgetItem(name='Interfaces')
|
ifs = TreeWidgetItem(name='Interfaces')
|
||||||
mods = TreeWidgetItem(name='Modules')
|
mods = TreeWidgetItem(name='Modules')
|
||||||
|
@ -106,7 +106,8 @@ def get_file_paths(widget, open_file=True):
|
|||||||
|
|
||||||
def get_modules():
|
def get_modules():
|
||||||
modules = {}
|
modules = {}
|
||||||
generalConfig.init()
|
if not generalConfig.initialized:
|
||||||
|
generalConfig.init()
|
||||||
base_path = generalConfig.basedir
|
base_path = generalConfig.basedir
|
||||||
# pylint: disable=too-many-nested-blocks
|
# pylint: disable=too-many-nested-blocks
|
||||||
for dirname in listdir(base_path):
|
for dirname in listdir(base_path):
|
||||||
@ -157,7 +158,8 @@ def get_interface_class_from_name(name):
|
|||||||
def get_interfaces():
|
def get_interfaces():
|
||||||
# TODO class must be found out like for modules
|
# TODO class must be found out like for modules
|
||||||
interfaces = []
|
interfaces = []
|
||||||
generalConfig.init()
|
if not generalConfig.initialized:
|
||||||
|
generalConfig.init()
|
||||||
interface_path = path.join(generalConfig.basedir, 'frappy',
|
interface_path = path.join(generalConfig.basedir, 'frappy',
|
||||||
'protocol', 'interface')
|
'protocol', 'interface')
|
||||||
for filename in listdir(interface_path):
|
for filename in listdir(interface_path):
|
||||||
|
@ -27,7 +27,6 @@
|
|||||||
<property name="font">
|
<property name="font">
|
||||||
<font>
|
<font>
|
||||||
<pointsize>18</pointsize>
|
<pointsize>18</pointsize>
|
||||||
<weight>75</weight>
|
|
||||||
<bold>true</bold>
|
<bold>true</bold>
|
||||||
</font>
|
</font>
|
||||||
</property>
|
</property>
|
||||||
|
@ -21,7 +21,6 @@
|
|||||||
<property name="font">
|
<property name="font">
|
||||||
<font>
|
<font>
|
||||||
<pointsize>12</pointsize>
|
<pointsize>12</pointsize>
|
||||||
<weight>75</weight>
|
|
||||||
<bold>true</bold>
|
<bold>true</bold>
|
||||||
<underline>true</underline>
|
<underline>true</underline>
|
||||||
</font>
|
</font>
|
||||||
|
@ -57,11 +57,12 @@ class GeneralConfig:
|
|||||||
|
|
||||||
:param configfile: if present, keys and values from the [FRAPPY] section are read
|
:param configfile: if present, keys and values from the [FRAPPY] section are read
|
||||||
|
|
||||||
default values for 'piddir', 'logdir' and 'confdir' are guessed from the
|
The following locations are searched for the generalConfig.cfg file.
|
||||||
location of this source file and from sys.executable.
|
- command line argument
|
||||||
|
- environment variable FRAPPY_CONFIG_FILE
|
||||||
if configfile is not given, the general config file is determined by
|
- git location (../cfg)
|
||||||
the env. variable FRAPPY_CONFIG_FILE or <confdir>/generalConfig.cfg is used
|
- local location (cwd)
|
||||||
|
- global location (/etc/frappy)
|
||||||
|
|
||||||
if a configfile is given, the values from the FRAPPY section are
|
if a configfile is given, the values from the FRAPPY section are
|
||||||
overriding above defaults
|
overriding above defaults
|
||||||
@ -69,37 +70,12 @@ class GeneralConfig:
|
|||||||
finally, the env. variables FRAPPY_PIDDIR, FRAPPY_LOGDIR and FRAPPY_CONFDIR
|
finally, the env. variables FRAPPY_PIDDIR, FRAPPY_LOGDIR and FRAPPY_CONFDIR
|
||||||
are overriding these values when given
|
are overriding these values when given
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
configfile = self._get_file_location(configfile)
|
||||||
|
|
||||||
cfg = {}
|
cfg = {}
|
||||||
mandatory = 'piddir', 'logdir', 'confdir'
|
mandatory = 'piddir', 'logdir', 'confdir'
|
||||||
repodir = Path(__file__).parents[2].expanduser().resolve()
|
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:
|
if configfile:
|
||||||
parser = ConfigParser()
|
parser = ConfigParser()
|
||||||
parser.optionxform = str
|
parser.optionxform = str
|
||||||
@ -113,28 +89,63 @@ class GeneralConfig:
|
|||||||
cfg[key] = ':'.join(path.expanduser(v) for v in value.split(':'))
|
cfg[key] = ':'.join(path.expanduser(v) for v in value.split(':'))
|
||||||
if cfg.get('confdir') is None:
|
if cfg.get('confdir') is None:
|
||||||
cfg['confdir'] = configfile.parent
|
cfg['confdir'] = configfile.parent
|
||||||
|
# environment variables will overwrite the config file
|
||||||
|
missing_keys = []
|
||||||
for key in mandatory:
|
for key in mandatory:
|
||||||
env = environ.get(f'FRAPPY_{key.upper()}')
|
env = environ.get(f'FRAPPY_{key.upper()}') or cfg.get(key)
|
||||||
if env is not None:
|
if env is None:
|
||||||
if ':' in env:
|
if self.defaults.get(key) is None:
|
||||||
cfg[key] = [Path(v) for v in env.split(':')]
|
missing_keys.append(key)
|
||||||
else:
|
else:
|
||||||
cfg[key] = Path(env)
|
if not isinstance(env, Path):
|
||||||
missing_keys = [
|
if key == 'confdir':
|
||||||
key for key in mandatory
|
env = [Path(v) for v in env.split(':')]
|
||||||
if cfg.get(key) is None and self.defaults.get(key) is None
|
else:
|
||||||
]
|
env = Path(env)
|
||||||
|
cfg[key] = env
|
||||||
if missing_keys:
|
if missing_keys:
|
||||||
if configfile:
|
if configfile:
|
||||||
raise KeyError(f"missing value for {' and '.join(missing_keys)} in {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):
|
if 'confdir' in cfg and isinstance(cfg['confdir'], Path):
|
||||||
cfg['confdir'] = [cfg['confdir']]
|
cfg['confdir'] = [cfg['confdir']]
|
||||||
# this is not customizable
|
# this is not customizable
|
||||||
cfg['basedir'] = repodir
|
cfg['basedir'] = repodir
|
||||||
self._config = cfg
|
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):
|
def __getitem__(self, key):
|
||||||
"""access for keys known to exist
|
"""access for keys known to exist
|
||||||
|
|
||||||
|
@ -55,7 +55,7 @@ class MultiEvent(threading.Event):
|
|||||||
|
|
||||||
def __init__(self, default_timeout=None):
|
def __init__(self, default_timeout=None):
|
||||||
self.events = set()
|
self.events = set()
|
||||||
self._lock = threading.Lock()
|
self._lock = threading.RLock()
|
||||||
self.default_timeout = default_timeout or None # treat 0 as None
|
self.default_timeout = default_timeout or None # treat 0 as None
|
||||||
self.name = None # default event name
|
self.name = None # default event name
|
||||||
self._actions = [] # actions to be executed on trigger
|
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, \
|
from frappy.errors import BadValueError, CommunicationFailedError, ConfigError, \
|
||||||
ProgrammingError, SECoPError, secop_error, RangeError
|
ProgrammingError, SECoPError, secop_error, RangeError
|
||||||
from frappy.lib import formatException, mkthread, UniqueObject
|
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.properties import HasProperties, Property
|
||||||
from frappy.logging import RemoteLogHandler
|
from frappy.logging import RemoteLogHandler
|
||||||
|
|
||||||
@ -41,6 +41,7 @@ from frappy.logging import RemoteLogHandler
|
|||||||
# from .interfaces import SECoP_BASE_CLASSES
|
# from .interfaces import SECoP_BASE_CLASSES
|
||||||
# WORKAROUND:
|
# WORKAROUND:
|
||||||
SECoP_BASE_CLASSES = ['Readable', 'Writable', 'Drivable', 'Communicator']
|
SECoP_BASE_CLASSES = ['Readable', 'Writable', 'Drivable', 'Communicator']
|
||||||
|
PREDEF_ORDER = list(PREDEFINED_ACCESSIBLES)
|
||||||
|
|
||||||
Done = UniqueObject('Done')
|
Done = UniqueObject('Done')
|
||||||
"""a special return value for a read_<param>/write_<param> method
|
"""a special return value for a read_<param>/write_<param> method
|
||||||
@ -59,7 +60,6 @@ class HasAccessibles(HasProperties):
|
|||||||
(so the dispatcher will get notified of changed values)
|
(so the dispatcher will get notified of changed values)
|
||||||
"""
|
"""
|
||||||
isWrapped = False
|
isWrapped = False
|
||||||
checkedMethods = set()
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def __init_subclass__(cls): # pylint: disable=too-many-branches
|
def __init_subclass__(cls): # pylint: disable=too-many-branches
|
||||||
@ -77,7 +77,7 @@ class HasAccessibles(HasProperties):
|
|||||||
for key, value in base.__dict__.items():
|
for key, value in base.__dict__.items():
|
||||||
if isinstance(value, Accessible):
|
if isinstance(value, Accessible):
|
||||||
value.updateProperties(merged_properties.setdefault(key, {}))
|
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)
|
new_names.append(key)
|
||||||
accessibles[key] = value
|
accessibles[key] = value
|
||||||
override_values.pop(key, None)
|
override_values.pop(key, None)
|
||||||
@ -97,17 +97,15 @@ class HasAccessibles(HasProperties):
|
|||||||
aobj.merge(merged_properties[aname])
|
aobj.merge(merged_properties[aname])
|
||||||
accessibles[aname] = aobj
|
accessibles[aname] = aobj
|
||||||
|
|
||||||
# rebuild order: (1) inherited items, (2) items from paramOrder, (3) new accessibles
|
# rebuild order:
|
||||||
# move (2) to the end
|
# (1) predefined accessibles, in a predefined order, (2) inherited custom items, (3) new custom items
|
||||||
paramOrder = cls.__dict__.get('paramOrder', ())
|
# move (1) to the beginning
|
||||||
for aname in paramOrder:
|
for key in reversed(PREDEF_ORDER):
|
||||||
if aname in accessibles:
|
if key in accessibles:
|
||||||
accessibles.move_to_end(aname)
|
accessibles.move_to_end(key, last=False)
|
||||||
# ignore unknown names
|
|
||||||
# move (3) to the end
|
# move (3) to the end
|
||||||
for aname in new_names:
|
for aname in new_names:
|
||||||
if aname not in paramOrder:
|
accessibles.move_to_end(aname)
|
||||||
accessibles.move_to_end(aname)
|
|
||||||
cls.accessibles = accessibles
|
cls.accessibles = accessibles
|
||||||
|
|
||||||
cls.wrappedAttributes = {'isWrapped': True}
|
cls.wrappedAttributes = {'isWrapped': True}
|
||||||
@ -115,8 +113,8 @@ class HasAccessibles(HasProperties):
|
|||||||
wrapped_name = '_' + cls.__name__
|
wrapped_name = '_' + cls.__name__
|
||||||
for pname, pobj in accessibles.items():
|
for pname, pobj in accessibles.items():
|
||||||
# wrap of reading/writing funcs
|
# wrap of reading/writing funcs
|
||||||
if not isinstance(pobj, Parameter):
|
if not isinstance(pobj, Parameter) or pobj.optional:
|
||||||
# nothing to do for Commands
|
# nothing to do for Commands and optional parameters
|
||||||
continue
|
continue
|
||||||
|
|
||||||
rname = 'read_' + pname
|
rname = 'read_' + pname
|
||||||
@ -189,10 +187,8 @@ class HasAccessibles(HasProperties):
|
|||||||
if new_value is Done: # TODO: to be removed when all code using Done is updated
|
if new_value is Done: # TODO: to be removed when all code using Done is updated
|
||||||
return getattr(self, pname)
|
return getattr(self, pname)
|
||||||
new_value = value if new_value is None else validate(new_value)
|
new_value = value if new_value is None else validate(new_value)
|
||||||
except Exception as e:
|
except SECoPError as e:
|
||||||
if isinstance(e, SECoPError):
|
e.raising_methods.append(f'{self.name}.write_{pname}')
|
||||||
e.raising_methods.append(f'{self.name}.write_{pname}')
|
|
||||||
self.announceUpdate(pname, err=e)
|
|
||||||
raise
|
raise
|
||||||
self.announceUpdate(pname, new_value, validate=False)
|
self.announceUpdate(pname, new_value, validate=False)
|
||||||
return new_value
|
return new_value
|
||||||
@ -202,16 +198,15 @@ class HasAccessibles(HasProperties):
|
|||||||
new_wfunc.__module__ = cls.__module__
|
new_wfunc.__module__ = cls.__module__
|
||||||
cls.wrappedAttributes[wname] = new_wfunc
|
cls.wrappedAttributes[wname] = new_wfunc
|
||||||
|
|
||||||
cls.checkedMethods.update(cls.wrappedAttributes)
|
|
||||||
|
|
||||||
# check for programming errors
|
# check for programming errors
|
||||||
for attrname in dir(cls):
|
for attrname, func in cls.__dict__.items():
|
||||||
prefix, _, pname = attrname.partition('_')
|
prefix, _, pname = attrname.partition('_')
|
||||||
if not pname:
|
if not pname:
|
||||||
continue
|
continue
|
||||||
if prefix == 'do':
|
if prefix == 'do':
|
||||||
raise ProgrammingError(f'{cls.__name__!r}: old style command {attrname!r} not supported anymore')
|
raise ProgrammingError(f'{cls.__name__!r}: old style command {attrname!r} not supported anymore')
|
||||||
if prefix in ('read', 'write') and attrname not in cls.checkedMethods:
|
if (prefix in ('read', 'write') and attrname not in cls.wrappedAttributes
|
||||||
|
and not hasattr(func, 'poll')): # may be a handler, which always has a poll attribute
|
||||||
raise ProgrammingError(f'{cls.__name__}.{attrname} defined, but {pname!r} is no parameter')
|
raise ProgrammingError(f'{cls.__name__}.{attrname} defined, but {pname!r} is no parameter')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -322,10 +317,13 @@ class Module(HasAccessibles):
|
|||||||
slowinterval = Property('poll interval for other parameters', FloatRange(0.1, 120), default=15)
|
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',
|
omit_unchanged_within = Property('default for minimum time between updates of unchanged values',
|
||||||
NoneOr(FloatRange(0)), export=False, default=None)
|
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
|
enablePoll = True
|
||||||
|
|
||||||
pollInfo = None
|
pollInfo = None
|
||||||
triggerPoll = None # trigger event for polls. used on io modules and modules without io
|
triggerPoll = None # trigger event for polls. used on io modules and modules without io
|
||||||
|
__poller = None # the poller thread, if used
|
||||||
|
|
||||||
def __init__(self, name, logger, cfgdict, srv):
|
def __init__(self, name, logger, cfgdict, srv):
|
||||||
# remember the secnode for interacting with other modules and the
|
# remember the secnode for interacting with other modules and the
|
||||||
@ -391,6 +389,8 @@ class Module(HasAccessibles):
|
|||||||
accessibles = self.accessibles
|
accessibles = self.accessibles
|
||||||
self.accessibles = {}
|
self.accessibles = {}
|
||||||
for aname, aobj in accessibles.items():
|
for aname, aobj in accessibles.items():
|
||||||
|
if aobj.optional:
|
||||||
|
continue
|
||||||
# make a copy of the Parameter/Command object
|
# make a copy of the Parameter/Command object
|
||||||
aobj = aobj.copy()
|
aobj = aobj.copy()
|
||||||
acfg = cfgdict.pop(aname, None)
|
acfg = cfgdict.pop(aname, None)
|
||||||
@ -451,9 +451,12 @@ class Module(HasAccessibles):
|
|||||||
self.parameters[name] = accessible
|
self.parameters[name] = accessible
|
||||||
if isinstance(accessible, Command):
|
if isinstance(accessible, Command):
|
||||||
self.commands[name] = accessible
|
self.commands[name] = accessible
|
||||||
if cfg:
|
if cfg is not None:
|
||||||
try:
|
try:
|
||||||
for propname, propvalue in cfg.items():
|
for propname, propvalue in cfg.items():
|
||||||
|
if propname in {'value', 'default', 'constant'}:
|
||||||
|
# these properties have ValueType(), but should be checked for datatype
|
||||||
|
accessible.datatype(cfg[propname])
|
||||||
accessible.setProperty(propname, propvalue)
|
accessible.setProperty(propname, propvalue)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
self.errors.append(f"'{name}' has no property '{propname}'")
|
self.errors.append(f"'{name}' has no property '{propname}'")
|
||||||
@ -517,13 +520,13 @@ class Module(HasAccessibles):
|
|||||||
with self.updateLock:
|
with self.updateLock:
|
||||||
pobj = self.parameters[pname]
|
pobj = self.parameters[pname]
|
||||||
timestamp = timestamp or time.time()
|
timestamp = timestamp or time.time()
|
||||||
|
changed = False
|
||||||
if not err:
|
if not err:
|
||||||
try:
|
try:
|
||||||
if validate:
|
if validate:
|
||||||
value = pobj.datatype(value)
|
value = pobj.datatype(value)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
err = e
|
err = e
|
||||||
changed = False
|
|
||||||
else:
|
else:
|
||||||
changed = pobj.value != value or pobj.readerror
|
changed = pobj.value != value or pobj.readerror
|
||||||
# store the value even in case of error
|
# store the value even in case of error
|
||||||
@ -610,7 +613,7 @@ class Module(HasAccessibles):
|
|||||||
# we do not need self.errors any longer. should we delete it?
|
# we do not need self.errors any longer. should we delete it?
|
||||||
# del self.errors
|
# del self.errors
|
||||||
if self.polledModules:
|
if self.polledModules:
|
||||||
mkthread(self.__pollThread, self.polledModules, start_events.get_trigger())
|
self.__poller = mkthread(self.__pollThread, self.polledModules, start_events.get_trigger())
|
||||||
self.startModuleDone = True
|
self.startModuleDone = True
|
||||||
|
|
||||||
def initialReads(self):
|
def initialReads(self):
|
||||||
@ -623,8 +626,28 @@ class Module(HasAccessibles):
|
|||||||
all parameters are polled once
|
all parameters are polled once
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
def stopPollThread(self):
|
||||||
|
"""trigger the poll thread to stop
|
||||||
|
|
||||||
|
this is called on shutdown
|
||||||
|
"""
|
||||||
|
if self.__poller:
|
||||||
|
self.polledModules.clear()
|
||||||
|
self.triggerPoll.set()
|
||||||
|
|
||||||
|
def joinPollThread(self, timeout):
|
||||||
|
"""wait for poll thread to finish
|
||||||
|
|
||||||
|
if the wait time exceeds <timeout> seconds, return and log a warning
|
||||||
|
"""
|
||||||
|
if self.__poller:
|
||||||
|
self.stopPollThread()
|
||||||
|
self.__poller.join(timeout)
|
||||||
|
if self.__poller.is_alive():
|
||||||
|
self.log.warning('can not stop poller')
|
||||||
|
|
||||||
def shutdownModule(self):
|
def shutdownModule(self):
|
||||||
"""called when the sever shuts down
|
"""called when the server shuts down
|
||||||
|
|
||||||
any cleanup-work should be performed here, like closing threads and
|
any cleanup-work should be performed here, like closing threads and
|
||||||
saving data.
|
saving data.
|
||||||
@ -727,13 +750,14 @@ class Module(HasAccessibles):
|
|||||||
if not polled_modules: # no polls needed - exit thread
|
if not polled_modules: # no polls needed - exit thread
|
||||||
return
|
return
|
||||||
to_poll = ()
|
to_poll = ()
|
||||||
while True:
|
while modules: # modules will be cleared on shutdown
|
||||||
now = time.time()
|
now = time.time()
|
||||||
wait_time = 999
|
wait_time = 999
|
||||||
for mobj in modules:
|
for mobj in modules:
|
||||||
pinfo = mobj.pollInfo
|
pinfo = mobj.pollInfo
|
||||||
wait_time = min(pinfo.last_main + pinfo.interval - now, wait_time,
|
if pinfo:
|
||||||
pinfo.last_slow + mobj.slowinterval - now)
|
wait_time = min(pinfo.last_main + pinfo.interval - now, wait_time,
|
||||||
|
pinfo.last_slow + mobj.slowinterval - now)
|
||||||
if wait_time > 0 and not to_poll:
|
if wait_time > 0 and not to_poll:
|
||||||
# nothing to do
|
# nothing to do
|
||||||
self.triggerPoll.wait(wait_time)
|
self.triggerPoll.wait(wait_time)
|
||||||
@ -742,7 +766,7 @@ class Module(HasAccessibles):
|
|||||||
# call doPoll of all modules where due
|
# call doPoll of all modules where due
|
||||||
for mobj in modules:
|
for mobj in modules:
|
||||||
pinfo = mobj.pollInfo
|
pinfo = mobj.pollInfo
|
||||||
if now > pinfo.last_main + pinfo.interval:
|
if pinfo and now > pinfo.last_main + pinfo.interval:
|
||||||
try:
|
try:
|
||||||
pinfo.last_main = (now // pinfo.interval) * pinfo.interval
|
pinfo.last_main = (now // pinfo.interval) * pinfo.interval
|
||||||
except ZeroDivisionError:
|
except ZeroDivisionError:
|
||||||
@ -762,7 +786,7 @@ class Module(HasAccessibles):
|
|||||||
# collect due slow polls
|
# collect due slow polls
|
||||||
for mobj in modules:
|
for mobj in modules:
|
||||||
pinfo = mobj.pollInfo
|
pinfo = mobj.pollInfo
|
||||||
if now > pinfo.last_slow + mobj.slowinterval:
|
if pinfo and now > pinfo.last_slow + mobj.slowinterval:
|
||||||
to_poll.extend(pinfo.polled_parameters)
|
to_poll.extend(pinfo.polled_parameters)
|
||||||
pinfo.last_slow = (now // mobj.slowinterval) * mobj.slowinterval
|
pinfo.last_slow = (now // mobj.slowinterval) * mobj.slowinterval
|
||||||
if to_poll:
|
if to_poll:
|
||||||
|
@ -47,6 +47,7 @@ class Accessible(HasProperties):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
ownProperties = None
|
ownProperties = None
|
||||||
|
optional = False
|
||||||
|
|
||||||
def init(self, kwds):
|
def init(self, kwds):
|
||||||
# do not use self.propertyValues.update here, as no invalid values should be
|
# do not use self.propertyValues.update here, as no invalid values should be
|
||||||
@ -96,6 +97,8 @@ class Accessible(HasProperties):
|
|||||||
props = []
|
props = []
|
||||||
for k, v in sorted(self.propertyValues.items()):
|
for k, v in sorted(self.propertyValues.items()):
|
||||||
props.append(f'{k}={v!r}')
|
props.append(f'{k}={v!r}')
|
||||||
|
if self.optional:
|
||||||
|
props.append('optional=True')
|
||||||
return f"{self.__class__.__name__}({', '.join(props)})"
|
return f"{self.__class__.__name__}({', '.join(props)})"
|
||||||
|
|
||||||
def fixExport(self):
|
def fixExport(self):
|
||||||
@ -191,8 +194,9 @@ class Parameter(Accessible):
|
|||||||
readerror = None
|
readerror = None
|
||||||
omit_unchanged_within = 0
|
omit_unchanged_within = 0
|
||||||
|
|
||||||
def __init__(self, description=None, datatype=None, inherit=True, **kwds):
|
def __init__(self, description=None, datatype=None, inherit=True, optional=False, **kwds):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
self.optional = optional
|
||||||
if 'poll' in kwds and generalConfig.tolerate_poll_property:
|
if 'poll' in kwds and generalConfig.tolerate_poll_property:
|
||||||
kwds.pop('poll')
|
kwds.pop('poll')
|
||||||
if datatype is None:
|
if datatype is None:
|
||||||
@ -226,10 +230,16 @@ class Parameter(Accessible):
|
|||||||
def __get__(self, instance, owner):
|
def __get__(self, instance, owner):
|
||||||
if instance is None:
|
if instance is None:
|
||||||
return self
|
return self
|
||||||
return instance.parameters[self.name].value
|
try:
|
||||||
|
return instance.parameters[self.name].value
|
||||||
|
except KeyError:
|
||||||
|
raise ProgrammingError(f'optional parameter {self.name} it is not implemented') from None
|
||||||
|
|
||||||
def __set__(self, obj, value):
|
def __set__(self, obj, value):
|
||||||
obj.announceUpdate(self.name, value)
|
try:
|
||||||
|
obj.announceUpdate(self.name, value)
|
||||||
|
except KeyError:
|
||||||
|
raise ProgrammingError(f'optional parameter {self.name} it is not implemented') from None
|
||||||
|
|
||||||
def __set_name__(self, owner, name):
|
def __set_name__(self, owner, name):
|
||||||
self.name = name
|
self.name = name
|
||||||
@ -259,8 +269,16 @@ class Parameter(Accessible):
|
|||||||
merged_properties.update(self.ownProperties)
|
merged_properties.update(self.ownProperties)
|
||||||
|
|
||||||
def create_from_value(self, properties, value):
|
def create_from_value(self, properties, value):
|
||||||
"""return a clone with given value and inherited properties"""
|
"""return a clone with given value and inherited properties
|
||||||
return self.clone(properties, value=self.datatype(value))
|
|
||||||
|
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):
|
def merge(self, merged_properties):
|
||||||
"""merge with inherited properties
|
"""merge with inherited properties
|
||||||
@ -358,9 +376,6 @@ class Command(Accessible):
|
|||||||
* True: exported, name automatic.
|
* True: exported, name automatic.
|
||||||
* a string: exported with custom name''', OrType(BoolType(), StringType()),
|
* a string: exported with custom name''', OrType(BoolType(), StringType()),
|
||||||
export=False, default=True)
|
export=False, default=True)
|
||||||
# optional = Property(
|
|
||||||
# '[internal] is the command optional to implement? (vs. mandatory)', BoolType(),
|
|
||||||
# export=False, default=False, settable=False)
|
|
||||||
datatype = Property(
|
datatype = Property(
|
||||||
"datatype of the command, auto generated from 'argument' and 'result'",
|
"datatype of the command, auto generated from 'argument' and 'result'",
|
||||||
DataTypeType(), extname='datainfo', export='always')
|
DataTypeType(), extname='datainfo', export='always')
|
||||||
@ -376,8 +391,9 @@ class Command(Accessible):
|
|||||||
|
|
||||||
func = None
|
func = None
|
||||||
|
|
||||||
def __init__(self, argument=False, *, result=None, inherit=True, **kwds):
|
def __init__(self, argument=False, *, result=None, inherit=True, optional=False, **kwds):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
self.optional = optional
|
||||||
if 'datatype' in kwds:
|
if 'datatype' in kwds:
|
||||||
# self.init will complain about invalid keywords except 'datatype', as this is a property
|
# self.init will complain about invalid keywords except 'datatype', as this is a property
|
||||||
raise ProgrammingError("Command() got an invalid keyword 'datatype'")
|
raise ProgrammingError("Command() got an invalid keyword 'datatype'")
|
||||||
@ -403,8 +419,8 @@ class Command(Accessible):
|
|||||||
|
|
||||||
def __set_name__(self, owner, name):
|
def __set_name__(self, owner, name):
|
||||||
self.name = name
|
self.name = name
|
||||||
if self.func is None:
|
if self.func is None and not self.optional:
|
||||||
raise ProgrammingError(f'Command {owner.__name__}.{name} must be used as a method decorator')
|
raise ProgrammingError(f'Command {owner.__name__}.{name} must be optional or used as a method decorator')
|
||||||
|
|
||||||
self.fixExport()
|
self.fixExport()
|
||||||
self.datatype = CommandType(self.argument, self.result)
|
self.datatype = CommandType(self.argument, self.result)
|
||||||
@ -462,7 +478,8 @@ class Command(Accessible):
|
|||||||
def create_from_value(self, properties, value):
|
def create_from_value(self, properties, value):
|
||||||
"""return a clone with given value and inherited properties
|
"""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):
|
if not callable(value):
|
||||||
raise ProgrammingError(f'{self.name} = {value!r} is overriding a Command')
|
raise ProgrammingError(f'{self.name} = {value!r} is overriding a Command')
|
||||||
return self.clone(properties)(value)
|
return self.clone(properties)(value)
|
||||||
@ -564,15 +581,18 @@ class Limit(Parameter):
|
|||||||
|
|
||||||
|
|
||||||
# list of predefined accessibles with their type
|
# list of predefined accessibles with their type
|
||||||
|
# the order of this list affects the parameter order
|
||||||
PREDEFINED_ACCESSIBLES = {
|
PREDEFINED_ACCESSIBLES = {
|
||||||
'value': Parameter,
|
'value': Parameter,
|
||||||
'status': Parameter,
|
'status': Parameter,
|
||||||
'target': Parameter,
|
'target': Parameter,
|
||||||
'pollinterval': Parameter,
|
'pollinterval': Parameter,
|
||||||
'ramp': Parameter,
|
'ramp': Parameter,
|
||||||
'user_ramp': Parameter,
|
'use_ramp': Parameter,
|
||||||
'setpoint': Parameter,
|
'setpoint': Parameter,
|
||||||
'time_to_target': Parameter,
|
'time_to_target': Parameter,
|
||||||
|
'controlled_by': Parameter,
|
||||||
|
'control_active': Parameter,
|
||||||
'unit': Parameter, # reserved name
|
'unit': Parameter, # reserved name
|
||||||
'loglevel': Parameter, # reserved name
|
'loglevel': Parameter, # reserved name
|
||||||
'mode': Parameter, # reserved name
|
'mode': Parameter, # reserved name
|
||||||
|
@ -97,7 +97,9 @@ class Playground(Server):
|
|||||||
for modname, cfg in kwds.items():
|
for modname, cfg in kwds.items():
|
||||||
cfg.setdefault('description', modname)
|
cfg.setdefault('description', modname)
|
||||||
self.log = logger.log
|
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._testonly = True # stops before calling startModule
|
||||||
self._cfgfiles = 'main'
|
self._cfgfiles = 'main'
|
||||||
self.module_cfg = {}
|
self.module_cfg = {}
|
||||||
@ -106,6 +108,7 @@ class Playground(Server):
|
|||||||
if cfgfiles:
|
if cfgfiles:
|
||||||
if not generalConfig.initialized:
|
if not generalConfig.initialized:
|
||||||
generalConfig.init()
|
generalConfig.init()
|
||||||
|
cfgfiles = [s.strip() for s in cfgfiles.split(',')]
|
||||||
merged_cfg = load_config(cfgfiles, self.log)
|
merged_cfg = load_config(cfgfiles, self.log)
|
||||||
merged_cfg.pop('node', None)
|
merged_cfg.pop('node', None)
|
||||||
self.module_cfg = merged_cfg
|
self.module_cfg = merged_cfg
|
||||||
|
@ -143,6 +143,7 @@ class HasProperties(HasDescriptors):
|
|||||||
try:
|
try:
|
||||||
# try to apply bare value to Property
|
# try to apply bare value to Property
|
||||||
po.value = po.datatype.validate(value)
|
po.value = po.datatype.validate(value)
|
||||||
|
setattr(cls, pn, po) # replace bare value by updated Property
|
||||||
except BadValueError:
|
except BadValueError:
|
||||||
if callable(value):
|
if callable(value):
|
||||||
raise ProgrammingError(f'method {cls.__name__}.{pn} collides with property of {base.__name__}') from None
|
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):
|
class Router(frappy.protocol.dispatcher.Dispatcher):
|
||||||
singlenode = None
|
|
||||||
|
|
||||||
def __init__(self, name, logger, options, srv):
|
def __init__(self, name, logger, options, srv):
|
||||||
"""initialize router
|
"""initialize router
|
||||||
|
|
||||||
Use the option node = <uri> for a single node or
|
Use the option node = <uri> for a single node or
|
||||||
nodes = ["<uri1>", "<uri2>" ...] for multiple nodes.
|
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.
|
else the description property is a merge from all client node properties.
|
||||||
"""
|
"""
|
||||||
uri = options.pop('node', None)
|
uri = options.pop('node', None)
|
||||||
uris = options.pop('nodes', None)
|
uris = options.pop('nodes', None)
|
||||||
if uri and uris:
|
try:
|
||||||
raise frappy.errors.ConfigError('can not specify node _and_ nodes')
|
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)
|
super().__init__(name, logger, options, srv)
|
||||||
if uri:
|
self.nodes = [SecopClient(uri, logger.getChild(f'routed{i}'), self) for i, uri in enumerate(uris)]
|
||||||
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)]
|
|
||||||
# register callbacks
|
# register callbacks
|
||||||
for node in self.nodes:
|
for node in self.nodes:
|
||||||
node.register_callback(None, node.updateEvent, node.descriptiveDataChange, node.nodeStateChange)
|
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)
|
logger.warning('can not connect to node %r', node.nodename)
|
||||||
|
|
||||||
def handle_describe(self, conn, specifier, data):
|
def handle_describe(self, conn, specifier, data):
|
||||||
if self.singlenode:
|
if len(self.nodes) == 1 and not self.secnode.modules:
|
||||||
return DESCRIPTIONREPLY, specifier, self.singlenode.descriptive_data
|
return DESCRIPTIONREPLY, specifier, self.nodes[0].descriptive_data
|
||||||
reply = super().handle_describe(conn, specifier, data)
|
reply = super().handle_describe(conn, specifier, data)
|
||||||
result = reply[2]
|
result = reply[2]
|
||||||
allmodules = result.get('modules', {})
|
allmodules = result.get('modules', {})
|
||||||
@ -144,6 +148,7 @@ class Router(frappy.protocol.dispatcher.Dispatcher):
|
|||||||
self.log.info('module %r is already present', modname)
|
self.log.info('module %r is already present', modname)
|
||||||
else:
|
else:
|
||||||
allmodules[modname] = moddesc
|
allmodules[modname] = moddesc
|
||||||
|
moddesc.setdefault('original_id', equipment_id)
|
||||||
result['modules'] = allmodules
|
result['modules'] = allmodules
|
||||||
result['description'] = '\n\n'.join(node_description)
|
result['description'] = '\n\n'.join(node_description)
|
||||||
return DESCRIPTIONREPLY, specifier, result
|
return DESCRIPTIONREPLY, specifier, result
|
||||||
|
@ -102,7 +102,6 @@ class Handler:
|
|||||||
"""create the wrapped read_* or write_* methods"""
|
"""create the wrapped read_* or write_* methods"""
|
||||||
# at this point, this 'method_names' entry is no longer used -> delete
|
# at this point, this 'method_names' entry is no longer used -> delete
|
||||||
self.method_names.discard((self.func.__module__, self.func.__qualname__))
|
self.method_names.discard((self.func.__module__, self.func.__qualname__))
|
||||||
owner.checkedMethods.add(name)
|
|
||||||
for key in self.keys:
|
for key in self.keys:
|
||||||
wrapped = self.wrap(key)
|
wrapped = self.wrap(key)
|
||||||
method_name = self.prefix + key
|
method_name = self.prefix + key
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
#
|
#
|
||||||
# *****************************************************************************
|
# *****************************************************************************
|
||||||
|
|
||||||
|
import time
|
||||||
import traceback
|
import traceback
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
|
||||||
@ -54,8 +55,17 @@ class SecNode:
|
|||||||
self.name = name
|
self.name = name
|
||||||
|
|
||||||
def add_secnode_property(self, prop, value):
|
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
|
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):
|
def get_module(self, modulename):
|
||||||
""" Returns a fully initialized module. Or None, if something went
|
""" Returns a fully initialized module. Or None, if something went
|
||||||
wrong during instatiating/initializing the module."""
|
wrong during instatiating/initializing the module."""
|
||||||
@ -246,6 +256,15 @@ class SecNode:
|
|||||||
|
|
||||||
def shutdown_modules(self):
|
def shutdown_modules(self):
|
||||||
"""Call 'shutdownModule' for all modules."""
|
"""Call 'shutdownModule' for all modules."""
|
||||||
|
# stop pollers
|
||||||
|
for mod in self.modules.values():
|
||||||
|
mod.stopPollThread()
|
||||||
|
# do not yet join here, as we want to wait in parallel
|
||||||
|
now = time.time()
|
||||||
|
deadline = now + 0.5 # should be long enough for most read functions to finish
|
||||||
|
for mod in self.modules.values():
|
||||||
|
mod.joinPollThread(max(0, deadline - now))
|
||||||
|
now = time.time()
|
||||||
for name in self._getSortedModules():
|
for name in self._getSortedModules():
|
||||||
self.modules[name].shutdownModule()
|
self.modules[name].shutdownModule()
|
||||||
|
|
||||||
|
103
frappy/server.py
103
frappy/server.py
@ -26,6 +26,7 @@ import os
|
|||||||
import signal
|
import signal
|
||||||
import sys
|
import sys
|
||||||
import threading
|
import threading
|
||||||
|
import time
|
||||||
|
|
||||||
import mlzlog
|
import mlzlog
|
||||||
|
|
||||||
@ -36,6 +37,7 @@ from frappy.lib.multievent import MultiEvent
|
|||||||
from frappy.logging import init_remote_logging
|
from frappy.logging import init_remote_logging
|
||||||
from frappy.params import PREDEFINED_ACCESSIBLES
|
from frappy.params import PREDEFINED_ACCESSIBLES
|
||||||
from frappy.secnode import SecNode
|
from frappy.secnode import SecNode
|
||||||
|
from frappy.protocol.discovery import UDPListener
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from daemon import DaemonContext
|
from daemon import DaemonContext
|
||||||
@ -67,9 +69,9 @@ class Server:
|
|||||||
- name: the node name
|
- name: the node name
|
||||||
- parent_logger: the logger to inherit from. a handler is installed by
|
- parent_logger: the logger to inherit from. a handler is installed by
|
||||||
the server to provide remote logging
|
the server to provide remote logging
|
||||||
- cfgfiles: if not given, defaults to name
|
- cfgfiles: if not given, defaults to [name]
|
||||||
may be a comma separated list of cfg files
|
may be a list of cfg files
|
||||||
items ending with .cfg are taken as paths, else .cfg is appended and
|
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
|
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
|
- 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
|
if not given, the interface is taken from the config file. In case of
|
||||||
@ -93,9 +95,9 @@ class Server:
|
|||||||
self._testonly = testonly
|
self._testonly = testonly
|
||||||
|
|
||||||
if not cfgfiles:
|
if not cfgfiles:
|
||||||
cfgfiles = name
|
cfgfiles = [name]
|
||||||
# sanitize name (in case it is a cfgfile)
|
# 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):
|
if isinstance(parent_logger, mlzlog.MLZLogger):
|
||||||
self.log = parent_logger.getChild(name, True)
|
self.log = parent_logger.getChild(name, True)
|
||||||
else:
|
else:
|
||||||
@ -106,7 +108,6 @@ class Server:
|
|||||||
self.node_cfg = merged_cfg.pop('node')
|
self.node_cfg = merged_cfg.pop('node')
|
||||||
self.module_cfg = merged_cfg
|
self.module_cfg = merged_cfg
|
||||||
if interface:
|
if interface:
|
||||||
self.node_cfg['equipment_id'] = name
|
|
||||||
self.node_cfg['interface'] = str(interface)
|
self.node_cfg['interface'] = str(interface)
|
||||||
elif not self.node_cfg.get('interface'):
|
elif not self.node_cfg.get('interface'):
|
||||||
raise ConfigError('No interface specified in configuration or arguments!')
|
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.SIGINT, self.signal_handler)
|
||||||
signal.signal(signal.SIGTERM, self.signal_handler)
|
signal.signal(signal.SIGTERM, self.signal_handler)
|
||||||
|
|
||||||
|
self.discovery = None
|
||||||
|
|
||||||
def signal_handler(self, num, frame):
|
def signal_handler(self, num, frame):
|
||||||
if hasattr(self, 'interfaces') and self.interfaces:
|
if hasattr(self, 'interfaces') and self.interfaces:
|
||||||
self.shutdown()
|
self.shutdown()
|
||||||
@ -164,6 +167,7 @@ class Server:
|
|||||||
print(formatException(verbose=True))
|
print(formatException(verbose=True))
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
# client interfaces
|
||||||
self.interfaces = {}
|
self.interfaces = {}
|
||||||
iface_threads = []
|
iface_threads = []
|
||||||
# default_timeout 12 sec: TCPServer might need up to 10 sec to wait for Address no longer in use
|
# 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()
|
lock = threading.Lock()
|
||||||
failed = {}
|
failed = {}
|
||||||
interfaces = [self.node_cfg['interface']] + self.node_cfg.get('secondary', [])
|
interfaces = [self.node_cfg['interface']] + self.node_cfg.get('secondary', [])
|
||||||
# TODO: check if only one interface of each type is open?
|
# allow missing "tcp://"
|
||||||
for interface in interfaces:
|
interfaces = [iface if '://' in iface else f'tcp://{iface}' for iface in interfaces]
|
||||||
opts = {'uri': interface}
|
with lock:
|
||||||
t = mkthread(
|
for interface in interfaces:
|
||||||
self._interfaceThread,
|
opts = {'uri': interface}
|
||||||
opts,
|
t = mkthread(
|
||||||
lock,
|
self._interfaceThread,
|
||||||
failed,
|
opts,
|
||||||
interfaces_started.get_trigger(),
|
lock,
|
||||||
)
|
failed,
|
||||||
iface_threads.append(t)
|
interfaces,
|
||||||
|
interfaces_started.get_trigger(),
|
||||||
|
)
|
||||||
|
iface_threads.append(t)
|
||||||
if not interfaces_started.wait():
|
if not interfaces_started.wait():
|
||||||
for iface in interfaces:
|
for iface in interfaces:
|
||||||
if iface not in failed and iface not in self.interfaces:
|
if iface not in failed and iface not in self.interfaces:
|
||||||
@ -192,15 +199,33 @@ class Server:
|
|||||||
if not self.interfaces:
|
if not self.interfaces:
|
||||||
self.log.error('no interface started')
|
self.log.error('no interface started')
|
||||||
return
|
return
|
||||||
self.secnode.add_secnode_property('_interfaces', list(self.interfaces.keys()))
|
self.secnode.add_secnode_property('_interfaces', list(self.interfaces))
|
||||||
self.log.info('startup done with interface(s) %s' % ', '.join(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:
|
if systemd:
|
||||||
systemd.daemon.notify("READY=1\nSTATUS=accepting requests")
|
systemd.daemon.notify("READY=1\nSTATUS=accepting requests")
|
||||||
|
|
||||||
# we wait here on the thread finishing, which means we got a
|
if os.name == 'nt':
|
||||||
# signal to shut down or an exception was raised
|
# workaround: thread.join() on Windows blocks and is not
|
||||||
for t in iface_threads:
|
# interruptible by the signal handler, so loop and check
|
||||||
t.join()
|
# 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:
|
while failed:
|
||||||
iface, err = failed.popitem()
|
iface, err = failed.popitem()
|
||||||
@ -208,11 +233,11 @@ class Server:
|
|||||||
|
|
||||||
self.log.info('stopped listening, cleaning up %d modules',
|
self.log.info('stopped listening, cleaning up %d modules',
|
||||||
len(self.secnode.modules))
|
len(self.secnode.modules))
|
||||||
# if systemd:
|
if systemd:
|
||||||
# if self._restart:
|
if self._restart:
|
||||||
# systemd.daemon.notify('RELOADING=1')
|
systemd.daemon.notify('RELOADING=1')
|
||||||
# else:
|
else:
|
||||||
# systemd.daemon.notify('STOPPING=1')
|
systemd.daemon.notify('STOPPING=1')
|
||||||
self.secnode.shutdown_modules()
|
self.secnode.shutdown_modules()
|
||||||
if self._restart:
|
if self._restart:
|
||||||
self.restart_hook()
|
self.restart_hook()
|
||||||
@ -227,13 +252,14 @@ class Server:
|
|||||||
|
|
||||||
def shutdown(self):
|
def shutdown(self):
|
||||||
self._restart = False
|
self._restart = False
|
||||||
|
if self.discovery:
|
||||||
|
self.discovery.shutdown()
|
||||||
for iface in self.interfaces.values():
|
for iface in self.interfaces.values():
|
||||||
iface.shutdown()
|
iface.shutdown()
|
||||||
|
|
||||||
def _interfaceThread(self, opts, lock, failed, start_cb):
|
def _interfaceThread(self, opts, lock, failed, interfaces, start_cb):
|
||||||
iface = opts['uri']
|
iface = opts['uri']
|
||||||
scheme, _, _ = iface.rpartition('://')
|
scheme = iface.split('://')[0]
|
||||||
scheme = scheme or 'tcp'
|
|
||||||
cls = get_class(self.INTERFACES[scheme])
|
cls = get_class(self.INTERFACES[scheme])
|
||||||
try:
|
try:
|
||||||
with cls(scheme, self.log.getChild(scheme), opts, self) as interface:
|
with cls(scheme, self.log.getChild(scheme), opts, self) as interface:
|
||||||
@ -247,9 +273,12 @@ class Server:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
with lock:
|
with lock:
|
||||||
failed[iface] = e
|
failed[iface] = e
|
||||||
start_cb()
|
interfaces.remove(iface)
|
||||||
return
|
start_cb() # callback should also be called on failure
|
||||||
self.log.info(f'stopped {iface}')
|
else:
|
||||||
|
with lock:
|
||||||
|
interfaces.remove(iface)
|
||||||
|
self.log.info(f'stopped {iface}')
|
||||||
|
|
||||||
def _processCfg(self):
|
def _processCfg(self):
|
||||||
"""Processes the module configuration.
|
"""Processes the module configuration.
|
||||||
@ -263,10 +292,8 @@ class Server:
|
|||||||
errors = []
|
errors = []
|
||||||
opts = dict(self.node_cfg)
|
opts = dict(self.node_cfg)
|
||||||
cls = get_class(opts.pop('cls'))
|
cls = get_class(opts.pop('cls'))
|
||||||
name = opts.pop('name', self._cfgfiles)
|
self.secnode = SecNode(self.name, self.log.getChild('secnode'), opts, self)
|
||||||
# TODO: opts not in both
|
self.dispatcher = cls(self.name, self.log.getChild('dispatcher'), opts, self)
|
||||||
self.secnode = SecNode(name, self.log.getChild('secnode'), opts, self)
|
|
||||||
self.dispatcher = cls(name, self.log.getChild('dispatcher'), opts, self)
|
|
||||||
|
|
||||||
# add other options as SECNode properties, those with '_' prefixed will
|
# add other options as SECNode properties, those with '_' prefixed will
|
||||||
# get exported
|
# get exported
|
||||||
|
@ -142,4 +142,5 @@ class SimDrivable(SimReadable, Drivable):
|
|||||||
|
|
||||||
@Command
|
@Command
|
||||||
def stop(self):
|
def stop(self):
|
||||||
|
"""set target to value"""
|
||||||
self.target = self.value
|
self.target = self.value
|
||||||
|
@ -215,7 +215,10 @@ class HasStates:
|
|||||||
self.read_status()
|
self.read_status()
|
||||||
if fast_poll:
|
if fast_poll:
|
||||||
sm.reset_fast_poll = True
|
sm.reset_fast_poll = True
|
||||||
self.setFastPoll(True)
|
if fast_poll is True:
|
||||||
|
self.setFastPoll(True)
|
||||||
|
else:
|
||||||
|
self.setFastPoll(True, fast_poll)
|
||||||
self.pollInfo.trigger(True) # trigger poller
|
self.pollInfo.trigger(True) # trigger poller
|
||||||
|
|
||||||
def stop_machine(self, stopped_status=(IDLE, 'stopped')):
|
def stop_machine(self, stopped_status=(IDLE, 'stopped')):
|
||||||
|
@ -69,14 +69,16 @@ class TemperatureLoop(TemperatureSensor, Drivable):
|
|||||||
# lakeshore loop number to be used for this module
|
# lakeshore loop number to be used for this module
|
||||||
loop = Property('lakeshore loop', IntRange(1, 2), default=1)
|
loop = Property('lakeshore loop', IntRange(1, 2), default=1)
|
||||||
target = Parameter(datatype=FloatRange(unit='K', min=0, max=1500))
|
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)
|
tolerance = Parameter('convergence criterion', FloatRange(0), default=0.1, readonly=False)
|
||||||
_driving = False
|
_driving = False
|
||||||
|
|
||||||
|
def write_heater_range(self, value):
|
||||||
|
self.communicate(f'RANGE {self.loop},{value};RANGE?{self.loop}')
|
||||||
|
|
||||||
def write_target(self, target):
|
def write_target(self, target):
|
||||||
# reactivate heater in case it was switched off
|
# 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.write_heater_range(self.heater_range)
|
||||||
self.communicate(f'RANGE {self.loop},{self.heater_range};RANGE?{self.loop}')
|
|
||||||
self.communicate(f'SETP {self.loop},{target};*OPC?')
|
self.communicate(f'SETP {self.loop},{target};*OPC?')
|
||||||
self._driving = True
|
self._driving = True
|
||||||
# Setting the status attribute triggers an update message for the SECoP status
|
# Setting the status attribute triggers an update message for the SECoP status
|
||||||
@ -85,23 +87,21 @@ class TemperatureLoop(TemperatureSensor, Drivable):
|
|||||||
return target
|
return target
|
||||||
|
|
||||||
def read_status(self):
|
def read_status(self):
|
||||||
code = int(self.communicate(f'RDGST?{self.channel}'))
|
status = super().read_status()
|
||||||
if code >= 128:
|
if status[0] == ERROR:
|
||||||
text = 'units overrange'
|
return status
|
||||||
elif code >= 64:
|
if abs(self.target - self.value) > self.tolerance:
|
||||||
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:
|
|
||||||
if self._driving:
|
if self._driving:
|
||||||
return BUSY, 'approaching setpoint'
|
return BUSY, 'approaching setpoint'
|
||||||
return WARN, 'temperature out of tolerance'
|
return WARN, 'temperature out of tolerance'
|
||||||
else: # within tolerance: simple convergence criterion
|
# within tolerance: simple convergence criterion
|
||||||
self._driving = False
|
self._driving = False
|
||||||
return IDLE, ''
|
return IDLE, ''
|
||||||
return ERROR, text
|
|
||||||
|
|
||||||
|
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.modules import Communicator, Drivable, Parameter, Property, Readable, Module, Attached
|
||||||
from frappy.params import Command
|
from frappy.params import Command
|
||||||
from frappy.dynamic import Pinata
|
from frappy.dynamic import Pinata
|
||||||
from frappy.errors import RangeError
|
from frappy.errors import RangeError, HardwareError
|
||||||
|
|
||||||
class Pin(Pinata):
|
class Pin(Pinata):
|
||||||
def scanModules(self):
|
def scanModules(self):
|
||||||
@ -72,8 +72,12 @@ class Heater(Drivable):
|
|||||||
maxheaterpower = Parameter('maximum allowed heater power',
|
maxheaterpower = Parameter('maximum allowed heater power',
|
||||||
datatype=FloatRange(0, 100), unit='W',
|
datatype=FloatRange(0, 100), unit='W',
|
||||||
)
|
)
|
||||||
|
error_message = Parameter('simulated error message', StringType(),
|
||||||
|
default='', readonly=False)
|
||||||
|
|
||||||
def read_value(self):
|
def read_value(self):
|
||||||
|
if self.error_message:
|
||||||
|
raise HardwareError(self.error_message)
|
||||||
return round(100 * random.random(), 1)
|
return round(100 * random.random(), 1)
|
||||||
|
|
||||||
def write_target(self, target):
|
def write_target(self, target):
|
||||||
|
@ -1,25 +1,37 @@
|
|||||||
"""
|
# *****************************************************************************
|
||||||
Created on Tue Nov 26 15:42:43 2019
|
# 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
|
||||||
@author: tartarotti_d-adm
|
# 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 numpy as np
|
||||||
import ctypes as ct
|
import ctypes as ct
|
||||||
import time
|
from scipy.signal import butter, filtfilt
|
||||||
from numpy import sqrt, arctan2, sin, cos
|
|
||||||
|
|
||||||
#from pylab import *
|
|
||||||
|
|
||||||
from scipy import signal
|
# For different trigger modes
|
||||||
|
|
||||||
#ADQAPI = ct.cdll.LoadLibrary("ADQAPI.dll")
|
|
||||||
ADQAPI = ct.cdll.LoadLibrary("libadq.so.0")
|
|
||||||
|
|
||||||
#For different trigger modes
|
|
||||||
SW_TRIG = 1
|
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_2 = 7
|
||||||
EXT_TRIG_3 = 8
|
EXT_TRIG_3 = 8
|
||||||
LVL_TRIG = 3
|
LVL_TRIG = 3
|
||||||
@ -27,29 +39,54 @@ INT_TRIG = 4
|
|||||||
LVL_FALLING = 0
|
LVL_FALLING = 0
|
||||||
LVL_RISING = 1
|
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_TRANSFER_MODE_NORMAL = 0x00
|
||||||
ADQ_CHANNELS_MASK = 0x3
|
ADQ_CHANNELS_MASK = 0x3
|
||||||
|
|
||||||
#f_LO = 40
|
GHz = 1e9
|
||||||
|
RMS_TO_VPP = 2 * np.sqrt(2)
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
class Adq(object):
|
class Timer:
|
||||||
|
def __init__(self):
|
||||||
|
self.data = [(time.time(), 'start')]
|
||||||
|
|
||||||
|
def __call__(self, text=''):
|
||||||
|
now = time.time()
|
||||||
|
prev = self.data[-1][0]
|
||||||
|
self.data.append((now, text))
|
||||||
|
return now - prev
|
||||||
|
|
||||||
|
def summary(self):
|
||||||
|
return ' '.join(f'{txt} {tim:.3f}' for tim, txt in self.data[1:])
|
||||||
|
|
||||||
|
def show(self):
|
||||||
|
first = prev = self.data[0][0]
|
||||||
|
print('---', first)
|
||||||
|
for tim, txt in self.data[1:]:
|
||||||
|
print(f'{(tim - first) * 1000:9.3f} {(tim - prev) * 1000:9.3f} ms {txt}')
|
||||||
|
prev = tim
|
||||||
|
|
||||||
|
|
||||||
|
class Adq:
|
||||||
|
sample_rate = 2 * GHz
|
||||||
max_number_of_channels = 2
|
max_number_of_channels = 2
|
||||||
samp_freq = 2
|
ndecimate = 50 # decimation ratio (2GHz / 40 MHz)
|
||||||
#ndecimate = 50 # decimation ratio (2GHz / 40 MHz)
|
number_of_records = 1
|
||||||
ndecimate = 50
|
samples_per_record = 16384
|
||||||
|
bw_cutoff = 10E6
|
||||||
|
trigger = EXT_TRIG_1
|
||||||
|
adq_num = 1
|
||||||
|
data = None
|
||||||
|
busy = False
|
||||||
|
|
||||||
|
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()
|
ADQAPI.ADQAPI_GetRevision()
|
||||||
|
|
||||||
# Manually set return type from some ADQAPI functions
|
# Manually set return type from some ADQAPI functions
|
||||||
@ -60,89 +97,84 @@ class Adq(object):
|
|||||||
# Create ADQControlUnit
|
# Create ADQControlUnit
|
||||||
self.adq_cu = ct.c_void_p(ADQAPI.CreateADQControlUnit())
|
self.adq_cu = ct.c_void_p(ADQAPI.CreateADQControlUnit())
|
||||||
ADQAPI.ADQControlUnit_EnableErrorTrace(self.adq_cu, 3, '.')
|
ADQAPI.ADQControlUnit_EnableErrorTrace(self.adq_cu, 3, '.')
|
||||||
self.adq_num = 1
|
|
||||||
|
|
||||||
# Find ADQ devices
|
# Find ADQ devices
|
||||||
ADQAPI.ADQControlUnit_FindDevices(self.adq_cu)
|
ADQAPI.ADQControlUnit_FindDevices(self.adq_cu)
|
||||||
n_of_ADQ = ADQAPI.ADQControlUnit_NofADQ(self.adq_cu)
|
n_of_adq = ADQAPI.ADQControlUnit_NofADQ(self.adq_cu)
|
||||||
if n_of_ADQ != 1:
|
if n_of_adq != 1:
|
||||||
raise ValueError('number of ADQs must be 1, not %d' % n_of_ADQ)
|
print('number of ADQs must be 1, not %d' % n_of_adq)
|
||||||
|
print('it seems the ADQ was not properly closed')
|
||||||
|
print('please try again or reboot')
|
||||||
|
sys.exit(0)
|
||||||
|
atexit.register(self.deletecu)
|
||||||
|
signal.signal(signal.SIGTERM, lambda *_: sys.exit(0))
|
||||||
|
|
||||||
rev = ADQAPI.ADQ_GetRevision(self.adq_cu, self.adq_num)
|
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')
|
out = [f'Connected to ADQ #1, FPGA Revision: {revision[0]}']
|
||||||
# Print revision information
|
if revision[1]:
|
||||||
print('FPGA Revision: {}'.format(revision[0]))
|
out.append('Local copy')
|
||||||
if (revision[1]):
|
else:
|
||||||
print('Local copy')
|
if revision[2]:
|
||||||
else :
|
out.append('SVN Managed - Mixed Revision')
|
||||||
print('SVN Managed')
|
else:
|
||||||
if (revision[2]):
|
out.append('SVN Updated')
|
||||||
print('Mixed Revision')
|
print(', '.join(out))
|
||||||
else :
|
ADQAPI.ADQ_SetClockSource(self.adq_cu, self.adq_num, ADQ_CLOCK_EXT_REF)
|
||||||
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);
|
|
||||||
|
|
||||||
##########################
|
##########################
|
||||||
# Test pattern
|
# Test pattern
|
||||||
#ADQAPI.ADQ_SetTestPatternMode(self.adq_cu, self.adq_num, 4)
|
# ADQAPI.ADQ_SetTestPatternMode(self.adq_cu, self.adq_num, 4)
|
||||||
##########################
|
##########################
|
||||||
# Sample skip
|
# 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
|
# set trigger mode
|
||||||
self.trigger = EXT_TRIG_1
|
if not ADQAPI.ADQ_SetTriggerMode(self.adq_cu, self.adq_num, self.trigger):
|
||||||
#trigger = LVL_TRIG
|
raise RuntimeError('ADQ_SetTriggerMode failed.')
|
||||||
success = ADQAPI.ADQ_SetTriggerMode(self.adq_cu, self.adq_num, self.trigger)
|
if self.trigger == LVL_TRIG:
|
||||||
if (success == 0):
|
if not ADQAPI.ADQ_SetLvlTrigLevel(self.adq_cu, self.adq_num, -100):
|
||||||
print('ADQ_SetTriggerMode failed.')
|
raise RuntimeError('ADQ_SetLvlTrigLevel failed.')
|
||||||
if (self.trigger == LVL_TRIG):
|
if not ADQAPI.ADQ_SetTrigLevelResetValue(self.adq_cu, self.adq_num, 1000):
|
||||||
success = ADQAPI.ADQ_SetLvlTrigLevel(self.adq_cu, self.adq_num, -100)
|
raise RuntimeError('ADQ_SetTrigLevelResetValue failed.')
|
||||||
if (success == 0):
|
if not ADQAPI.ADQ_SetLvlTrigChannel(self.adq_cu, self.adq_num, 1):
|
||||||
print('ADQ_SetLvlTrigLevel failed.')
|
raise RuntimeError('ADQ_SetLvlTrigChannel failed.')
|
||||||
success = ADQAPI.ADQ_SetTrigLevelResetValue(self.adq_cu, self.adq_num, 1000)
|
if not ADQAPI.ADQ_SetLvlTrigEdge(self.adq_cu, self.adq_num, LVL_RISING):
|
||||||
if (success == 0):
|
raise RuntimeError('ADQ_SetLvlTrigEdge failed.')
|
||||||
print('ADQ_SetTrigLevelResetValue failed.')
|
elif self.trigger == EXT_TRIG_1:
|
||||||
success = ADQAPI.ADQ_SetLvlTrigChannel(self.adq_cu, self.adq_num, 1)
|
if not ADQAPI.ADQ_SetExternTrigEdge(self.adq_cu, self.adq_num, 2):
|
||||||
if (success == 0):
|
raise RuntimeError('ADQ_SetLvlTrigEdge failed.')
|
||||||
print('ADQ_SetLvlTrigChannel failed.')
|
# if not ADQAPI.ADQ_SetTriggerThresholdVoltage(self.adq_cu, self.adq_num, trigger, ct.c_double(0.2)):
|
||||||
success = ADQAPI.ADQ_SetLvlTrigEdge(self.adq_cu, self.adq_num, LVL_RISING)
|
# raise RuntimeError('SetTriggerThresholdVoltage failed.')
|
||||||
if (success == 0):
|
# proabably the folloiwng is wrong.
|
||||||
print('ADQ_SetLvlTrigEdge failed.')
|
# print("CHANNEL:" + str(ct.c_int(ADQAPI.ADQ_GetLvlTrigChannel(self.adq_cu, self.adq_num))))
|
||||||
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.')
|
|
||||||
print("CHANNEL:"+str(ct.c_int(ADQAPI.ADQ_GetLvlTrigChannel(self.adq_cu, self.adq_num))))
|
|
||||||
self.setup_target_buffers()
|
|
||||||
|
|
||||||
def setup_target_buffers(self):
|
def init(self, samples_per_record=None, number_of_records=None):
|
||||||
|
"""initialize dimensions and store result object"""
|
||||||
|
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
|
# Setup target buffers for data
|
||||||
self.target_buffers=(ct.POINTER(ct.c_int16 * self.samples_per_record * self.number_of_records)
|
self.target_buffers = (ct.POINTER(ct.c_int16 * self.samples_per_record * self.number_of_records)
|
||||||
* self.max_number_of_channels)()
|
* self.max_number_of_channels)()
|
||||||
for bufp in self.target_buffers:
|
for bufp in self.target_buffers:
|
||||||
bufp.contents = (ct.c_int16 * self.samples_per_record * self.number_of_records)()
|
bufp.contents = (ct.c_int16 * self.samples_per_record * self.number_of_records)()
|
||||||
|
|
||||||
def deletecu(self):
|
def deletecu(self):
|
||||||
# Only disarm trigger after data is collected
|
cu = self.__dict__.pop('adq_cu', None)
|
||||||
ADQAPI.ADQ_DisarmTrigger(self.adq_cu, self.adq_num)
|
if cu is None:
|
||||||
ADQAPI.ADQ_MultiRecordClose(self.adq_cu, self.adq_num);
|
return
|
||||||
|
print('shut down ADQ')
|
||||||
|
# Only disarm trigger after data is collected
|
||||||
|
ADQAPI.ADQ_DisarmTrigger(cu, self.adq_num)
|
||||||
|
ADQAPI.ADQ_MultiRecordClose(cu, self.adq_num)
|
||||||
# Delete ADQControlunit
|
# Delete ADQControlunit
|
||||||
ADQAPI.DeleteADQControlUnit(self.adq_cu)
|
ADQAPI.DeleteADQControlUnit(cu)
|
||||||
|
print('ADQ closed')
|
||||||
|
|
||||||
def start(self):
|
def start(self, data):
|
||||||
"""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
|
# Start acquisition
|
||||||
ADQAPI.ADQ_MultiRecordSetup(self.adq_cu, self.adq_num,
|
ADQAPI.ADQ_MultiRecordSetup(self.adq_cu, self.adq_num,
|
||||||
self.number_of_records,
|
self.number_of_records,
|
||||||
@ -150,79 +182,59 @@ class Adq(object):
|
|||||||
|
|
||||||
ADQAPI.ADQ_DisarmTrigger(self.adq_cu, self.adq_num)
|
ADQAPI.ADQ_DisarmTrigger(self.adq_cu, self.adq_num)
|
||||||
ADQAPI.ADQ_ArmTrigger(self.adq_cu, self.adq_num)
|
ADQAPI.ADQ_ArmTrigger(self.adq_cu, self.adq_num)
|
||||||
|
self.data = data
|
||||||
|
|
||||||
def getdata(self):
|
def get_data(self):
|
||||||
"""wait for aquisition to be finished and get data"""
|
"""get new data if available"""
|
||||||
#start = time.time()
|
ready = False
|
||||||
while(ADQAPI.ADQ_GetAcquiredAll(self.adq_cu,self.adq_num) == 0):
|
data = self.data
|
||||||
time.sleep(0.001)
|
if not data:
|
||||||
#if (self.trigger == SW_TRIG):
|
self.busy = False
|
||||||
# ADQAPI.ADQ_SWTrig(self.adq_cu, self.adq_num)
|
return None # no new data
|
||||||
#mid = time.time()
|
|
||||||
status = ADQAPI.ADQ_GetData(self.adq_cu, self.adq_num, self.target_buffers,
|
if ADQAPI.ADQ_GetAcquiredAll(self.adq_cu, self.adq_num):
|
||||||
self.samples_per_record * self.number_of_records, 2,
|
ready = True
|
||||||
0, self.number_of_records, ADQ_CHANNELS_MASK,
|
data.timer('ready')
|
||||||
0, self.samples_per_record, ADQ_TRANSFER_MODE_NORMAL);
|
else:
|
||||||
#print(time.time()-mid,mid-start)
|
if self.trigger == SW_TRIG:
|
||||||
if not status:
|
ADQAPI.ADQ_SWTrig(self.adq_cu, self.adq_num)
|
||||||
raise ValueError('no succesS from ADQ_GetDATA')
|
if not ready:
|
||||||
# Now this is an array with all records, but the time is artificial
|
self.busy = True
|
||||||
data = []
|
return None
|
||||||
for ch in range(2):
|
self.data = None
|
||||||
onedim = np.frombuffer(self.target_buffers[ch].contents, dtype=np.int16)
|
t = time.time()
|
||||||
data.append(onedim.reshape(self.number_of_records, self.samples_per_record) / float(2**14)) # 14 bits ADC
|
# 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')
|
||||||
|
data.retrieve(self)
|
||||||
return data
|
return 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):
|
class PEdata:
|
||||||
newx = np.linspace(0, self.samples_per_record /2, self.samples_per_record)
|
def __init__(self, adq):
|
||||||
s0 = channel /((2**16)/2)*0.5*np.exp(1j*2*np.pi*f_LO/(1e3)*newx)
|
self.sample_rate = adq.sample_rate
|
||||||
I0 = s0.real
|
self.samp_freq = self.sample_rate / GHz
|
||||||
Q0 = s0.imag
|
self.number_of_records = adq.number_of_records
|
||||||
return I0, Q0
|
self.timer = Timer()
|
||||||
|
|
||||||
|
def retrieve(self, adq):
|
||||||
|
data = []
|
||||||
|
rawsignal = []
|
||||||
|
for ch in range(2):
|
||||||
|
onedim = np.frombuffer(adq.target_buffers[ch].contents, dtype=np.int16)
|
||||||
|
rawsignal.append(onedim[:adq.samples_per_record])
|
||||||
|
# convert 16 bit int to a value in the range -1 .. 1
|
||||||
|
data.append(onedim.reshape(adq.number_of_records, adq.samples_per_record) / float(2 ** 15))
|
||||||
|
# Now this is an array with all records, but the time is artificial
|
||||||
|
self.data = data
|
||||||
|
self.rawsignal = rawsignal
|
||||||
|
self.timer('retrieved')
|
||||||
|
|
||||||
def fitting(self, data, f_LO, ti, tf):
|
def sinW(self, sig, freq, 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):
|
|
||||||
# sig: signal array
|
# sig: signal array
|
||||||
# freq
|
# freq
|
||||||
# ti, tf: initial and end time
|
# ti, tf: initial and end time
|
||||||
@ -241,30 +253,28 @@ class Adq(object):
|
|||||||
def mix(self, sigin, sigout, freq, ti, tf):
|
def mix(self, sigin, sigout, freq, ti, tf):
|
||||||
# sigin, sigout: signal array, incomping, output
|
# sigin, sigout: signal array, incomping, output
|
||||||
# freq
|
# 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)
|
a, b = self.sinW(sigin, freq, ti, tf)
|
||||||
phase = arctan2(a,b) * 180 / np.pi
|
amp = np.sqrt(a**2 + b**2)
|
||||||
amp = sqrt(a**2 + b**2)
|
|
||||||
a, b = a/amp, b/amp
|
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
|
t = np.arange(len(sigout)) / self.samp_freq
|
||||||
wave1 = sigout * (a * cos(2*np.pi*freq*t) + b * sin(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 * sin(2*np.pi*freq*t) - b * cos(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
|
return wave1, wave2
|
||||||
|
|
||||||
def averageiq(self, data, freq, ti, tf):
|
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][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
|
return iorq.sum(axis=0) / self.number_of_records
|
||||||
|
|
||||||
def filtro(self, iorq, cutoff):
|
def filtro(self, iorq, cutoff):
|
||||||
b, a = butter_lowpass(cutoff, self.samp_freq*1e9)
|
# butter lowpass
|
||||||
|
nyq = 0.5 * self.sample_rate
|
||||||
#ifi = np.array(signal.filtfilt(b,a,iorq[0]))
|
normal_cutoff = cutoff / nyq
|
||||||
#qf = np.array(signal.filtfilt(b,a,iorq[1]))
|
order = 5
|
||||||
iqf = [signal.filtfilt(b,a,iorq[i]) for i in np.arange(len(iorq))]
|
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
|
return iqf
|
||||||
|
|
||||||
def box(self, iorq, ti, tf):
|
def box(self, iorq, ti, tf):
|
||||||
@ -273,21 +283,115 @@ class Adq(object):
|
|||||||
bxa = [sum(iorq[i][si:sf])/(sf-si) for i in np.arange(len(iorq))]
|
bxa = [sum(iorq[i][si:sf])/(sf-si) for i in np.arange(len(iorq))]
|
||||||
return bxa
|
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"""
|
"""return iq values of rois and prepare plottable curves for iq"""
|
||||||
times = []
|
self.timer('gates')
|
||||||
times.append(('aviq', time.time()))
|
try:
|
||||||
iq = self.averageiq(data,freq*1e-9,*pulse)
|
self.ndecimate = int(round(self.sample_rate / freq))
|
||||||
times.append(('filtro', time.time()))
|
except TypeError as e:
|
||||||
iqf = self.filtro(iq,self.bw_cutoff)
|
raise TypeError(f'{self.sample_rate}/{freq} {e}')
|
||||||
m = len(iqf[0]) // self.ndecimate
|
iq = self.averageiq(self.data, freq / GHz, *pulse)
|
||||||
times.append(('iqdec', time.time()))
|
self.timer('aviq')
|
||||||
|
iqf = self.filtro(iq, bw_cutoff)
|
||||||
|
self.timer('filtro')
|
||||||
|
m = max(1, len(iqf[0]) // self.ndecimate)
|
||||||
|
ll = m * self.ndecimate
|
||||||
|
iqf = [iqfx[0:ll] for iqfx in iqf]
|
||||||
|
self.timer('iqf')
|
||||||
iqd = np.average(np.resize(iqf, (2, m, self.ndecimate)), axis=2)
|
iqd = np.average(np.resize(iqf, (2, m, self.ndecimate)), axis=2)
|
||||||
|
self.timer('avg')
|
||||||
t_axis = np.arange(m) * self.ndecimate / self.samp_freq
|
t_axis = np.arange(m) * self.ndecimate / self.samp_freq
|
||||||
pulsig = np.abs(data[0][0])
|
pulsig = np.abs(self.data[0][0])
|
||||||
times.append(('pulsig', time.time()))
|
self.timer('pulsig')
|
||||||
pulsig = np.average(np.resize(pulsig, (m, self.ndecimate)), axis=1)
|
pulsig = np.average(np.resize(pulsig, (m, self.ndecimate)), axis=1)
|
||||||
self.curves = (t_axis, iqd[0], iqd[1], pulsig)
|
result = ([self.box(iqf, *r) for r in roi], # gates
|
||||||
#print(times)
|
(t_axis, iqd[0], iqd[1], pulsig)) # curves
|
||||||
return [self.box(iqf,*r) for r in roi]
|
self.timer('result')
|
||||||
|
# self.timer.show()
|
||||||
|
# ns = len(self.rawsignal[0]) * self.number_of_records
|
||||||
|
# print(f'{ns} {ns / 2e6} ms')
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
class Namespace:
|
||||||
|
"""holds channel or other data"""
|
||||||
|
def __init__(self, **kwds):
|
||||||
|
self.__dict__.update(**kwds)
|
||||||
|
|
||||||
|
|
||||||
|
class RUSdata:
|
||||||
|
def __init__(self, adq, freq, periods, delay_samples):
|
||||||
|
self.sample_rate = adq.sample_rate
|
||||||
|
self.freq = freq
|
||||||
|
self.periods = periods
|
||||||
|
self.delay_samples = delay_samples
|
||||||
|
self.samples_per_record = adq.samples_per_record
|
||||||
|
self.inp = Namespace(idx=0, name='input')
|
||||||
|
self.out = Namespace(idx=1, name='output')
|
||||||
|
self.channels = (self.inp, self.out)
|
||||||
|
self.timer = Timer()
|
||||||
|
|
||||||
|
def retrieve(self, adq):
|
||||||
|
self.timer('start retrieve')
|
||||||
|
npts = self.samples_per_record - self.delay_samples
|
||||||
|
nbin = max(1, npts // (self.periods * 60)) # for performance reasons, do the binning first
|
||||||
|
nreduced = npts // nbin
|
||||||
|
ft = 2 * np.pi * self.freq * nbin / self.sample_rate * np.arange(nreduced)
|
||||||
|
self.timer('create time axis')
|
||||||
|
# complex_sinusoid = np.exp(1j * ft) # do not use this, below is 33 % faster
|
||||||
|
complex_sinusoid = 1j * np.sin(ft) + np.cos(ft)
|
||||||
|
self.timer('sinusoid')
|
||||||
|
|
||||||
|
rawsignal = [] # for raw plot
|
||||||
|
for chan in self.channels: # looping over input and output
|
||||||
|
# although the ADC is only 14 bit it is represented as unsigend 16 bit numbers,
|
||||||
|
# and due to some calculations (calibration) the last 2 bits are not zero
|
||||||
|
beg = self.delay_samples
|
||||||
|
isignal = np.frombuffer(adq.target_buffers[chan.idx].contents, dtype=np.int16)[beg:beg+nreduced * nbin]
|
||||||
|
self.timer('isignal')
|
||||||
|
reduced = isignal.reshape((-1, nbin)).mean(axis=1) # this converts also int16 to float
|
||||||
|
self.timer('reduce')
|
||||||
|
rawsignal.append(reduced)
|
||||||
|
chan.signal = signal = reduced * 2 ** -16 # in V -> peak to peak 1 V ~ +- 0.5 V
|
||||||
|
self.timer('divide')
|
||||||
|
# calculate RMS * sqrt(2) -> peak sinus amplitude.
|
||||||
|
# may be higher than the input range by a factor 1.4 when heavily clipped
|
||||||
|
chan.amplitude = np.sqrt((signal ** 2).mean()) * RMS_TO_VPP
|
||||||
|
self.timer('amp')
|
||||||
|
chan.mixed = signal * complex_sinusoid
|
||||||
|
self.timer('mix')
|
||||||
|
chan.mean = chan.mixed.mean()
|
||||||
|
self.timer('mean')
|
||||||
|
self.rawsignal = rawsignal
|
||||||
|
if self.inp.mean:
|
||||||
|
self.iq = self.out.mean / self.inp.mean
|
||||||
|
else:
|
||||||
|
self.iq = 0
|
||||||
|
|
||||||
|
def get_quality(self):
|
||||||
|
"""get signal quality info
|
||||||
|
|
||||||
|
quality info (small values indicate good quality):
|
||||||
|
- input_stddev:
|
||||||
|
the imaginary part indicates deviations in phase
|
||||||
|
the real part indicates deviations in amplitude
|
||||||
|
- output_slope:
|
||||||
|
the imaginary part indicates a turning phase (rad/sec)
|
||||||
|
the real part indicates changes in amplitude (0.01 ~= 1%/sec)
|
||||||
|
"""
|
||||||
|
self.timer('get_quality')
|
||||||
|
npts = len(self.channels[0].signal)
|
||||||
|
nper = npts // self.periods
|
||||||
|
for chan in self.channels:
|
||||||
|
mean = chan.mixed.mean()
|
||||||
|
chan.reduced = chan.mixed[:self.periods * nper].reshape((-1, nper)).mean(axis=1) / mean
|
||||||
|
|
||||||
|
timeaxis = np.arange(len(self.out.reduced)) * self.sample_rate / self.freq
|
||||||
|
result = Namespace(
|
||||||
|
input_stddev=self.inp.reduced.std(),
|
||||||
|
output_slope=np.polyfit(timeaxis, self.out.reduced, 1)[0])
|
||||||
|
self.timer('got_quality')
|
||||||
|
self.timer.show()
|
||||||
|
ns = len(self.rawsignal[0])
|
||||||
|
print(f'{ns} {ns / 2e6} ms')
|
||||||
|
return result
|
||||||
|
131
frappy_psi/autofill.py
Normal file
131
frappy_psi/autofill.py
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
# *****************************************************************************
|
||||||
|
#
|
||||||
|
# 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>
|
||||||
|
#
|
||||||
|
# *****************************************************************************
|
||||||
|
|
||||||
|
import time
|
||||||
|
from frappy.core import Attached, Readable, Writable, Parameter, Command, \
|
||||||
|
IDLE, BUSY, DISABLED, ERROR
|
||||||
|
from frappy.datatypes import FloatRange, StatusType, TupleOf, EnumType
|
||||||
|
from frappy.states import HasStates, Retry, status_code
|
||||||
|
|
||||||
|
|
||||||
|
class AutoFill(HasStates, Readable):
|
||||||
|
level = Attached(Readable)
|
||||||
|
valve = Attached(Writable)
|
||||||
|
status = Parameter(datatype=StatusType(Readable, 'BUSY'))
|
||||||
|
mode = Parameter('auto mode', EnumType(disabled=0, auto=30), readonly=False)
|
||||||
|
|
||||||
|
fill_level = Parameter('low threshold triggering start filling',
|
||||||
|
FloatRange(unit='%'), readonly=False)
|
||||||
|
full_level = Parameter('high threshold triggering stop filling',
|
||||||
|
FloatRange(unit='%'), readonly=False)
|
||||||
|
fill_minutes_range = Parameter('range of possible fill rate',
|
||||||
|
TupleOf(FloatRange(unit='min'), FloatRange(unit='min')),
|
||||||
|
readonly=False)
|
||||||
|
hold_hours_range = Parameter('range of possible consumption rate',
|
||||||
|
TupleOf(FloatRange(unit='h'), FloatRange(unit='h')),
|
||||||
|
readonly=False)
|
||||||
|
fill_delay = Parameter('delay for cooling the transfer line',
|
||||||
|
FloatRange(unit='min'), readonly=False)
|
||||||
|
|
||||||
|
def read_status(self):
|
||||||
|
if self.mode == 'DISABLED':
|
||||||
|
return DISABLED, ''
|
||||||
|
vstatus = self.valve.status
|
||||||
|
if vstatus[0] // 100 != IDLE // 100:
|
||||||
|
self.stop_machine(vstatus)
|
||||||
|
return vstatus
|
||||||
|
status = self.level.read_status(self)
|
||||||
|
if status[0] // 100 == IDLE // 100:
|
||||||
|
return HasStates.read_status(self)
|
||||||
|
self.stop_machine(status)
|
||||||
|
return status
|
||||||
|
|
||||||
|
def write_mode(self, mode):
|
||||||
|
if mode == 'DISABLED':
|
||||||
|
self.stop_machine((DISABLED, ''))
|
||||||
|
elif mode == 'AUTO':
|
||||||
|
self.start_machine(self.watching)
|
||||||
|
return mode
|
||||||
|
|
||||||
|
@status_code(BUSY)
|
||||||
|
def watching(self, state):
|
||||||
|
if state.init:
|
||||||
|
self.valve.write_target(0)
|
||||||
|
delta = state.delta(10)
|
||||||
|
raw = self.level.value
|
||||||
|
if raw > self.value:
|
||||||
|
self.value -= delta / (3600 * self.hold_hours_range[1])
|
||||||
|
elif raw < self.value:
|
||||||
|
self.value -= delta / (3600 * self.hold_hours_range[0])
|
||||||
|
else:
|
||||||
|
self.value = raw
|
||||||
|
if self.value < self.fill_level:
|
||||||
|
return self.precooling
|
||||||
|
return Retry
|
||||||
|
|
||||||
|
@status_code(BUSY)
|
||||||
|
def precooling(self, state):
|
||||||
|
if state.init:
|
||||||
|
state.fillstart = state.now
|
||||||
|
self.valve.write_target(1)
|
||||||
|
delta = state.delta(1)
|
||||||
|
raw = self.level.value
|
||||||
|
if raw > self.value:
|
||||||
|
self.value += delta / (60 * self.fill_minutes_range[0])
|
||||||
|
elif raw < self.value:
|
||||||
|
self.value -= delta / (60 * self.fill_minutes_range[0])
|
||||||
|
else:
|
||||||
|
self.value = raw
|
||||||
|
if self.value > self.full_level:
|
||||||
|
return self.watching
|
||||||
|
if state.now > state.fillstart + self.fill_delay * 60:
|
||||||
|
return self.filling
|
||||||
|
return Retry
|
||||||
|
|
||||||
|
@status_code(BUSY)
|
||||||
|
def filling(self, state):
|
||||||
|
delta = state.delta(1)
|
||||||
|
raw = self.level.value
|
||||||
|
if raw > self.value:
|
||||||
|
self.value += delta / (60 * self.fill_minutes_range[0])
|
||||||
|
elif raw < self.value:
|
||||||
|
self.value += delta / (60 * self.fill_minutes_range[1])
|
||||||
|
else:
|
||||||
|
self.value = raw
|
||||||
|
if self.value > self.full_level:
|
||||||
|
return self.watching
|
||||||
|
return Retry
|
||||||
|
|
||||||
|
def on_cleanup(self, state):
|
||||||
|
try:
|
||||||
|
self.valve.write_target(0)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
super().on_cleanup()
|
||||||
|
|
||||||
|
@Command()
|
||||||
|
def fill(self):
|
||||||
|
self.mode = 'AUTO'
|
||||||
|
self.start_machine(self.precooling, fillstart=time.time())
|
||||||
|
|
||||||
|
@Command()
|
||||||
|
def stop(self):
|
||||||
|
self.start_machine(self.watching)
|
@ -17,10 +17,39 @@
|
|||||||
# Markus Zolliker <markus.zolliker@psi.ch>
|
# Markus Zolliker <markus.zolliker@psi.ch>
|
||||||
# Jael Celia Lorenzana <jael-celia.lorenzana@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
|
from frappy.core import StringIO, Readable, Parameter, FloatRange, Writable, HasIO, BoolType
|
||||||
|
|
||||||
|
|
||||||
|
# define the IO class
|
||||||
class IO(StringIO):
|
class IO(StringIO):
|
||||||
end_of_line = ('OK\r', '\r')
|
end_of_line = ('OK\r', '\r')
|
||||||
default_settings = {'baudrate': 9600}
|
default_settings = {'baudrate': 9600}
|
||||||
|
128
frappy_psi/butterflyvalve.py
Normal file
128
frappy_psi/butterflyvalve.py
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
# *****************************************************************************
|
||||||
|
#
|
||||||
|
# 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:
|
||||||
|
# M. Zolliker <markus.zolliker@psi.ch>
|
||||||
|
#
|
||||||
|
# *****************************************************************************
|
||||||
|
|
||||||
|
from frappy.core import Attached, Command, EnumType, FloatRange, \
|
||||||
|
Drivable, Parameter, BUSY, IDLE, ERROR
|
||||||
|
|
||||||
|
|
||||||
|
class Valve(Drivable):
|
||||||
|
motor = Attached(Drivable) # refers to motor module
|
||||||
|
value = Parameter('valve state',
|
||||||
|
EnumType(closed=0, open=1, undefined=2),
|
||||||
|
default=2)
|
||||||
|
status = Parameter() # inherit properties from Drivable
|
||||||
|
target = Parameter('valve target',
|
||||||
|
EnumType(closed=0, open=1),
|
||||||
|
readonly=False)
|
||||||
|
# TODO: convert to properties after tests
|
||||||
|
open_pos = Parameter('target position for open state', FloatRange(), readonly=False, default=80)
|
||||||
|
mid_pos = Parameter('position for changing speed', FloatRange(), readonly=False, default=5)
|
||||||
|
fast_speed = Parameter('normal speed', FloatRange(), readonly=False, default=40)
|
||||||
|
slow_speed = Parameter('reduced speed', FloatRange(), readonly=False, default=10)
|
||||||
|
__motor_target = None
|
||||||
|
__status = IDLE, ''
|
||||||
|
__value = 'undefined'
|
||||||
|
__drivestate = 0 # 2 when driving to intermediate target or on retry, 1 when driving to final target, 0 when idle
|
||||||
|
|
||||||
|
def doPoll(self):
|
||||||
|
mot = self.motor
|
||||||
|
motpos = mot.read_value()
|
||||||
|
scode, stext = mot.read_status()
|
||||||
|
drivestate = self.__drivestate
|
||||||
|
if scode >= ERROR:
|
||||||
|
if self.__drivestate and self.__remaining_tries > 0:
|
||||||
|
drivestate = 2
|
||||||
|
self.__remaining_tries -= 1
|
||||||
|
mot.reset()
|
||||||
|
mot.write_speed(self.slow_speed)
|
||||||
|
self.__status = BUSY, f'retry {self._action}'
|
||||||
|
else:
|
||||||
|
self.__status = ERROR, f'valve motor: {stext}'
|
||||||
|
elif scode < BUSY:
|
||||||
|
if self.__motor_target is not None and mot.target != self.__motor_target:
|
||||||
|
self.__status = ERROR, 'motor was driven directly'
|
||||||
|
elif drivestate == 2:
|
||||||
|
self.goto(self.target)
|
||||||
|
drivestate = 1
|
||||||
|
else:
|
||||||
|
if -3 < motpos < 3:
|
||||||
|
self.__value = 'closed'
|
||||||
|
self.__status = IDLE, ''
|
||||||
|
elif self.open_pos * 0.5 < motpos < self.open_pos * 1.5:
|
||||||
|
self.__value = 'open'
|
||||||
|
self.__status = IDLE, ''
|
||||||
|
else:
|
||||||
|
self.__status = ERROR, 'undefined'
|
||||||
|
if self.__drivestate and not self.isBusy(self.__status):
|
||||||
|
drivestate = 0
|
||||||
|
self.__motor_target = None
|
||||||
|
self.setFastPoll(False)
|
||||||
|
self.__drivestate = drivestate
|
||||||
|
self.read_status()
|
||||||
|
self.read_value()
|
||||||
|
|
||||||
|
def read_status(self):
|
||||||
|
return self.__status
|
||||||
|
|
||||||
|
def read_value(self):
|
||||||
|
if self.read_status()[0] >= BUSY:
|
||||||
|
return 'undefined'
|
||||||
|
return self.__value
|
||||||
|
|
||||||
|
def goto(self, target):
|
||||||
|
"""go to open, closed or intermediate position
|
||||||
|
|
||||||
|
the intermediate position is targeted when a speed change is needed
|
||||||
|
|
||||||
|
return 2 when a retry is needed, 1 else
|
||||||
|
"""
|
||||||
|
mot = self.motor
|
||||||
|
if target: # 'open'
|
||||||
|
self._action = 'opening'
|
||||||
|
if True or mot.value > self.mid_pos:
|
||||||
|
mot.write_speed(self.fast_speed)
|
||||||
|
self.__motor_target = mot.write_target(self.open_pos)
|
||||||
|
return 1
|
||||||
|
mot.write_speed(self.slow_speed)
|
||||||
|
self.__motor_target = mot.write_target(self.mid_pos)
|
||||||
|
return 2
|
||||||
|
self._action = 'closing'
|
||||||
|
if mot.value > self.mid_pos * 2:
|
||||||
|
mot.write_speed(self.fast_speed)
|
||||||
|
self.__motor_target = mot.write_target(self.mid_pos)
|
||||||
|
return 2
|
||||||
|
mot.write_speed(self.slow_speed)
|
||||||
|
self.__motor_target = mot.write_target(0)
|
||||||
|
return 1
|
||||||
|
|
||||||
|
def write_target(self, target):
|
||||||
|
self.__remaining_tries = 5
|
||||||
|
self.__drivestate = self.goto(target)
|
||||||
|
self.__status = BUSY, self._action
|
||||||
|
self.read_status()
|
||||||
|
self.read_value()
|
||||||
|
self.setFastPoll(True)
|
||||||
|
|
||||||
|
@Command() # python decorator to mark it as a command
|
||||||
|
def stop(self):
|
||||||
|
"""stop the motor -> value might get undefined"""
|
||||||
|
self.__drivestate = 0
|
||||||
|
self.motor.stop()
|
@ -22,6 +22,7 @@
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
from pathlib import Path
|
||||||
from os.path import basename, dirname, exists, join
|
from os.path import basename, dirname, exists, join
|
||||||
|
|
||||||
import numpy as np
|
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.errors import ProgrammingError, RangeError
|
||||||
from frappy.lib import clamp
|
from frappy.lib import clamp
|
||||||
|
|
||||||
|
|
||||||
|
def identity(x):
|
||||||
|
return x
|
||||||
|
|
||||||
|
|
||||||
|
def exp10(x):
|
||||||
|
return 10 ** np.array(x)
|
||||||
|
|
||||||
|
|
||||||
to_scale = {
|
to_scale = {
|
||||||
'lin': lambda x: x,
|
'lin': identity,
|
||||||
'log': lambda x: np.log10(x),
|
'log': np.log10,
|
||||||
}
|
}
|
||||||
from_scale = {
|
from_scale = {
|
||||||
'lin': lambda x: x,
|
'lin': identity,
|
||||||
'log': lambda x: 10 ** np.array(x),
|
'log': exp10,
|
||||||
}
|
}
|
||||||
TYPES = [ # lakeshore type, inp-type, loglog
|
TYPES = [ # lakeshore type, inp-type, loglog
|
||||||
('DT', 'si', False), # Si diode
|
('DT', 'si', False), # Si diode
|
||||||
@ -55,7 +65,7 @@ TYPES = [ # lakeshore type, inp-type, loglog
|
|||||||
|
|
||||||
OPTION_TYPE = {
|
OPTION_TYPE = {
|
||||||
'loglog': 0, # boolean
|
'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)
|
'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):
|
def get_curve(newscale, curves):
|
||||||
"""get curve from curve cache (converts not existing ones)
|
"""get curve from curve cache (converts not existing ones)
|
||||||
|
|
||||||
@ -247,6 +249,7 @@ def get_curve(newscale, curves):
|
|||||||
class CalCurve(HasOptions):
|
class CalCurve(HasOptions):
|
||||||
EXTRAPOLATION_AMOUNT = 0.1
|
EXTRAPOLATION_AMOUNT = 0.1
|
||||||
MAX_EXTRAPOLATION_FACTOR = 2
|
MAX_EXTRAPOLATION_FACTOR = 2
|
||||||
|
filename = None # calibration file
|
||||||
|
|
||||||
def __init__(self, calibspec=None, *, x=None, y=None, cubic_spline=True, **options):
|
def __init__(self, calibspec=None, *, x=None, y=None, cubic_spline=True, **options):
|
||||||
"""calibration curve
|
"""calibration curve
|
||||||
@ -257,7 +260,7 @@ class CalCurve(HasOptions):
|
|||||||
[<full path> | <name>][,<key>=<value> ...]
|
[<full path> | <name>][,<key>=<value> ...]
|
||||||
for <key>/<value> as in parser arguments
|
for <key>/<value> as in parser arguments
|
||||||
:param x, y: x and y arrays (given instead of calibspec)
|
: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
|
:param options: options for parsers
|
||||||
"""
|
"""
|
||||||
self.options = options
|
self.options = options
|
||||||
@ -265,26 +268,31 @@ class CalCurve(HasOptions):
|
|||||||
parser = StdParser()
|
parser = StdParser()
|
||||||
parser.xdata = x
|
parser.xdata = x
|
||||||
parser.ydata = y
|
parser.ydata = y
|
||||||
|
self.calibname = 'custom'
|
||||||
else:
|
else:
|
||||||
if x or y:
|
if x or y:
|
||||||
raise ProgrammingError('can not give both calibspec and x,y ')
|
raise ProgrammingError('can not give both calibspec and x,y ')
|
||||||
sensopt = calibspec.split(',')
|
sensopt = calibspec.split(',')
|
||||||
calibname = sensopt.pop(0)
|
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
|
kind = None
|
||||||
pathlist = os.environ.get('FRAPPY_CALIB_PATH', '').split(':')
|
pathlist = [Path(p.strip()) for p in os.environ.get('FRAPPY_CALIB_PATH', '').split(':')]
|
||||||
pathlist.append(join(dirname(__file__), 'calcurves'))
|
pathlist.append(Path(dirname(__file__)) / 'calcurves')
|
||||||
for path in pathlist:
|
for path in pathlist:
|
||||||
# first try without adding kind
|
# first try without adding kind
|
||||||
filename = join(path.strip(), calibname)
|
filename = path / calibname
|
||||||
if exists(filename):
|
if filename.exists():
|
||||||
kind = ext if dot else None
|
kind = ext if dot else None
|
||||||
break
|
break
|
||||||
# then try adding all kinds as extension
|
# then try adding all kinds as extension
|
||||||
for nam in calibname, calibname.upper(), calibname.lower():
|
for nam in calibname, calibname.upper(), calibname.lower():
|
||||||
for kind in PARSERS:
|
for kind in PARSERS:
|
||||||
filename = join(path.strip(), '%s.%s' % (nam, kind))
|
filename = path / f'{nam}.{kind}'
|
||||||
if exists(filename):
|
if exists(filename):
|
||||||
|
self.filename = filename
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
continue
|
continue
|
||||||
@ -328,6 +336,7 @@ class CalCurve(HasOptions):
|
|||||||
not_incr_idx = np.argwhere(x[1:] <= x[:-1])
|
not_incr_idx = np.argwhere(x[1:] <= x[:-1])
|
||||||
if len(not_incr_idx):
|
if len(not_incr_idx):
|
||||||
raise RangeError('x not monotonic at x=%.4g' % x[not_incr_idx[0]])
|
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.x = {parser.xscale: x}
|
||||||
self.y = {parser.yscale: y}
|
self.y = {parser.yscale: y}
|
||||||
@ -344,8 +353,7 @@ class CalCurve(HasOptions):
|
|||||||
self.convert_x = to_scale[newscale]
|
self.convert_x = to_scale[newscale]
|
||||||
self.convert_y = from_scale[newscale]
|
self.convert_y = from_scale[newscale]
|
||||||
self.calibrange = self.options.get('calibrange')
|
self.calibrange = self.options.get('calibrange')
|
||||||
dirty = set()
|
self.extra_points = (0, 0)
|
||||||
self.extra_points = False
|
|
||||||
self.cutted = False
|
self.cutted = False
|
||||||
if self.calibrange:
|
if self.calibrange:
|
||||||
self.calibrange = sorted(self.calibrange)
|
self.calibrange = sorted(self.calibrange)
|
||||||
@ -371,7 +379,6 @@ class CalCurve(HasOptions):
|
|||||||
self.y = {newscale: y}
|
self.y = {newscale: y}
|
||||||
ibeg = 0
|
ibeg = 0
|
||||||
iend = len(x)
|
iend = len(x)
|
||||||
dirty.add('xy')
|
|
||||||
else:
|
else:
|
||||||
self.extra_points = ibeg, len(x) - iend
|
self.extra_points = ibeg, len(x) - iend
|
||||||
else:
|
else:
|
||||||
@ -493,13 +500,48 @@ class CalCurve(HasOptions):
|
|||||||
except IndexError:
|
except IndexError:
|
||||||
return defaultx
|
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
|
"""export curve for downloading to hardware
|
||||||
|
|
||||||
:param nmax: max number of points. if the number of given points is bigger,
|
:param nmax: max number of points. if the number of given points is bigger,
|
||||||
the points with the lowest interpolation error are omitted
|
the points with the lowest interpolation error are omitted
|
||||||
:param logformat: a list with two elements of None, True or False
|
:param logformat: a list with two elements of None, True or False for x and y
|
||||||
True: use log, False: use line, None: use log if self.loglog
|
True: use log, False: use lin, None: use log if self.loglog
|
||||||
values None are replaced with the effectively used format
|
values None are replaced with the effectively used format
|
||||||
False / True are replaced by [False, False] / [True, True]
|
False / True are replaced by [False, False] / [True, True]
|
||||||
default is False
|
default is False
|
||||||
@ -507,25 +549,26 @@ class CalCurve(HasOptions):
|
|||||||
:param extrapolate: a flag indicating whether the curves should be extrapolated
|
:param extrapolate: a flag indicating whether the curves should be extrapolated
|
||||||
to the preset extrapolation range
|
to the preset extrapolation range
|
||||||
:param xlimits: max x range
|
:param xlimits: max x range
|
||||||
|
:param nmin: minimum number of points
|
||||||
:return: numpy array with 2 dimensions returning the curve
|
:return: numpy array with 2 dimensions returning the curve
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if logformat in (True, False):
|
if logformat in (True, False):
|
||||||
logformat = [logformat, logformat]
|
logformat = (logformat, logformat)
|
||||||
|
self.logformat = list(logformat)
|
||||||
try:
|
try:
|
||||||
scales = []
|
scales = []
|
||||||
for idx, logfmt in enumerate(logformat):
|
for idx, logfmt in enumerate(logformat):
|
||||||
if logfmt and self.lin_forced[idx]:
|
if logfmt and self.lin_forced[idx]:
|
||||||
raise ValueError('%s must contain positive values only' % 'xy'[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')
|
scales.append('log' if linlog else 'lin')
|
||||||
xscale, yscale = scales
|
xscale, yscale = scales
|
||||||
except (TypeError, AssertionError):
|
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
|
xr = self.spline.x[1:-1] # raw units, excluding extrapolated points
|
||||||
x1, x2 = xmin, xmax = x[0], x[-1]
|
x1, x2 = xmin, xmax = xr[0], xr[-1]
|
||||||
y1, y2 = sorted(self.spline([x1, x2]))
|
|
||||||
|
|
||||||
if extrapolate and not yrange:
|
if extrapolate and not yrange:
|
||||||
yrange = self.exty
|
yrange = self.exty
|
||||||
@ -535,42 +578,100 @@ class CalCurve(HasOptions):
|
|||||||
lim = to_scale[self.scale](xlimits)
|
lim = to_scale[self.scale](xlimits)
|
||||||
xmin = clamp(xmin, *lim)
|
xmin = clamp(xmin, *lim)
|
||||||
xmax = clamp(xmax, *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:
|
if xmin != x1 or xmax != x2:
|
||||||
ibeg, iend = np.searchsorted(x, (xmin, xmax))
|
i, j = np.searchsorted(xr, (xmin, xmax))
|
||||||
if abs(x[ibeg] - xmin) < 0.1 * (x[ibeg + 1] - x[ibeg]):
|
if abs(xr[i] - xmin) < 0.1 * (xr[i + 1] - xr[i]):
|
||||||
# remove first point, if close
|
# remove first point, if close
|
||||||
ibeg += 1
|
i += 1
|
||||||
if abs(x[iend - 1] - xmax) < 0.1 * (x[iend - 1] - x[iend - 2]):
|
if abs(xr[j - 1] - xmax) < 0.1 * (xr[j - 1] - xr[j - 2]):
|
||||||
# remove last point, if close
|
# remove last point, if close
|
||||||
iend -= 1
|
j -= 1
|
||||||
x = np.concatenate(([xmin], x[ibeg:iend], [xmax]))
|
offset = i - 1
|
||||||
y = self.spline(x)
|
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
|
# convert to exported scale
|
||||||
if xscale != self.scale:
|
if xscale == self.scale:
|
||||||
x = to_scale[xscale](from_scale[self.scale](x))
|
xbwd = identity
|
||||||
if yscale != self.scale:
|
x = xr
|
||||||
y = to_scale[yscale](from_scale[self.scale](y))
|
else:
|
||||||
|
if self.scale == 'log':
|
||||||
# reduce number of points, if needed
|
xfwd, xbwd = from_scale[self.scale], to_scale[self.scale]
|
||||||
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])
|
|
||||||
else:
|
else:
|
||||||
deviation[i:j] = np.abs(ym - y[i:j]) / (np.abs(ym + y[i:j]) + 1e-10)
|
xfwd, xbwd = to_scale[xscale], from_scale[xscale]
|
||||||
if n <= nmax:
|
x = xfwd(xr)
|
||||||
break
|
if yscale == self.scale:
|
||||||
idx = np.argmin(deviation[1:-1]) + 1 # find index of the smallest error
|
yfwd = identity
|
||||||
y = np.delete(y, idx)
|
y = yr
|
||||||
x = np.delete(x, idx)
|
else:
|
||||||
deviation = np.delete(deviation, idx)
|
if self.scale == 'log':
|
||||||
n -= 1
|
yfwd = from_scale[self.scale]
|
||||||
# index range to recalculate
|
else:
|
||||||
i, j = max(1, idx - 1), min(n - 1, idx + 1)
|
yfwd = to_scale[yscale]
|
||||||
self.deviation = deviation # for debugging purposes
|
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)
|
return np.stack([x, y], axis=1)
|
||||||
|
139
frappy_psi/ccracks.py
Normal file
139
frappy_psi/ccracks.py
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
import os
|
||||||
|
from glob import glob
|
||||||
|
from pathlib import Path
|
||||||
|
from configparser import ConfigParser
|
||||||
|
from frappy.errors import ConfigError
|
||||||
|
|
||||||
|
|
||||||
|
class Rack:
|
||||||
|
configbase = Path('/home/l_samenv/.config/frappy_instruments')
|
||||||
|
|
||||||
|
def __init__(self, modfactory, **kwds):
|
||||||
|
self.modfactory = modfactory
|
||||||
|
instpath = self.configbase / os.environ['Instrument']
|
||||||
|
sections = {}
|
||||||
|
self.config = {}
|
||||||
|
files = glob(str(instpath / '*.ini'))
|
||||||
|
for filename in files:
|
||||||
|
parser = ConfigParser()
|
||||||
|
parser.optionxform = str
|
||||||
|
parser.read([filename])
|
||||||
|
for section in parser.sections():
|
||||||
|
prev = sections.get(section)
|
||||||
|
if prev:
|
||||||
|
raise ConfigError(f'duplicate {section} section in {filename} and {prev}')
|
||||||
|
sections[section] = filename
|
||||||
|
self.config.update(parser.items(section))
|
||||||
|
if 'rack' not in sections:
|
||||||
|
raise ConfigError(f'no rack found in {instpath}')
|
||||||
|
self.props = {} # dict (<property>, <method>) of value
|
||||||
|
self.mods = {} # dict (<property>, <method>) of list of <cfg>
|
||||||
|
|
||||||
|
def set_props(self, mod, **kwds):
|
||||||
|
for prop, method in kwds.items():
|
||||||
|
value = self.props.get((prop, method))
|
||||||
|
if value is None:
|
||||||
|
# add mod to the list of cfgs to be fixed
|
||||||
|
self.mods.setdefault((prop, method), []).append(mod)
|
||||||
|
else:
|
||||||
|
# set prop in current module
|
||||||
|
if not mod.get(prop): # do not override given and not empty property
|
||||||
|
mod[prop] = value
|
||||||
|
|
||||||
|
def fix_props(self, method, **kwds):
|
||||||
|
for prop, value in kwds.items():
|
||||||
|
if (prop, method) in self.props:
|
||||||
|
raise ConfigError(f'duplicate call to {method}()')
|
||||||
|
self.props[prop, method] = value
|
||||||
|
# set property in modules to be fixed
|
||||||
|
for mod in self.mods.get((prop, method), ()):
|
||||||
|
mod[prop] = value
|
||||||
|
|
||||||
|
def lakeshore(self, ls_uri=None, io='ls_io', dev='ls', model='336', **kwds):
|
||||||
|
Mod = self.modfactory
|
||||||
|
self.fix_props('lakeshore', io=io, device=dev)
|
||||||
|
self.ls_model = model
|
||||||
|
self.ls_dev = dev
|
||||||
|
ls_uri = ls_uri or self.config.get('ls_uri')
|
||||||
|
Mod(io, cls=f'frappy_psi.lakeshore.IO{self.ls_model}',
|
||||||
|
description='comm. to lakeshore in cc rack', uri=ls_uri)
|
||||||
|
self.dev = Mod(dev, cls=f'frappy_psi.lakeshore.Device{self.ls_model}',
|
||||||
|
description='lakeshore in cc rack', io=io, curve_handling=True)
|
||||||
|
|
||||||
|
def sensor(self, name, channel, calcurve, **kwds):
|
||||||
|
Mod = self.modfactory
|
||||||
|
kwds.setdefault('cls', f'frappy_psi.lakeshore.Sensor{self.ls_model}')
|
||||||
|
kwds.setdefault('description', f'T sensor {name}')
|
||||||
|
mod = Mod(name, channel=channel, calcurve=calcurve,
|
||||||
|
device=self.ls_dev, **kwds)
|
||||||
|
self.set_props(mod, io='lakeshore', dev='lakeshore')
|
||||||
|
|
||||||
|
def loop(self, name, channel, calcurve, output_module, **kwds):
|
||||||
|
Mod = self.modfactory
|
||||||
|
kwds.setdefault('cls', f'frappy_psi.lakeshore.Loop{self.ls_model}')
|
||||||
|
kwds.setdefault('description', f'T loop {name}')
|
||||||
|
Mod(name, channel=channel, calcurve=calcurve, output_module=output_module,
|
||||||
|
device=self.ls_dev, **kwds)
|
||||||
|
self.fix_props(f'heater({output_module})', description=f'heater for {name}')
|
||||||
|
|
||||||
|
def heater(self, name, output_no, max_heater, resistance, **kwds):
|
||||||
|
Mod = self.modfactory
|
||||||
|
if output_no == 1:
|
||||||
|
kwds.setdefault('cls', f'frappy_psi.lakeshore.MainOutput{self.ls_model}')
|
||||||
|
elif output_no == 2:
|
||||||
|
kwds.setdefault('cls', f'frappy_psi.lakeshore.SecondaryOutput{self.ls_model}')
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
kwds.setdefault('description', '')
|
||||||
|
mod = Mod(name, max_heater=max_heater, resistance=resistance, **kwds)
|
||||||
|
self.set_props(mod, io='lakeshore', device='lakeshore', description=f'heater({name})')
|
||||||
|
|
||||||
|
def ccu(self, ccu_uri=None, ccu_io='ccu_io', he=None, n2=None, **kwds):
|
||||||
|
Mod = self.modfactory
|
||||||
|
self.ccu_io = ccu_io
|
||||||
|
ccu_uri = ccu_uri or self.config.get('ccu_uri')
|
||||||
|
# self.devname = ccu_devname
|
||||||
|
Mod(ccu_io, 'frappy_psi.ccu4.IO',
|
||||||
|
'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.ccu_io)
|
||||||
|
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=ccu_io,
|
||||||
|
valve=valve, upper=upper, lower=lower)
|
||||||
|
Mod(valve, cls='frappy_psi.ccu4.N2FillValve',
|
||||||
|
description='LN2 fill valve', io=ccu_io)
|
||||||
|
Mod(upper, cls='frappy_psi.ccu4.N2TempSensor',
|
||||||
|
description='upper LN2 sensor')
|
||||||
|
Mod(lower, cls='frappy_psi.ccu4.N2TempSensor',
|
||||||
|
description='lower LN2 sensor')
|
||||||
|
|
||||||
|
def hepump(self, hepump_uri=None, hepump_type=None, hepump_io='hepump_io',
|
||||||
|
hepump='hepump', hepump_mot='hepump_mot', hepump_valve='hepump_valve',
|
||||||
|
flow_sensor='flow_sensor', pump_pressure='pump_pressure', nv='nv',
|
||||||
|
ccu_io='ccu_io', **kwds):
|
||||||
|
Mod = self.modfactory
|
||||||
|
hepump_type = hepump_type or self.config.get('hepump_type', 'no')
|
||||||
|
Mod(nv, 'frappy_psi.ccu4.NeedleValveFlow', 'flow from flow sensor or pump pressure',
|
||||||
|
flow_sensor=flow_sensor, pressure=pump_pressure, io=ccu_io)
|
||||||
|
Mod(pump_pressure, 'frappy_psi.ccu4.Pressure', 'He pump pressure', io=ccu_io)
|
||||||
|
if hepump_type == 'no':
|
||||||
|
print('no pump, no flow meter - using flow from pressure alone')
|
||||||
|
return
|
||||||
|
hepump_uri = hepump_uri or self.config['hepump_uri']
|
||||||
|
Mod(hepump_io, 'frappy.io.BytesIO', 'He pump connection', uri=hepump_uri)
|
||||||
|
Mod(hepump, 'frappy_psi.hepump.HePump', 'He pump', pump_type=hepump_type,
|
||||||
|
valvemotor=hepump_mot, valve=hepump_valve, flow=nv)
|
||||||
|
Mod(hepump_mot, 'frappy_psi.hepump.Motor', 'He pump valve motor', io=hepump_io, maxcurrent=2.8)
|
||||||
|
Mod(hepump_valve, 'frappy_psi.butterflyvalve.Valve', 'He pump valve', motor=hepump_mot)
|
||||||
|
Mod(flow_sensor, 'frappy_psi.sensirion.FlowSensor', 'Flow Sensor', io=hepump_io, nsamples=160)
|
||||||
|
|
@ -22,51 +22,65 @@
|
|||||||
"""drivers for CCU4, the cryostat control unit at SINQ"""
|
"""drivers for CCU4, the cryostat control unit at SINQ"""
|
||||||
import time
|
import time
|
||||||
import math
|
import math
|
||||||
|
from frappy.lib.enum import Enum
|
||||||
|
from frappy.lib import clamp
|
||||||
# the most common Frappy classes can be imported from frappy.core
|
# the most common Frappy classes can be imported from frappy.core
|
||||||
from frappy.core import HasIO, Parameter, Command, Readable, Writable, Drivable, \
|
from frappy.core import HasIO, Parameter, Command, Readable, Writable, Drivable, \
|
||||||
Property, StringIO, BUSY, IDLE, WARN, ERROR, DISABLED, Attached
|
Property, StringIO, BUSY, IDLE, WARN, ERROR, DISABLED, Attached, nopoll
|
||||||
from frappy.datatypes import BoolType, EnumType, FloatRange, StructOf, \
|
from frappy.datatypes import BoolType, EnumType, FloatRange, StructOf, \
|
||||||
StatusType, IntRange, StringType, TupleOf
|
StatusType, IntRange, StringType, TupleOf
|
||||||
from frappy.dynamic import Pinata
|
|
||||||
from frappy.errors import CommunicationFailedError
|
from frappy.errors import CommunicationFailedError
|
||||||
from frappy.states import HasStates, status_code, Retry
|
from frappy.states import HasStates, status_code, Retry
|
||||||
|
|
||||||
|
|
||||||
class CCU4IO(StringIO):
|
M = Enum(idle=0, opening=1, closing=2, opened=3, closed=4, no_motor=5)
|
||||||
|
A = Enum(disabled=0, manual=1, auto=2)
|
||||||
|
|
||||||
|
|
||||||
|
class IO(StringIO):
|
||||||
"""communication with CCU4"""
|
"""communication with CCU4"""
|
||||||
# for completeness: (not needed, as it is the default)
|
# for completeness: (not needed, as it is the default)
|
||||||
end_of_line = '\n'
|
end_of_line = '\n'
|
||||||
# on connect, we send 'cid' and expect a reply starting with 'CCU4'
|
# on connect, we send 'cid' and expect a reply starting with 'CCU4'
|
||||||
identification = [('cid', r'CCU4.*')]
|
identification = [('cid', r'cid=CCU4.*')]
|
||||||
|
|
||||||
|
|
||||||
class CCU4Base(HasIO):
|
class Base(HasIO):
|
||||||
ioClass = CCU4IO
|
ioClass = IO
|
||||||
|
|
||||||
def command(self, *args, **kwds):
|
def command(self, **kwds):
|
||||||
"""send a command and get the response
|
"""send a command and get the response
|
||||||
|
|
||||||
:param args: the name of the parameters to query
|
:param kwds: <parameter>=<value> for changing a parameter <parameter>=<type> for querying a parameter
|
||||||
:param kwds: <parameter>=<value> for changing a parameter
|
|
||||||
:returns: the (new) values of the parameters
|
: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()
|
reply = self.io.communicate(' '.join(cmds)).split()
|
||||||
names = list(args) + list(kwds)
|
if len(reply) != len(types):
|
||||||
if len(reply) != len(names):
|
|
||||||
raise CommunicationFailedError('number of reply items does not match')
|
raise CommunicationFailedError('number of reply items does not match')
|
||||||
result = []
|
result = []
|
||||||
for given, item in zip(names, reply):
|
for (given, typ), res in zip(types.items(), reply):
|
||||||
name, txtvalue = item.split('=')
|
name, txtvalue = res.split('=')
|
||||||
if given != name:
|
if given != name:
|
||||||
raise CommunicationFailedError('result keys do not match given keys')
|
raise CommunicationFailedError('result keys do not match given keys')
|
||||||
result.append(float(txtvalue))
|
result.append(typ(txtvalue))
|
||||||
if len(result) == 1:
|
if len(kwds) == 1:
|
||||||
return result[0]
|
return result[0]
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
class HeLevel(CCU4Base, Readable):
|
class HeLevel(Base, Readable):
|
||||||
"""He Level channel of CCU4"""
|
"""He Level channel of CCU4"""
|
||||||
|
|
||||||
value = Parameter(unit='%')
|
value = Parameter(unit='%')
|
||||||
@ -89,13 +103,13 @@ class HeLevel(CCU4Base, Readable):
|
|||||||
}
|
}
|
||||||
|
|
||||||
def read_value(self):
|
def read_value(self):
|
||||||
return self.command('h')
|
return self.command(h=float)
|
||||||
|
|
||||||
def read_status(self):
|
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):
|
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
|
return value
|
||||||
|
|
||||||
def write_sample_rate(self, value):
|
def write_sample_rate(self, value):
|
||||||
@ -108,10 +122,10 @@ class HeLevel(CCU4Base, Readable):
|
|||||||
return self.command(hfu=value)
|
return self.command(hfu=value)
|
||||||
|
|
||||||
|
|
||||||
class Valve(CCU4Base, Writable):
|
class Valve(Base, Writable):
|
||||||
value = Parameter('relay state', BoolType())
|
value = Parameter('relay state', BoolType())
|
||||||
target = Parameter('relay target', BoolType())
|
target = Parameter('relay target', BoolType())
|
||||||
ioClass = CCU4IO
|
ioClass = IO
|
||||||
STATE_MAP = {0: (0, (IDLE, 'off')),
|
STATE_MAP = {0: (0, (IDLE, 'off')),
|
||||||
1: (1, (IDLE, 'on')),
|
1: (1, (IDLE, 'on')),
|
||||||
2: (0, (ERROR, 'no valve')),
|
2: (0, (ERROR, 'no valve')),
|
||||||
@ -130,7 +144,7 @@ class Valve(CCU4Base, Writable):
|
|||||||
self.command(**self._close_command)
|
self.command(**self._close_command)
|
||||||
|
|
||||||
def read_status(self):
|
def read_status(self):
|
||||||
state = self.command(self._query_state)
|
state = int(self.command(**self._query_state))
|
||||||
self.value, status = self.STATE_MAP[state]
|
self.value, status = self.STATE_MAP[state]
|
||||||
return status
|
return status
|
||||||
|
|
||||||
@ -138,13 +152,13 @@ class Valve(CCU4Base, Writable):
|
|||||||
class HeFillValve(Valve):
|
class HeFillValve(Valve):
|
||||||
_open_command = {'hcd': 1, 'hf': 1}
|
_open_command = {'hcd': 1, 'hf': 1}
|
||||||
_close_command = {'hcd': 0, 'hf': 0}
|
_close_command = {'hcd': 0, 'hf': 0}
|
||||||
_query_state = 'hv'
|
_query_state = {'hv': int}
|
||||||
|
|
||||||
|
|
||||||
class N2FillValve(Valve):
|
class N2FillValve(Valve):
|
||||||
_open_command = {'nc': 1}
|
_open_command = {'nc': 1}
|
||||||
_close_command = {'nc': 0}
|
_close_command = {'nc': 0}
|
||||||
_query_state = 'nv'
|
_query_state = {'nv': int}
|
||||||
|
|
||||||
|
|
||||||
class AuxValve(Valve):
|
class AuxValve(Valve):
|
||||||
@ -153,21 +167,21 @@ class AuxValve(Valve):
|
|||||||
def initModule(self):
|
def initModule(self):
|
||||||
self._open_command = {f'vc{self.channel}': 1}
|
self._open_command = {f'vc{self.channel}': 1}
|
||||||
self._close_command = {f'vc{self.channel}': 0}
|
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):
|
class N2TempSensor(Readable):
|
||||||
value = Parameter('LN2 T sensor', FloatRange(unit='K'), default=0)
|
value = Parameter('LN2 T sensor', FloatRange(unit='K'), default=0)
|
||||||
|
|
||||||
|
|
||||||
class N2Level(CCU4Base, Pinata, Readable):
|
class N2Level(Base, Readable):
|
||||||
valve = Attached(Writable, mandatory=False)
|
valve = Attached(Writable, mandatory=False)
|
||||||
lower = Attached(Readable, mandatory=False)
|
lower = Attached(Readable, mandatory=False)
|
||||||
upper = Attached(Readable, mandatory=False)
|
upper = Attached(Readable, mandatory=False)
|
||||||
|
|
||||||
value = Parameter('vessel state', EnumType(empty=0, ok=1, full=2))
|
value = Parameter('vessel state', EnumType(empty=0, ok=1, full=2))
|
||||||
status = Parameter(datatype=StatusType(Readable, 'BUSY'))
|
status = Parameter(datatype=StatusType(Readable, 'DISABLED', 'BUSY'))
|
||||||
mode = Parameter('auto mode', EnumType(disabled=0, manual=1, auto=2), readonly=False)
|
mode = Parameter('auto mode', EnumType(A), readonly=False, default=A.manual)
|
||||||
|
|
||||||
threshold = Parameter('threshold triggering start/stop filling',
|
threshold = Parameter('threshold triggering start/stop filling',
|
||||||
FloatRange(unit='K'), readonly=False)
|
FloatRange(unit='K'), readonly=False)
|
||||||
@ -192,31 +206,22 @@ class N2Level(CCU4Base, Pinata, Readable):
|
|||||||
5: (WARN, 'empty'),
|
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):
|
def initialReads(self):
|
||||||
self.command(nav=1) # tell CCU4 to activate LN2 sensor readings
|
self.command(nav=1) # tell CCU4 to activate LN2 sensor readings
|
||||||
super().initialReads()
|
super().initialReads()
|
||||||
|
|
||||||
def read_status(self):
|
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 not self.valve or not auto:
|
||||||
if self.mode == 'auto':
|
if self.mode == A.auto:
|
||||||
# no valve assigned
|
# no valve assigned
|
||||||
self.mode = 'manual'
|
self.mode = A.manual
|
||||||
if self.mode == 'disabled':
|
if self.mode == A.disabled:
|
||||||
return DISABLED, ''
|
return DISABLED, ''
|
||||||
status = self.STATUS_MAP[nstate]
|
status = self.STATUS_MAP[nstate]
|
||||||
if status[0] // 100 != IDLE // 100:
|
if status[0] // 100 != IDLE // 100:
|
||||||
return status
|
return status
|
||||||
if self.mode == 'manual':
|
if self.mode == A.manual:
|
||||||
return IDLE, ''
|
return IDLE, ''
|
||||||
vstatus = self.valve.status
|
vstatus = self.valve.status
|
||||||
if vstatus[0] // 100 == WARN // 100:
|
if vstatus[0] // 100 == WARN // 100:
|
||||||
@ -229,7 +234,7 @@ class N2Level(CCU4Base, Pinata, Readable):
|
|||||||
|
|
||||||
def read_value(self):
|
def read_value(self):
|
||||||
# read sensors
|
# read sensors
|
||||||
lower, upper = self.command('nl', 'nu')
|
lower, upper = self.command(nl=float, nu=float)
|
||||||
if self.lower:
|
if self.lower:
|
||||||
self.lower.value = lower
|
self.lower.value = lower
|
||||||
if self.upper:
|
if self.upper:
|
||||||
@ -241,7 +246,7 @@ class N2Level(CCU4Base, Pinata, Readable):
|
|||||||
return 'empty'
|
return 'empty'
|
||||||
|
|
||||||
def write_mode(self, mode):
|
def write_mode(self, mode):
|
||||||
if mode == 'auto':
|
if mode == A.auto:
|
||||||
if self.isBusy():
|
if self.isBusy():
|
||||||
return mode
|
return mode
|
||||||
# set to watching
|
# set to watching
|
||||||
@ -252,7 +257,7 @@ class N2Level(CCU4Base, Pinata, Readable):
|
|||||||
return mode
|
return mode
|
||||||
|
|
||||||
def read_threshold(self):
|
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
|
return value
|
||||||
|
|
||||||
def write_threshold(self, value):
|
def write_threshold(self, value):
|
||||||
@ -266,153 +271,335 @@ class N2Level(CCU4Base, Pinata, Readable):
|
|||||||
|
|
||||||
@Command()
|
@Command()
|
||||||
def fill(self):
|
def fill(self):
|
||||||
self.mode = 'auto'
|
"""start filling"""
|
||||||
self.io.write(nc=1)
|
self.mode = A.auto
|
||||||
|
self.command(nc=1)
|
||||||
|
|
||||||
@Command()
|
@Command()
|
||||||
def stop(self):
|
def stop(self):
|
||||||
if self.mode == 'auto':
|
"""stop filling"""
|
||||||
|
if self.mode == A.auto:
|
||||||
# set to watching
|
# set to watching
|
||||||
self.command(nc=3)
|
self.command(nc=3)
|
||||||
else:
|
else:
|
||||||
# set to off
|
# set to off
|
||||||
self.io.write(nc=0)
|
self.command(nc=0)
|
||||||
|
|
||||||
|
|
||||||
class FlowPressure(CCU4Base, Readable):
|
class HasFilter:
|
||||||
|
__value1 = None
|
||||||
|
__value = None
|
||||||
|
__last = None
|
||||||
|
|
||||||
|
def filter(self, filter_time, value):
|
||||||
|
now = time.time()
|
||||||
|
if self.__value is None:
|
||||||
|
self.__last = now
|
||||||
|
self.__value1 = value
|
||||||
|
self.__value = value
|
||||||
|
weight = (now - self.__last) / filter_time
|
||||||
|
self.__value1 += weight * (value - self.__value)
|
||||||
|
self.__value += weight * (self.__value1 - self.__value)
|
||||||
|
self.__last = now
|
||||||
|
return self.__value
|
||||||
|
|
||||||
|
|
||||||
|
class Pressure(HasFilter, Base, Readable):
|
||||||
value = Parameter(unit='mbar')
|
value = Parameter(unit='mbar')
|
||||||
mbar_offset = Parameter(unit='mbar', default=0.8, readonly=False)
|
mbar_offset = Parameter('offset in mbar', FloatRange(unit='mbar'), default=0.8, readonly=False)
|
||||||
|
filter_time = Parameter('filter time', FloatRange(unit='sec'), readonly=False, default=3)
|
||||||
pollinterval = Parameter(default=0.25)
|
pollinterval = Parameter(default=0.25)
|
||||||
|
|
||||||
def read_value(self):
|
def read_value(self):
|
||||||
return self.filter(self.command('f')) - self.mbar_offset
|
return self.filter(self.filter_time, self.command(f=float)) - self.mbar_offset
|
||||||
|
|
||||||
|
|
||||||
class NeedleValve(HasStates, CCU4Base, Drivable):
|
class NeedleValveFlow(HasStates, Base, Drivable):
|
||||||
flow = Attached(Readable, mandatory=False)
|
flow_sensor = Attached(Readable, mandatory=False)
|
||||||
flow_pressure = Attached(Readable, mandatory=False)
|
pressure = Attached(Pressure, mandatory=False)
|
||||||
|
use_pressure = Parameter('flag (use pressure instead of flow meter)', BoolType(),
|
||||||
|
readonly=False, default=False)
|
||||||
|
lnm_per_mbar = Parameter('scale factor', FloatRange(unit='lnm/mbar'), readonly=False, default=0.6)
|
||||||
|
|
||||||
value = Parameter(unit='ln/min')
|
value = Parameter(unit='ln/min')
|
||||||
target = Parameter(unit='ln/min')
|
target = Parameter(unit='ln/min')
|
||||||
|
|
||||||
lnm_per_mbar = Parameter(unit='ln/min/mbar', default=0.6, readonly=False)
|
motor_state = Parameter('motor_state', EnumType(M), default=0)
|
||||||
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))
|
|
||||||
tolerance = Parameter('tolerance', FloatRange(0), value=0.25, readonly=False)
|
tolerance = Parameter('tolerance', FloatRange(0), value=0.25, readonly=False)
|
||||||
tolerance2 = Parameter('tolerance limit above 2 lnm', FloatRange(0), value=0.5, 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)
|
prop = Parameter('proportional term', FloatRange(unit='s/lnm'), readonly=False, default=0.001)
|
||||||
deriv = Parameter('min progress time constant', FloatRange(unit='s'),
|
deriv = Parameter('min progress time constant', FloatRange(unit='s'),
|
||||||
default=30, readonly=False)
|
default=30, readonly=False)
|
||||||
settle = Parameter('time within tolerance before getting quiet', FloatRange(unit='s'),
|
settle = Parameter('time within tolerance before getting quiet', FloatRange(unit='s'),
|
||||||
default=30, readonly=False)
|
default=30, readonly=False)
|
||||||
step_factor = Parameter('factor (no progress time) / (min step size)', FloatRange(), default=300)
|
step_factor = Parameter('factor (no progress time) / (min step size)', FloatRange(), default=300, readonly=False)
|
||||||
control_active = Parameter('control active flag', BoolType(), readonly=False)
|
control_active = Parameter('control active flag', BoolType(), readonly=False, default=1)
|
||||||
pollinterval = Parameter(default=1)
|
min_open_step = Parameter('minimal open step', FloatRange(unit='s'), readonly=False, default=0.06)
|
||||||
|
min_close_step = Parameter('minimal close step', FloatRange(unit='s'), readonly=False, default=0.05)
|
||||||
|
raw_open_step = Parameter('step after direction change', FloatRange(unit='s'), readonly=False, default=0.12)
|
||||||
|
raw_close_step = Parameter('step after direction change', FloatRange(unit='s'), readonly=False, default=0.04)
|
||||||
|
pollinterval = Parameter(default=5)
|
||||||
_ref_time = 0
|
_ref_time = 0
|
||||||
_ref_dif = 0
|
_ref_dif = 0
|
||||||
_last_cycle = 0
|
_dir = 0
|
||||||
_last_progress = 0
|
_rawdir = 0
|
||||||
_step = 0
|
_step = 0
|
||||||
|
|
||||||
def initModule(self):
|
def initModule(self):
|
||||||
|
if self.pressure:
|
||||||
|
self.pressure.addCallback('value', self.update_from_pressure)
|
||||||
|
if self.flow_sensor:
|
||||||
|
self.flow_sensor.addCallback('value', self.update_from_flow)
|
||||||
super().initModule()
|
super().initModule()
|
||||||
if self.flow_pressure:
|
|
||||||
self.flow_pressure.addCallback('value', self.update_flow_pressure)
|
def update_from_flow(self, value):
|
||||||
if self.flow:
|
if not self.use_pressure:
|
||||||
self.flow.addCallback('value', self.update_flow)
|
self.value = value
|
||||||
self.write_tolerance(self.tolerance)
|
# self.cycle_machine()
|
||||||
|
|
||||||
|
def update_from_pressure(self, value):
|
||||||
|
if self.use_pressure:
|
||||||
|
self.value = value * self.lnm_per_mbar
|
||||||
|
# self.cycle_machine()
|
||||||
|
|
||||||
|
# def doPoll(self):
|
||||||
|
# """only the updates should trigger the machine"""
|
||||||
|
|
||||||
|
def read_value(self):
|
||||||
|
p = self.pressure.read_value() * self.lnm_per_mbar
|
||||||
|
f = self.flow_sensor.read_value()
|
||||||
|
# self.log.info('p %g f %g +- %.2g', p, f, self.flow_sensor.stddev)
|
||||||
|
self.read_motor_state()
|
||||||
|
return p if self.use_pressure else f
|
||||||
|
|
||||||
def write_tolerance(self, tolerance):
|
def write_tolerance(self, tolerance):
|
||||||
if hasattr(self.flow_pressure, 'tolerance'):
|
if hasattr(self.pressure, 'tolerance'):
|
||||||
self.flow_pressure.tolerance = tolerance / self.lnm_per_mbar
|
self.pressure.tolerance = tolerance / self.lnm_per_mbar
|
||||||
if hasattr(self.flow, 'tolerance'):
|
if hasattr(self.flow_sensor, 'tolerance'):
|
||||||
self.flow.tolerance = tolerance
|
self.flow_sensor.tolerance = tolerance
|
||||||
|
|
||||||
def read_use_pressure(self):
|
def read_use_pressure(self):
|
||||||
if self.flow_pressure:
|
if self.pressure:
|
||||||
if self.flow:
|
if self.flow_sensor:
|
||||||
return self.use_pressure
|
return self.use_pressure
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def update_flow(self, value):
|
|
||||||
if not self.use_pressure:
|
|
||||||
self.value = value
|
|
||||||
self.doPoll()
|
|
||||||
|
|
||||||
def update_flow_pressure(self, value):
|
|
||||||
if self.use_pressure:
|
|
||||||
self.value = value * self.lnm_per_mbar
|
|
||||||
self.doPoll()
|
|
||||||
|
|
||||||
def write_target(self, value):
|
def write_target(self, value):
|
||||||
self.start_machine(self.controlling, in_tol_time=0,
|
self.start_machine(self.change_target, fast_poll=1)
|
||||||
ref_time=0, ref_dif=0, prev_dif=0)
|
|
||||||
|
|
||||||
@status_code(BUSY)
|
@status_code(BUSY)
|
||||||
def unblock_from_open(self, state):
|
def change_target(self, state):
|
||||||
self.motor_state = self.command('fm')
|
state.in_tol_time = 0
|
||||||
if self.motor_state == 'opened':
|
state.last_minstep = {}
|
||||||
self.command(mp=-60)
|
state.last_progress = state.now
|
||||||
return Retry
|
state.ref_time = 0
|
||||||
if self.motor_state == 'closing':
|
state.ref_dif = 0
|
||||||
return Retry
|
state.prev_dif = 0 # used?
|
||||||
if self.motor_state == 'closed':
|
state.last_close_time = 0
|
||||||
if self.value > max(1, self.target):
|
state.last_pulse_time = 0
|
||||||
return Retry
|
state.raw_fact = 1
|
||||||
state.flow_before = self.value
|
state.raw_step = 0
|
||||||
state.wiggle = 1
|
if abs(self.target - self.value) < self._tolerance():
|
||||||
state.start_wiggle = state.now
|
return self.at_target
|
||||||
self.command(mp=60)
|
return self.raw_control
|
||||||
return self.unblock_open
|
|
||||||
return self.approaching
|
def start_direction(self, state):
|
||||||
|
if self.target > self.value:
|
||||||
|
self._dir = 1
|
||||||
|
state.minstep = self.min_open_step
|
||||||
|
else:
|
||||||
|
self._dir = -1
|
||||||
|
state.minstep = self.min_close_step
|
||||||
|
state.prev = []
|
||||||
|
|
||||||
|
def perform_pulse(self, state):
|
||||||
|
tol = self._tolerance()
|
||||||
|
dif = self.target - self.value
|
||||||
|
difdir = dif * self._dir
|
||||||
|
state.last_pulse_time = state.now
|
||||||
|
if difdir > tol:
|
||||||
|
step = state.minstep + (difdir - tol) * self.prop
|
||||||
|
elif difdir > 0:
|
||||||
|
step = state.minstep * difdir / tol
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
self.log.info('MP %g dif=%g tol=%g', step * self._dir, dif, tol)
|
||||||
|
self.command(mp=clamp(-1, step * self._dir, 1))
|
||||||
|
|
||||||
@status_code(BUSY)
|
@status_code(BUSY)
|
||||||
def unblock_open(self, state):
|
def raw_control(self, state):
|
||||||
self.motor_state = self.command('fm')
|
tol = self._tolerance()
|
||||||
if self.value < state.flow_before:
|
if state.init:
|
||||||
state.flow_before_open = self.value
|
self.start_direction(state)
|
||||||
elif self.value > state.flow_before + 1:
|
state.raw_step = self.raw_open_step if self._dir > 0 else -self.raw_close_step
|
||||||
state.wiggle = -state.wiggle / 2
|
state.raw_fact = 1
|
||||||
self.command(mp=state.wiggle)
|
# if self.read_motor_state() == M.closed:
|
||||||
state.start_wiggle = state.now
|
# # TODO: also check for flow near lower limit ? but only once after change_target
|
||||||
return self.unblock_close
|
# self.log.info('start with fast opening')
|
||||||
if self.motor_state == 'opening':
|
# state.raw_step = 1
|
||||||
|
# self._dir = 1
|
||||||
|
difdir = (self.target - self.value) * self._dir
|
||||||
|
state.prev.append(difdir)
|
||||||
|
state.diflim = max(0, difdir - tol * 1)
|
||||||
|
state.success = 0
|
||||||
|
self.command(mp=state.raw_step)
|
||||||
|
self.log.info('first rawstep %g', state.raw_step)
|
||||||
|
state.last_pulse_time = state.now
|
||||||
|
state.raw_pulse_cnt = 0
|
||||||
|
state.cycle_cnt = 0
|
||||||
return Retry
|
return Retry
|
||||||
if self.motor_state == 'idle':
|
difdir = (self.target - self.value) * self._dir
|
||||||
self.command(mp=state.wiggle)
|
state.cycle_cnt += 1
|
||||||
|
state.prev.append(difdir)
|
||||||
|
del state.prev[:-5]
|
||||||
|
if state.prev[-1] > max(state.prev[:-1]):
|
||||||
|
# TODO: use the amount of overshoot to reduce the raw_step
|
||||||
|
state.cycle_cnt = 0
|
||||||
|
self.log.info('difference is increasing %s', ' '.join(f'{v:g}' for v in state.prev))
|
||||||
return Retry
|
return Retry
|
||||||
if self.motor_state == 'opened':
|
if state.cycle_cnt >= 5:
|
||||||
if state.now < state.start_wiggle + 20:
|
state.cycle_cnt = 0
|
||||||
return Retry
|
state.diflim = max(tol, min(state.prev) - tol * 0.5)
|
||||||
return self.final_status(ERROR, 'can not open')
|
state.raw_pulse_cnt += 1
|
||||||
|
self.command(mp=state.raw_step * state.raw_fact)
|
||||||
|
self.log.info('rawstep %g', state.raw_step)
|
||||||
|
if state.raw_pulse_cnt % 5 == 0 and state.raw_pulse_cnt > 5:
|
||||||
|
state.raw_fact *= 1.25
|
||||||
|
return Retry
|
||||||
|
if difdir >= state.diflim:
|
||||||
|
state.success = max(0, state.success - 1)
|
||||||
|
return Retry
|
||||||
|
state.success += 1
|
||||||
|
if state.success <= 3:
|
||||||
|
return Retry
|
||||||
|
if state.raw_pulse_cnt < 3:
|
||||||
|
state.raw_fact = 1 - (3 - state.raw_pulse_cnt) ** 2 * 0.05
|
||||||
|
if state.raw_fact != 1:
|
||||||
|
if self._dir > 0:
|
||||||
|
self.raw_open_step *= state.raw_fact
|
||||||
|
self.log.info('raw_open_step %g f=%g', self.raw_open_step, state.raw_fact)
|
||||||
|
self.min_open_pulse = min(self.min_open_pulse, self.raw_open_step)
|
||||||
|
else:
|
||||||
|
self.raw_close_step *= state.raw_fact
|
||||||
|
self.log.info('raw_close_step %g f=%g', self.raw_close_step, state.raw_fact)
|
||||||
|
self.min_close_pulse = min(self.min_close_pulse, self.raw_close_step)
|
||||||
return self.controlling
|
return self.controlling
|
||||||
|
|
||||||
|
# @status_code(BUSY)
|
||||||
|
# def raw_control(self, state):
|
||||||
|
# tol = self._tolerance()
|
||||||
|
# if state.init:
|
||||||
|
# self.start_direction(state)
|
||||||
|
# if self._dir != self._rawdir:
|
||||||
|
# self._rawdir = self._dir
|
||||||
|
# state.first_step = self.first_open_step if self._dir > 0 else -self.first_close_step
|
||||||
|
# else:
|
||||||
|
# state.first_step = 0
|
||||||
|
# state.first_fact = 1
|
||||||
|
# # if self.read_motor_state() == M.closed:
|
||||||
|
# # # TODO: also check for flow near lower limit ? but only once after change_target
|
||||||
|
# # self.log.info('start with fast opening')
|
||||||
|
# # state.first_step = 1
|
||||||
|
# # self._dir = 1
|
||||||
|
# difdir = (self.target - self.value) * self._dir
|
||||||
|
# state.prev = [difdir]
|
||||||
|
# state.diflim = max(0, difdir - tol * 0.5)
|
||||||
|
# state.success = 0
|
||||||
|
# if state.first_step:
|
||||||
|
# self.command(mp=state.first_step)
|
||||||
|
# else:
|
||||||
|
# self.perform_pulse(state)
|
||||||
|
# self.log.info('firststep %g', state.first_step)
|
||||||
|
# state.last_pulse_time = state.now
|
||||||
|
# state.raw_pulse_cnt = 0
|
||||||
|
# return Retry
|
||||||
|
# difdir = (self.target - self.value) * self._dir
|
||||||
|
# if state.delta(5):
|
||||||
|
# state.diflim = max(0, min(state.prev) - tol * 0.1)
|
||||||
|
# state.prev = [difdir]
|
||||||
|
# state.raw_pulse_cnt += 1
|
||||||
|
# if state.first_step and state.raw_pulse_cnt % 10 == 0:
|
||||||
|
# self.command(mp=state.first_step * state.first_fact)
|
||||||
|
# self.log.info('repeat firststep %g', state.first_step * state.first_fact)
|
||||||
|
# state.first_fact *= 1.25
|
||||||
|
# else:
|
||||||
|
# self.perform_pulse(state)
|
||||||
|
# return Retry
|
||||||
|
# state.prev.append(difdir)
|
||||||
|
# if difdir >= state.diflim:
|
||||||
|
# state.success = max(0, state.success - 1)
|
||||||
|
# return Retry
|
||||||
|
# state.success += 1
|
||||||
|
# if state.success <= 5:
|
||||||
|
# return Retry
|
||||||
|
# if state.first_step:
|
||||||
|
# if state.raw_pulse_cnt < 3:
|
||||||
|
# state.first_fact = 1 - (3 - state.raw_pulse_cnt) ** 2 * 0.04
|
||||||
|
# if state.first_fact != 1:
|
||||||
|
# if self._dir > 0:
|
||||||
|
# self.first_open_step *= state.first_fact
|
||||||
|
# self.log.info('first_open_step %g f=%g', self.first_open_step, state.first_fact)
|
||||||
|
# else:
|
||||||
|
# self.first_close_step *= state.first_fact
|
||||||
|
# self.log.info('first_close_step %g f=%g', self.first_close_step, state.first_fact)
|
||||||
|
# return self.controlling
|
||||||
|
|
||||||
@status_code(BUSY)
|
@status_code(BUSY)
|
||||||
def unblock_close(self, state):
|
def controlling(self, state):
|
||||||
self.motor_state = self.command('fm')
|
dif = self.target - self.value
|
||||||
if self.value > state.flow_before:
|
if state.init:
|
||||||
state.flow_before_open = self.value
|
self.start_direction(state)
|
||||||
elif self.value < state.flow_before - 1:
|
state.ref_dif = abs(dif)
|
||||||
if state.wiggle < self.prop * 2:
|
state.ref_time = state.now
|
||||||
return self.final_status(IDLE, '')
|
state.in_tol_time = 0
|
||||||
state.wiggle = -state.wiggle / 2
|
difdir = dif * self._dir # negative when overshoot happend
|
||||||
self.command(mp=state.wiggle)
|
# difdif = dif - state.prev_dif
|
||||||
state.start_wiggle = state.now
|
# state.prev_dif = dif
|
||||||
return self.unblock_open
|
expected_dif = state.ref_dif * math.exp((state.ref_time - state.now) / self.deriv)
|
||||||
if self.motor_state == 'closing':
|
|
||||||
|
tol = self._tolerance()
|
||||||
|
if difdir < tol:
|
||||||
|
# prev_minstep = state.last_minstep.pop(self._dir, None)
|
||||||
|
# attr = 'min_open_step' if self._dir > 0 else 'min_close_step'
|
||||||
|
# if prev_minstep is not None:
|
||||||
|
# # increase minstep
|
||||||
|
# minstep = getattr(self, attr)
|
||||||
|
# setattr(self, attr, minstep * 1.1)
|
||||||
|
# self.log.info('increase %s to %g', attr, minstep)
|
||||||
|
if difdir > -tol: # within tolerance
|
||||||
|
delta = state.delta()
|
||||||
|
state.in_tol_time += delta
|
||||||
|
if state.in_tol_time > self.settle:
|
||||||
|
# state.last_minstep.pop(self._dir, None)
|
||||||
|
self.log.info('at target %g %g', dif, tol)
|
||||||
|
return self.at_target
|
||||||
|
if difdir < 0:
|
||||||
|
return Retry
|
||||||
|
# self.log.info('minstep=0 dif=%g', dif)
|
||||||
|
else: # overshoot
|
||||||
|
self.log.info('overshoot %g', dif)
|
||||||
|
return self.raw_control
|
||||||
|
# # overshoot
|
||||||
|
# prev_minstep = state.last_minstep.pop(self._dir, None)
|
||||||
|
# if prev_minstep is None:
|
||||||
|
# minstep = getattr(self, attr) * 0.9
|
||||||
|
# self.log.info('decrease %s to %g', attr, minstep)
|
||||||
|
# setattr(self, attr, minstep)
|
||||||
|
# self.start_step(state, self.target)
|
||||||
|
# still approaching
|
||||||
|
if difdir <= expected_dif:
|
||||||
|
if difdir < expected_dif / 1.25 - tol:
|
||||||
|
state.ref_time = state.now
|
||||||
|
state.ref_dif = (difdir + tol) * 1.25
|
||||||
|
# self.log.info('new ref %g', state.ref_dif)
|
||||||
|
state.last_progress = state.now
|
||||||
|
return Retry # progressing: no pulse needed
|
||||||
|
if state.now < state.last_pulse_time + 2.5:
|
||||||
return Retry
|
return Retry
|
||||||
if self.motor_state == 'idle':
|
# TODO: check motor state for closed / opened ?
|
||||||
self.command(mp=state.wiggle)
|
self.perform_pulse(state)
|
||||||
return Retry
|
return Retry
|
||||||
if self.motor_state == 'closed':
|
|
||||||
if state.now < state.start_wiggle + 20:
|
|
||||||
return Retry
|
|
||||||
return self.final_status(ERROR, 'can not close')
|
|
||||||
return self.final_status(WARN, 'unblock interrupted')
|
|
||||||
|
|
||||||
def _tolerance(self):
|
def _tolerance(self):
|
||||||
return min(self.tolerance * min(1, self.value / 2), self.tolerance2)
|
return min(self.tolerance * min(1, self.value / 2), self.tolerance2)
|
||||||
@ -422,50 +609,67 @@ class NeedleValve(HasStates, CCU4Base, Drivable):
|
|||||||
dif = self.target - self.value
|
dif = self.target - self.value
|
||||||
if abs(dif) > self._tolerance():
|
if abs(dif) > self._tolerance():
|
||||||
state.in_tol_time = 0
|
state.in_tol_time = 0
|
||||||
|
self.log.info('unstable %g', dif)
|
||||||
return self.unstable
|
return self.unstable
|
||||||
return Retry
|
return Retry
|
||||||
|
|
||||||
@status_code(IDLE, 'unstable')
|
@status_code(IDLE, 'unstable')
|
||||||
def unstable(self, state):
|
def unstable(self, state):
|
||||||
|
difdir = (self.target - self.value) * self._dir
|
||||||
|
if difdir < 0 or self._dir == 0:
|
||||||
|
return self.raw_control
|
||||||
return self.controlling(state)
|
return self.controlling(state)
|
||||||
|
|
||||||
|
def read_motor_state(self):
|
||||||
|
return self.command(fm=int)
|
||||||
|
|
||||||
|
@Command
|
||||||
|
def close(self):
|
||||||
|
"""close valve fully"""
|
||||||
|
self.command(mp=-60)
|
||||||
|
self.motor_state = self.command(fm=int)
|
||||||
|
self.start_machine(self.closing)
|
||||||
|
|
||||||
@status_code(BUSY)
|
@status_code(BUSY)
|
||||||
def controlling(self, state):
|
def closing(self, state):
|
||||||
delta = state.delta(0)
|
if state.init:
|
||||||
dif = self.target - self.value
|
state.start_time = state.now
|
||||||
difdif = dif - state.prev_dif
|
self.read_motor_state()
|
||||||
state.prev_dif = dif
|
if self.motor_state == M.closing:
|
||||||
self.motor_state = self.command('fm')
|
|
||||||
if self.motor_state == 'closed':
|
|
||||||
if dif < 0 or difdif < 0:
|
|
||||||
return Retry
|
|
||||||
return self.unblock_from_open
|
|
||||||
elif self.motor_state == 'opened': # trigger also when flow too high?
|
|
||||||
if dif > 0 or difdif > 0:
|
|
||||||
return Retry
|
|
||||||
self.command(mp=-60)
|
|
||||||
return self.unblock_from_open
|
|
||||||
|
|
||||||
tolerance = self._tolerance()
|
|
||||||
if abs(dif) < tolerance:
|
|
||||||
state.in_tol_time += delta
|
|
||||||
if state.in_tol_time > self.settle:
|
|
||||||
return self.at_target
|
|
||||||
return Retry
|
return Retry
|
||||||
expected_dif = state.ref_dif * math.exp((state.now - state.ref_time) / self.deriv)
|
if self.motor_state == M.closed:
|
||||||
if abs(dif) < expected_dif:
|
return self.final_status(IDLE, 'closed')
|
||||||
if abs(dif) < expected_dif / 1.25:
|
if state.now < state.start_time + 1:
|
||||||
state.ref_time = state.now
|
|
||||||
state.ref_dif = abs(dif) * 1.25
|
|
||||||
state.last_progress = state.now
|
|
||||||
return Retry # progress is fast enough
|
|
||||||
state.ref_time = state.now
|
|
||||||
state.ref_dif = abs(dif)
|
|
||||||
state.step += dif * delta * self.prop
|
|
||||||
if abs(state.step) < (state.now - state.last_progress) / self.step_factor:
|
|
||||||
# wait until step size is big enough
|
|
||||||
return Retry
|
return Retry
|
||||||
self.command(mp=state.step)
|
return self.final_status(IDLE, 'fixed')
|
||||||
return Retry
|
|
||||||
|
|
||||||
|
@Command
|
||||||
|
def open(self):
|
||||||
|
"""open valve fully"""
|
||||||
|
self.command(mp=60)
|
||||||
|
self.read_motor_state()
|
||||||
|
self.start_machine(self.opening)
|
||||||
|
|
||||||
|
@status_code(BUSY)
|
||||||
|
def opening(self, state):
|
||||||
|
if state.init:
|
||||||
|
state.start_time = state.now
|
||||||
|
self.read_motor_state()
|
||||||
|
if self.motor_state == M.opening:
|
||||||
|
return Retry
|
||||||
|
if self.motor_state == M.opened:
|
||||||
|
return self.final_status(IDLE, 'opened')
|
||||||
|
if state.now < state.start_time + 1:
|
||||||
|
return Retry
|
||||||
|
return self.final_status(IDLE, 'fixed')
|
||||||
|
|
||||||
|
@Command(FloatRange())
|
||||||
|
def pulse(self, value):
|
||||||
|
"""perform a motor pulse"""
|
||||||
|
self.log.info('pulse %g', value)
|
||||||
|
self.command(mp=value)
|
||||||
|
if value > 0:
|
||||||
|
self.motor_state = M.opening
|
||||||
|
return self.opening
|
||||||
|
self.motor_state = M.closing
|
||||||
|
return self.closing
|
||||||
|
@ -56,7 +56,7 @@ class Drums(Writable):
|
|||||||
self._pos = 0
|
self._pos = 0
|
||||||
for i, action in enumerate(self.pattern[self._pos:]):
|
for i, action in enumerate(self.pattern[self._pos:]):
|
||||||
upper = action.upper()
|
upper = action.upper()
|
||||||
relais = self.actions.get(action.upper())
|
relais = self.actions.get(upper)
|
||||||
if relais:
|
if relais:
|
||||||
relais.write_target(upper == action) # True when capital letter
|
relais.write_target(upper == action) # True when capital letter
|
||||||
else:
|
else:
|
||||||
|
71
frappy_psi/hepump.py
Normal file
71
frappy_psi/hepump.py
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
# *****************************************************************************
|
||||||
|
#
|
||||||
|
# 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>
|
||||||
|
#
|
||||||
|
# *****************************************************************************
|
||||||
|
|
||||||
|
from frappy.core import BoolType, FloatRange, Parameter, Readable, Writable, Attached, EnumType, nopoll
|
||||||
|
from frappy_psi.trinamic import Motor
|
||||||
|
from frappy_psi.ccu4 import Pressure, NeedleValveFlow
|
||||||
|
|
||||||
|
|
||||||
|
class ValveMotor(Motor):
|
||||||
|
has_inputs = True
|
||||||
|
|
||||||
|
|
||||||
|
class HePump(Writable):
|
||||||
|
valvemotor = Attached(Motor)
|
||||||
|
flow = Attached(NeedleValveFlow)
|
||||||
|
valve = Attached(Writable)
|
||||||
|
value = Parameter(datatype=BoolType())
|
||||||
|
target = Parameter(datatype=BoolType())
|
||||||
|
pump_type = Parameter('pump type', EnumType(no=0, neodry=1, xds35=2, sv65=3), readonly=False, default=0)
|
||||||
|
eco_mode = Parameter('eco mode', BoolType(), readonly=False)
|
||||||
|
has_feedback = Parameter('feedback works', BoolType(), readonly=False, default=True)
|
||||||
|
|
||||||
|
FLOW_SCALE = {'no': 0, 'neodry': 0.55, 'xds35': 0.6, 'sv65': 0.9}
|
||||||
|
|
||||||
|
def write_target(self, value):
|
||||||
|
self.valvemotor.write_output0(value)
|
||||||
|
|
||||||
|
def read_target(self):
|
||||||
|
return self.valvemotor.read_output0()
|
||||||
|
|
||||||
|
def read_value(self):
|
||||||
|
if self.has_feedback:
|
||||||
|
return not self.valvemotor.read_input3()
|
||||||
|
return self.target
|
||||||
|
|
||||||
|
def write_pump_type(self, value):
|
||||||
|
self.flow.pressure_scale = self.FLOW_SCALE[value.name]
|
||||||
|
|
||||||
|
def read_eco_mode(self):
|
||||||
|
if self.pump_type == 'xds35':
|
||||||
|
return self.valvemotor.read_output1()
|
||||||
|
return False
|
||||||
|
|
||||||
|
def write_eco_mode(self, value):
|
||||||
|
if self.pump_type == 'xds35':
|
||||||
|
return self.valvemotor.write_output1(value)
|
||||||
|
# else silently ignore
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -18,16 +18,39 @@
|
|||||||
# Jael Celia Lorenzana <jael-celia.lorenzana@psi.ch>
|
# Jael Celia Lorenzana <jael-celia.lorenzana@psi.ch>
|
||||||
# *****************************************************************************
|
# *****************************************************************************
|
||||||
|
|
||||||
|
import os
|
||||||
|
from glob import glob
|
||||||
from frappy.core import Readable, Writable, Parameter, BoolType, StringType,\
|
from frappy.core import Readable, Writable, Parameter, BoolType, StringType,\
|
||||||
FloatRange, Property, TupleOf, ERROR, IDLE
|
FloatRange, Property, TupleOf, ERROR, IDLE
|
||||||
|
from frappy.errors import ConfigError
|
||||||
from math import log
|
from math import log
|
||||||
|
|
||||||
|
basepaths = '/sys/class/ionopimax', '/sys/class/ionopi'
|
||||||
|
|
||||||
|
|
||||||
class Base:
|
class Base:
|
||||||
addr = Property('address', StringType())
|
addr = Property('address', StringType())
|
||||||
|
_devpath = None
|
||||||
|
devclass = None
|
||||||
|
|
||||||
|
def initModule(self):
|
||||||
|
super().initModule()
|
||||||
|
# candidates = glob(f'/sys/class/iono*/*/{self.addr}')
|
||||||
|
# if not candidates:
|
||||||
|
# raise ConfigError(f'can not find path for {self.addr}')
|
||||||
|
for basepath in basepaths:
|
||||||
|
for devclass in ([self.devclass] if isinstance(self.devclass, str) else self.devclass):
|
||||||
|
devpath = f'{basepath}/{devclass}'
|
||||||
|
if os.path.exists(devpath):
|
||||||
|
self._devpath = devpath
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
self.log.info('%s does not exist', devpath)
|
||||||
|
else:
|
||||||
|
raise ConfigError(f'device path for {self.devclass} not found {devpath}')
|
||||||
|
|
||||||
def read(self, addr, scale=None):
|
def read(self, addr, scale=None):
|
||||||
with open(f'/sys/class/ionopimax/{self.devclass}/{addr}') as f:
|
with open(f'{self._devpath}/{addr}') as f:
|
||||||
result = f.read()
|
result = f.read()
|
||||||
if scale:
|
if scale:
|
||||||
return float(result) / scale
|
return float(result) / scale
|
||||||
@ -35,7 +58,7 @@ class Base:
|
|||||||
|
|
||||||
def write(self, addr, value, scale=None):
|
def write(self, addr, value, scale=None):
|
||||||
value = str(round(value * scale)) if scale else str(value)
|
value = str(round(value * scale)) if scale else str(value)
|
||||||
with open(f'/sys/class/ionopimax/{self.devclass}/{addr}', 'w') as f:
|
with open(f'{self._devpath}/{addr}', 'w') as f:
|
||||||
f.write(value)
|
f.write(value)
|
||||||
|
|
||||||
|
|
||||||
@ -49,7 +72,7 @@ class DigitalInput(Base, Readable):
|
|||||||
|
|
||||||
class DigitalOutput(DigitalInput, Writable):
|
class DigitalOutput(DigitalInput, Writable):
|
||||||
target = Parameter('output state', BoolType(), readonly=False)
|
target = Parameter('output state', BoolType(), readonly=False)
|
||||||
devclass = 'digital_out'
|
devclass = 'digital_out', 'relay'
|
||||||
|
|
||||||
def write_target(self, value):
|
def write_target(self, value):
|
||||||
self.write(self.addr, value, 1)
|
self.write(self.addr, value, 1)
|
||||||
|
@ -1,16 +1,65 @@
|
|||||||
"""
|
# *****************************************************************************
|
||||||
Created on Tue Feb 4 11:07:56 2020
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify it under
|
||||||
@author: tartarotti_d-adm
|
# 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>
|
||||||
|
#
|
||||||
|
# *****************************************************************************
|
||||||
|
"""support for ultrasound plot clients"""
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import matplotlib.pyplot as plt
|
import matplotlib.pyplot as plt
|
||||||
|
# disable the behaviour of raising the window to the front each time it is updated
|
||||||
|
plt.rcParams["figure.raise_window"] = False
|
||||||
|
|
||||||
|
|
||||||
|
NAN = float('nan')
|
||||||
|
|
||||||
|
|
||||||
|
class Pause:
|
||||||
|
"""allows to leave the plot loop when the window is closed
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
pause = Pause(fig)
|
||||||
|
|
||||||
|
# do initial plots
|
||||||
|
plt.show()
|
||||||
|
while pause(0.5):
|
||||||
|
# do plot updates
|
||||||
|
plt.draw()
|
||||||
|
"""
|
||||||
|
def __init__(self, fig):
|
||||||
|
fig.canvas.mpl_connect('close_event', self.on_close)
|
||||||
|
self.running = True
|
||||||
|
|
||||||
|
def on_close(self, event):
|
||||||
|
self.running = False
|
||||||
|
|
||||||
|
def __call__(self, interval):
|
||||||
|
try:
|
||||||
|
plt.pause(interval)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
return self.running
|
||||||
|
|
||||||
|
|
||||||
def rect(x1, x2, y1, y2):
|
def rect(x1, x2, y1, y2):
|
||||||
return np.array([[x1,x2,x2,x1,x1],[y1,y1,y2,y2,y1]])
|
return np.array([[x1,x2,x2,x1,x1],[y1,y1,y2,y2,y1]])
|
||||||
|
|
||||||
NAN = float('nan')
|
|
||||||
|
|
||||||
def rects(intervals, y12):
|
def rects(intervals, y12):
|
||||||
result = [rect(*intervals[0], *y12)]
|
result = [rect(*intervals[0], *y12)]
|
||||||
@ -19,6 +68,7 @@ def rects(intervals, y12):
|
|||||||
result.append(rect(*x12, *y12))
|
result.append(rect(*x12, *y12))
|
||||||
return np.concatenate(result, axis=1)
|
return np.concatenate(result, axis=1)
|
||||||
|
|
||||||
|
|
||||||
class Plot:
|
class Plot:
|
||||||
def __init__(self, maxy):
|
def __init__(self, maxy):
|
||||||
self.lines = {}
|
self.lines = {}
|
||||||
@ -26,6 +76,10 @@ class Plot:
|
|||||||
self.first = True
|
self.first = True
|
||||||
self.fig = None
|
self.fig = None
|
||||||
|
|
||||||
|
def pause(self, interval):
|
||||||
|
"""will be overridden when figure is created"""
|
||||||
|
return False
|
||||||
|
|
||||||
def set_line(self, iax, name, data, fmt, **kwds):
|
def set_line(self, iax, name, data, fmt, **kwds):
|
||||||
"""
|
"""
|
||||||
plot or update a line
|
plot or update a line
|
||||||
@ -68,6 +122,7 @@ class Plot:
|
|||||||
if self.first:
|
if self.first:
|
||||||
plt.ion()
|
plt.ion()
|
||||||
self.fig, axleft = plt.subplots(figsize=(15,7))
|
self.fig, axleft = plt.subplots(figsize=(15,7))
|
||||||
|
self.pause = Pause(self.fig)
|
||||||
plt.title("I/Q", fontsize=14)
|
plt.title("I/Q", fontsize=14)
|
||||||
axleft.set_xlim(0, curves[0][-1])
|
axleft.set_xlim(0, curves[0][-1])
|
||||||
self.ax = [axleft, axleft.twinx()]
|
self.ax = [axleft, axleft.twinx()]
|
||||||
@ -97,5 +152,6 @@ class Plot:
|
|||||||
self.first = False
|
self.first = False
|
||||||
|
|
||||||
plt.draw()
|
plt.draw()
|
||||||
|
# TODO: do not know why this is needed:
|
||||||
self.fig.canvas.draw()
|
self.fig.canvas.draw()
|
||||||
self.fig.canvas.flush_events()
|
self.fig.canvas.flush_events()
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -63,7 +63,12 @@ def parse_result(reply):
|
|||||||
|
|
||||||
class LakeShoreIO(HasIO):
|
class LakeShoreIO(HasIO):
|
||||||
def set_param(self, cmd, *args):
|
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(' ', '?')
|
tail = cmd.replace(' ', '?')
|
||||||
reply = self.io.communicate(f'{head};{tail}')
|
reply = self.io.communicate(f'{head};{tail}')
|
||||||
return parse_result(reply)
|
return parse_result(reply)
|
||||||
@ -99,7 +104,7 @@ class Switcher(LakeShoreIO, ChannelSwitcher):
|
|||||||
if channelno is None:
|
if channelno is None:
|
||||||
self.status = 'ERROR', 'no enabled channel'
|
self.status = 'ERROR', 'no enabled channel'
|
||||||
return
|
return
|
||||||
self.set_param(f'SCAN {channelno},0')
|
self.set_param('SCAN ', channelno, 0)
|
||||||
|
|
||||||
def doPoll(self):
|
def doPoll(self):
|
||||||
"""poll buttons
|
"""poll buttons
|
||||||
@ -160,7 +165,7 @@ class Switcher(LakeShoreIO, ChannelSwitcher):
|
|||||||
self.measure_delay = chan.dwell
|
self.measure_delay = chan.dwell
|
||||||
|
|
||||||
def set_active_channel(self, chan):
|
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()
|
chan._last_range_change = time.monotonic()
|
||||||
self.set_delays(chan)
|
self.set_delays(chan)
|
||||||
|
|
||||||
@ -227,7 +232,7 @@ class ResChannel(LakeShoreIO, Channel):
|
|||||||
now = time.monotonic()
|
now = time.monotonic()
|
||||||
if now + 0.5 < max(self._last_range_change, self.switcher._start_switch) + self.pause:
|
if now + 0.5 < max(self._last_range_change, self.switcher._start_switch) + self.pause:
|
||||||
return None
|
return None
|
||||||
result = self.get_param(f'RDGR{self.channel}')
|
result = self.get_param(f'RDGR?{self.channel}')
|
||||||
if self.autorange:
|
if self.autorange:
|
||||||
self.fix_autorange()
|
self.fix_autorange()
|
||||||
if now + 0.5 > self._last_range_change + self.pause:
|
if now + 0.5 > self._last_range_change + self.pause:
|
||||||
@ -251,7 +256,7 @@ class ResChannel(LakeShoreIO, Channel):
|
|||||||
|
|
||||||
def read_value(self):
|
def read_value(self):
|
||||||
if self.channel == self.switcher.value == self.switcher.target:
|
if self.channel == self.switcher.value == self.switcher.target:
|
||||||
value = self._read_value()
|
value = self.get_value()
|
||||||
if value is not None:
|
if value is not None:
|
||||||
return value
|
return value
|
||||||
return self.value # return previous value
|
return self.value # return previous value
|
||||||
@ -264,7 +269,7 @@ class ResChannel(LakeShoreIO, Channel):
|
|||||||
|
|
||||||
@CommonReadHandler(rdgrng_params)
|
@CommonReadHandler(rdgrng_params)
|
||||||
def read_rdgrng(self):
|
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
|
self._prev_rdgrng = iscur, exc
|
||||||
if autorange: # pressed autorange button
|
if autorange: # pressed autorange button
|
||||||
if not self._toggle_autorange:
|
if not self._toggle_autorange:
|
||||||
@ -273,7 +278,12 @@ class ResChannel(LakeShoreIO, Channel):
|
|||||||
vexc = 0 if excoff or iscur else exc
|
vexc = 0 if excoff or iscur else exc
|
||||||
if (rng, iexc, vexc) != (self.range, self.iexc, self.vexc):
|
if (rng, iexc, vexc) != (self.range, self.iexc, self.vexc):
|
||||||
self._last_range_change = time.monotonic()
|
self._last_range_change = time.monotonic()
|
||||||
self.range, self.iexc, self.vexc = rng, iexc, vexc
|
try:
|
||||||
|
self.range, self.iexc, self.vexc = rng, iexc, vexc
|
||||||
|
except Exception:
|
||||||
|
# avoid raising errors on disabled channel
|
||||||
|
if self.enabled:
|
||||||
|
raise
|
||||||
|
|
||||||
@CommonWriteHandler(rdgrng_params)
|
@CommonWriteHandler(rdgrng_params)
|
||||||
def write_rdgrng(self, change):
|
def write_rdgrng(self, change):
|
||||||
@ -293,8 +303,7 @@ class ResChannel(LakeShoreIO, Channel):
|
|||||||
excoff = 1
|
excoff = 1
|
||||||
rng = change['range']
|
rng = change['range']
|
||||||
if self.autorange:
|
if self.autorange:
|
||||||
if rng < self.minrange:
|
rng = max(rng, self.minrange)
|
||||||
rng = self.minrange
|
|
||||||
self.set_param(f'RDGRNG {self.channel}', iscur, exc, rng, 0, excoff)
|
self.set_param(f'RDGRNG {self.channel}', iscur, exc, rng, 0, excoff)
|
||||||
self.read_range()
|
self.read_range()
|
||||||
|
|
||||||
|
@ -16,84 +16,38 @@
|
|||||||
# Module authors:
|
# Module authors:
|
||||||
# Markus Zolliker <markus.zolliker@psi.ch>
|
# 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
|
from frappy.modules import Communicator
|
||||||
|
|
||||||
|
|
||||||
class Ls370Sim(Communicator):
|
class _Ls37xSim(Communicator):
|
||||||
CHANNEL_COMMANDS = [
|
# commands containing %d for the channel number
|
||||||
('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):
|
|
||||||
CHANNEL_COMMANDS = [
|
CHANNEL_COMMANDS = [
|
||||||
('RDGR?%d', '1.0'),
|
('RDGR?%d', '1.0'),
|
||||||
('RDGK?%d', '1.5'),
|
('RDGK?%d', '1.5'),
|
||||||
('RDGST?%d', '0'),
|
('RDGST?%d', '0'),
|
||||||
('RDGRNG?%d', '0,5,5,0,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'),
|
('FILTER?%d', '1,5,80'),
|
||||||
]
|
]
|
||||||
|
# commands not related to a channel
|
||||||
OTHER_COMMANDS = [
|
OTHER_COMMANDS = [
|
||||||
('*IDN?', 'LSCI,MODEL372,372184,05302003'),
|
|
||||||
('SCAN?', '3,1'),
|
('SCAN?', '3,1'),
|
||||||
('PID?1', '10,10,0'),
|
|
||||||
('*OPC?', '1'),
|
('*OPC?', '1'),
|
||||||
]
|
]
|
||||||
|
|
||||||
def earlyInit(self):
|
def earlyInit(self):
|
||||||
super().earlyInit()
|
super().earlyInit()
|
||||||
|
self._res = {}
|
||||||
|
self._start = time.time()
|
||||||
self._data = dict(self.OTHER_COMMANDS)
|
self._data = dict(self.OTHER_COMMANDS)
|
||||||
for fmt, v in self.CHANNEL_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
|
self._data[fmt % chan] = v
|
||||||
|
|
||||||
def communicate(self, command):
|
def communicate(self, command):
|
||||||
@ -105,6 +59,10 @@ class Ls372Sim(Communicator):
|
|||||||
self._data['RDGST?%d' % channel] = '6'
|
self._data['RDGST?%d' % channel] = '6'
|
||||||
else:
|
else:
|
||||||
self._data['RDGST?%d' % channel] = '0'
|
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(';')
|
chunks = command.split(';')
|
||||||
reply = []
|
reply = []
|
||||||
@ -112,7 +70,7 @@ class Ls372Sim(Communicator):
|
|||||||
if '?' in chunk:
|
if '?' in chunk:
|
||||||
reply.append(self._data[chunk])
|
reply.append(self._data[chunk])
|
||||||
else:
|
else:
|
||||||
for nqarg in (1,0):
|
for nqarg in (1, 0):
|
||||||
if nqarg == 0:
|
if nqarg == 0:
|
||||||
qcmd, arg = chunk.split(' ', 1)
|
qcmd, arg = chunk.split(' ', 1)
|
||||||
qcmd += '?'
|
qcmd += '?'
|
||||||
@ -125,3 +83,16 @@ class Ls372Sim(Communicator):
|
|||||||
reply = ';'.join(reply)
|
reply = ';'.join(reply)
|
||||||
self.comLog('< %s' % reply)
|
self.comLog('< %s' % reply)
|
||||||
return 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)
|
self.setFastPoll(True, 1.0)
|
||||||
return self.start_ramp_to_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(BUSY, 'ramping field')
|
@status_code(BUSY, 'ramping field')
|
||||||
def ramp_to_target(self, sm):
|
def ramp_to_target(self, sm):
|
||||||
if sm.init:
|
if sm.init:
|
||||||
@ -324,15 +333,6 @@ class Magfield(SimpleMagfield):
|
|||||||
self._last_target = sm.target
|
self._last_target = sm.target
|
||||||
return self.start_ramp_to_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)
|
@status_code(Status.RAMPING)
|
||||||
def ramp_to_target(self, sm):
|
def ramp_to_target(self, sm):
|
||||||
dif = abs(self.value - sm.target)
|
dif = abs(self.value - sm.target)
|
||||||
|
@ -375,6 +375,7 @@ class HeaterOutput(HasInput, Writable):
|
|||||||
|
|
||||||
class HeaterUpdate(HeaterOutput):
|
class HeaterUpdate(HeaterOutput):
|
||||||
kind = 'HTR,TEMP'
|
kind = 'HTR,TEMP'
|
||||||
|
target = 0 # switch off loop on startup
|
||||||
|
|
||||||
def update_target(self, module, value):
|
def update_target(self, module, value):
|
||||||
self.change(f'DEV::TEMP:LOOP:ENAB', False, off_on)
|
self.change(f'DEV::TEMP:LOOP:ENAB', False, off_on)
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user