Compare commits
57 Commits
Author | SHA1 | Date | |
---|---|---|---|
6d63c4e0df | |||
98fa19ce3b | |||
7f83f76d38 | |||
0ab849d0cf | |||
8ee49caba5 | |||
b1de9218bd | |||
8eaad86b66 | |||
85400a2777 | |||
dda4afbe5b | |||
9b079ddf4b | |||
898da75b89 | |||
a7a846dfba | |||
6da671df62 | |||
bdb14af4af | |||
e57ad9826e | |||
8775103bf8 | |||
![]() |
5636a76152 | ||
![]() |
745cc69e9e | ||
![]() |
b4c0a827f0 | ||
d57416a73e | |||
![]() |
8dcf6ca658 | ||
bc66a314c4 | |||
6fac63d769 | |||
e41692bf2c | |||
dff3bd2f24 | |||
b67e5a9260 | |||
4815f4e6b4 | |||
e8ec9b415a | |||
5b9e36180e | |||
f1b59e4150 | |||
17070ca732 | |||
![]() |
d618fafe4b | ||
![]() |
dd1dfb3094 | ||
8d6617e288 | |||
![]() |
fdec531c99 | ||
a246584c4a | |||
![]() |
00ef174292 | ||
![]() |
ada66f4851 | ||
![]() |
a9be6475b1 | ||
![]() |
f380289a84 | ||
![]() |
528d80652c | ||
![]() |
7c6df58906 | ||
![]() |
1851c0ac43 | ||
880d472a4a | |||
![]() |
25ff96873b | ||
![]() |
82881049c4 | ||
![]() |
60c9737cfe | ||
![]() |
632db924eb | ||
![]() |
261121297b | ||
![]() |
1bd243f3d2 | ||
![]() |
7c3f9f7196 | ||
9074dfda9d | |||
de32eb09e6 | |||
2e97f0f0ce | |||
0b06acf304 | |||
cc90291358 | |||
03b4604643 |
@ -204,6 +204,9 @@ max-statements=150
|
||||
# Maximum number of parents for a class (see R0901).
|
||||
max-parents=20
|
||||
|
||||
# Maximum number of positional arguments
|
||||
max-positional-arguments=10
|
||||
|
||||
# Maximum number of attributes for a class (see R0902).
|
||||
max-attributes=50
|
||||
|
||||
|
@ -23,6 +23,8 @@
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from frappy.lib import generalConfig
|
||||
from frappy.logging import logger
|
||||
|
||||
# Add import path for inplace usage
|
||||
sys.path.insert(0, str(Path(__file__).absolute().parents[1]))
|
||||
@ -30,6 +32,8 @@ sys.path.insert(0, str(Path(__file__).absolute().parents[1]))
|
||||
from frappy.client.interactive import Console
|
||||
from frappy.playground import play, USAGE
|
||||
|
||||
generalConfig.init()
|
||||
logger.init()
|
||||
if len(sys.argv) > 1:
|
||||
play(sys.argv[1])
|
||||
else:
|
||||
|
128
bin/frappy-scan
Executable file
128
bin/frappy-scan
Executable file
@ -0,0 +1,128 @@
|
||||
#!/usr/bin/env python3
|
||||
# *****************************************************************************
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free Software
|
||||
# Foundation; either version 2 of the License, or (at your option) any later
|
||||
# version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#
|
||||
# Module authors:
|
||||
# Alexander Zaft <a.zaft@fz-juelich.de>
|
||||
#
|
||||
# *****************************************************************************
|
||||
|
||||
"""SEC node autodiscovery tool."""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import select
|
||||
import socket
|
||||
import sys
|
||||
from collections import namedtuple
|
||||
from time import time as currenttime
|
||||
|
||||
UDP_PORT = 10767
|
||||
|
||||
Answer = namedtuple('Answer',
|
||||
'address, port, equipment_id, firmware, description')
|
||||
|
||||
|
||||
def decode(msg, addr):
|
||||
msg = msg.decode('utf-8')
|
||||
try:
|
||||
data = json.loads(msg)
|
||||
except Exception:
|
||||
return None
|
||||
if not isinstance(data, dict):
|
||||
return None
|
||||
if data.get('SECoP') != 'node':
|
||||
return None
|
||||
try:
|
||||
eq_id = data['equipment_id']
|
||||
fw = data['firmware']
|
||||
desc = data['description']
|
||||
port = data['port']
|
||||
except KeyError:
|
||||
return None
|
||||
addr, _scanport = addr
|
||||
return Answer(addr, port, eq_id, fw, desc)
|
||||
|
||||
|
||||
def print_answer(answer, *, short=False):
|
||||
if short:
|
||||
# NOTE: keep this easily parseable!
|
||||
print(f'{answer.equipment_id} {answer.address}:{answer.port}')
|
||||
return
|
||||
print(f'Found {answer.equipment_id} at {answer.address}:')
|
||||
print(f' Port: {answer.port}')
|
||||
print(f' Firmware: {answer.firmware}')
|
||||
desc = answer.description.replace('\n', '\n ')
|
||||
print(f' Node description: {desc}')
|
||||
print()
|
||||
|
||||
|
||||
def scan(max_wait=1.0):
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
|
||||
# send a general broadcast
|
||||
try:
|
||||
s.sendto(json.dumps(dict(SECoP='discover')).encode('utf-8'),
|
||||
('255.255.255.255', UDP_PORT))
|
||||
except OSError as e:
|
||||
print('could not send the broadcast:', e)
|
||||
# we still keep listening for self-announcements
|
||||
start = currenttime()
|
||||
seen = set()
|
||||
while currenttime() < start + max_wait:
|
||||
res = select.select([s], [], [], 0.1)
|
||||
if res[0]:
|
||||
try:
|
||||
msg, addr = s.recvfrom(1024)
|
||||
except socket.error: # pragma: no cover
|
||||
continue
|
||||
answer = decode(msg, addr)
|
||||
if answer is None:
|
||||
continue
|
||||
if (answer.address, answer.equipment_id, answer.port) in seen:
|
||||
continue
|
||||
seen.add((answer.address, answer.equipment_id, answer.port))
|
||||
yield answer
|
||||
|
||||
|
||||
def listen(*, short=False):
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
if os.name == 'nt':
|
||||
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
else:
|
||||
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
|
||||
s.bind(('0.0.0.0', UDP_PORT))
|
||||
while True:
|
||||
try:
|
||||
msg, addr = s.recvfrom(1024)
|
||||
except KeyboardInterrupt:
|
||||
break
|
||||
answer = decode(msg, addr)
|
||||
if answer:
|
||||
print_answer(answer, short=short)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('-l', '--listen', action='store_true',
|
||||
help='Print short info. '
|
||||
'Keep listening after the broadcast.')
|
||||
args = parser.parse_args(sys.argv[1:])
|
||||
for answer in scan():
|
||||
print_answer(answer, short=args.listen)
|
||||
if args.listen:
|
||||
listen(short=args.listen)
|
@ -35,7 +35,15 @@ from frappy.server import Server
|
||||
|
||||
|
||||
def parseArgv(argv):
|
||||
parser = argparse.ArgumentParser(description="Manage a SECoP server")
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Manage a SECoP server",
|
||||
epilog="""The server needs some configuration, by default from the
|
||||
generalConfig.cfg file. the keys confdir, logdir and piddir have to
|
||||
be set.
|
||||
Alternatively, one can set the environment variables FRAPPY_CONFDIR
|
||||
FRAPPY_LOGDIR and FRAPPY_PIDDIR to set the required values.
|
||||
"""
|
||||
)
|
||||
loggroup = parser.add_mutually_exclusive_group()
|
||||
loggroup.add_argument("-v", "--verbose",
|
||||
help="Output lots of diagnostic information",
|
||||
@ -60,9 +68,9 @@ def parseArgv(argv):
|
||||
action='store',
|
||||
help="comma separated list of cfg files,\n"
|
||||
"defaults to <name_of_the_instance>.\n"
|
||||
"cfgfiles given without '.cfg' extension are searched"
|
||||
" in the configuration directory,"
|
||||
" else they are treated as path names",
|
||||
"If a config file contains a slash, it is treated as a"
|
||||
"path, otherwise the file is searched for in the "
|
||||
"configuration directory.",
|
||||
default=None)
|
||||
parser.add_argument('-g',
|
||||
'--gencfg',
|
||||
@ -96,7 +104,9 @@ def main(argv=None):
|
||||
generalConfig.init(args.gencfg)
|
||||
logger.init(loglevel)
|
||||
|
||||
srv = Server(args.name, logger.log, cfgfiles=args.cfgfiles,
|
||||
cfgfiles = [s.strip() for s in args.cfgfiles.split(',')] if args.cfgfiles else None
|
||||
|
||||
srv = Server(args.name, logger.log, cfgfiles=cfgfiles,
|
||||
interface=args.port, testonly=args.test)
|
||||
|
||||
if args.daemonize:
|
||||
|
226
bin/sim-server
226
bin/sim-server
@ -22,22 +22,35 @@
|
||||
|
||||
Usage:
|
||||
|
||||
bin/stringio-server <communciator> <server port>
|
||||
bin/sim-server <communicator class> -p <server port> [-o <option1>=<value> <option2>=<value>]
|
||||
|
||||
open a server on <server port> to communicate with the string based <communicator> over TCP/IP.
|
||||
|
||||
Use cases, mainly for test purposes:
|
||||
- as a T, if the hardware allows only one connection, and more than one is needed
|
||||
- relay to a communicator not using TCP/IP, if Frappy should run on an other host
|
||||
|
||||
- relay to a hardware simulation written as a communicator
|
||||
|
||||
> bin/sim-server frappy_psi.ls370sim.Ls370Sim
|
||||
|
||||
- relay to a communicator not using TCP/IP, if Frappy should run on an other host
|
||||
|
||||
> bin/sim-server frappy.io.StringIO -o uri=serial:///dev/tty...
|
||||
|
||||
- as a T, if the hardware allows only one connection, and more than one is needed:
|
||||
|
||||
> bin/sim-server frappy.io.StringIO -o uri=tcp://<host>:<port>
|
||||
|
||||
typically using communicator class frappy.io.StringIO
|
||||
"""
|
||||
|
||||
import sys
|
||||
import argparse
|
||||
from pathlib import Path
|
||||
import asyncore
|
||||
import socket
|
||||
import time
|
||||
import os
|
||||
from ast import literal_eval
|
||||
from socketserver import BaseRequestHandler, ThreadingTCPServer
|
||||
|
||||
# Add import path for inplace usage
|
||||
sys.path.insert(0, str(Path(__file__).absolute().parents[1]))
|
||||
@ -45,92 +58,6 @@ sys.path.insert(0, str(Path(__file__).absolute().parents[1]))
|
||||
from frappy.lib import get_class, formatException, mkthread
|
||||
|
||||
|
||||
class LineHandler(asyncore.dispatcher_with_send):
|
||||
|
||||
def __init__(self, sock):
|
||||
self.buffer = b""
|
||||
asyncore.dispatcher_with_send.__init__(self, sock)
|
||||
self.crlf = 0
|
||||
|
||||
def handle_line(self, line):
|
||||
raise NotImplementedError
|
||||
|
||||
def handle_read(self):
|
||||
data = self.recv(8192)
|
||||
if data:
|
||||
parts = data.split(b"\n")
|
||||
if len(parts) == 1:
|
||||
self.buffer += data
|
||||
else:
|
||||
self.handle_line((self.buffer + parts[0]).decode('latin_1'))
|
||||
for part in parts[1:-1]:
|
||||
if part[-1] == b"\r":
|
||||
self.crlf = True
|
||||
part = part[:-1]
|
||||
else:
|
||||
self.crlf = False
|
||||
self.handle_line(part.decode('latin_1'))
|
||||
self.buffer = parts[-1]
|
||||
|
||||
def send_line(self, line):
|
||||
self.send((line + ("\r\n" if self.crlf else "\n")).encode('latin_1'))
|
||||
|
||||
|
||||
class LineServer(asyncore.dispatcher):
|
||||
|
||||
def __init__(self, port, line_handler_cls, handler_args):
|
||||
asyncore.dispatcher.__init__(self)
|
||||
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
self.set_reuse_addr()
|
||||
self.bind(('0.0.0.0', port))
|
||||
self.listen(5)
|
||||
print('accept connections at port', port)
|
||||
self.line_handler_cls = line_handler_cls
|
||||
self.handler_args = handler_args
|
||||
|
||||
def handle_accept(self):
|
||||
pair = self.accept()
|
||||
if pair is not None:
|
||||
sock, addr = pair
|
||||
print("Incoming connection from %s" % repr(addr))
|
||||
self.line_handler_cls(sock, self.handler_args)
|
||||
|
||||
def loop(self):
|
||||
asyncore.loop()
|
||||
|
||||
|
||||
class Server(LineServer):
|
||||
|
||||
class Dispatcher:
|
||||
def announce_update(self, *_):
|
||||
pass
|
||||
|
||||
def announce_update_error(self, *_):
|
||||
pass
|
||||
|
||||
def __init__(self, *args, **kwds):
|
||||
super().__init__(*args, **kwds)
|
||||
self.secnode = None
|
||||
self.dispatcher = self.Dispatcher()
|
||||
|
||||
|
||||
class Handler(LineHandler):
|
||||
def __init__(self, sock, handler_args):
|
||||
super().__init__(sock)
|
||||
self.module = handler_args['module']
|
||||
self.verbose = handler_args['verbose']
|
||||
|
||||
def handle_line(self, line):
|
||||
try:
|
||||
reply = self.module.communicate(line.strip())
|
||||
if self.verbose:
|
||||
print('%-40s | %s' % (line, reply))
|
||||
except Exception:
|
||||
print(formatException(verbose=True))
|
||||
return
|
||||
self.send_line(reply)
|
||||
|
||||
|
||||
class Logger:
|
||||
def debug(self, *args):
|
||||
pass
|
||||
@ -144,43 +71,126 @@ class Logger:
|
||||
exception = error = warn = info
|
||||
|
||||
|
||||
class TcpRequestHandler(BaseRequestHandler):
|
||||
def setup(self):
|
||||
print(f'connection opened from {self.client_address}')
|
||||
self.running = True
|
||||
self.request.settimeout(1)
|
||||
self.data = b''
|
||||
|
||||
def finish(self):
|
||||
"""called when handle() terminates, i.e. the socket closed"""
|
||||
# close socket
|
||||
try:
|
||||
self.request.shutdown(socket.SHUT_RDWR)
|
||||
except Exception:
|
||||
pass
|
||||
finally:
|
||||
print(f'connection closed from {self.client_address}')
|
||||
self.request.close()
|
||||
|
||||
def poller(self):
|
||||
while True:
|
||||
time.sleep(1.0)
|
||||
self.module.doPoll()
|
||||
|
||||
def handle(self):
|
||||
"""handle a new connection"""
|
||||
# do a copy of the options, as they are consumed
|
||||
self.module = self.server.modulecls(
|
||||
'mod', Logger(), dict(self.server.options), self.server)
|
||||
self.module.earlyInit()
|
||||
|
||||
mkthread(self.poller)
|
||||
while self.running:
|
||||
try:
|
||||
newdata = self.request.recv(1024)
|
||||
if not newdata:
|
||||
return
|
||||
except socket.timeout:
|
||||
# no new data during read, continue
|
||||
continue
|
||||
self.data += newdata
|
||||
while self.running:
|
||||
message, sep, self.data = self.data.partition(b'\n')
|
||||
if not sep:
|
||||
break
|
||||
cmd = message.decode('latin-1')
|
||||
try:
|
||||
reply = self.module.communicate(cmd.strip())
|
||||
if self.server.verbose:
|
||||
print('%-40s | %s' % (cmd, reply))
|
||||
except Exception:
|
||||
print(formatException(verbose=True))
|
||||
return
|
||||
outdata = reply.encode('latin-1') + b'\n'
|
||||
try:
|
||||
self.request.sendall(outdata)
|
||||
except Exception as e:
|
||||
print(repr(e))
|
||||
self.running = False
|
||||
|
||||
|
||||
class Server(ThreadingTCPServer):
|
||||
allow_reuse_address = os.name != 'nt' # False on Windows systems
|
||||
|
||||
class Dispatcher:
|
||||
def announce_update(self, *_):
|
||||
pass
|
||||
|
||||
def announce_update_error(self, *_):
|
||||
pass
|
||||
|
||||
def __init__(self, port, modulecls, options, verbose=False):
|
||||
super().__init__(('', port), TcpRequestHandler,
|
||||
bind_and_activate=True)
|
||||
self.secnode = None
|
||||
self.dispatcher = self.Dispatcher()
|
||||
self.verbose = verbose
|
||||
self.modulecls = get_class(modulecls)
|
||||
self.options = options
|
||||
print(f'started sim-server listening on port {port}')
|
||||
|
||||
|
||||
def parse_argv(argv):
|
||||
parser = argparse.ArgumentParser(description="Simulate HW with a serial interface")
|
||||
parser = argparse.ArgumentParser(description="Relay to a communicator (simulated HW or other)")
|
||||
parser.add_argument("-v", "--verbose",
|
||||
help="output full communication",
|
||||
action='store_true', default=False)
|
||||
parser.add_argument("cls",
|
||||
type=str,
|
||||
help="simulator class.\n",)
|
||||
help="communicator class.\n",)
|
||||
parser.add_argument('-p',
|
||||
'--port',
|
||||
action='store',
|
||||
help='server port or uri',
|
||||
default=2089)
|
||||
parser.add_argument('-o',
|
||||
'--options',
|
||||
action='store',
|
||||
nargs='*',
|
||||
help='options in the form key=value',
|
||||
default=None)
|
||||
return parser.parse_args(argv)
|
||||
|
||||
|
||||
def poller(pollfunc):
|
||||
while True:
|
||||
time.sleep(1.0)
|
||||
pollfunc()
|
||||
|
||||
|
||||
def main(argv=None):
|
||||
if argv is None:
|
||||
argv = sys.argv
|
||||
|
||||
args = parse_argv(argv[1:])
|
||||
|
||||
opts = {'description': 'simulator'}
|
||||
|
||||
handler_args = {'verbose': args.verbose}
|
||||
srv = Server(int(args.port), Handler, handler_args)
|
||||
module = get_class(args.cls)(args.cls, Logger(), opts, srv)
|
||||
handler_args['module'] = module
|
||||
module.earlyInit()
|
||||
mkthread(poller, module.doPoll)
|
||||
srv.loop()
|
||||
options = {'description': ''}
|
||||
for item in args.options or ():
|
||||
key, eq, value = item.partition('=')
|
||||
if not eq:
|
||||
raise ValueError(f"missing '=' in {item}")
|
||||
try:
|
||||
value = literal_eval(value)
|
||||
except Exception:
|
||||
pass
|
||||
options[key] = value
|
||||
srv = Server(int(args.port), args.cls, options, args.verbose)
|
||||
srv.serve_forever()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
67
cfg/PEUS.py
Normal file
67
cfg/PEUS.py
Normal file
@ -0,0 +1,67 @@
|
||||
Node(equipment_id = 'pe_ultrasound.psi.ch',
|
||||
description = 'pulse echo ultra sound setup',
|
||||
interface = 'tcp://5000',
|
||||
)
|
||||
|
||||
Mod('f',
|
||||
cls = 'frappy_psi.ultrasound.Frequency',
|
||||
description = 'ultrasound frequency and acquisition loop',
|
||||
uri = 'serial:///dev/ttyS1',
|
||||
pars = 'pars',
|
||||
pollinterval = 0.1,
|
||||
time = 900, # start time
|
||||
size = 5000,
|
||||
freq = 1.17568e+06,
|
||||
basefreq = 4.14902e+07,
|
||||
control = False,
|
||||
rusmode = False,
|
||||
amp = 5.0,
|
||||
nr = 1000, #500 #300 #100 #50 #30 #10 #5 #3 #1 #1000 #500 #300 #100 #50 #30 #10 #5 #3 #1 #500
|
||||
sr = 32768, #16384
|
||||
plot = True,
|
||||
maxstep = 100000,
|
||||
bw = 10E6, #butter worth filter bandwidth
|
||||
maxy = 0.7, # y scale for plot
|
||||
curves = 'curves', # module to transmit curves:
|
||||
)
|
||||
|
||||
Mod('curves',
|
||||
cls = 'frappy_psi.ultrasound.Curves',
|
||||
description = 't, I, Q and pulse arrays for plot',
|
||||
)
|
||||
|
||||
Mod('delay',
|
||||
cls = 'frappy__psi.dg645.Delay',
|
||||
description = 'delay line with 2 channels',
|
||||
uri = 'serial:///dev/ttyS2',
|
||||
on1 = 1e-9,
|
||||
on2 = 1E-9,
|
||||
off1 = 400e-9,
|
||||
off2 = 600e-9,
|
||||
)
|
||||
|
||||
Mod('pars',
|
||||
cls = 'frappy_psi.ultrasound.Pars',
|
||||
description = 'SEA parameters',
|
||||
)
|
||||
|
||||
def roi(nr, time=None, size=300):
|
||||
Mod(f'roi{nr}',
|
||||
cls = 'frappy_psi.ultrasound.Roi',
|
||||
description = f'I/Q of region {nr}',
|
||||
main = 'f',
|
||||
time=time or 4000,
|
||||
size=size,
|
||||
enable=time is not None,
|
||||
)
|
||||
|
||||
roi(0, 2450) # you may add size as argument if not default
|
||||
roi(1, 5950)
|
||||
roi(2, 9475)
|
||||
roi(3, 12900)
|
||||
roi(4, 16100)
|
||||
roi(5) # disabled
|
||||
roi(6)
|
||||
roi(7)
|
||||
roi(8)
|
||||
roi(9)
|
62
cfg/RUS.py
Normal file
62
cfg/RUS.py
Normal file
@ -0,0 +1,62 @@
|
||||
Node(equipment_id = 'r_ultrasound.psi.ch',
|
||||
description = 'resonant ultra sound setup',
|
||||
interface = 'tcp://5000',
|
||||
)
|
||||
|
||||
Mod('f',
|
||||
cls = 'frappy_psi.ultrasound.Frequency',
|
||||
description = 'ultrasound frequency and acquisition loop',
|
||||
uri = 'serial:///dev/ttyS1',
|
||||
pars = 'pars',
|
||||
pollinterval = 0.1,
|
||||
time = 900, # start time
|
||||
size = 5000,
|
||||
freq = 1.e+03,
|
||||
basefreq = 1.E+3,
|
||||
control = False,
|
||||
rusmode = False,
|
||||
amp = 2.5,
|
||||
nr = 1, #500 #300 #100 #50 #30 #10 #5 #3 #1 #1000 #500 #300 #100 #50 #30 #10 #5 #3 #1 #500
|
||||
sr = 1E8, #16384
|
||||
plot = True,
|
||||
maxstep = 100000,
|
||||
bw = 10E6, #butter worth filter bandwidth
|
||||
maxy = 0.7, # y scale for plot
|
||||
curves = 'curves', # module to transmit curves:
|
||||
)
|
||||
|
||||
Mod('curves',
|
||||
cls = 'frappy_psi.ultrasound.Curves',
|
||||
description = 't, I, Q and pulse arrays for plot',
|
||||
)
|
||||
|
||||
Mod('roi0',
|
||||
cls = 'frappy_psi.ultrasound.Roi',
|
||||
description = 'I/Q of region in the control loop',
|
||||
time = 300, # this is the center of roi:
|
||||
size = 5000,
|
||||
main = f,
|
||||
)
|
||||
|
||||
Mod('roi1',
|
||||
cls = 'frappy_psi.ultrasound.Roi',
|
||||
description = 'I/Q of region 1',
|
||||
time = 100, # this is the center of roi:
|
||||
size = 300,
|
||||
main = f,
|
||||
)
|
||||
|
||||
Mod('delay',
|
||||
cls = 'frappy__psi.dg645.Delay',
|
||||
description = 'delay line with 2 channels',
|
||||
uri = 'serial:///dev/ttyS2',
|
||||
on1 = 1e-9,
|
||||
on2 = 1E-9,
|
||||
off1 = 400e-9,
|
||||
off2 = 600e-9,
|
||||
)
|
||||
|
||||
Mod('pars',
|
||||
cls = 'frappy_psi.ultrasound.Pars',
|
||||
description = 'SEA parameters',
|
||||
)
|
@ -12,9 +12,9 @@ Mod('sea_main',
|
||||
Mod('tt',
|
||||
'frappy_psi.sea.SeaDrivable', '',
|
||||
io='sea_main',
|
||||
meaning=['temperature_regulation', 20],
|
||||
meaning=['temperature_regulation', 27],
|
||||
sea_object='tt',
|
||||
rel_paths=['tm', 'set', 'dblctrl'],
|
||||
rel_paths=['tm', '.', 'set', 'dblctrl'],
|
||||
)
|
||||
|
||||
Mod('cc',
|
||||
|
@ -12,9 +12,9 @@ Mod('sea_main',
|
||||
Mod('tt',
|
||||
'frappy_psi.sea.SeaDrivable', '',
|
||||
io='sea_main',
|
||||
meaning=['temperature_regulation', 20],
|
||||
meaning=['temperature_regulation', 27],
|
||||
sea_object='tt',
|
||||
rel_paths=['tm', 'set', 'dblctrl'],
|
||||
rel_paths=['tm', '.', 'set', 'dblctrl'],
|
||||
)
|
||||
|
||||
Mod('cc',
|
||||
|
@ -12,9 +12,9 @@ Mod('sea_main',
|
||||
Mod('tt',
|
||||
'frappy_psi.sea.SeaDrivable', '',
|
||||
io='sea_main',
|
||||
meaning=['temperature_regulation', 20],
|
||||
meaning=['temperature_regulation', 27],
|
||||
sea_object='tt',
|
||||
rel_paths=['tm', 'set', 'dblctrl'],
|
||||
rel_paths=['tm', '.', 'set', 'dblctrl'],
|
||||
)
|
||||
|
||||
Mod('cc',
|
||||
|
@ -12,9 +12,9 @@ Mod('sea_main',
|
||||
Mod('tt',
|
||||
'frappy_psi.sea.SeaDrivable', '',
|
||||
io='sea_main',
|
||||
meaning=['temperature_regulation', 20],
|
||||
meaning=['temperature_regulation', 27],
|
||||
sea_object='tt',
|
||||
rel_paths=['tm', 'set', 'dblctrl'],
|
||||
rel_paths=['tm', '.', 'set', 'dblctrl'],
|
||||
)
|
||||
|
||||
Mod('cc',
|
||||
|
@ -12,9 +12,9 @@ Mod('sea_main',
|
||||
Mod('tt',
|
||||
'frappy_psi.sea.SeaDrivable', '',
|
||||
io='sea_main',
|
||||
meaning=['temperature_regulation', 20],
|
||||
meaning=['temperature_regulation', 27],
|
||||
sea_object='tt',
|
||||
rel_paths=['tm', 'set', 'dblctrl'],
|
||||
rel_paths=['tm', '.', 'set', 'dblctrl'],
|
||||
)
|
||||
|
||||
Mod('cc',
|
||||
|
@ -12,9 +12,9 @@ Mod('sea_main',
|
||||
Mod('tt',
|
||||
'frappy_psi.sea.SeaDrivable', '',
|
||||
io='sea_main',
|
||||
meaning=['temperature_regulation', 20],
|
||||
meaning=['temperature_regulation', 27],
|
||||
sea_object='tt',
|
||||
rel_paths=['tm', 'set', 'dblctrl'],
|
||||
rel_paths=['tm', '.', 'set', 'dblctrl'],
|
||||
)
|
||||
|
||||
Mod('pauto',
|
||||
|
@ -12,7 +12,7 @@ Mod('sea_main',
|
||||
Mod('tt',
|
||||
'frappy_psi.sea.SeaDrivable', '',
|
||||
io='sea_main',
|
||||
meaning=['temperature_regulation', 20],
|
||||
meaning=['temperature_regulation', 27],
|
||||
sea_object='tt',
|
||||
rel_paths=['tt', 'set'],
|
||||
)
|
||||
|
@ -12,9 +12,9 @@ Mod('sea_main',
|
||||
Mod('tt',
|
||||
'frappy_psi.sea.SeaDrivable', '',
|
||||
io='sea_main',
|
||||
meaning=['temperature_regulation', 20],
|
||||
meaning=['temperature_regulation', 27],
|
||||
sea_object='tt',
|
||||
rel_paths=['tm', 'set', 'dblctrl'],
|
||||
rel_paths=['tm', '.', 'set', 'dblctrl'],
|
||||
)
|
||||
|
||||
Mod('cc',
|
||||
|
@ -12,9 +12,9 @@ Mod('sea_main',
|
||||
Mod('tt',
|
||||
'frappy_psi.sea.SeaDrivable', '',
|
||||
io='sea_main',
|
||||
meaning=['temperature_regulation', 20],
|
||||
meaning=['temperature_regulation', 27],
|
||||
sea_object='tt',
|
||||
rel_paths=['tm', 'set', 'dblctrl'],
|
||||
rel_paths=['tm', '.', 'set', 'dblctrl'],
|
||||
)
|
||||
|
||||
Mod('cc',
|
||||
|
@ -12,7 +12,7 @@ Mod('sea_main',
|
||||
Mod('ts',
|
||||
'frappy_psi.sea.SeaDrivable', '',
|
||||
io='sea_main',
|
||||
meaning=['temperature_regulation', 20],
|
||||
meaning=['temperature_regulation', 27],
|
||||
sea_object='tt',
|
||||
rel_paths=['ts', 'set']
|
||||
)
|
||||
|
@ -13,7 +13,7 @@ Mod('th',
|
||||
'frappy_psi.sea.SeaReadable',
|
||||
'sample heater temperature',
|
||||
io='sea_main',
|
||||
meaning=['temperature_regulation', 20],
|
||||
meaning=['temperature_regulation', 27],
|
||||
sea_object='tt',
|
||||
rel_paths=['ts', 'setsamp']
|
||||
)
|
||||
@ -22,7 +22,7 @@ Mod('tm',
|
||||
'frappy_psi.sea.SeaDrivable', '',
|
||||
io='sea_main',
|
||||
sea_object='tt',
|
||||
rel_paths=['tm', 'set']
|
||||
rel_paths=['tm', '.', 'set']
|
||||
)
|
||||
|
||||
Mod('ts',
|
||||
|
@ -12,9 +12,9 @@ Mod('sea_main',
|
||||
Mod('tt',
|
||||
'frappy_psi.sea.SeaDrivable', '',
|
||||
io='sea_main',
|
||||
meaning=['temperature_regulation', 20],
|
||||
meaning=['temperature_regulation', 27],
|
||||
sea_object='tt',
|
||||
rel_paths=['tm', 'set', 'dblctrl'],
|
||||
rel_paths=['tm', '.', 'set', 'dblctrl'],
|
||||
)
|
||||
|
||||
Mod('cc',
|
||||
|
@ -10,9 +10,9 @@ Mod('sea_main',
|
||||
Mod('tt',
|
||||
'frappy_psi.sea.SeaDrivable', '',
|
||||
io = 'sea_main',
|
||||
meaning=['temperature_regulation', 20],
|
||||
meaning=['temperature_regulation', 27],
|
||||
sea_object = 'tt',
|
||||
rel_paths=['tm', 'set', 'dblctrl'],
|
||||
rel_paths=['tm', '.', 'set', 'dblctrl'],
|
||||
)
|
||||
Mod('cc',
|
||||
'frappy_psi.sea.SeaReadable', '',
|
||||
|
@ -12,9 +12,9 @@ Mod('sea_main',
|
||||
Mod('tt',
|
||||
'frappy_psi.sea.SeaDrivable', '',
|
||||
io='sea_main',
|
||||
meaning=['temperature_regulation', 20],
|
||||
meaning=['temperature_regulation', 27],
|
||||
sea_object='tt',
|
||||
rel_paths=['tm', 'set', 'dblctrl'],
|
||||
rel_paths=['tm', '.', 'set', 'dblctrl'],
|
||||
)
|
||||
|
||||
Mod('cc',
|
||||
|
@ -12,9 +12,9 @@ Mod('sea_main',
|
||||
Mod('tt',
|
||||
'frappy_psi.sea.SeaDrivable', '',
|
||||
io='sea_main',
|
||||
meaning=['temperature_regulation', 20],
|
||||
meaning=['temperature_regulation', 27],
|
||||
sea_object='tt',
|
||||
rel_paths=['tm', 'set', 'dblctrl'],
|
||||
rel_paths=['tm', '.', 'set', 'dblctrl'],
|
||||
)
|
||||
|
||||
Mod('th',
|
||||
|
@ -10,11 +10,12 @@ Mod('sea_main',
|
||||
)
|
||||
|
||||
Mod('tt',
|
||||
'frappy_psi.sea.SeaDrivable', '',
|
||||
'frappy_psi.sea.LscDrivable', '',
|
||||
io='sea_main',
|
||||
meaning=['temperature_regulation', 20],
|
||||
meaning=['temperature_regulation', 27],
|
||||
sea_object='tt',
|
||||
rel_paths=['tm', 'set', 'dblctrl'],
|
||||
sensor_path='tm',
|
||||
set_path='set',
|
||||
)
|
||||
|
||||
Mod('cc',
|
||||
@ -69,15 +70,13 @@ Mod('lev',
|
||||
Mod('tcoil1',
|
||||
'frappy_psi.sea.SeaReadable', '',
|
||||
io='sea_main',
|
||||
sea_object='tcoil',
|
||||
rel_paths=['ta'],
|
||||
sea_path='tcoil/ta',
|
||||
)
|
||||
|
||||
Mod('tcoil2',
|
||||
'frappy_psi.sea.SeaReadable', '',
|
||||
io='sea_main',
|
||||
sea_object='tcoil',
|
||||
rel_paths=['tb'],
|
||||
sea_path='tcoil/tb',
|
||||
)
|
||||
|
||||
Mod('table',
|
||||
|
@ -12,9 +12,9 @@ Mod('sea_main',
|
||||
Mod('tt',
|
||||
'frappy_psi.sea.SeaDrivable', '',
|
||||
io='sea_main',
|
||||
meaning=['temperature_regulation', 20],
|
||||
meaning=['temperature_regulation', 27],
|
||||
sea_object='tt',
|
||||
rel_paths=['tm', 'set', 'dblctrl', 'voltage'],
|
||||
rel_paths=['tm', '.', 'set', 'dblctrl', 'voltage'],
|
||||
extra_modules=['manualpower'],
|
||||
)
|
||||
|
||||
|
@ -12,9 +12,9 @@ Mod('sea_main',
|
||||
Mod('tt',
|
||||
'frappy_psi.sea.SeaDrivable', '',
|
||||
io='sea_main',
|
||||
meaning=['temperature_regulation', 20],
|
||||
meaning=['temperature_regulation', 27],
|
||||
sea_object='tt',
|
||||
rel_paths=['tm', 'set', 'dblctrl'],
|
||||
rel_paths=['tm', '.', 'set', 'dblctrl'],
|
||||
)
|
||||
|
||||
Mod('th',
|
||||
|
@ -16,10 +16,10 @@ Mod('sea_main',
|
||||
|
||||
Mod('tt',
|
||||
'frappy_psi.sea.SeaDrivable', '',
|
||||
meaning=['temperature_regulation', 20],
|
||||
meaning=['temperature_regulation', 27],
|
||||
io='sea_main',
|
||||
sea_object='tt',
|
||||
rel_paths=['tm', 'set', 'dblctrl'],
|
||||
rel_paths=['tm', '.', 'set', 'dblctrl'],
|
||||
)
|
||||
|
||||
Mod('th',
|
||||
|
@ -12,9 +12,9 @@ Mod('sea_main',
|
||||
Mod('tt',
|
||||
'frappy_psi.sea.SeaDrivable', '',
|
||||
io='sea_main',
|
||||
meaning=['temperature_regulation', 20],
|
||||
meaning=['temperature_regulation', 27],
|
||||
sea_object='tt',
|
||||
rel_paths=['tm', 'set', 'dblctrl'],
|
||||
rel_paths=['tm', '.', 'set', 'dblctrl'],
|
||||
)
|
||||
|
||||
Mod('cc',
|
||||
|
@ -25,7 +25,7 @@ Mod('ips',
|
||||
Mod('T_stat',
|
||||
'frappy_psi.mercury.TemperatureAutoFlow',
|
||||
'static heat exchanger temperature',
|
||||
meaning=['temperature_regulation', 20],
|
||||
meaning=['temperature_regulation', 27],
|
||||
output_module='htr_stat',
|
||||
needle_valve='p_stat',
|
||||
slot='DB6.T1',
|
||||
|
@ -23,7 +23,7 @@ Mod('ips',
|
||||
Mod('T_stat',
|
||||
'frappy_psi.mercury.TemperatureAutoFlow',
|
||||
'static heat exchanger temperature',
|
||||
meaning=['temperature_regulation', 20],
|
||||
meaning=['temperature_regulation', 27],
|
||||
output_module='htr_stat',
|
||||
needle_valve='p_stat',
|
||||
slot='DB6.T1',
|
||||
|
73
cfg/main/ori2_cfg.py
Normal file
73
cfg/main/ori2_cfg.py
Normal file
@ -0,0 +1,73 @@
|
||||
Node('ori3.config.sea.psi.ch',
|
||||
'orange cryostat with 50 mm sample space',
|
||||
)
|
||||
|
||||
Mod('sea_main',
|
||||
'frappy_psi.sea.SeaClient',
|
||||
'main sea connection for ori2.config',
|
||||
config='ori2.config',
|
||||
service='main',
|
||||
)
|
||||
|
||||
Mod('tt',
|
||||
'frappy_psi.sea.SeaDrivable', '',
|
||||
io='sea_main',
|
||||
meaning=['temperature_regulation', 27],
|
||||
sea_object='tt',
|
||||
rel_paths=['tm', '.', 'set', 'dblctrl'],
|
||||
)
|
||||
|
||||
Mod('cc',
|
||||
'frappy_psi.sea.SeaReadable', '',
|
||||
io='sea_main',
|
||||
sea_object='cc',
|
||||
extra_modules=['h'],
|
||||
)
|
||||
|
||||
Mod('lev',
|
||||
'frappy_psi.sea.SeaReadable', '',
|
||||
io='sea_main',
|
||||
single_module='cc.h',
|
||||
)
|
||||
|
||||
Mod('nv',
|
||||
'frappy_psi.sea.SeaWritable', '',
|
||||
io='sea_main',
|
||||
sea_object='nv',
|
||||
)
|
||||
|
||||
Mod('ln2fill',
|
||||
'frappy_psi.sea.SeaWritable', '',
|
||||
io='sea_main',
|
||||
sea_object='ln2fill',
|
||||
)
|
||||
|
||||
Mod('hefill',
|
||||
'frappy_psi.sea.SeaWritable', '',
|
||||
io='sea_main',
|
||||
sea_object='hefill',
|
||||
)
|
||||
|
||||
Mod('hepump',
|
||||
'frappy_psi.sea.SeaWritable', '',
|
||||
io='sea_main',
|
||||
sea_object='hepump',
|
||||
)
|
||||
|
||||
Mod('hemot',
|
||||
'frappy_psi.sea.SeaDrivable', '',
|
||||
io='sea_main',
|
||||
sea_object='hemot',
|
||||
)
|
||||
|
||||
Mod('nvflow',
|
||||
'frappy_psi.sea.SeaReadable', '',
|
||||
io='sea_main',
|
||||
sea_object='nvflow',
|
||||
)
|
||||
|
||||
Mod('table',
|
||||
'frappy_psi.sea.SeaReadable', '',
|
||||
io='sea_main',
|
||||
sea_object='table',
|
||||
)
|
@ -12,9 +12,9 @@ Mod('sea_main',
|
||||
Mod('tt',
|
||||
'frappy_psi.sea.SeaDrivable', '',
|
||||
io='sea_main',
|
||||
meaning=['temperature_regulation', 20],
|
||||
meaning=['temperature_regulation', 27],
|
||||
sea_object='tt',
|
||||
rel_paths=['tm', 'set', 'dblctrl'],
|
||||
rel_paths=['tm', '.', 'set', 'dblctrl'],
|
||||
)
|
||||
|
||||
Mod('cc',
|
||||
|
@ -12,9 +12,9 @@ Mod('sea_main',
|
||||
Mod('tt',
|
||||
'frappy_psi.sea.SeaDrivable', '',
|
||||
io='sea_main',
|
||||
meaning=['temperature_regulation', 20],
|
||||
meaning=['temperature_regulation', 27],
|
||||
sea_object='tt',
|
||||
rel_paths=['tm', 'set', 'dblctrl'],
|
||||
rel_paths=['tm', '.', 'set', 'dblctrl'],
|
||||
)
|
||||
|
||||
Mod('cc',
|
||||
|
@ -12,9 +12,9 @@ Mod('sea_main',
|
||||
Mod('tt',
|
||||
'frappy_psi.sea.SeaDrivable', '',
|
||||
io='sea_main',
|
||||
meaning=['temperature_regulation', 20],
|
||||
meaning=['temperature_regulation', 27],
|
||||
sea_object='tt',
|
||||
rel_paths=['tm', 'set', 'dblctrl'],
|
||||
rel_paths=['tm', '.', 'set', 'dblctrl'],
|
||||
)
|
||||
|
||||
Mod('cc',
|
||||
|
@ -12,9 +12,9 @@ Mod('sea_main',
|
||||
Mod('tt',
|
||||
'frappy_psi.sea.SeaDrivable', '',
|
||||
io = 'sea_main',
|
||||
meaning=['temperature_regulation', 20],
|
||||
meaning=['temperature_regulation', 27],
|
||||
sea_object = 'tt',
|
||||
rel_paths=['tm', 'set', 'dblctrl'],
|
||||
rel_paths=['tm', '.', 'set', 'dblctrl'],
|
||||
)
|
||||
|
||||
Mod('cc',
|
||||
|
@ -12,9 +12,9 @@ Mod('sea_main',
|
||||
Mod('tt',
|
||||
'frappy_psi.sea.SeaDrivable', '',
|
||||
io='sea_main',
|
||||
meaning=['temperature_regulation', 20],
|
||||
meaning=['temperature_regulation', 27],
|
||||
sea_object='tt',
|
||||
rel_paths=['tm', 'set', 'dblctrl'],
|
||||
rel_paths=['tm', '.', 'set', 'dblctrl'],
|
||||
)
|
||||
|
||||
Mod('cc',
|
||||
|
@ -18,7 +18,7 @@ Mod('itc2',
|
||||
Mod('T_stat',
|
||||
'frappy_psi.mercury.TemperatureAutoFlow',
|
||||
'static heat exchanger temperature',
|
||||
meaning=['temperature_regulation', 20],
|
||||
meaning=['temperature_regulation', 27],
|
||||
output_module='htr_stat',
|
||||
needle_valve='p_stat',
|
||||
slot='DB6.T1',
|
||||
@ -168,6 +168,8 @@ Mod('htr_nvd',
|
||||
io='itc2',
|
||||
)
|
||||
|
||||
# Motor controller is not yet available!
|
||||
#
|
||||
#Mod('om_io',
|
||||
# 'frappy_psi.phytron.PhytronIO',
|
||||
# 'dom motor IO',
|
||||
|
@ -18,22 +18,22 @@
|
||||
{"path": "t1", "type": "float", "readonly": false, "cmd": "run tt", "kids": 3},
|
||||
{"path": "t1/raw", "type": "float", "readonly": false, "cmd": "run tt"},
|
||||
{"path": "t1/curve", "type": "text", "readonly": false, "cmd": "tt t1/curve", "kids": 1},
|
||||
{"path": "t1/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt t1/curve/points"},
|
||||
{"path": "t1/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt t1/curve/points", "visibility": 3},
|
||||
{"path": "t1/valid", "type": "bool", "readonly": false, "cmd": "run tt"},
|
||||
{"path": "t2", "type": "float", "readonly": false, "cmd": "run tt", "kids": 3},
|
||||
{"path": "t2/raw", "type": "float", "readonly": false, "cmd": "run tt"},
|
||||
{"path": "t2/curve", "type": "text", "readonly": false, "cmd": "tt t2/curve", "kids": 1},
|
||||
{"path": "t2/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt t2/curve/points"},
|
||||
{"path": "t2/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt t2/curve/points", "visibility": 3},
|
||||
{"path": "t2/valid", "type": "bool", "readonly": false, "cmd": "run tt"},
|
||||
{"path": "t3", "type": "float", "readonly": false, "cmd": "run tt", "kids": 3},
|
||||
{"path": "t3/raw", "type": "float", "readonly": false, "cmd": "run tt"},
|
||||
{"path": "t3/curve", "type": "text", "readonly": false, "cmd": "tt t3/curve", "kids": 1},
|
||||
{"path": "t3/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt t3/curve/points"},
|
||||
{"path": "t3/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt t3/curve/points", "visibility": 3},
|
||||
{"path": "t3/valid", "type": "bool", "readonly": false, "cmd": "run tt"},
|
||||
{"path": "t4", "type": "float", "readonly": false, "cmd": "run tt", "kids": 3},
|
||||
{"path": "t4/raw", "type": "float", "readonly": false, "cmd": "run tt"},
|
||||
{"path": "t4/curve", "type": "text", "readonly": false, "cmd": "tt t4/curve", "kids": 1},
|
||||
{"path": "t4/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt t4/curve/points"},
|
||||
{"path": "t4/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt t4/curve/points", "visibility": 3},
|
||||
{"path": "t4/valid", "type": "bool", "readonly": false, "cmd": "run tt"},
|
||||
{"path": "tref", "type": "float", "readonly": false, "cmd": "run tt"},
|
||||
{"path": "tout", "type": "float", "readonly": false, "cmd": "run tt"},
|
||||
|
@ -269,7 +269,7 @@
|
||||
{"path": "custompar", "type": "float", "readonly": false, "cmd": "hemot custompar"}]},
|
||||
|
||||
"mf": {"base": "/mf", "params": [
|
||||
{"path": "", "type": "float", "kids": 26},
|
||||
{"path": "", "type": "float", "cmd": "run mf", "kids": 26},
|
||||
{"path": "persmode", "type": "int", "readonly": false, "cmd": "mf persmode"},
|
||||
{"path": "perswitch", "type": "int"},
|
||||
{"path": "nowait", "type": "int", "readonly": false, "cmd": "mf nowait"},
|
||||
|
@ -1,4 +1,5 @@
|
||||
{"tt": {"base": "/tt", "params": [{"path": "", "type": "float", "readonly": false, "cmd": "run tt", "description": "tt", "kids": 18},
|
||||
{"tt": {"base": "/tt", "params": [
|
||||
{"path": "", "type": "float", "readonly": false, "cmd": "run tt", "description": "tt", "kids": 18},
|
||||
{"path": "send", "type": "text", "readonly": false, "cmd": "tt send", "visibility": 3},
|
||||
{"path": "status", "type": "text", "visibility": 3},
|
||||
{"path": "is_running", "type": "int", "readonly": false, "cmd": "tt is_running", "visibility": 3},
|
||||
@ -85,7 +86,10 @@
|
||||
{"path": "setsamp/integ", "type": "float", "readonly": false, "cmd": "tt setsamp/integ", "description": "bigger means faster"},
|
||||
{"path": "setsamp/deriv", "type": "float", "readonly": false, "cmd": "tt setsamp/deriv"},
|
||||
{"path": "display", "type": "text", "readonly": false, "cmd": "tt display"},
|
||||
{"path": "remote", "type": "bool"}]}, "cc": {"base": "/cc", "params": [{"path": "", "type": "bool", "kids": 96},
|
||||
{"path": "remote", "type": "bool"}]},
|
||||
|
||||
"cc": {"base": "/cc", "params": [
|
||||
{"path": "", "type": "bool", "kids": 96},
|
||||
{"path": "send", "type": "text", "readonly": false, "cmd": "cc send", "visibility": 3},
|
||||
{"path": "status", "type": "text", "visibility": 3},
|
||||
{"path": "autodevice", "type": "bool", "readonly": false, "cmd": "cc autodevice"},
|
||||
@ -181,7 +185,10 @@
|
||||
{"path": "tm", "type": "float", "visibility": 3},
|
||||
{"path": "tv", "type": "float", "visibility": 3},
|
||||
{"path": "tq", "type": "float", "visibility": 3},
|
||||
{"path": "bdl", "type": "float", "readonly": false, "cmd": "cc bdl"}]}, "nv": {"base": "/nv", "params": [{"path": "", "type": "enum", "enum": {"fixed": 0, "controlled": 1, "automatic": 2, "close": 3, "open": 4}, "readonly": false, "cmd": "nv", "kids": 11},
|
||||
{"path": "bdl", "type": "float", "readonly": false, "cmd": "cc bdl"}]},
|
||||
|
||||
"nv": {"base": "/nv", "params": [
|
||||
{"path": "", "type": "enum", "enum": {"fixed": 0, "controlled": 1, "automatic": 2, "close": 3, "open": 4}, "readonly": false, "cmd": "nv", "kids": 11},
|
||||
{"path": "send", "type": "text", "readonly": false, "cmd": "nv send", "visibility": 3},
|
||||
{"path": "status", "type": "text", "visibility": 3},
|
||||
{"path": "motstat", "type": "enum", "enum": {"idle": 0, "opening": 1, "closing": 2, "opened": 3, "closed": 4, "no_motor": 5}},
|
||||
@ -231,7 +238,11 @@
|
||||
{"path": "autoflow/flowtarget", "type": "float"},
|
||||
{"path": "calib", "type": "none", "kids": 2},
|
||||
{"path": "calib/ln_per_min_per_mbar", "type": "float", "readonly": false, "cmd": "nv calib/ln_per_min_per_mbar"},
|
||||
{"path": "calib/mbar_offset", "type": "float", "readonly": false, "cmd": "nv calib/mbar_offset"}]}, "hepump": {"base": "/hepump", "params": [{"path": "", "type": "enum", "enum": {"neodry": 8, "xds35_auto": 0, "xds35_manual": 1, "sv65": 2, "other": 3, "no": -1}, "readonly": false, "cmd": "hepump", "description": "xds35: scroll pump, sv65: leybold", "kids": 9},
|
||||
{"path": "calib/mbar_offset", "type": "float", "readonly": false, "cmd": "nv calib/mbar_offset"}]},
|
||||
|
||||
|
||||
"hepump": {"base": "/hepump", "params": [
|
||||
{"path": "", "type": "enum", "enum": {"neodry": 8, "xds35_auto": 0, "xds35_manual": 1, "sv65": 2, "other": 3, "no": -1}, "readonly": false, "cmd": "hepump", "description": "xds35: scroll pump, sv65: leybold", "kids": 9},
|
||||
{"path": "send", "type": "text", "readonly": false, "cmd": "hepump send", "visibility": 3},
|
||||
{"path": "status", "type": "text", "visibility": 3},
|
||||
{"path": "running", "type": "bool", "readonly": false, "cmd": "hepump running"},
|
||||
@ -239,7 +250,10 @@
|
||||
{"path": "auto", "type": "bool", "readonly": false, "cmd": "hepump auto"},
|
||||
{"path": "valve", "type": "enum", "enum": {"closed": 0, "closing": 1, "opening": 2, "opened": 3, "undefined": 4}, "readonly": false, "cmd": "hepump valve"},
|
||||
{"path": "eco_t_lim", "type": "float", "readonly": false, "cmd": "hepump eco_t_lim", "description": "switch off eco mode when T_set < eco_t_lim and T < eco_t_lim * 2"},
|
||||
{"path": "calib", "type": "float", "readonly": false, "cmd": "hepump calib", "visibility": 3}]}, "hemot": {"base": "/hepump/hemot", "params": [{"path": "", "type": "float", "readonly": false, "cmd": "run hemot", "kids": 30},
|
||||
{"path": "calib", "type": "float", "readonly": false, "cmd": "hepump calib", "visibility": 3}]},
|
||||
|
||||
"hemot": {"base": "/hepump/hemot", "params": [
|
||||
{"path": "", "type": "float", "readonly": false, "cmd": "run hemot", "kids": 30},
|
||||
{"path": "send", "type": "text", "readonly": false, "cmd": "hemot send", "visibility": 3},
|
||||
{"path": "status", "type": "text", "visibility": 3},
|
||||
{"path": "is_running", "type": "int", "readonly": false, "cmd": "hemot is_running", "visibility": 3},
|
||||
@ -270,7 +284,8 @@
|
||||
{"path": "eeprom", "type": "enum", "enum": {"ok": 0, "dirty": 1, "save": 2, "load": 3}, "readonly": false, "cmd": "hemot eeprom"},
|
||||
{"path": "customadr", "type": "text", "readonly": false, "cmd": "hemot customadr"},
|
||||
{"path": "custompar", "type": "float", "readonly": false, "cmd": "hemot custompar"}]},
|
||||
"ln2fill": {"base": "/ln2fill", "params": [
|
||||
"
|
||||
ln2fill": {"base": "/ln2fill", "params": [
|
||||
{"path": "", "type": "enum", "enum": {"watching": 0, "fill": 1, "inactive": 2}, "readonly": false, "cmd": "ln2fill", "kids": 14},
|
||||
{"path": "send", "type": "text", "readonly": false, "cmd": "ln2fill send", "visibility": 3},
|
||||
{"path": "status", "type": "text", "visibility": 3},
|
||||
@ -285,7 +300,10 @@
|
||||
{"path": "maxholdhours", "type": "float", "readonly": false, "cmd": "ln2fill maxholdhours"},
|
||||
{"path": "tolerance", "type": "float", "readonly": false, "cmd": "ln2fill tolerance"},
|
||||
{"path": "badreadingminutes", "type": "float", "readonly": false, "cmd": "ln2fill badreadingminutes"},
|
||||
{"path": "tubecoolingminutes", "type": "float", "readonly": false, "cmd": "ln2fill tubecoolingminutes"}]}, "hefill": {"base": "/hefill", "params": [{"path": "", "type": "enum", "enum": {"watching": 0, "fill": 1, "inactive": 2}, "readonly": false, "cmd": "hefill", "kids": 16},
|
||||
{"path": "tubecoolingminutes", "type": "float", "readonly": false, "cmd": "ln2fill tubecoolingminutes"}]},
|
||||
|
||||
"hefill": {"base": "/hefill", "params": [
|
||||
{"path": "", "type": "enum", "enum": {"watching": 0, "fill": 1, "inactive": 2}, "readonly": false, "cmd": "hefill", "kids": 16},
|
||||
{"path": "send", "type": "text", "readonly": false, "cmd": "hefill send", "visibility": 3},
|
||||
{"path": "status", "type": "text", "visibility": 3},
|
||||
{"path": "state", "type": "text"},
|
||||
@ -301,11 +319,17 @@
|
||||
{"path": "badreadingminutes", "type": "float", "readonly": false, "cmd": "hefill badreadingminutes"},
|
||||
{"path": "tubecoolingminutes", "type": "float", "readonly": false, "cmd": "hefill tubecoolingminutes"},
|
||||
{"path": "vessellimit", "type": "float", "readonly": false, "cmd": "hefill vessellimit"},
|
||||
{"path": "vext", "type": "float"}]}, "lev": {"base": "/lev", "params": [{"path": "", "type": "float", "kids": 4},
|
||||
{"path": "vext", "type": "float"}]},
|
||||
|
||||
"lev": {"base": "/lev", "params": [
|
||||
{"path": "", "type": "float", "kids": 4},
|
||||
{"path": "send", "type": "text", "readonly": false, "cmd": "lev send", "visibility": 3},
|
||||
{"path": "status", "type": "text", "visibility": 3},
|
||||
{"path": "mode", "type": "enum", "enum": {"slow": 0, "fast (switches to slow automatically after filling)": 1}, "readonly": false, "cmd": "lev mode"},
|
||||
{"path": "n2", "type": "float"}]}, "mf": {"base": "/mf", "params": [{"path": "", "type": "float", "kids": 26},
|
||||
{"path": "n2", "type": "float"}]},
|
||||
|
||||
"mf": {"base": "/mf", "params": [
|
||||
{"path": "", "type": "float", "cmd": "run mf", "kids": 26},
|
||||
{"path": "persmode", "type": "int", "readonly": false, "cmd": "mf persmode"},
|
||||
{"path": "perswitch", "type": "int"},
|
||||
{"path": "nowait", "type": "int", "readonly": false, "cmd": "mf nowait"},
|
||||
@ -330,7 +354,10 @@
|
||||
{"path": "driver", "type": "text", "visibility": 3},
|
||||
{"path": "creationCmd", "type": "text", "visibility": 3},
|
||||
{"path": "targetValue", "type": "float"},
|
||||
{"path": "status", "type": "text", "readonly": false, "cmd": "mf status", "visibility": 3}]}, "tcoil": {"base": "/tcoil", "params": [{"path": "", "type": "float", "kids": 11},
|
||||
{"path": "status", "type": "text", "readonly": false, "cmd": "mf status", "visibility": 3}]},
|
||||
|
||||
"tcoil": {"base": "/tcoil", "params": [
|
||||
{"path": "", "type": "float", "kids": 11},
|
||||
{"path": "send", "type": "text", "readonly": false, "cmd": "tcoil send", "visibility": 3},
|
||||
{"path": "status", "type": "text", "visibility": 3},
|
||||
{"path": "excitation", "type": "float", "readonly": false, "cmd": "tcoil excitation", "visibility": 3},
|
||||
@ -338,40 +365,43 @@
|
||||
{"path": "td/enable", "type": "bool", "readonly": false, "cmd": "tcoil td/enable"},
|
||||
{"path": "td/r", "type": "float"},
|
||||
{"path": "td/curve", "type": "text", "readonly": false, "cmd": "tcoil td/curve", "kids": 3},
|
||||
{"path": "td/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil td/curve/adjust"},
|
||||
{"path": "td/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil td/curve/points"},
|
||||
{"path": "td/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil td/curve/cpoints"},
|
||||
{"path": "td/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil td/curve/adjust", "visibility": 3},
|
||||
{"path": "td/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil td/curve/points", "visibility": 3},
|
||||
{"path": "td/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil td/curve/cpoints", "visibility": 3},
|
||||
{"path": "tc", "type": "float", "visibility": 3, "kids": 3},
|
||||
{"path": "tc/enable", "type": "bool", "readonly": false, "cmd": "tcoil tc/enable"},
|
||||
{"path": "tc/r", "type": "float"},
|
||||
{"path": "tc/curve", "type": "text", "readonly": false, "cmd": "tcoil tc/curve", "kids": 3},
|
||||
{"path": "tc/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil tc/curve/adjust"},
|
||||
{"path": "tc/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil tc/curve/points"},
|
||||
{"path": "tc/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil tc/curve/cpoints"},
|
||||
{"path": "tc/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil tc/curve/adjust", "visibility": 3},
|
||||
{"path": "tc/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil tc/curve/points", "visibility": 3},
|
||||
{"path": "tc/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil tc/curve/cpoints", "visibility": 3},
|
||||
{"path": "tb", "type": "float", "visibility": 3, "kids": 3},
|
||||
{"path": "tb/enable", "type": "bool", "readonly": false, "cmd": "tcoil tb/enable"},
|
||||
{"path": "tb/r", "type": "float"},
|
||||
{"path": "tb/curve", "type": "text", "readonly": false, "cmd": "tcoil tb/curve", "kids": 3},
|
||||
{"path": "tb/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil tb/curve/adjust"},
|
||||
{"path": "tb/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil tb/curve/points"},
|
||||
{"path": "tb/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil tb/curve/cpoints"},
|
||||
{"path": "tb/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil tb/curve/adjust", "visibility": 3},
|
||||
{"path": "tb/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil tb/curve/points", "visibility": 3},
|
||||
{"path": "tb/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil tb/curve/cpoints", "visibility": 3},
|
||||
{"path": "ta", "type": "float", "visibility": 3, "kids": 3},
|
||||
{"path": "ta/enable", "type": "bool", "readonly": false, "cmd": "tcoil ta/enable"},
|
||||
{"path": "ta/r", "type": "float"},
|
||||
{"path": "ta/curve", "type": "text", "readonly": false, "cmd": "tcoil ta/curve", "kids": 3},
|
||||
{"path": "ta/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil ta/curve/adjust"},
|
||||
{"path": "ta/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil ta/curve/points"},
|
||||
{"path": "ta/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil ta/curve/cpoints"},
|
||||
{"path": "ta/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil ta/curve/adjust", "visibility": 3},
|
||||
{"path": "ta/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil ta/curve/points", "visibility": 3},
|
||||
{"path": "ta/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil ta/curve/cpoints", "visibility": 3},
|
||||
{"path": "ref", "type": "float", "visibility": 3, "kids": 3},
|
||||
{"path": "ref/enable", "type": "bool", "readonly": false, "cmd": "tcoil ref/enable"},
|
||||
{"path": "ref/r", "type": "float"},
|
||||
{"path": "ref/curve", "type": "text", "readonly": false, "cmd": "tcoil ref/curve", "kids": 3},
|
||||
{"path": "ref/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil ref/curve/adjust"},
|
||||
{"path": "ref/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil ref/curve/points"},
|
||||
{"path": "ref/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil ref/curve/cpoints"},
|
||||
{"path": "ref/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil ref/curve/adjust", "visibility": 3},
|
||||
{"path": "ref/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil ref/curve/points", "visibility": 3},
|
||||
{"path": "ref/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil ref/curve/cpoints", "visibility": 3},
|
||||
{"path": "ext", "type": "float", "visibility": 3},
|
||||
{"path": "com", "type": "float", "visibility": 3},
|
||||
{"path": "gnd", "type": "float", "visibility": 3}]}, "table": {"base": "/table", "params": [{"path": "", "type": "none", "kids": 17},
|
||||
{"path": "gnd", "type": "float", "visibility": 3}]},
|
||||
|
||||
"table": {"base": "/table", "params": [
|
||||
{"path": "", "type": "none", "kids": 17},
|
||||
{"path": "send", "type": "text", "readonly": false, "cmd": "table send", "visibility": 3},
|
||||
{"path": "status", "type": "text", "visibility": 3},
|
||||
{"path": "fix_tt_set_prop", "type": "bool", "readonly": false, "cmd": "table fix_tt_set_prop"},
|
||||
@ -388,8 +418,14 @@
|
||||
{"path": "tbl_tt_dblctrl_prop_up", "type": "text", "readonly": false, "cmd": "table tbl_tt_dblctrl_prop_up", "description": "enter value pair separated with colon T1:par1 T2:par2 ..."},
|
||||
{"path": "fix_tt_dblctrl_prop_lo", "type": "bool", "readonly": false, "cmd": "table fix_tt_dblctrl_prop_lo"},
|
||||
{"path": "val_tt_dblctrl_prop_lo", "type": "float"},
|
||||
{"path": "tbl_tt_dblctrl_prop_lo", "type": "text", "readonly": false, "cmd": "table tbl_tt_dblctrl_prop_lo", "description": "enter value pair separated with colon T1:par1 T2:par2 ..."}]}, "ccu2": {"base": "/sics/ccu2", "params": [{"path": "", "type": "text", "readonly": false, "cmd": "ccu2", "kids": 23},
|
||||
{"path": "tasks", "type": "none", "visibility": 3}]}, "lnv": {"base": "/lnv", "params": [{"path": "", "type": "enum", "enum": {"off": 5, "fixed": 0, "controlling": 1, "close": 3, "open": 4}, "readonly": false, "cmd": "lnv", "kids": 12},
|
||||
{"path": "tbl_tt_dblctrl_prop_lo", "type": "text", "readonly": false, "cmd": "table tbl_tt_dblctrl_prop_lo", "description": "enter value pair separated with colon T1:par1 T2:par2 ..."}]},
|
||||
|
||||
"ccu2": {"base": "/sics/ccu2", "params": [
|
||||
{"path": "", "type": "text", "readonly": false, "cmd": "ccu2", "kids": 23},
|
||||
{"path": "tasks", "type": "none", "visibility": 3}]},
|
||||
|
||||
"lnv": {"base": "/lnv", "params": [
|
||||
{"path": "", "type": "enum", "enum": {"off": 5, "fixed": 0, "controlling": 1, "close": 3, "open": 4}, "readonly": false, "cmd": "lnv", "kids": 12},
|
||||
{"path": "send", "type": "text", "readonly": false, "cmd": "lnv send", "visibility": 3},
|
||||
{"path": "status", "type": "text", "visibility": 3},
|
||||
{"path": "set", "type": "float", "readonly": false, "cmd": "lnv set"},
|
||||
@ -427,7 +463,10 @@
|
||||
{"path": "autoflow/difmax", "type": "float"},
|
||||
{"path": "autoflow/setmin", "type": "float"},
|
||||
{"path": "autoflow/setmax", "type": "float"},
|
||||
{"path": "autoflow/flowtarget", "type": "float"}]}, "lpr": {"base": "/lpr", "params": [{"path": "", "type": "float", "readonly": false, "cmd": "run lpr", "description": "lpr", "kids": 28},
|
||||
{"path": "autoflow/flowtarget", "type": "float"}]},
|
||||
|
||||
"lpr": {"base": "/lpr", "params": [
|
||||
{"path": "", "type": "float", "readonly": false, "cmd": "run lpr", "description": "lpr", "kids": 28},
|
||||
{"path": "send", "type": "text", "readonly": false, "cmd": "lpr send", "visibility": 3},
|
||||
{"path": "status", "type": "text", "visibility": 3},
|
||||
{"path": "is_running", "type": "int", "readonly": false, "cmd": "lpr is_running", "visibility": 3},
|
||||
@ -455,12 +494,13 @@
|
||||
{"path": "running", "type": "int"},
|
||||
{"path": "tolerance", "type": "float", "readonly": false, "cmd": "lpr tolerance"},
|
||||
{"path": "maxwait", "type": "float", "readonly": false, "cmd": "lpr maxwait"},
|
||||
{"path": "settle", "type": "float", "readonly": false, "cmd": "lpr settle"}]}, "lambdawatch": {"base": "/lambdawatch", "params": [{"path": "", "type": "float", "kids": 6},
|
||||
{"path": "settle", "type": "float", "readonly": false, "cmd": "lpr settle"}]},
|
||||
|
||||
"lambdawatch": {"base": "/lambdawatch", "params": [
|
||||
{"path": "", "type": "float", "kids": 6},
|
||||
{"path": "send", "type": "text", "readonly": false, "cmd": "lambdawatch send", "visibility": 3},
|
||||
{"path": "status", "type": "text", "visibility": 3},
|
||||
{"path": "safefield", "type": "float", "readonly": false, "cmd": "lambdawatch safefield"},
|
||||
{"path": "maxfield", "type": "float", "readonly": false, "cmd": "lambdawatch maxfield"},
|
||||
{"path": "safetemp", "type": "float", "readonly": false, "cmd": "lambdawatch safetemp"},
|
||||
{"path": "coiltemp", "type": "text", "readonly": false, "cmd": "lambdawatch coiltemp"}]}, "prep0": {"base": "/prep0", "params": [{"path": "", "type": "text", "readonly": false, "cmd": "prep0", "kids": 2},
|
||||
{"path": "send", "type": "text", "readonly": false, "cmd": "prep0 send", "visibility": 3},
|
||||
{"path": "status", "type": "text", "visibility": 3}]}}
|
||||
{"path": "coiltemp", "type": "text", "readonly": false, "cmd": "lambdawatch coiltemp"}]}}
|
||||
|
@ -371,37 +371,37 @@
|
||||
{"path": "td/enable", "type": "bool", "readonly": false, "cmd": "tcoil td/enable"},
|
||||
{"path": "td/r", "type": "float"},
|
||||
{"path": "td/curve", "type": "text", "readonly": false, "cmd": "tcoil td/curve", "kids": 3},
|
||||
{"path": "td/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil td/curve/adjust"},
|
||||
{"path": "td/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil td/curve/points"},
|
||||
{"path": "td/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil td/curve/cpoints"},
|
||||
{"path": "td/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil td/curve/adjust", "visibility": 3},
|
||||
{"path": "td/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil td/curve/points", "visibility": 3},
|
||||
{"path": "td/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil td/curve/cpoints", "visibility": 3},
|
||||
{"path": "tb", "type": "float", "visibility": 3, "kids": 3},
|
||||
{"path": "tb/enable", "type": "bool", "readonly": false, "cmd": "tcoil tb/enable"},
|
||||
{"path": "tb/r", "type": "float"},
|
||||
{"path": "tb/curve", "type": "text", "readonly": false, "cmd": "tcoil tb/curve", "kids": 3},
|
||||
{"path": "tb/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil tb/curve/adjust"},
|
||||
{"path": "tb/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil tb/curve/points"},
|
||||
{"path": "tb/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil tb/curve/cpoints"},
|
||||
{"path": "tb/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil tb/curve/adjust", "visibility": 3},
|
||||
{"path": "tb/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil tb/curve/points", "visibility": 3},
|
||||
{"path": "tb/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil tb/curve/cpoints", "visibility": 3},
|
||||
{"path": "tc", "type": "float", "visibility": 3, "kids": 3},
|
||||
{"path": "tc/enable", "type": "bool", "readonly": false, "cmd": "tcoil tc/enable"},
|
||||
{"path": "tc/r", "type": "float"},
|
||||
{"path": "tc/curve", "type": "text", "readonly": false, "cmd": "tcoil tc/curve", "kids": 3},
|
||||
{"path": "tc/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil tc/curve/adjust"},
|
||||
{"path": "tc/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil tc/curve/points"},
|
||||
{"path": "tc/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil tc/curve/cpoints"},
|
||||
{"path": "tc/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil tc/curve/adjust", "visibility": 3},
|
||||
{"path": "tc/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil tc/curve/points", "visibility": 3},
|
||||
{"path": "tc/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil tc/curve/cpoints", "visibility": 3},
|
||||
{"path": "ta", "type": "float", "visibility": 3, "kids": 3},
|
||||
{"path": "ta/enable", "type": "bool", "readonly": false, "cmd": "tcoil ta/enable"},
|
||||
{"path": "ta/r", "type": "float"},
|
||||
{"path": "ta/curve", "type": "text", "readonly": false, "cmd": "tcoil ta/curve", "kids": 3},
|
||||
{"path": "ta/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil ta/curve/adjust"},
|
||||
{"path": "ta/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil ta/curve/points"},
|
||||
{"path": "ta/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil ta/curve/cpoints"},
|
||||
{"path": "ta/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil ta/curve/adjust", "visibility": 3},
|
||||
{"path": "ta/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil ta/curve/points", "visibility": 3},
|
||||
{"path": "ta/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil ta/curve/cpoints", "visibility": 3},
|
||||
{"path": "ref", "type": "float", "visibility": 3, "kids": 3},
|
||||
{"path": "ref/enable", "type": "bool", "readonly": false, "cmd": "tcoil ref/enable"},
|
||||
{"path": "ref/r", "type": "float"},
|
||||
{"path": "ref/curve", "type": "text", "readonly": false, "cmd": "tcoil ref/curve", "kids": 3},
|
||||
{"path": "ref/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil ref/curve/adjust"},
|
||||
{"path": "ref/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil ref/curve/points"},
|
||||
{"path": "ref/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil ref/curve/cpoints"},
|
||||
{"path": "ref/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil ref/curve/adjust", "visibility": 3},
|
||||
{"path": "ref/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil ref/curve/points", "visibility": 3},
|
||||
{"path": "ref/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil ref/curve/cpoints", "visibility": 3},
|
||||
{"path": "ext", "type": "float", "visibility": 3},
|
||||
{"path": "com", "type": "float", "visibility": 3},
|
||||
{"path": "gnd", "type": "float", "visibility": 3}]},
|
||||
|
@ -371,37 +371,37 @@
|
||||
{"path": "ta/enable", "type": "bool", "readonly": false, "cmd": "tcoil ta/enable"},
|
||||
{"path": "ta/r", "type": "float"},
|
||||
{"path": "ta/curve", "type": "text", "readonly": false, "cmd": "tcoil ta/curve", "kids": 3},
|
||||
{"path": "ta/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil ta/curve/adjust"},
|
||||
{"path": "ta/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil ta/curve/points"},
|
||||
{"path": "ta/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil ta/curve/cpoints"},
|
||||
{"path": "ta/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil ta/curve/adjust", "visibility": 3},
|
||||
{"path": "ta/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil ta/curve/points", "visibility": 3},
|
||||
{"path": "ta/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil ta/curve/cpoints", "visibility": 3},
|
||||
{"path": "tb", "type": "float", "kids": 3},
|
||||
{"path": "tb/enable", "type": "bool", "readonly": false, "cmd": "tcoil tb/enable"},
|
||||
{"path": "tb/r", "type": "float"},
|
||||
{"path": "tb/curve", "type": "text", "readonly": false, "cmd": "tcoil tb/curve", "kids": 3},
|
||||
{"path": "tb/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil tb/curve/adjust"},
|
||||
{"path": "tb/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil tb/curve/points"},
|
||||
{"path": "tb/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil tb/curve/cpoints"},
|
||||
{"path": "tb/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil tb/curve/adjust", "visibility": 3},
|
||||
{"path": "tb/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil tb/curve/points", "visibility": 3},
|
||||
{"path": "tb/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil tb/curve/cpoints", "visibility": 3},
|
||||
{"path": "td", "type": "float", "visibility": 3, "kids": 3},
|
||||
{"path": "td/enable", "type": "bool", "readonly": false, "cmd": "tcoil td/enable"},
|
||||
{"path": "td/r", "type": "float"},
|
||||
{"path": "td/curve", "type": "text", "readonly": false, "cmd": "tcoil td/curve", "kids": 3},
|
||||
{"path": "td/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil td/curve/adjust"},
|
||||
{"path": "td/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil td/curve/points"},
|
||||
{"path": "td/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil td/curve/cpoints"},
|
||||
{"path": "td/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil td/curve/adjust", "visibility": 3},
|
||||
{"path": "td/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil td/curve/points", "visibility": 3},
|
||||
{"path": "td/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil td/curve/cpoints", "visibility": 3},
|
||||
{"path": "ref", "type": "float", "visibility": 3, "kids": 3},
|
||||
{"path": "ref/enable", "type": "bool", "readonly": false, "cmd": "tcoil ref/enable"},
|
||||
{"path": "ref/r", "type": "float"},
|
||||
{"path": "ref/curve", "type": "text", "readonly": false, "cmd": "tcoil ref/curve", "kids": 3},
|
||||
{"path": "ref/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil ref/curve/adjust"},
|
||||
{"path": "ref/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil ref/curve/points"},
|
||||
{"path": "ref/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil ref/curve/cpoints"},
|
||||
{"path": "ref/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil ref/curve/adjust", "visibility": 3},
|
||||
{"path": "ref/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil ref/curve/points", "visibility": 3},
|
||||
{"path": "ref/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil ref/curve/cpoints", "visibility": 3},
|
||||
{"path": "tc", "type": "float", "visibility": 3, "kids": 3},
|
||||
{"path": "tc/enable", "type": "bool", "readonly": false, "cmd": "tcoil tc/enable"},
|
||||
{"path": "tc/r", "type": "float"},
|
||||
{"path": "tc/curve", "type": "text", "readonly": false, "cmd": "tcoil tc/curve", "kids": 3},
|
||||
{"path": "tc/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil tc/curve/adjust"},
|
||||
{"path": "tc/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil tc/curve/points"},
|
||||
{"path": "tc/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil tc/curve/cpoints"},
|
||||
{"path": "tc/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil tc/curve/adjust", "visibility": 3},
|
||||
{"path": "tc/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil tc/curve/points", "visibility": 3},
|
||||
{"path": "tc/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil tc/curve/cpoints", "visibility": 3},
|
||||
{"path": "ext", "type": "float", "visibility": 3},
|
||||
{"path": "com", "type": "float", "visibility": 3},
|
||||
{"path": "gnd", "type": "float", "visibility": 3}]},
|
||||
|
@ -370,37 +370,37 @@
|
||||
{"path": "ta/enable", "type": "bool", "readonly": false, "cmd": "tcoil ta/enable"},
|
||||
{"path": "ta/r", "type": "float"},
|
||||
{"path": "ta/curve", "type": "text", "readonly": false, "cmd": "tcoil ta/curve", "kids": 3},
|
||||
{"path": "ta/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil ta/curve/adjust"},
|
||||
{"path": "ta/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil ta/curve/points"},
|
||||
{"path": "ta/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil ta/curve/cpoints"},
|
||||
{"path": "ta/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil ta/curve/adjust", "visibility": 3},
|
||||
{"path": "ta/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil ta/curve/points", "visibility": 3},
|
||||
{"path": "ta/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil ta/curve/cpoints", "visibility": 3},
|
||||
{"path": "tb", "type": "float", "kids": 3},
|
||||
{"path": "tb/enable", "type": "bool", "readonly": false, "cmd": "tcoil tb/enable"},
|
||||
{"path": "tb/r", "type": "float"},
|
||||
{"path": "tb/curve", "type": "text", "readonly": false, "cmd": "tcoil tb/curve", "kids": 3},
|
||||
{"path": "tb/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil tb/curve/adjust"},
|
||||
{"path": "tb/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil tb/curve/points"},
|
||||
{"path": "tb/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil tb/curve/cpoints"},
|
||||
{"path": "tb/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil tb/curve/adjust", "visibility": 3},
|
||||
{"path": "tb/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil tb/curve/points", "visibility": 3},
|
||||
{"path": "tb/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil tb/curve/cpoints", "visibility": 3},
|
||||
{"path": "td", "type": "float", "visibility": 3, "kids": 3},
|
||||
{"path": "td/enable", "type": "bool", "readonly": false, "cmd": "tcoil td/enable"},
|
||||
{"path": "td/r", "type": "float"},
|
||||
{"path": "td/curve", "type": "text", "readonly": false, "cmd": "tcoil td/curve", "kids": 3},
|
||||
{"path": "td/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil td/curve/adjust"},
|
||||
{"path": "td/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil td/curve/points"},
|
||||
{"path": "td/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil td/curve/cpoints"},
|
||||
{"path": "td/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil td/curve/adjust", "visibility": 3},
|
||||
{"path": "td/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil td/curve/points", "visibility": 3},
|
||||
{"path": "td/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil td/curve/cpoints", "visibility": 3},
|
||||
{"path": "ref", "type": "float", "visibility": 3, "kids": 3},
|
||||
{"path": "ref/enable", "type": "bool", "readonly": false, "cmd": "tcoil ref/enable"},
|
||||
{"path": "ref/r", "type": "float"},
|
||||
{"path": "ref/curve", "type": "text", "readonly": false, "cmd": "tcoil ref/curve", "kids": 3},
|
||||
{"path": "ref/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil ref/curve/adjust"},
|
||||
{"path": "ref/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil ref/curve/points"},
|
||||
{"path": "ref/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil ref/curve/cpoints"},
|
||||
{"path": "ref/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil ref/curve/adjust", "visibility": 3},
|
||||
{"path": "ref/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil ref/curve/points", "visibility": 3},
|
||||
{"path": "ref/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil ref/curve/cpoints", "visibility": 3},
|
||||
{"path": "tc", "type": "float", "visibility": 3, "kids": 3},
|
||||
{"path": "tc/enable", "type": "bool", "readonly": false, "cmd": "tcoil tc/enable"},
|
||||
{"path": "tc/r", "type": "float"},
|
||||
{"path": "tc/curve", "type": "text", "readonly": false, "cmd": "tcoil tc/curve", "kids": 3},
|
||||
{"path": "tc/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil tc/curve/adjust"},
|
||||
{"path": "tc/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil tc/curve/points"},
|
||||
{"path": "tc/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil tc/curve/cpoints"},
|
||||
{"path": "tc/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil tc/curve/adjust", "visibility": 3},
|
||||
{"path": "tc/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil tc/curve/points", "visibility": 3},
|
||||
{"path": "tc/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil tc/curve/cpoints", "visibility": 3},
|
||||
{"path": "ext", "type": "float", "visibility": 3},
|
||||
{"path": "com", "type": "float", "visibility": 3},
|
||||
{"path": "gnd", "type": "float", "visibility": 3}]},
|
||||
|
@ -371,37 +371,37 @@
|
||||
{"path": "ta/enable", "type": "bool", "readonly": false, "cmd": "tcoil ta/enable"},
|
||||
{"path": "ta/r", "type": "float"},
|
||||
{"path": "ta/curve", "type": "text", "readonly": false, "cmd": "tcoil ta/curve", "kids": 3},
|
||||
{"path": "ta/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil ta/curve/adjust"},
|
||||
{"path": "ta/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil ta/curve/points"},
|
||||
{"path": "ta/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil ta/curve/cpoints"},
|
||||
{"path": "ta/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil ta/curve/adjust", "visibility": 3},
|
||||
{"path": "ta/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil ta/curve/points", "visibility": 3},
|
||||
{"path": "ta/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil ta/curve/cpoints", "visibility": 3},
|
||||
{"path": "tb", "type": "float", "kids": 3},
|
||||
{"path": "tb/enable", "type": "bool", "readonly": false, "cmd": "tcoil tb/enable"},
|
||||
{"path": "tb/r", "type": "float"},
|
||||
{"path": "tb/curve", "type": "text", "readonly": false, "cmd": "tcoil tb/curve", "kids": 3},
|
||||
{"path": "tb/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil tb/curve/adjust"},
|
||||
{"path": "tb/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil tb/curve/points"},
|
||||
{"path": "tb/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil tb/curve/cpoints"},
|
||||
{"path": "tb/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil tb/curve/adjust", "visibility": 3},
|
||||
{"path": "tb/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil tb/curve/points", "visibility": 3},
|
||||
{"path": "tb/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil tb/curve/cpoints", "visibility": 3},
|
||||
{"path": "td", "type": "float", "visibility": 3, "kids": 3},
|
||||
{"path": "td/enable", "type": "bool", "readonly": false, "cmd": "tcoil td/enable"},
|
||||
{"path": "td/r", "type": "float"},
|
||||
{"path": "td/curve", "type": "text", "readonly": false, "cmd": "tcoil td/curve", "kids": 3},
|
||||
{"path": "td/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil td/curve/adjust"},
|
||||
{"path": "td/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil td/curve/points"},
|
||||
{"path": "td/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil td/curve/cpoints"},
|
||||
{"path": "td/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil td/curve/adjust", "visibility": 3},
|
||||
{"path": "td/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil td/curve/points", "visibility": 3},
|
||||
{"path": "td/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil td/curve/cpoints", "visibility": 3},
|
||||
{"path": "ref", "type": "float", "visibility": 3, "kids": 3},
|
||||
{"path": "ref/enable", "type": "bool", "readonly": false, "cmd": "tcoil ref/enable"},
|
||||
{"path": "ref/r", "type": "float"},
|
||||
{"path": "ref/curve", "type": "text", "readonly": false, "cmd": "tcoil ref/curve", "kids": 3},
|
||||
{"path": "ref/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil ref/curve/adjust"},
|
||||
{"path": "ref/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil ref/curve/points"},
|
||||
{"path": "ref/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil ref/curve/cpoints"},
|
||||
{"path": "ref/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil ref/curve/adjust", "visibility": 3},
|
||||
{"path": "ref/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil ref/curve/points", "visibility": 3},
|
||||
{"path": "ref/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil ref/curve/cpoints", "visibility": 3},
|
||||
{"path": "tc", "type": "float", "visibility": 3, "kids": 3},
|
||||
{"path": "tc/enable", "type": "bool", "readonly": false, "cmd": "tcoil tc/enable"},
|
||||
{"path": "tc/r", "type": "float"},
|
||||
{"path": "tc/curve", "type": "text", "readonly": false, "cmd": "tcoil tc/curve", "kids": 3},
|
||||
{"path": "tc/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil tc/curve/adjust"},
|
||||
{"path": "tc/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil tc/curve/points"},
|
||||
{"path": "tc/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil tc/curve/cpoints"},
|
||||
{"path": "tc/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil tc/curve/adjust", "visibility": 3},
|
||||
{"path": "tc/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil tc/curve/points", "visibility": 3},
|
||||
{"path": "tc/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil tc/curve/cpoints", "visibility": 3},
|
||||
{"path": "ext", "type": "float", "visibility": 3},
|
||||
{"path": "com", "type": "float", "visibility": 3},
|
||||
{"path": "gnd", "type": "float", "visibility": 3}]},
|
||||
|
@ -365,37 +365,37 @@
|
||||
{"path": "ta/enable", "type": "bool", "readonly": false, "cmd": "tcoil ta/enable"},
|
||||
{"path": "ta/r", "type": "float"},
|
||||
{"path": "ta/curve", "type": "text", "readonly": false, "cmd": "tcoil ta/curve", "kids": 3},
|
||||
{"path": "ta/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil ta/curve/adjust"},
|
||||
{"path": "ta/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil ta/curve/points"},
|
||||
{"path": "ta/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil ta/curve/cpoints"},
|
||||
{"path": "ta/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil ta/curve/adjust", "visibility": 3},
|
||||
{"path": "ta/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil ta/curve/points", "visibility": 3},
|
||||
{"path": "ta/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil ta/curve/cpoints", "visibility": 3},
|
||||
{"path": "tb", "type": "float", "visibility": 3, "kids": 3},
|
||||
{"path": "tb/enable", "type": "bool", "readonly": false, "cmd": "tcoil tb/enable"},
|
||||
{"path": "tb/r", "type": "float"},
|
||||
{"path": "tb/curve", "type": "text", "readonly": false, "cmd": "tcoil tb/curve", "kids": 3},
|
||||
{"path": "tb/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil tb/curve/adjust"},
|
||||
{"path": "tb/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil tb/curve/points"},
|
||||
{"path": "tb/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil tb/curve/cpoints"},
|
||||
{"path": "tb/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil tb/curve/adjust", "visibility": 3},
|
||||
{"path": "tb/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil tb/curve/points", "visibility": 3},
|
||||
{"path": "tb/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil tb/curve/cpoints", "visibility": 3},
|
||||
{"path": "td", "type": "float", "visibility": 3, "kids": 3},
|
||||
{"path": "td/enable", "type": "bool", "readonly": false, "cmd": "tcoil td/enable"},
|
||||
{"path": "td/r", "type": "float"},
|
||||
{"path": "td/curve", "type": "text", "readonly": false, "cmd": "tcoil td/curve", "kids": 3},
|
||||
{"path": "td/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil td/curve/adjust"},
|
||||
{"path": "td/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil td/curve/points"},
|
||||
{"path": "td/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil td/curve/cpoints"},
|
||||
{"path": "td/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil td/curve/adjust", "visibility": 3},
|
||||
{"path": "td/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil td/curve/points", "visibility": 3},
|
||||
{"path": "td/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil td/curve/cpoints", "visibility": 3},
|
||||
{"path": "ref", "type": "float", "visibility": 3, "kids": 3},
|
||||
{"path": "ref/enable", "type": "bool", "readonly": false, "cmd": "tcoil ref/enable"},
|
||||
{"path": "ref/r", "type": "float"},
|
||||
{"path": "ref/curve", "type": "text", "readonly": false, "cmd": "tcoil ref/curve", "kids": 3},
|
||||
{"path": "ref/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil ref/curve/adjust"},
|
||||
{"path": "ref/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil ref/curve/points"},
|
||||
{"path": "ref/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil ref/curve/cpoints"},
|
||||
{"path": "ref/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil ref/curve/adjust", "visibility": 3},
|
||||
{"path": "ref/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil ref/curve/points", "visibility": 3},
|
||||
{"path": "ref/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil ref/curve/cpoints", "visibility": 3},
|
||||
{"path": "tc", "type": "float", "visibility": 3, "kids": 3},
|
||||
{"path": "tc/enable", "type": "bool", "readonly": false, "cmd": "tcoil tc/enable"},
|
||||
{"path": "tc/r", "type": "float"},
|
||||
{"path": "tc/curve", "type": "text", "readonly": false, "cmd": "tcoil tc/curve", "kids": 3},
|
||||
{"path": "tc/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil tc/curve/adjust"},
|
||||
{"path": "tc/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil tc/curve/points"},
|
||||
{"path": "tc/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil tc/curve/cpoints"},
|
||||
{"path": "tc/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil tc/curve/adjust", "visibility": 3},
|
||||
{"path": "tc/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil tc/curve/points", "visibility": 3},
|
||||
{"path": "tc/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil tc/curve/cpoints", "visibility": 3},
|
||||
{"path": "ext", "type": "float", "visibility": 3},
|
||||
{"path": "com", "type": "float", "visibility": 3},
|
||||
{"path": "gnd", "type": "float", "visibility": 3}]},
|
||||
|
@ -331,37 +331,37 @@
|
||||
{"path": "ta/enable", "type": "bool", "readonly": false, "cmd": "tcoil ta/enable"},
|
||||
{"path": "ta/r", "type": "float"},
|
||||
{"path": "ta/curve", "type": "text", "readonly": false, "cmd": "tcoil ta/curve", "kids": 3},
|
||||
{"path": "ta/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil ta/curve/adjust"},
|
||||
{"path": "ta/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil ta/curve/points"},
|
||||
{"path": "ta/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil ta/curve/cpoints"},
|
||||
{"path": "ta/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil ta/curve/adjust", "visibility": 3},
|
||||
{"path": "ta/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil ta/curve/points", "visibility": 3},
|
||||
{"path": "ta/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil ta/curve/cpoints", "visibility": 3},
|
||||
{"path": "tb", "type": "float", "visibility": 3, "kids": 3},
|
||||
{"path": "tb/enable", "type": "bool", "readonly": false, "cmd": "tcoil tb/enable"},
|
||||
{"path": "tb/r", "type": "float"},
|
||||
{"path": "tb/curve", "type": "text", "readonly": false, "cmd": "tcoil tb/curve", "kids": 3},
|
||||
{"path": "tb/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil tb/curve/adjust"},
|
||||
{"path": "tb/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil tb/curve/points"},
|
||||
{"path": "tb/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil tb/curve/cpoints"},
|
||||
{"path": "tb/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil tb/curve/adjust", "visibility": 3},
|
||||
{"path": "tb/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil tb/curve/points", "visibility": 3},
|
||||
{"path": "tb/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil tb/curve/cpoints", "visibility": 3},
|
||||
{"path": "td", "type": "float", "visibility": 3, "kids": 3},
|
||||
{"path": "td/enable", "type": "bool", "readonly": false, "cmd": "tcoil td/enable"},
|
||||
{"path": "td/r", "type": "float"},
|
||||
{"path": "td/curve", "type": "text", "readonly": false, "cmd": "tcoil td/curve", "kids": 3},
|
||||
{"path": "td/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil td/curve/adjust"},
|
||||
{"path": "td/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil td/curve/points"},
|
||||
{"path": "td/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil td/curve/cpoints"},
|
||||
{"path": "td/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil td/curve/adjust", "visibility": 3},
|
||||
{"path": "td/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil td/curve/points", "visibility": 3},
|
||||
{"path": "td/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil td/curve/cpoints", "visibility": 3},
|
||||
{"path": "ref", "type": "float", "visibility": 3, "kids": 3},
|
||||
{"path": "ref/enable", "type": "bool", "readonly": false, "cmd": "tcoil ref/enable"},
|
||||
{"path": "ref/r", "type": "float"},
|
||||
{"path": "ref/curve", "type": "text", "readonly": false, "cmd": "tcoil ref/curve", "kids": 3},
|
||||
{"path": "ref/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil ref/curve/adjust"},
|
||||
{"path": "ref/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil ref/curve/points"},
|
||||
{"path": "ref/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil ref/curve/cpoints"},
|
||||
{"path": "ref/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil ref/curve/adjust", "visibility": 3},
|
||||
{"path": "ref/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil ref/curve/points", "visibility": 3},
|
||||
{"path": "ref/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil ref/curve/cpoints", "visibility": 3},
|
||||
{"path": "tc", "type": "float", "visibility": 3, "kids": 3},
|
||||
{"path": "tc/enable", "type": "bool", "readonly": false, "cmd": "tcoil tc/enable"},
|
||||
{"path": "tc/r", "type": "float"},
|
||||
{"path": "tc/curve", "type": "text", "readonly": false, "cmd": "tcoil tc/curve", "kids": 3},
|
||||
{"path": "tc/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil tc/curve/adjust"},
|
||||
{"path": "tc/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil tc/curve/points"},
|
||||
{"path": "tc/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil tc/curve/cpoints"},
|
||||
{"path": "tc/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil tc/curve/adjust", "visibility": 3},
|
||||
{"path": "tc/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil tc/curve/points", "visibility": 3},
|
||||
{"path": "tc/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil tc/curve/cpoints", "visibility": 3},
|
||||
{"path": "ext", "type": "float", "visibility": 3},
|
||||
{"path": "com", "type": "float", "visibility": 3},
|
||||
{"path": "gnd", "type": "float", "visibility": 3}]},
|
||||
|
307
cfg/sea/ori2.config.json
Normal file
307
cfg/sea/ori2.config.json
Normal file
@ -0,0 +1,307 @@
|
||||
{"tt": {"base": "/tt", "params": [
|
||||
{"path": "", "type": "float", "readonly": false, "cmd": "run tt", "description": "tt", "kids": 19},
|
||||
{"path": "send", "type": "text", "readonly": false, "cmd": "tt send", "visibility": 3},
|
||||
{"path": "status", "type": "text", "visibility": 3},
|
||||
{"path": "is_running", "type": "int", "readonly": false, "cmd": "tt is_running", "visibility": 3},
|
||||
{"path": "mainloop", "type": "text", "readonly": false, "cmd": "tt mainloop", "visibility": 3},
|
||||
{"path": "target", "type": "float"},
|
||||
{"path": "running", "type": "int"},
|
||||
{"path": "tolerance", "type": "float", "readonly": false, "cmd": "tt tolerance"},
|
||||
{"path": "maxwait", "type": "float", "readonly": false, "cmd": "tt maxwait"},
|
||||
{"path": "settle", "type": "float", "readonly": false, "cmd": "tt settle"},
|
||||
{"path": "log", "type": "text", "readonly": false, "cmd": "tt log", "visibility": 3, "kids": 4},
|
||||
{"path": "log/mean", "type": "float", "visibility": 3},
|
||||
{"path": "log/m2", "type": "float", "visibility": 3},
|
||||
{"path": "log/stddev", "type": "float", "visibility": 3},
|
||||
{"path": "log/n", "type": "float", "visibility": 3},
|
||||
{"path": "dblctrl", "type": "bool", "readonly": false, "cmd": "tt dblctrl", "kids": 9},
|
||||
{"path": "dblctrl/tshift", "type": "float", "readonly": false, "cmd": "tt dblctrl/tshift"},
|
||||
{"path": "dblctrl/mode", "type": "enum", "enum": {"disabled": -1, "inactive": 0, "stable": 1, "up": 2, "down": 3}, "readonly": false, "cmd": "tt dblctrl/mode"},
|
||||
{"path": "dblctrl/shift_up", "type": "float"},
|
||||
{"path": "dblctrl/shift_lo", "type": "float"},
|
||||
{"path": "dblctrl/t_min", "type": "float"},
|
||||
{"path": "dblctrl/t_max", "type": "float"},
|
||||
{"path": "dblctrl/int2", "type": "float", "readonly": false, "cmd": "tt dblctrl/int2"},
|
||||
{"path": "dblctrl/prop_up", "type": "float", "readonly": false, "cmd": "tt dblctrl/prop_up"},
|
||||
{"path": "dblctrl/prop_lo", "type": "float", "readonly": false, "cmd": "tt dblctrl/prop_lo"},
|
||||
{"path": "tm", "type": "float", "kids": 4},
|
||||
{"path": "tm/curve", "type": "text", "readonly": false, "cmd": "tt tm/curve", "kids": 1},
|
||||
{"path": "tm/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt tm/curve/points", "visibility": 3},
|
||||
{"path": "tm/alarm", "type": "float", "readonly": false, "cmd": "tt tm/alarm"},
|
||||
{"path": "tm/stddev", "type": "float"},
|
||||
{"path": "tm/raw", "type": "float"},
|
||||
{"path": "ts", "type": "float", "kids": 4},
|
||||
{"path": "ts/curve", "type": "text", "readonly": false, "cmd": "tt ts/curve", "kids": 1},
|
||||
{"path": "ts/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt ts/curve/points", "visibility": 3},
|
||||
{"path": "ts/alarm", "type": "float", "readonly": false, "cmd": "tt ts/alarm"},
|
||||
{"path": "ts/stddev", "type": "float"},
|
||||
{"path": "ts/raw", "type": "float"},
|
||||
{"path": "ts_2", "type": "float", "visibility": 3, "kids": 4},
|
||||
{"path": "ts_2/curve", "type": "text", "readonly": false, "cmd": "tt ts_2/curve", "visibility": 3, "kids": 1},
|
||||
{"path": "ts_2/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt ts_2/curve/points", "visibility": 3},
|
||||
{"path": "ts_2/alarm", "type": "float", "readonly": false, "cmd": "tt ts_2/alarm", "visibility": 3},
|
||||
{"path": "ts_2/stddev", "type": "float", "visibility": 3},
|
||||
{"path": "ts_2/raw", "type": "float", "visibility": 3},
|
||||
{"path": "set", "type": "float", "readonly": false, "cmd": "tt set", "kids": 18},
|
||||
{"path": "set/mode", "type": "enum", "enum": {"disabled": -1, "off": 0, "controlling": 1, "manual": 2}, "readonly": false, "cmd": "tt set/mode"},
|
||||
{"path": "set/reg", "type": "float"},
|
||||
{"path": "set/ramp", "type": "float", "readonly": false, "cmd": "tt set/ramp", "description": "maximum ramp in K/min (0: ramp off)"},
|
||||
{"path": "set/wramp", "type": "float", "readonly": false, "cmd": "tt set/wramp"},
|
||||
{"path": "set/smooth", "type": "float", "readonly": false, "cmd": "tt set/smooth", "description": "smooth time (minutes)"},
|
||||
{"path": "set/channel", "type": "text", "readonly": false, "cmd": "tt set/channel"},
|
||||
{"path": "set/limit", "type": "float", "readonly": false, "cmd": "tt set/limit"},
|
||||
{"path": "set/resist", "type": "float", "readonly": false, "cmd": "tt set/resist"},
|
||||
{"path": "set/maxheater", "type": "text", "readonly": false, "cmd": "tt set/maxheater", "description": "maximum heater limit, units should be given without space: W, mW, A, mA"},
|
||||
{"path": "set/linearpower", "type": "float", "readonly": false, "cmd": "tt set/linearpower", "description": "when not 0, it is the maximum effective power, and the power is linear to the heater output"},
|
||||
{"path": "set/maxpowerlim", "type": "float", "description": "the maximum power limit (before any booster or converter)"},
|
||||
{"path": "set/maxpower", "type": "float", "readonly": false, "cmd": "tt set/maxpower", "description": "maximum power [W]"},
|
||||
{"path": "set/maxcurrent", "type": "float", "description": "the maximum current before any booster or converter"},
|
||||
{"path": "set/manualpower", "type": "float", "readonly": false, "cmd": "tt set/manualpower"},
|
||||
{"path": "set/power", "type": "float"},
|
||||
{"path": "set/prop", "type": "float", "readonly": false, "cmd": "tt set/prop", "description": "bigger means more gain"},
|
||||
{"path": "set/integ", "type": "float", "readonly": false, "cmd": "tt set/integ", "description": "bigger means faster"},
|
||||
{"path": "set/deriv", "type": "float", "readonly": false, "cmd": "tt set/deriv"},
|
||||
{"path": "display", "type": "text", "readonly": false, "cmd": "tt display"},
|
||||
{"path": "remote", "type": "bool"}]},
|
||||
|
||||
"cc": {"base": "/cc", "params": [
|
||||
{"path": "", "type": "bool", "kids": 96},
|
||||
{"path": "send", "type": "text", "readonly": false, "cmd": "cc send", "visibility": 3},
|
||||
{"path": "status", "type": "text", "visibility": 3},
|
||||
{"path": "autodevice", "type": "bool", "readonly": false, "cmd": "cc autodevice"},
|
||||
{"path": "fav", "type": "bool", "readonly": false, "cmd": "cc fav"},
|
||||
{"path": "f", "type": "float"},
|
||||
{"path": "fs", "type": "enum", "enum": {"ok": 0, "no_sens": 1}, "readonly": false, "cmd": "cc fs"},
|
||||
{"path": "mav", "type": "bool", "readonly": false, "cmd": "cc mav"},
|
||||
{"path": "fm", "type": "enum", "enum": {"idle": 0, "opening": 1, "closing": 2, "opened": 3, "closed": 4, "no_motor": 5}},
|
||||
{"path": "fa", "type": "enum", "enum": {"fixed": 0, "controlled": 1, "automatic": 2, "offline": 3}, "readonly": false, "cmd": "cc fa"},
|
||||
{"path": "mp", "type": "float", "readonly": false, "cmd": "cc mp"},
|
||||
{"path": "msp", "type": "float"},
|
||||
{"path": "mmp", "type": "float"},
|
||||
{"path": "mc", "type": "float", "readonly": false, "cmd": "cc mc"},
|
||||
{"path": "mfc", "type": "float", "readonly": false, "cmd": "cc mfc"},
|
||||
{"path": "moc", "type": "float", "readonly": false, "cmd": "cc moc"},
|
||||
{"path": "mtc", "type": "float", "readonly": false, "cmd": "cc mtc"},
|
||||
{"path": "mtl", "type": "float"},
|
||||
{"path": "mft", "type": "float", "readonly": false, "cmd": "cc mft"},
|
||||
{"path": "mt", "type": "float"},
|
||||
{"path": "mo", "type": "float"},
|
||||
{"path": "mcr", "type": "float"},
|
||||
{"path": "mot", "type": "float"},
|
||||
{"path": "mw", "type": "float", "readonly": false, "cmd": "cc mw", "description": "correction pulse after automatic open"},
|
||||
{"path": "hav", "type": "enum", "type": "enum", "enum": {"none": 0, "int": 1, "ext": 2}, "readonly": false, "cmd": "cc hav"},
|
||||
{"path": "h", "type": "float"},
|
||||
{"path": "hr", "type": "float"},
|
||||
{"path": "hc", "type": "float"},
|
||||
{"path": "hu", "type": "float"},
|
||||
{"path": "hh", "type": "float", "readonly": false, "cmd": "cc hh"},
|
||||
{"path": "hl", "type": "float", "readonly": false, "cmd": "cc hl"},
|
||||
{"path": "htf", "type": "float", "readonly": false, "cmd": "cc htf", "description": "meas. period in fast mode"},
|
||||
{"path": "hts", "type": "float", "readonly": false, "cmd": "cc hts", "description": "meas. period in slow mode"},
|
||||
{"path": "hd", "type": "float", "readonly": false, "cmd": "cc hd"},
|
||||
{"path": "hwr", "type": "float", "readonly": false, "cmd": "cc hwr"},
|
||||
{"path": "hem", "type": "float", "readonly": false, "cmd": "cc hem", "description": "sensor length in mm from top to empty pos."},
|
||||
{"path": "hfu", "type": "float", "readonly": false, "cmd": "cc hfu", "description": "sensor length in mm from top to full pos."},
|
||||
{"path": "hcd", "type": "enum", "enum": {"stop": 0, "fill": 1, "off": 2, "auto": 3, "manual": 7}, "readonly": false, "cmd": "cc hcd"},
|
||||
{"path": "hv", "type": "enum", "enum": {"fill_valve_off": 0, "filling": 1, "no_fill_valve": 2, "timeout": 3, "timeout1": 4}},
|
||||
{"path": "hsf", "type": "enum", "enum": {"sens_ok": 0, "sens_warm": 1, "no_sens": 2, "timeout": 3, "not_yet_read": 4, "disabled": 5}},
|
||||
{"path": "ha", "type": "bool", "readonly": false, "cmd": "cc ha"},
|
||||
{"path": "hm", "type": "bool"},
|
||||
{"path": "hf", "type": "enum", "enum": {"slow": 0, "fast": 1}, "readonly": false, "cmd": "cc hf"},
|
||||
{"path": "hbe", "type": "bool", "readonly": false, "cmd": "cc hbe"},
|
||||
{"path": "hmf", "type": "float"},
|
||||
{"path": "hms", "type": "float"},
|
||||
{"path": "hit", "type": "float", "readonly": false, "cmd": "cc hit"},
|
||||
{"path": "hft", "type": "int", "readonly": false, "cmd": "cc hft"},
|
||||
{"path": "hea", "type": "enum", "enum": {"0": 0, "1": 1, "6": 2}, "readonly": false, "cmd": "cc hea"},
|
||||
{"path": "hch", "type": "int", "readonly": false, "cmd": "cc hch", "visibility": 3},
|
||||
{"path": "hwr0", "type": "float", "readonly": false, "cmd": "cc hwr0", "visibility": 3},
|
||||
{"path": "hem0", "type": "float", "readonly": false, "cmd": "cc hem0", "description": "sensor length in mm from top to empty pos.", "visibility": 3},
|
||||
{"path": "hfu0", "type": "float", "readonly": false, "cmd": "cc hfu0", "description": "sensor length in mm from top to full pos.", "visibility": 3},
|
||||
{"path": "hd0", "type": "float", "readonly": false, "cmd": "cc hd0", "description": "external sensor drive current (mA)", "visibility": 3},
|
||||
{"path": "h0", "type": "float", "visibility": 3},
|
||||
{"path": "hs0", "type": "enum", "enum": {"sens_ok": 0, "sens_warm": 1, "no_sens": 2, "timeout": 3, "not_yet_read": 4, "disabled": 5}, "visibility": 3},
|
||||
{"path": "h1", "type": "float", "visibility": 3},
|
||||
{"path": "hs1", "type": "enum", "enum": {"sens_ok": 0, "sens_warm": 1, "no_sens": 2, "timeout": 3, "not_yet_read": 4, "disabled": 5}, "visibility": 3},
|
||||
{"path": "h2", "type": "float", "visibility": 3},
|
||||
{"path": "hs2", "type": "enum", "enum": {"sens_ok": 0, "sens_warm": 1, "no_sens": 2, "timeout": 3, "not_yet_read": 4, "disabled": 5}, "visibility": 3},
|
||||
{"path": "h3", "type": "float", "visibility": 3},
|
||||
{"path": "hs3", "type": "enum", "enum": {"sens_ok": 0, "sens_warm": 1, "no_sens": 2, "timeout": 3, "not_yet_read": 4, "disabled": 5}, "visibility": 3},
|
||||
{"path": "h4", "type": "float", "visibility": 3},
|
||||
{"path": "hs4", "type": "enum", "enum": {"sens_ok": 0, "sens_warm": 1, "no_sens": 2, "timeout": 3, "not_yet_read": 4, "disabled": 5}, "visibility": 3},
|
||||
{"path": "h5", "type": "float", "visibility": 3},
|
||||
{"path": "hs5", "type": "enum", "enum": {"sens_ok": 0, "sens_warm": 1, "no_sens": 2, "timeout": 3, "not_yet_read": 4, "disabled": 5}, "visibility": 3},
|
||||
{"path": "hfb", "type": "float"},
|
||||
{"path": "nav", "type": "enum", "type": "enum", "enum": {"none": 0, "int": 1, "ext": 2}, "readonly": false, "cmd": "cc nav"},
|
||||
{"path": "nu", "type": "float"},
|
||||
{"path": "nl", "type": "float"},
|
||||
{"path": "nth", "type": "float", "readonly": false, "cmd": "cc nth"},
|
||||
{"path": "ntc", "type": "float", "readonly": false, "cmd": "cc ntc"},
|
||||
{"path": "ntm", "type": "float", "readonly": false, "cmd": "cc ntm"},
|
||||
{"path": "ns", "type": "enum", "enum": {"sens_ok": 0, "no_sens": 1, "short_circuit": 2, "upside_down": 3, "sens_warm": 4, "empty": 5}},
|
||||
{"path": "na", "type": "bool", "readonly": false, "cmd": "cc na"},
|
||||
{"path": "nv", "type": "enum", "enum": {"fill_valve_off": 0, "filling": 1, "no_fill_valve": 2, "timeout": 3, "timeout1": 4, "boost": 5}},
|
||||
{"path": "nc", "type": "enum", "enum": {"stop": 0, "fill": 1, "off": 2, "auto": 3}, "readonly": false, "cmd": "cc nc"},
|
||||
{"path": "nfb", "type": "float"},
|
||||
{"path": "cda", "type": "float"},
|
||||
{"path": "cdb", "type": "float"},
|
||||
{"path": "cba", "type": "float"},
|
||||
{"path": "cbb", "type": "float"},
|
||||
{"path": "cvs", "type": "int"},
|
||||
{"path": "csp", "type": "int"},
|
||||
{"path": "cdv", "type": "text", "readonly": false, "cmd": "cc cdv"},
|
||||
{"path": "cic", "type": "text", "readonly": false, "cmd": "cc cic"},
|
||||
{"path": "cin", "type": "text"},
|
||||
{"path": "cds", "type": "enum", "enum": {"local": 0, "remote": 1, "loading": 2, "by_code": 3, "by_touch": 4}, "readonly": false, "cmd": "cc cds"},
|
||||
{"path": "timing", "type": "bool", "readonly": false, "cmd": "cc timing"},
|
||||
{"path": "tc", "type": "float", "visibility": 3},
|
||||
{"path": "tn", "type": "float", "visibility": 3},
|
||||
{"path": "th", "type": "float", "visibility": 3},
|
||||
{"path": "tf", "type": "float", "visibility": 3},
|
||||
{"path": "tm", "type": "float", "visibility": 3},
|
||||
{"path": "tv", "type": "float", "visibility": 3},
|
||||
{"path": "tq", "type": "float", "visibility": 3},
|
||||
{"path": "bdl", "type": "float", "readonly": false, "cmd": "cc bdl"}]},
|
||||
|
||||
"nv": {"base": "/nv", "params": [
|
||||
{"path": "", "type": "enum", "enum": {"fixed": 0, "controlled": 1, "automatic": 2, "close": 3, "open": 4}, "readonly": false, "cmd": "nv", "kids": 11},
|
||||
{"path": "send", "type": "text", "readonly": false, "cmd": "nv send", "visibility": 3},
|
||||
{"path": "status", "type": "text", "visibility": 3},
|
||||
{"path": "motstat", "type": "enum", "enum": {"idle": 0, "opening": 1, "closing": 2, "opened": 3, "closed": 4, "no_motor": 5}},
|
||||
{"path": "flow", "type": "float"},
|
||||
{"path": "set", "type": "float", "readonly": false, "cmd": "nv set"},
|
||||
{"path": "flowmax", "type": "float", "readonly": false, "cmd": "nv flowmax"},
|
||||
{"path": "flowp", "type": "float"},
|
||||
{"path": "span", "type": "float"},
|
||||
{"path": "ctrl", "type": "none", "kids": 13},
|
||||
{"path": "ctrl/regtext", "type": "text"},
|
||||
{"path": "ctrl/prop_o", "type": "float", "readonly": false, "cmd": "nv ctrl/prop_o", "description": "prop [sec/mbar] when opening. above 4 mbar a 10 times lower value is used"},
|
||||
{"path": "ctrl/prop_c", "type": "float", "readonly": false, "cmd": "nv ctrl/prop_c", "description": "prop [sec/mbar] when closing. above 4 mbar a 10 times lower value is used"},
|
||||
{"path": "ctrl/deriv_o", "type": "float", "readonly": false, "cmd": "nv ctrl/deriv_o", "description": "convergence target time [sec] when opening"},
|
||||
{"path": "ctrl/deriv_c", "type": "float", "readonly": false, "cmd": "nv ctrl/deriv_c", "description": "convergence target time [sec] when closing"},
|
||||
{"path": "ctrl/minpulse_o", "type": "float", "readonly": false, "cmd": "nv ctrl/minpulse_o", "description": "minimum close pulse [sec]"},
|
||||
{"path": "ctrl/minpulse_c", "type": "float", "readonly": false, "cmd": "nv ctrl/minpulse_c", "description": "standard close pulse [sec]"},
|
||||
{"path": "ctrl/hystpulse_o", "type": "float", "readonly": false, "cmd": "nv ctrl/hystpulse_o", "description": "motor pulse to overcome hysteresis when opening"},
|
||||
{"path": "ctrl/hystpulse_c", "type": "float", "readonly": false, "cmd": "nv ctrl/hystpulse_c", "description": "motor pulse to overcome hysteresis when closing"},
|
||||
{"path": "ctrl/tol", "type": "float", "readonly": false, "cmd": "nv ctrl/tol", "description": "valid below 3 mbar"},
|
||||
{"path": "ctrl/tolhigh", "type": "float", "readonly": false, "cmd": "nv ctrl/tolhigh", "description": "valid above 4 mbar"},
|
||||
{"path": "ctrl/openpulse", "type": "float", "readonly": false, "cmd": "nv ctrl/openpulse", "description": "time to open from completely closed to a significant opening"},
|
||||
{"path": "ctrl/adjust_minpulse", "type": "bool", "readonly": false, "cmd": "nv ctrl/adjust_minpulse", "description": "adjust minpulse automatically"},
|
||||
{"path": "autoflow", "type": "none", "kids": 24},
|
||||
{"path": "autoflow/suspended", "type": "bool", "readonly": false, "cmd": "nv autoflow/suspended"},
|
||||
{"path": "autoflow/prop", "type": "float", "readonly": false, "cmd": "nv autoflow/prop"},
|
||||
{"path": "autoflow/flowstd", "type": "float", "readonly": false, "cmd": "nv autoflow/flowstd"},
|
||||
{"path": "autoflow/flowlim", "type": "float", "readonly": false, "cmd": "nv autoflow/flowlim"},
|
||||
{"path": "autoflow/smooth", "type": "float", "readonly": false, "cmd": "nv autoflow/smooth"},
|
||||
{"path": "autoflow/difSize", "type": "float", "readonly": false, "cmd": "nv autoflow/difSize"},
|
||||
{"path": "autoflow/difRange", "type": "float", "readonly": false, "cmd": "nv autoflow/difRange"},
|
||||
{"path": "autoflow/flowSize", "type": "float", "readonly": false, "cmd": "nv autoflow/flowSize"},
|
||||
{"path": "autoflow/convTime", "type": "float", "readonly": false, "cmd": "nv autoflow/convTime"},
|
||||
{"path": "autoflow/Tmin", "type": "float", "readonly": false, "cmd": "nv autoflow/Tmin"},
|
||||
{"path": "autoflow/script", "type": "text", "readonly": false, "cmd": "nv autoflow/script"},
|
||||
{"path": "autoflow/getTemp", "type": "text", "readonly": false, "cmd": "nv autoflow/getTemp"},
|
||||
{"path": "autoflow/getTset", "type": "text", "readonly": false, "cmd": "nv autoflow/getTset"},
|
||||
{"path": "autoflow/getFlow", "type": "text", "readonly": false, "cmd": "nv autoflow/getFlow"},
|
||||
{"path": "autoflow/difBuf", "type": "text"},
|
||||
{"path": "autoflow/flowBuf", "type": "text"},
|
||||
{"path": "autoflow/flowset", "type": "float"},
|
||||
{"path": "autoflow/flowmin", "type": "float"},
|
||||
{"path": "autoflow/flowmax", "type": "float"},
|
||||
{"path": "autoflow/difmin", "type": "float"},
|
||||
{"path": "autoflow/difmax", "type": "float"},
|
||||
{"path": "autoflow/setmin", "type": "float"},
|
||||
{"path": "autoflow/setmax", "type": "float"},
|
||||
{"path": "autoflow/flowtarget", "type": "float"},
|
||||
{"path": "calib", "type": "none", "kids": 2},
|
||||
{"path": "calib/ln_per_min_per_mbar", "type": "float", "readonly": false, "cmd": "nv calib/ln_per_min_per_mbar"},
|
||||
{"path": "calib/mbar_offset", "type": "float", "readonly": false, "cmd": "nv calib/mbar_offset"}]},
|
||||
|
||||
"ln2fill": {"base": "/ln2fill", "params": [
|
||||
{"path": "", "type": "enum", "enum": {"watching": 0, "fill": 1, "inactive": 2}, "readonly": false, "cmd": "ln2fill", "kids": 3},
|
||||
{"path": "send", "type": "text", "readonly": false, "cmd": "ln2fill send", "visibility": 3},
|
||||
{"path": "status", "type": "text", "visibility": 3},
|
||||
{"path": "state", "type": "text"}]},
|
||||
|
||||
"hefill": {"base": "/hefill", "params": [
|
||||
{"path": "", "type": "enum", "enum": {"watching": 0, "fill": 1, "inactive": 2, "manualfill": 3}, "readonly": false, "cmd": "hefill", "kids": 6},
|
||||
{"path": "send", "type": "text", "readonly": false, "cmd": "hefill send", "visibility": 3},
|
||||
{"path": "status", "type": "text", "visibility": 3},
|
||||
{"path": "fast", "type": "enum", "enum": {"slow": 0, "fast": 1}, "readonly": false, "cmd": "cc hf"},
|
||||
{"path": "state", "type": "text"},
|
||||
{"path": "hefull", "type": "float", "readonly": false, "cmd": "cc hh"},
|
||||
{"path": "helow", "type": "float", "readonly": false, "cmd": "cc hl"}]},
|
||||
|
||||
"hepump": {"base": "/hepump", "params": [
|
||||
{"path": "", "type": "enum", "enum": {"neodry": 8, "xds35_auto": 0, "xds35_manual": 1, "sv65": 2, "other": 3, "no": -1}, "readonly": false, "cmd": "hepump", "description": "xds35: scroll pump, sv65: leybold", "kids": 10},
|
||||
{"path": "send", "type": "text", "readonly": false, "cmd": "hepump send", "visibility": 3},
|
||||
{"path": "status", "type": "text", "visibility": 3},
|
||||
{"path": "running", "type": "bool", "readonly": false, "cmd": "hepump running"},
|
||||
{"path": "eco", "type": "bool", "readonly": false, "cmd": "hepump eco", "visibility": 3},
|
||||
{"path": "auto", "type": "bool", "readonly": false, "cmd": "hepump auto", "visibility": 3},
|
||||
{"path": "valve", "type": "enum", "enum": {"closed": 0, "closing": 1, "opening": 2, "opened": 3, "undefined": 4}, "readonly": false, "cmd": "hepump valve"},
|
||||
{"path": "eco_t_lim", "type": "float", "readonly": false, "cmd": "hepump eco_t_lim", "description": "switch off eco mode when T_set < eco_t_lim and T < eco_t_lim * 2", "visibility": 3},
|
||||
{"path": "calib", "type": "float", "readonly": false, "cmd": "hepump calib", "visibility": 3},
|
||||
{"path": "health", "type": "float"}]},
|
||||
|
||||
"hemot": {"base": "/hepump/hemot", "params": [
|
||||
{"path": "", "type": "float", "readonly": false, "cmd": "run hemot", "visibility": 3, "kids": 30},
|
||||
{"path": "send", "type": "text", "readonly": false, "cmd": "hemot send", "visibility": 3},
|
||||
{"path": "status", "type": "text", "visibility": 3},
|
||||
{"path": "is_running", "type": "int", "readonly": false, "cmd": "hemot is_running", "visibility": 3},
|
||||
{"path": "pos", "type": "float"},
|
||||
{"path": "encoder", "type": "float"},
|
||||
{"path": "zero", "type": "float", "readonly": false, "cmd": "hemot zero"},
|
||||
{"path": "lowerlimit", "type": "float", "readonly": false, "cmd": "hemot lowerlimit"},
|
||||
{"path": "upperlimit", "type": "float", "readonly": false, "cmd": "hemot upperlimit"},
|
||||
{"path": "disablelimits", "type": "bool", "readonly": false, "cmd": "hemot disablelimits"},
|
||||
{"path": "verbose", "type": "bool", "readonly": false, "cmd": "hemot verbose"},
|
||||
{"path": "target", "type": "float"},
|
||||
{"path": "runstate", "type": "enum", "enum": {"idle": 0, "running": 1, "finished": 2, "error": 3}},
|
||||
{"path": "precision", "type": "float", "readonly": false, "cmd": "hemot precision"},
|
||||
{"path": "maxencdif", "type": "float", "readonly": false, "cmd": "hemot maxencdif"},
|
||||
{"path": "id", "type": "float", "readonly": false, "cmd": "hemot id"},
|
||||
{"path": "pump_number", "type": "float", "readonly": false, "cmd": "hemot pump_number"},
|
||||
{"path": "init", "type": "float", "readonly": false, "cmd": "hemot init"},
|
||||
{"path": "maxspeed", "type": "float", "readonly": false, "cmd": "hemot maxspeed"},
|
||||
{"path": "acceleration", "type": "float", "readonly": false, "cmd": "hemot acceleration"},
|
||||
{"path": "maxcurrent", "type": "float", "readonly": false, "cmd": "hemot maxcurrent"},
|
||||
{"path": "standbycurrent", "type": "float", "readonly": false, "cmd": "hemot standbycurrent"},
|
||||
{"path": "freewheeling", "type": "bool", "readonly": false, "cmd": "hemot freewheeling"},
|
||||
{"path": "output0", "type": "bool", "readonly": false, "cmd": "hemot output0"},
|
||||
{"path": "output1", "type": "bool", "readonly": false, "cmd": "hemot output1"},
|
||||
{"path": "input3", "type": "bool"},
|
||||
{"path": "pullup", "type": "float", "readonly": false, "cmd": "hemot pullup"},
|
||||
{"path": "nopumpfeedback", "type": "bool", "readonly": false, "cmd": "hemot nopumpfeedback"},
|
||||
{"path": "eeprom", "type": "enum", "enum": {"ok": 0, "dirty": 1, "save": 2, "load": 3}, "readonly": false, "cmd": "hemot eeprom"},
|
||||
{"path": "customadr", "type": "text", "readonly": false, "cmd": "hemot customadr"},
|
||||
{"path": "custompar", "type": "float", "readonly": false, "cmd": "hemot custompar"}]},
|
||||
|
||||
"nvflow": {"base": "/nvflow", "params": [
|
||||
{"path": "", "type": "float", "kids": 7},
|
||||
{"path": "send", "type": "text", "readonly": false, "cmd": "nvflow send", "visibility": 3},
|
||||
{"path": "status", "type": "text", "visibility": 3},
|
||||
{"path": "stddev", "type": "float"},
|
||||
{"path": "nsamples", "type": "int", "readonly": false, "cmd": "nvflow nsamples"},
|
||||
{"path": "offset", "type": "float", "readonly": false, "cmd": "nvflow offset"},
|
||||
{"path": "scale", "type": "float", "readonly": false, "cmd": "nvflow scale"},
|
||||
{"path": "save", "type": "bool", "readonly": false, "cmd": "nvflow save", "description": "unchecked: current calib is not saved. set checked: save calib"}]},
|
||||
|
||||
"table": {"base": "/table", "params": [
|
||||
{"path": "", "type": "none", "kids": 17},
|
||||
{"path": "send", "type": "text", "readonly": false, "cmd": "table send", "visibility": 3},
|
||||
{"path": "status", "type": "text", "visibility": 3},
|
||||
{"path": "fix_tt_set_prop", "type": "bool", "readonly": false, "cmd": "table fix_tt_set_prop"},
|
||||
{"path": "val_tt_set_prop", "type": "float"},
|
||||
{"path": "tbl_tt_set_prop", "type": "text", "readonly": false, "cmd": "table tbl_tt_set_prop", "description": "enter value pair separated with colon T1:par1 T2:par2 ..."},
|
||||
{"path": "fix_tt_set_integ", "type": "bool", "readonly": false, "cmd": "table fix_tt_set_integ"},
|
||||
{"path": "val_tt_set_integ", "type": "float"},
|
||||
{"path": "tbl_tt_set_integ", "type": "text", "readonly": false, "cmd": "table tbl_tt_set_integ", "description": "enter value pair separated with colon T1:par1 T2:par2 ..."},
|
||||
{"path": "fix_tt_dblctrl_int2", "type": "bool", "readonly": false, "cmd": "table fix_tt_dblctrl_int2"},
|
||||
{"path": "val_tt_dblctrl_int2", "type": "float"},
|
||||
{"path": "tbl_tt_dblctrl_int2", "type": "text", "readonly": false, "cmd": "table tbl_tt_dblctrl_int2", "description": "enter value pair separated with colon T1:par1 T2:par2 ..."},
|
||||
{"path": "fix_tt_dblctrl_prop_up", "type": "bool", "readonly": false, "cmd": "table fix_tt_dblctrl_prop_up"},
|
||||
{"path": "val_tt_dblctrl_prop_up", "type": "float"},
|
||||
{"path": "tbl_tt_dblctrl_prop_up", "type": "text", "readonly": false, "cmd": "table tbl_tt_dblctrl_prop_up", "description": "enter value pair separated with colon T1:par1 T2:par2 ..."},
|
||||
{"path": "fix_tt_dblctrl_prop_lo", "type": "bool", "readonly": false, "cmd": "table fix_tt_dblctrl_prop_lo"},
|
||||
{"path": "val_tt_dblctrl_prop_lo", "type": "float"},
|
||||
{"path": "tbl_tt_dblctrl_prop_lo", "type": "text", "readonly": false, "cmd": "table tbl_tt_dblctrl_prop_lo", "description": "enter value pair separated with colon T1:par1 T2:par2 ..."}]}}
|
@ -20,8 +20,8 @@ Mod('tsam',
|
||||
|
||||
Mod('ts',
|
||||
'frappy_psi.parmod.Converging',
|
||||
'virtual stick T',
|
||||
meaning=['temperature', 30],
|
||||
'stick T (controlled)',
|
||||
meaning=['temperature', 25],
|
||||
unit='K',
|
||||
read='tsam.value',
|
||||
write='tsam.setsamp',
|
||||
|
@ -16,7 +16,7 @@ Mod('ts',
|
||||
json_file='ma6.config.json',
|
||||
sea_object='tt',
|
||||
rel_paths=['ts', 'setsamp'],
|
||||
meaning=['temperature', 20],
|
||||
meaning=['temperature', 30],
|
||||
)
|
||||
|
||||
|
||||
@ -24,11 +24,10 @@ Mod('ts',
|
||||
Mod('ts',
|
||||
'frappy_psi.parmod.Converging',
|
||||
'drivable stick T using setsamp',
|
||||
meaning=['temperature', 30],
|
||||
meaning=['temperature', 25],
|
||||
unit='K',
|
||||
read='tsam.value',
|
||||
write='tsam.setsamp',
|
||||
meaning=['temperature', 20],
|
||||
settling_time=20,
|
||||
tolerance=1,
|
||||
)
|
||||
|
@ -13,8 +13,6 @@ Mod('ts',
|
||||
'frappy_psi.sea.SeaReadable', '',
|
||||
meaning=['temperature', 30],
|
||||
io='sea_stick',
|
||||
sea_object='tt',
|
||||
sea_path='tt/ts',
|
||||
json_file='ma7.config.json',
|
||||
rel_paths=['ts'],
|
||||
)
|
||||
|
||||
|
75
debian/changelog
vendored
75
debian/changelog
vendored
@ -1,3 +1,78 @@
|
||||
frappy-core (0.20.4) jammy; urgency=medium
|
||||
|
||||
[ Georg Brandl ]
|
||||
* remove unused file
|
||||
|
||||
[ Markus Zolliker ]
|
||||
* frappy.lib.multievent: avoid deadlock
|
||||
|
||||
[ Jens Krüger ]
|
||||
* PSI: Fix import error on ThermoFisher module
|
||||
|
||||
[ Markus Zolliker ]
|
||||
* frappy.client: catch all errors in handleError callback
|
||||
|
||||
[ Jens Krüger ]
|
||||
* Lib/config: Create a list of pathes only for confdir
|
||||
|
||||
-- Georg Brandl <jenkins@frm2.tum.de> Thu, 14 Nov 2024 14:43:54 +0100
|
||||
|
||||
frappy-core (0.20.3) jammy; urgency=medium
|
||||
|
||||
[ Georg Brandl ]
|
||||
* fixup test for cfg_editor utils to run from non-checkout, and fix names, and remove example code
|
||||
|
||||
[ Alexander Zaft ]
|
||||
* add generalConfig to etc
|
||||
|
||||
-- Georg Brandl <jenkins@frm2.tum.de> Thu, 07 Nov 2024 10:57:11 +0100
|
||||
|
||||
frappy-core (0.20.2) jammy; urgency=medium
|
||||
|
||||
[ Georg Brandl ]
|
||||
* pylint: do not try to infer too much
|
||||
|
||||
[ Alexander Zaft ]
|
||||
* test_server: basic description checks
|
||||
* simulation: fix extra_params default, ccidu1 cfg
|
||||
* sim: make amagnet sim cfg startable again
|
||||
* server: fix positional argument lint
|
||||
* server: show interfaces as custom property
|
||||
* core: fix Dispatcher and SECNode opts handling
|
||||
|
||||
[ Markus Zolliker ]
|
||||
* frappy_psi.sea: bugfix: revert change of updateEvent to udpateItem
|
||||
* fix playground
|
||||
|
||||
[ Alexander Zaft ]
|
||||
* config: allow using Prop(...)
|
||||
* config: fix typo
|
||||
* Revert "config: allow using Prop(...)"
|
||||
* generalconfig: streamlined config discovery
|
||||
|
||||
[ Markus Zolliker ]
|
||||
* better order of accessibles: 'value' 'status' and 'target' first
|
||||
|
||||
[ Alexander Zaft ]
|
||||
* server: fix windows ctrl-c
|
||||
|
||||
[ Georg Brandl ]
|
||||
* systemd: enable indication of reloading/stopping
|
||||
|
||||
[ Alexander Zaft ]
|
||||
* server: service discovery over UDP.
|
||||
|
||||
[ Markus Zolliker ]
|
||||
* generalConfig: fix the case when confdir is a list of paths
|
||||
|
||||
[ Georg Brandl ]
|
||||
* server: better handling of cfgfile argument
|
||||
|
||||
[ Alexander Zaft ]
|
||||
* fix frappy-server cfgfiles command
|
||||
|
||||
-- Georg Brandl <jenkins@frm2.tum.de> Wed, 06 Nov 2024 10:40:26 +0100
|
||||
|
||||
frappy-core (0.20.1) jammy; urgency=medium
|
||||
|
||||
* gui: do not add a console logger when there is no sys.stdout
|
||||
|
2
debian/frappy-core.install
vendored
2
debian/frappy-core.install
vendored
@ -1,6 +1,7 @@
|
||||
usr/bin/frappy-cli
|
||||
usr/bin/frappy-server
|
||||
usr/bin/frappy-play
|
||||
usr/bin/frappy-scan
|
||||
usr/lib/python3.*/dist-packages/frappy/*.py
|
||||
usr/lib/python3.*/dist-packages/frappy/__pycache__
|
||||
usr/lib/python3.*/dist-packages/frappy/lib
|
||||
@ -11,3 +12,4 @@ usr/lib/python3.*/dist-packages/frappy/RELEASE-VERSION
|
||||
usr/lib/python3.*/dist-packages/frappy_demo
|
||||
lib/systemd
|
||||
var/log/frappy
|
||||
etc/frappy/generalConfig.cfg
|
||||
|
1
debian/rules
vendored
1
debian/rules
vendored
@ -11,6 +11,7 @@ override_dh_install:
|
||||
rmdir debian/tmp
|
||||
mv debian/python3-frappy debian/tmp
|
||||
|
||||
install -m644 -Dt debian/tmp/etc/frappy etc/generalConfig.cfg
|
||||
dh_install -i -O--buildsystem=pybuild
|
||||
dh_missing --fail-missing
|
||||
|
||||
|
4
etc/generalConfig.cfg
Normal file
4
etc/generalConfig.cfg
Normal file
@ -0,0 +1,4 @@
|
||||
[FRAPPY]
|
||||
logdir = /var/log
|
||||
piddir = /var/run/frappy
|
||||
confdir = /etc/frappy
|
@ -22,8 +22,6 @@
|
||||
# *****************************************************************************
|
||||
"""general SECoP client"""
|
||||
|
||||
# pylint: disable=too-many-positional-arguments
|
||||
|
||||
import json
|
||||
import queue
|
||||
import re
|
||||
@ -481,7 +479,10 @@ class SecopClient(ProxyClient):
|
||||
continue
|
||||
except Exception as e:
|
||||
e.args = (f'error handling SECoP message {reply!r}: {e}',)
|
||||
self.callback(None, 'handleError', e)
|
||||
try:
|
||||
self.callback(None, 'handleError', e)
|
||||
except Exception:
|
||||
pass
|
||||
continue
|
||||
try:
|
||||
key = action, ident
|
||||
|
@ -56,10 +56,12 @@ class Param(dict):
|
||||
kwds['value'] = value
|
||||
super().__init__(**kwds)
|
||||
|
||||
|
||||
class Group(tuple):
|
||||
def __new__(cls, *args):
|
||||
return super().__new__(cls, args)
|
||||
|
||||
|
||||
class Mod(dict):
|
||||
def __init__(self, name, cls, description, **kwds):
|
||||
super().__init__(
|
||||
@ -70,7 +72,8 @@ class Mod(dict):
|
||||
|
||||
# matches name from spec
|
||||
if not re.match(r'^[a-zA-Z]\w{0,62}$', name, re.ASCII):
|
||||
raise ConfigError(f'Not a valid SECoP Module name: "{name}". Does it only contain letters, numbers and underscores?')
|
||||
raise ConfigError(f'Not a valid SECoP Module name: "{name}".'
|
||||
' Does it only contain letters, numbers and underscores?')
|
||||
# Make parameters out of all keywords
|
||||
groups = {}
|
||||
for key, val in kwds.items():
|
||||
@ -85,6 +88,7 @@ class Mod(dict):
|
||||
for member in members:
|
||||
self[member]['group'] = group
|
||||
|
||||
|
||||
class Collector:
|
||||
def __init__(self, cls):
|
||||
self.list = []
|
||||
@ -120,12 +124,14 @@ class Config(dict):
|
||||
def merge_modules(self, other):
|
||||
""" merges only the modules from 'other' into 'self'"""
|
||||
self.ambiguous |= self.module_names & other.module_names
|
||||
equipment_id = other['node']['equipment_id']
|
||||
for name, mod in other.items():
|
||||
if name == 'node':
|
||||
continue
|
||||
if name not in self.module_names:
|
||||
self.module_names.add(name)
|
||||
self[name] = mod
|
||||
mod['original_id'] = equipment_id
|
||||
|
||||
|
||||
def process_file(filename, log):
|
||||
@ -172,8 +178,8 @@ def load_config(cfgfiles, log):
|
||||
Only the node-section of the first config file will be returned.
|
||||
The others will be discarded.
|
||||
Arguments
|
||||
- cfgfiles : str
|
||||
Comma separated list of config-files
|
||||
- cfgfiles : list
|
||||
List of config file paths
|
||||
- log : frappy.logging.Mainlogger
|
||||
Logger aquired from frappy.logging
|
||||
Returns
|
||||
@ -181,8 +187,8 @@ def load_config(cfgfiles, log):
|
||||
merged configuration
|
||||
"""
|
||||
config = None
|
||||
for cfgfile in cfgfiles.split(','):
|
||||
filename = to_config_path(cfgfile, log)
|
||||
for cfgfile in cfgfiles:
|
||||
filename = to_config_path(str(cfgfile), log)
|
||||
log.debug('Parsing config file %s...', filename)
|
||||
cfg = process_file(filename, log)
|
||||
if config:
|
||||
|
@ -218,8 +218,9 @@ def write_config(file_name, tree_widget):
|
||||
with open(file_name, 'w', encoding='utf-8') as configfile:
|
||||
configfile.write('\n'.join(lines))
|
||||
|
||||
|
||||
def read_config(file_path, log):
|
||||
config = load_config(file_path, log)
|
||||
config = load_config([file_path], log)
|
||||
node = TreeWidgetItem(NODE)
|
||||
ifs = TreeWidgetItem(name='Interfaces')
|
||||
mods = TreeWidgetItem(name='Modules')
|
||||
|
@ -106,7 +106,8 @@ def get_file_paths(widget, open_file=True):
|
||||
|
||||
def get_modules():
|
||||
modules = {}
|
||||
generalConfig.init()
|
||||
if not generalConfig.initialized:
|
||||
generalConfig.init()
|
||||
base_path = generalConfig.basedir
|
||||
# pylint: disable=too-many-nested-blocks
|
||||
for dirname in listdir(base_path):
|
||||
@ -157,7 +158,8 @@ def get_interface_class_from_name(name):
|
||||
def get_interfaces():
|
||||
# TODO class must be found out like for modules
|
||||
interfaces = []
|
||||
generalConfig.init()
|
||||
if not generalConfig.initialized:
|
||||
generalConfig.init()
|
||||
interface_path = path.join(generalConfig.basedir, 'frappy',
|
||||
'protocol', 'interface')
|
||||
for filename in listdir(interface_path):
|
||||
|
@ -57,11 +57,12 @@ class GeneralConfig:
|
||||
|
||||
:param configfile: if present, keys and values from the [FRAPPY] section are read
|
||||
|
||||
default values for 'piddir', 'logdir' and 'confdir' are guessed from the
|
||||
location of this source file and from sys.executable.
|
||||
|
||||
if configfile is not given, the general config file is determined by
|
||||
the env. variable FRAPPY_CONFIG_FILE or <confdir>/generalConfig.cfg is used
|
||||
The following locations are searched for the generalConfig.cfg file.
|
||||
- command line argument
|
||||
- environment variable FRAPPY_CONFIG_FILE
|
||||
- git location (../cfg)
|
||||
- local location (cwd)
|
||||
- global location (/etc/frappy)
|
||||
|
||||
if a configfile is given, the values from the FRAPPY section are
|
||||
overriding above defaults
|
||||
@ -69,37 +70,12 @@ class GeneralConfig:
|
||||
finally, the env. variables FRAPPY_PIDDIR, FRAPPY_LOGDIR and FRAPPY_CONFDIR
|
||||
are overriding these values when given
|
||||
"""
|
||||
|
||||
configfile = self._get_file_location(configfile)
|
||||
|
||||
cfg = {}
|
||||
mandatory = 'piddir', 'logdir', 'confdir'
|
||||
repodir = Path(__file__).parents[2].expanduser().resolve()
|
||||
# create default paths
|
||||
if (Path(sys.executable).suffix == ".exe"
|
||||
and not Path(sys.executable).name.startswith('python')):
|
||||
# special MS windows environment
|
||||
confdir = Path('./')
|
||||
self.update_defaults(piddir=Path('./'), logdir=Path('./log'))
|
||||
elif path.exists(path.join(repodir, 'cfg')):
|
||||
# running from git repo
|
||||
confdir = repodir / 'cfg'
|
||||
# take logdir and piddir from <repodir>/cfg/generalConfig.cfg
|
||||
else:
|
||||
# running on installed system (typically with systemd)
|
||||
self.update_defaults(
|
||||
piddir=Path('/var/run/frappy'),
|
||||
logdir=Path('/var/log'),
|
||||
)
|
||||
confdir = Path('/etc/frappy')
|
||||
self.set_default('confdir', confdir)
|
||||
if configfile is None:
|
||||
configfile = environ.get('FRAPPY_CONFIG_FILE')
|
||||
if configfile:
|
||||
configfile = Path(configfile).expanduser()
|
||||
if not configfile.exists():
|
||||
raise FileNotFoundError(configfile)
|
||||
else:
|
||||
configfile = confdir / 'generalConfig.cfg'
|
||||
if not configfile.exists():
|
||||
configfile = None
|
||||
if configfile:
|
||||
parser = ConfigParser()
|
||||
parser.optionxform = str
|
||||
@ -113,28 +89,63 @@ class GeneralConfig:
|
||||
cfg[key] = ':'.join(path.expanduser(v) for v in value.split(':'))
|
||||
if cfg.get('confdir') is None:
|
||||
cfg['confdir'] = configfile.parent
|
||||
# environment variables will overwrite the config file
|
||||
missing_keys = []
|
||||
for key in mandatory:
|
||||
env = environ.get(f'FRAPPY_{key.upper()}')
|
||||
if env is not None:
|
||||
if ':' in env:
|
||||
cfg[key] = [Path(v) for v in env.split(':')]
|
||||
else:
|
||||
cfg[key] = Path(env)
|
||||
missing_keys = [
|
||||
key for key in mandatory
|
||||
if cfg.get(key) is None and self.defaults.get(key) is None
|
||||
]
|
||||
env = environ.get(f'FRAPPY_{key.upper()}') or cfg.get(key)
|
||||
if env is None:
|
||||
if self.defaults.get(key) is None:
|
||||
missing_keys.append(key)
|
||||
else:
|
||||
if not isinstance(env, Path):
|
||||
if key == 'confdir':
|
||||
env = [Path(v) for v in env.split(':')]
|
||||
else:
|
||||
env = Path(env)
|
||||
cfg[key] = env
|
||||
if missing_keys:
|
||||
if configfile:
|
||||
raise KeyError(f"missing value for {' and '.join(missing_keys)} in {configfile}")
|
||||
raise KeyError('missing %s'
|
||||
% ' and '.join('FRAPPY_%s' % k.upper() for k in missing_keys))
|
||||
|
||||
if len(missing_keys) < 3:
|
||||
# user specified at least one env variable already
|
||||
missing = ' (missing %s)' % ', '.join('FRAPPY_%s' % k.upper() for k in missing_keys)
|
||||
else:
|
||||
missing = ''
|
||||
raise FileNotFoundError(
|
||||
'Could not determine config file location for the general frappy config. '
|
||||
f'Provide a config file or all required environment variables{missing}. '
|
||||
'For more information, see frappy-server --help.'
|
||||
)
|
||||
if 'confdir' in cfg and isinstance(cfg['confdir'], Path):
|
||||
cfg['confdir'] = [cfg['confdir']]
|
||||
# this is not customizable
|
||||
cfg['basedir'] = repodir
|
||||
self._config = cfg
|
||||
|
||||
def _get_file_location(self, configfile):
|
||||
"""Determining the defaultConfig.cfg location as documented in init()"""
|
||||
# given as command line arg
|
||||
if configfile and Path(configfile).exists():
|
||||
return configfile
|
||||
# if not given as argument, check different sources
|
||||
# env variable
|
||||
fromenv = environ.get('FRAPPY_CONFIG_FILE')
|
||||
if fromenv and Path(fromenv).exists():
|
||||
return fromenv
|
||||
# from ../cfg (there if running from checkout)
|
||||
repodir = Path(__file__).parents[2].expanduser().resolve()
|
||||
if (repodir / 'cfg' / 'generalConfig.cfg').exists():
|
||||
return repodir / 'cfg' / 'generalConfig.cfg'
|
||||
localfile = Path.cwd() / 'generalConfig.cfg'
|
||||
if localfile.exists():
|
||||
return localfile
|
||||
# TODO: leave this hardcoded?
|
||||
globalfile = Path('/etc/frappy/generalConfig.cfg')
|
||||
if globalfile.exists():
|
||||
return globalfile
|
||||
return None
|
||||
|
||||
def __getitem__(self, key):
|
||||
"""access for keys known to exist
|
||||
|
||||
|
@ -55,7 +55,7 @@ class MultiEvent(threading.Event):
|
||||
|
||||
def __init__(self, default_timeout=None):
|
||||
self.events = set()
|
||||
self._lock = threading.Lock()
|
||||
self._lock = threading.RLock()
|
||||
self.default_timeout = default_timeout or None # treat 0 as None
|
||||
self.name = None # default event name
|
||||
self._actions = [] # actions to be executed on trigger
|
||||
|
@ -33,7 +33,7 @@ from frappy.datatypes import ArrayOf, BoolType, EnumType, FloatRange, \
|
||||
from frappy.errors import BadValueError, CommunicationFailedError, ConfigError, \
|
||||
ProgrammingError, SECoPError, secop_error, RangeError
|
||||
from frappy.lib import formatException, mkthread, UniqueObject
|
||||
from frappy.params import Accessible, Command, Parameter, Limit
|
||||
from frappy.params import Accessible, Command, Parameter, Limit, PREDEFINED_ACCESSIBLES
|
||||
from frappy.properties import HasProperties, Property
|
||||
from frappy.logging import RemoteLogHandler
|
||||
|
||||
@ -41,6 +41,7 @@ from frappy.logging import RemoteLogHandler
|
||||
# from .interfaces import SECoP_BASE_CLASSES
|
||||
# WORKAROUND:
|
||||
SECoP_BASE_CLASSES = ['Readable', 'Writable', 'Drivable', 'Communicator']
|
||||
PREDEF_ORDER = list(PREDEFINED_ACCESSIBLES)
|
||||
|
||||
Done = UniqueObject('Done')
|
||||
"""a special return value for a read_<param>/write_<param> method
|
||||
@ -77,7 +78,7 @@ class HasAccessibles(HasProperties):
|
||||
for key, value in base.__dict__.items():
|
||||
if isinstance(value, Accessible):
|
||||
value.updateProperties(merged_properties.setdefault(key, {}))
|
||||
if base == cls and key not in accessibles:
|
||||
if base == cls and key not in accessibles and key not in PREDEFINED_ACCESSIBLES:
|
||||
new_names.append(key)
|
||||
accessibles[key] = value
|
||||
override_values.pop(key, None)
|
||||
@ -97,17 +98,15 @@ class HasAccessibles(HasProperties):
|
||||
aobj.merge(merged_properties[aname])
|
||||
accessibles[aname] = aobj
|
||||
|
||||
# rebuild order: (1) inherited items, (2) items from paramOrder, (3) new accessibles
|
||||
# move (2) to the end
|
||||
paramOrder = cls.__dict__.get('paramOrder', ())
|
||||
for aname in paramOrder:
|
||||
if aname in accessibles:
|
||||
accessibles.move_to_end(aname)
|
||||
# ignore unknown names
|
||||
# rebuild order:
|
||||
# (1) predefined accessibles, in a predefined order, (2) inherited custom items, (3) new custom items
|
||||
# move (1) to the beginning
|
||||
for key in reversed(PREDEF_ORDER):
|
||||
if key in accessibles:
|
||||
accessibles.move_to_end(key, last=False)
|
||||
# move (3) to the end
|
||||
for aname in new_names:
|
||||
if aname not in paramOrder:
|
||||
accessibles.move_to_end(aname)
|
||||
accessibles.move_to_end(aname)
|
||||
cls.accessibles = accessibles
|
||||
|
||||
cls.wrappedAttributes = {'isWrapped': True}
|
||||
@ -189,10 +188,8 @@ class HasAccessibles(HasProperties):
|
||||
if new_value is Done: # TODO: to be removed when all code using Done is updated
|
||||
return getattr(self, pname)
|
||||
new_value = value if new_value is None else validate(new_value)
|
||||
except Exception as e:
|
||||
if isinstance(e, SECoPError):
|
||||
e.raising_methods.append(f'{self.name}.write_{pname}')
|
||||
self.announceUpdate(pname, err=e)
|
||||
except SECoPError as e:
|
||||
e.raising_methods.append(f'{self.name}.write_{pname}')
|
||||
raise
|
||||
self.announceUpdate(pname, new_value, validate=False)
|
||||
return new_value
|
||||
@ -322,6 +319,8 @@ class Module(HasAccessibles):
|
||||
slowinterval = Property('poll interval for other parameters', FloatRange(0.1, 120), default=15)
|
||||
omit_unchanged_within = Property('default for minimum time between updates of unchanged values',
|
||||
NoneOr(FloatRange(0)), export=False, default=None)
|
||||
original_id = Property('original equipment_id\n\ngiven only if different from equipment_id of node',
|
||||
NoneOr(StringType()), default=None, export=True) # exported as custom property _original_id
|
||||
enablePoll = True
|
||||
|
||||
pollInfo = None
|
||||
@ -517,13 +516,13 @@ class Module(HasAccessibles):
|
||||
with self.updateLock:
|
||||
pobj = self.parameters[pname]
|
||||
timestamp = timestamp or time.time()
|
||||
changed = False
|
||||
if not err:
|
||||
try:
|
||||
if validate:
|
||||
value = pobj.datatype(value)
|
||||
except Exception as e:
|
||||
err = e
|
||||
changed = False
|
||||
else:
|
||||
changed = pobj.value != value or pobj.readerror
|
||||
# store the value even in case of error
|
||||
|
@ -259,8 +259,16 @@ class Parameter(Accessible):
|
||||
merged_properties.update(self.ownProperties)
|
||||
|
||||
def create_from_value(self, properties, value):
|
||||
"""return a clone with given value and inherited properties"""
|
||||
return self.clone(properties, value=self.datatype(value))
|
||||
"""return a clone with given value and inherited properties
|
||||
|
||||
called when a Parameter is overridden with a bare value
|
||||
"""
|
||||
try:
|
||||
value = self.datatype(value)
|
||||
except Exception as e:
|
||||
raise ProgrammingError(f'{self.name} must be assigned to a Parameter '
|
||||
f'or a value compatible with {type(self.datatype).__name__}') from e
|
||||
return self.clone(properties, value=value)
|
||||
|
||||
def merge(self, merged_properties):
|
||||
"""merge with inherited properties
|
||||
@ -462,7 +470,8 @@ class Command(Accessible):
|
||||
def create_from_value(self, properties, value):
|
||||
"""return a clone with given value and inherited properties
|
||||
|
||||
this is needed when the @Command is missing on a method overriding a command"""
|
||||
called when the @Command is missing on a method overriding a command
|
||||
"""
|
||||
if not callable(value):
|
||||
raise ProgrammingError(f'{self.name} = {value!r} is overriding a Command')
|
||||
return self.clone(properties)(value)
|
||||
@ -564,15 +573,18 @@ class Limit(Parameter):
|
||||
|
||||
|
||||
# list of predefined accessibles with their type
|
||||
# the order of this list affects the parameter order
|
||||
PREDEFINED_ACCESSIBLES = {
|
||||
'value': Parameter,
|
||||
'status': Parameter,
|
||||
'target': Parameter,
|
||||
'pollinterval': Parameter,
|
||||
'ramp': Parameter,
|
||||
'user_ramp': Parameter,
|
||||
'use_ramp': Parameter,
|
||||
'setpoint': Parameter,
|
||||
'time_to_target': Parameter,
|
||||
'controlled_by': Parameter,
|
||||
'control_active': Parameter,
|
||||
'unit': Parameter, # reserved name
|
||||
'loglevel': Parameter, # reserved name
|
||||
'mode': Parameter, # reserved name
|
||||
|
@ -97,7 +97,9 @@ class Playground(Server):
|
||||
for modname, cfg in kwds.items():
|
||||
cfg.setdefault('description', modname)
|
||||
self.log = logger.log
|
||||
self.node_cfg = {'cls': 'frappy.playground.Dispatcher', 'name': 'playground'}
|
||||
self.node_cfg = {'cls': 'frappy.playground.Dispatcher',
|
||||
'name': 'playground',
|
||||
'description': 'playground'}
|
||||
self._testonly = True # stops before calling startModule
|
||||
self._cfgfiles = 'main'
|
||||
self.module_cfg = {}
|
||||
@ -106,6 +108,7 @@ class Playground(Server):
|
||||
if cfgfiles:
|
||||
if not generalConfig.initialized:
|
||||
generalConfig.init()
|
||||
cfgfiles = [s.strip() for s in cfgfiles.split(',')]
|
||||
merged_cfg = load_config(cfgfiles, self.log)
|
||||
merged_cfg.pop('node', None)
|
||||
self.module_cfg = merged_cfg
|
||||
|
@ -143,6 +143,7 @@ class HasProperties(HasDescriptors):
|
||||
try:
|
||||
# try to apply bare value to Property
|
||||
po.value = po.datatype.validate(value)
|
||||
setattr(cls, pn, po) # replace bare value by updated Property
|
||||
except BadValueError:
|
||||
if callable(value):
|
||||
raise ProgrammingError(f'method {cls.__name__}.{pn} collides with property of {base.__name__}') from None
|
||||
|
108
frappy/protocol/discovery.py
Normal file
108
frappy/protocol/discovery.py
Normal file
@ -0,0 +1,108 @@
|
||||
# *****************************************************************************
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free Software
|
||||
# Foundation; either version 2 of the License, or (at your option) any later
|
||||
# version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#
|
||||
# Module authors:
|
||||
# Alexander Zaft <a.zaft@fz-juelich.de>
|
||||
#
|
||||
# *****************************************************************************
|
||||
"""Discovery via UDP broadcasts."""
|
||||
|
||||
import os
|
||||
import json
|
||||
import socket
|
||||
|
||||
from frappy.lib import closeSocket
|
||||
from frappy.protocol.interface.tcp import format_address
|
||||
from frappy.version import get_version
|
||||
|
||||
UDP_PORT = 10767
|
||||
MAX_MESSAGE_LEN = 508
|
||||
|
||||
|
||||
class UDPListener:
|
||||
def __init__(self, equipment_id, description, ifaces, logger, *,
|
||||
startup_broadcast=True):
|
||||
self.equipment_id = equipment_id
|
||||
self.log = logger
|
||||
self.description = description or ''
|
||||
self.firmware = 'FRAPPY ' + get_version()
|
||||
self.ports = [int(iface.split('://')[1])
|
||||
for iface in ifaces if iface.startswith('tcp')]
|
||||
self.running = False
|
||||
self.is_enabled = True
|
||||
self.startup_broadcast = startup_broadcast
|
||||
|
||||
self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
if os.name == 'nt':
|
||||
self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
else:
|
||||
self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
|
||||
if startup_broadcast:
|
||||
self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
|
||||
self.sock.bind(('0.0.0.0', UDP_PORT))
|
||||
|
||||
available = MAX_MESSAGE_LEN - len(self._getMessage(2**16-1))
|
||||
if available < 0:
|
||||
desc_length = len(self.description.encode('utf-8'))
|
||||
if available + desc_length < 0:
|
||||
self.log.warn('Equipment id and firmware name exceed 430 byte '
|
||||
'limit, not answering to udp discovery')
|
||||
self.is_enabled = False
|
||||
else:
|
||||
self.log.debug('truncating description for udp discovery')
|
||||
# with errors='ignore', cutting insite a utf-8 glyph will not
|
||||
# report an error but remove the rest of the glyph from the
|
||||
# output.
|
||||
self.description = self.description \
|
||||
.encode('utf-8')[:available] \
|
||||
.decode('utf-8', errors='ignore')
|
||||
|
||||
def _getMessage(self, port):
|
||||
return json.dumps({
|
||||
'SECoP': 'node',
|
||||
'port': port,
|
||||
'equipment_id': self.equipment_id,
|
||||
'firmware': self.firmware,
|
||||
'description': self.description,
|
||||
}, ensure_ascii=False, separators=(',', ':')).encode('utf-8')
|
||||
|
||||
def run(self):
|
||||
if self.startup_broadcast:
|
||||
self.log.debug('Sending startup UDP broadcast.')
|
||||
for port in self.ports:
|
||||
self.sock.sendto(self._getMessage(port),
|
||||
('255.255.255.255', UDP_PORT))
|
||||
self.running = True
|
||||
while self.running and self.is_enabled:
|
||||
try:
|
||||
msg, addr = self.sock.recvfrom(1024)
|
||||
except socket.error:
|
||||
return
|
||||
try:
|
||||
request = json.loads(msg.decode('utf-8'))
|
||||
except json.JSONDecodeError:
|
||||
continue
|
||||
if 'SECoP' not in request or request['SECoP'] != 'discover':
|
||||
continue
|
||||
self.log.debug('Answering UDP broadcast from: %s',
|
||||
format_address(addr))
|
||||
for port in self.ports:
|
||||
self.sock.sendto(self._getMessage(port), addr)
|
||||
|
||||
def shutdown(self):
|
||||
self.log.debug('shut down of discovery listener')
|
||||
self.running = False
|
||||
closeSocket(self.sock)
|
@ -77,26 +77,30 @@ class SecopClient(frappy.client.SecopClient):
|
||||
|
||||
|
||||
class Router(frappy.protocol.dispatcher.Dispatcher):
|
||||
singlenode = None
|
||||
|
||||
def __init__(self, name, logger, options, srv):
|
||||
"""initialize router
|
||||
|
||||
Use the option node = <uri> for a single node or
|
||||
nodes = ["<uri1>", "<uri2>" ...] for multiple nodes.
|
||||
If a single node is given, the node properties are forwarded transparently,
|
||||
If a single node is given, and no more additional modules are given,
|
||||
the node properties are forwarded transparently,
|
||||
else the description property is a merge from all client node properties.
|
||||
"""
|
||||
uri = options.pop('node', None)
|
||||
uris = options.pop('nodes', None)
|
||||
if uri and uris:
|
||||
raise frappy.errors.ConfigError('can not specify node _and_ nodes')
|
||||
try:
|
||||
if uris is not None:
|
||||
if isinstance(uris, str) or not all(isinstance(v, str) for v in uris) or uri:
|
||||
raise TypeError()
|
||||
elif isinstance(uri, str):
|
||||
uris = [uri]
|
||||
else:
|
||||
raise TypeError()
|
||||
except Exception as e:
|
||||
raise frappy.errors.ConfigError("a router needs either 'node' as a string'"
|
||||
"' or 'nodes' as a list of strings") from e
|
||||
super().__init__(name, logger, options, srv)
|
||||
if uri:
|
||||
self.nodes = [SecopClient(uri, logger.getChild('routed'), self)]
|
||||
self.singlenode = self.nodes[0]
|
||||
else:
|
||||
self.nodes = [SecopClient(uri, logger.getChild(f'routed{i}'), self) for i, uri in enumerate(uris)]
|
||||
self.nodes = [SecopClient(uri, logger.getChild(f'routed{i}'), self) for i, uri in enumerate(uris)]
|
||||
# register callbacks
|
||||
for node in self.nodes:
|
||||
node.register_callback(None, node.updateEvent, node.descriptiveDataChange, node.nodeStateChange)
|
||||
@ -127,8 +131,8 @@ class Router(frappy.protocol.dispatcher.Dispatcher):
|
||||
logger.warning('can not connect to node %r', node.nodename)
|
||||
|
||||
def handle_describe(self, conn, specifier, data):
|
||||
if self.singlenode:
|
||||
return DESCRIPTIONREPLY, specifier, self.singlenode.descriptive_data
|
||||
if len(self.nodes) == 1 and not self.secnode.modules:
|
||||
return DESCRIPTIONREPLY, specifier, self.nodes[0].descriptive_data
|
||||
reply = super().handle_describe(conn, specifier, data)
|
||||
result = reply[2]
|
||||
allmodules = result.get('modules', {})
|
||||
@ -144,6 +148,7 @@ class Router(frappy.protocol.dispatcher.Dispatcher):
|
||||
self.log.info('module %r is already present', modname)
|
||||
else:
|
||||
allmodules[modname] = moddesc
|
||||
moddesc.setdefault('original_id', equipment_id)
|
||||
result['modules'] = allmodules
|
||||
result['description'] = '\n\n'.join(node_description)
|
||||
return DESCRIPTIONREPLY, specifier, result
|
||||
|
@ -54,8 +54,17 @@ class SecNode:
|
||||
self.name = name
|
||||
|
||||
def add_secnode_property(self, prop, value):
|
||||
"""Add SECNode property. If starting with an underscore, it is exported
|
||||
in the description."""
|
||||
self.nodeprops[prop] = value
|
||||
|
||||
def get_secnode_property(self, prop):
|
||||
"""Get SECNode property.
|
||||
|
||||
Returns None if not present.
|
||||
"""
|
||||
return self.nodeprops.get(prop)
|
||||
|
||||
def get_module(self, modulename):
|
||||
""" Returns a fully initialized module. Or None, if something went
|
||||
wrong during instatiating/initializing the module."""
|
||||
|
103
frappy/server.py
103
frappy/server.py
@ -26,6 +26,7 @@ import os
|
||||
import signal
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
|
||||
import mlzlog
|
||||
|
||||
@ -36,6 +37,7 @@ from frappy.lib.multievent import MultiEvent
|
||||
from frappy.logging import init_remote_logging
|
||||
from frappy.params import PREDEFINED_ACCESSIBLES
|
||||
from frappy.secnode import SecNode
|
||||
from frappy.protocol.discovery import UDPListener
|
||||
|
||||
try:
|
||||
from daemon import DaemonContext
|
||||
@ -67,9 +69,9 @@ class Server:
|
||||
- name: the node name
|
||||
- parent_logger: the logger to inherit from. a handler is installed by
|
||||
the server to provide remote logging
|
||||
- cfgfiles: if not given, defaults to name
|
||||
may be a comma separated list of cfg files
|
||||
items ending with .cfg are taken as paths, else .cfg is appended and
|
||||
- cfgfiles: if not given, defaults to [name]
|
||||
may be a list of cfg files
|
||||
items ending with .py are taken as paths, else _cfg.py is appended and
|
||||
files are looked up in the config path retrieved from the general config
|
||||
- interface: an uri of the from tcp://<port> or a bare port number for tcp
|
||||
if not given, the interface is taken from the config file. In case of
|
||||
@ -93,9 +95,9 @@ class Server:
|
||||
self._testonly = testonly
|
||||
|
||||
if not cfgfiles:
|
||||
cfgfiles = name
|
||||
cfgfiles = [name]
|
||||
# sanitize name (in case it is a cfgfile)
|
||||
name = os.path.splitext(os.path.basename(name))[0]
|
||||
self.name = name = os.path.splitext(os.path.basename(name))[0]
|
||||
if isinstance(parent_logger, mlzlog.MLZLogger):
|
||||
self.log = parent_logger.getChild(name, True)
|
||||
else:
|
||||
@ -106,7 +108,6 @@ class Server:
|
||||
self.node_cfg = merged_cfg.pop('node')
|
||||
self.module_cfg = merged_cfg
|
||||
if interface:
|
||||
self.node_cfg['equipment_id'] = name
|
||||
self.node_cfg['interface'] = str(interface)
|
||||
elif not self.node_cfg.get('interface'):
|
||||
raise ConfigError('No interface specified in configuration or arguments!')
|
||||
@ -116,6 +117,8 @@ class Server:
|
||||
signal.signal(signal.SIGINT, self.signal_handler)
|
||||
signal.signal(signal.SIGTERM, self.signal_handler)
|
||||
|
||||
self.discovery = None
|
||||
|
||||
def signal_handler(self, num, frame):
|
||||
if hasattr(self, 'interfaces') and self.interfaces:
|
||||
self.shutdown()
|
||||
@ -164,6 +167,7 @@ class Server:
|
||||
print(formatException(verbose=True))
|
||||
raise
|
||||
|
||||
# client interfaces
|
||||
self.interfaces = {}
|
||||
iface_threads = []
|
||||
# default_timeout 12 sec: TCPServer might need up to 10 sec to wait for Address no longer in use
|
||||
@ -171,17 +175,20 @@ class Server:
|
||||
lock = threading.Lock()
|
||||
failed = {}
|
||||
interfaces = [self.node_cfg['interface']] + self.node_cfg.get('secondary', [])
|
||||
# TODO: check if only one interface of each type is open?
|
||||
for interface in interfaces:
|
||||
opts = {'uri': interface}
|
||||
t = mkthread(
|
||||
self._interfaceThread,
|
||||
opts,
|
||||
lock,
|
||||
failed,
|
||||
interfaces_started.get_trigger(),
|
||||
)
|
||||
iface_threads.append(t)
|
||||
# allow missing "tcp://"
|
||||
interfaces = [iface if '://' in iface else f'tcp://{iface}' for iface in interfaces]
|
||||
with lock:
|
||||
for interface in interfaces:
|
||||
opts = {'uri': interface}
|
||||
t = mkthread(
|
||||
self._interfaceThread,
|
||||
opts,
|
||||
lock,
|
||||
failed,
|
||||
interfaces,
|
||||
interfaces_started.get_trigger(),
|
||||
)
|
||||
iface_threads.append(t)
|
||||
if not interfaces_started.wait():
|
||||
for iface in interfaces:
|
||||
if iface not in failed and iface not in self.interfaces:
|
||||
@ -192,15 +199,33 @@ class Server:
|
||||
if not self.interfaces:
|
||||
self.log.error('no interface started')
|
||||
return
|
||||
self.secnode.add_secnode_property('_interfaces', list(self.interfaces.keys()))
|
||||
self.log.info('startup done with interface(s) %s' % ', '.join(self.interfaces))
|
||||
self.secnode.add_secnode_property('_interfaces', list(self.interfaces))
|
||||
self.log.info('startup done with interface(s) %s',
|
||||
', '.join(self.interfaces))
|
||||
|
||||
# start discovery interface when we know where we listen
|
||||
self.discovery = UDPListener(
|
||||
self.secnode.equipment_id,
|
||||
self.secnode.get_secnode_property('description'),
|
||||
list(self.interfaces),
|
||||
self.log.getChild('discovery')
|
||||
)
|
||||
mkthread(self.discovery.run)
|
||||
|
||||
if systemd:
|
||||
systemd.daemon.notify("READY=1\nSTATUS=accepting requests")
|
||||
|
||||
# we wait here on the thread finishing, which means we got a
|
||||
# signal to shut down or an exception was raised
|
||||
for t in iface_threads:
|
||||
t.join()
|
||||
if os.name == 'nt':
|
||||
# workaround: thread.join() on Windows blocks and is not
|
||||
# interruptible by the signal handler, so loop and check
|
||||
# periodically whether the interfaces are still running.
|
||||
while True:
|
||||
time.sleep(1)
|
||||
if not interfaces:
|
||||
break
|
||||
else:
|
||||
for t in iface_threads:
|
||||
t.join()
|
||||
|
||||
while failed:
|
||||
iface, err = failed.popitem()
|
||||
@ -208,11 +233,11 @@ class Server:
|
||||
|
||||
self.log.info('stopped listening, cleaning up %d modules',
|
||||
len(self.secnode.modules))
|
||||
# if systemd:
|
||||
# if self._restart:
|
||||
# systemd.daemon.notify('RELOADING=1')
|
||||
# else:
|
||||
# systemd.daemon.notify('STOPPING=1')
|
||||
if systemd:
|
||||
if self._restart:
|
||||
systemd.daemon.notify('RELOADING=1')
|
||||
else:
|
||||
systemd.daemon.notify('STOPPING=1')
|
||||
self.secnode.shutdown_modules()
|
||||
if self._restart:
|
||||
self.restart_hook()
|
||||
@ -227,13 +252,14 @@ class Server:
|
||||
|
||||
def shutdown(self):
|
||||
self._restart = False
|
||||
if self.discovery:
|
||||
self.discovery.shutdown()
|
||||
for iface in self.interfaces.values():
|
||||
iface.shutdown()
|
||||
|
||||
def _interfaceThread(self, opts, lock, failed, start_cb):
|
||||
def _interfaceThread(self, opts, lock, failed, interfaces, start_cb):
|
||||
iface = opts['uri']
|
||||
scheme, _, _ = iface.rpartition('://')
|
||||
scheme = scheme or 'tcp'
|
||||
scheme = iface.split('://')[0]
|
||||
cls = get_class(self.INTERFACES[scheme])
|
||||
try:
|
||||
with cls(scheme, self.log.getChild(scheme), opts, self) as interface:
|
||||
@ -247,9 +273,12 @@ class Server:
|
||||
except Exception as e:
|
||||
with lock:
|
||||
failed[iface] = e
|
||||
start_cb()
|
||||
return
|
||||
self.log.info(f'stopped {iface}')
|
||||
interfaces.remove(iface)
|
||||
start_cb() # callback should also be called on failure
|
||||
else:
|
||||
with lock:
|
||||
interfaces.remove(iface)
|
||||
self.log.info(f'stopped {iface}')
|
||||
|
||||
def _processCfg(self):
|
||||
"""Processes the module configuration.
|
||||
@ -263,10 +292,8 @@ class Server:
|
||||
errors = []
|
||||
opts = dict(self.node_cfg)
|
||||
cls = get_class(opts.pop('cls'))
|
||||
name = opts.pop('name', self._cfgfiles)
|
||||
# TODO: opts not in both
|
||||
self.secnode = SecNode(name, self.log.getChild('secnode'), opts, self)
|
||||
self.dispatcher = cls(name, self.log.getChild('dispatcher'), opts, self)
|
||||
self.secnode = SecNode(self.name, self.log.getChild('secnode'), opts, self)
|
||||
self.dispatcher = cls(self.name, self.log.getChild('dispatcher'), opts, self)
|
||||
|
||||
# add other options as SECNode properties, those with '_' prefixed will
|
||||
# get exported
|
||||
|
@ -69,14 +69,16 @@ class TemperatureLoop(TemperatureSensor, Drivable):
|
||||
# lakeshore loop number to be used for this module
|
||||
loop = Property('lakeshore loop', IntRange(1, 2), default=1)
|
||||
target = Parameter(datatype=FloatRange(unit='K', min=0, max=1500))
|
||||
heater_range = Property('heater power range', IntRange(0, 5)) # max. 3 on LakeShore 336
|
||||
heater_range = Property('heater power range', IntRange(0, 3), readonly=False)
|
||||
tolerance = Parameter('convergence criterion', FloatRange(0), default=0.1, readonly=False)
|
||||
_driving = False
|
||||
|
||||
def write_heater_range(self, value):
|
||||
self.communicate(f'RANGE {self.loop},{value};RANGE?{self.loop}')
|
||||
|
||||
def write_target(self, target):
|
||||
# reactivate heater in case it was switched off
|
||||
# the command has to be changed in case of model 340 to f'RANGE {self.heater_range};RANGE?'
|
||||
self.communicate(f'RANGE {self.loop},{self.heater_range};RANGE?{self.loop}')
|
||||
self.write_heater_range(self.heater_range)
|
||||
self.communicate(f'SETP {self.loop},{target};*OPC?')
|
||||
self._driving = True
|
||||
# Setting the status attribute triggers an update message for the SECoP status
|
||||
@ -85,23 +87,21 @@ class TemperatureLoop(TemperatureSensor, Drivable):
|
||||
return target
|
||||
|
||||
def read_status(self):
|
||||
code = int(self.communicate(f'RDGST?{self.channel}'))
|
||||
if code >= 128:
|
||||
text = 'units overrange'
|
||||
elif code >= 64:
|
||||
text = 'units zero'
|
||||
elif code >= 32:
|
||||
text = 'temperature overrange'
|
||||
elif code >= 16:
|
||||
text = 'temperature underrange'
|
||||
elif code % 2:
|
||||
# ignore 'old reading', as this may happen in normal operation
|
||||
text = 'invalid reading'
|
||||
elif abs(self.target - self.value) > self.tolerance:
|
||||
status = super().read_status()
|
||||
if status[0] == ERROR:
|
||||
return status
|
||||
if abs(self.target - self.value) > self.tolerance:
|
||||
if self._driving:
|
||||
return BUSY, 'approaching setpoint'
|
||||
return WARN, 'temperature out of tolerance'
|
||||
else: # within tolerance: simple convergence criterion
|
||||
self._driving = False
|
||||
return IDLE, ''
|
||||
return ERROR, text
|
||||
# within tolerance: simple convergence criterion
|
||||
self._driving = False
|
||||
return IDLE, ''
|
||||
|
||||
|
||||
class TemperatureLoop340(TemperatureLoop):
|
||||
# slightly different behaviour for model 340
|
||||
heater_range = Property('heater power range', IntRange(0, 5))
|
||||
|
||||
def write_heater_range(self, value):
|
||||
self.communicate(f'RANGE {value};RANGE?')
|
||||
|
@ -26,7 +26,7 @@ from frappy.datatypes import FloatRange, StringType, ValueType, TupleOf, StructO
|
||||
from frappy.modules import Communicator, Drivable, Parameter, Property, Readable, Module, Attached
|
||||
from frappy.params import Command
|
||||
from frappy.dynamic import Pinata
|
||||
from frappy.errors import RangeError
|
||||
from frappy.errors import RangeError, HardwareError
|
||||
|
||||
class Pin(Pinata):
|
||||
def scanModules(self):
|
||||
@ -72,8 +72,12 @@ class Heater(Drivable):
|
||||
maxheaterpower = Parameter('maximum allowed heater power',
|
||||
datatype=FloatRange(0, 100), unit='W',
|
||||
)
|
||||
error_message = Parameter('simulated error message', StringType(),
|
||||
default='', readonly=False)
|
||||
|
||||
def read_value(self):
|
||||
if self.error_message:
|
||||
raise HardwareError(self.error_message)
|
||||
return round(100 * random.random(), 1)
|
||||
|
||||
def write_target(self, target):
|
||||
|
@ -1,293 +1,313 @@
|
||||
"""
|
||||
Created on Tue Nov 26 15:42:43 2019
|
||||
|
||||
@author: tartarotti_d-adm
|
||||
"""
|
||||
|
||||
|
||||
import numpy as np
|
||||
import ctypes as ct
|
||||
import time
|
||||
from numpy import sqrt, arctan2, sin, cos
|
||||
|
||||
#from pylab import *
|
||||
|
||||
from scipy import signal
|
||||
|
||||
#ADQAPI = ct.cdll.LoadLibrary("ADQAPI.dll")
|
||||
ADQAPI = ct.cdll.LoadLibrary("libadq.so.0")
|
||||
|
||||
#For different trigger modes
|
||||
SW_TRIG = 1
|
||||
EXT_TRIG_1 = 2 #This external trigger does not work if the level of the trigger is very close to 0.5V. Now we have it close to 3V, and it works
|
||||
EXT_TRIG_2 = 7
|
||||
EXT_TRIG_3 = 8
|
||||
LVL_TRIG = 3
|
||||
INT_TRIG = 4
|
||||
LVL_FALLING = 0
|
||||
LVL_RISING = 1
|
||||
|
||||
#samples_per_record=16384
|
||||
ADQ_TRANSFER_MODE_NORMAL = 0x00
|
||||
ADQ_CHANNELS_MASK = 0x3
|
||||
|
||||
#f_LO = 40
|
||||
|
||||
def butter_lowpass(cutoff, sr, order=5):
|
||||
nyq = 0.5 * sr
|
||||
normal_cutoff = cutoff / nyq
|
||||
b, a = signal.butter(order, normal_cutoff, btype = 'low', analog = False)
|
||||
return b, a
|
||||
|
||||
|
||||
class Adq(object):
|
||||
max_number_of_channels = 2
|
||||
samp_freq = 2
|
||||
#ndecimate = 50 # decimation ratio (2GHz / 40 MHz)
|
||||
ndecimate = 50
|
||||
|
||||
def __init__(self, number_of_records, samples_per_record, bw_cutoff):
|
||||
self.number_of_records = number_of_records
|
||||
self.samples_per_record = samples_per_record
|
||||
self.bw_cutoff = bw_cutoff
|
||||
ADQAPI.ADQAPI_GetRevision()
|
||||
|
||||
# Manually set return type from some ADQAPI functions
|
||||
ADQAPI.CreateADQControlUnit.restype = ct.c_void_p
|
||||
ADQAPI.ADQ_GetRevision.restype = ct.c_void_p
|
||||
ADQAPI.ADQ_GetPtrStream.restype = ct.POINTER(ct.c_int16)
|
||||
ADQAPI.ADQControlUnit_FindDevices.argtypes = [ct.c_void_p]
|
||||
# Create ADQControlUnit
|
||||
self.adq_cu = ct.c_void_p(ADQAPI.CreateADQControlUnit())
|
||||
ADQAPI.ADQControlUnit_EnableErrorTrace(self.adq_cu, 3, '.')
|
||||
self.adq_num = 1
|
||||
|
||||
# Find ADQ devices
|
||||
ADQAPI.ADQControlUnit_FindDevices(self.adq_cu)
|
||||
n_of_ADQ = ADQAPI.ADQControlUnit_NofADQ(self.adq_cu)
|
||||
if n_of_ADQ != 1:
|
||||
raise ValueError('number of ADQs must be 1, not %d' % n_of_ADQ)
|
||||
|
||||
rev = ADQAPI.ADQ_GetRevision(self.adq_cu, self.adq_num)
|
||||
revision = ct.cast(rev,ct.POINTER(ct.c_int))
|
||||
print('\nConnected to ADQ #1')
|
||||
# Print revision information
|
||||
print('FPGA Revision: {}'.format(revision[0]))
|
||||
if (revision[1]):
|
||||
print('Local copy')
|
||||
else :
|
||||
print('SVN Managed')
|
||||
if (revision[2]):
|
||||
print('Mixed Revision')
|
||||
else :
|
||||
print('SVN Updated')
|
||||
print('')
|
||||
|
||||
ADQ_CLOCK_INT_INTREF = 0 #internal clock source
|
||||
ADQ_CLOCK_EXT_REF = 1 #internal clock source, external reference
|
||||
ADQ_CLOCK_EXT_CLOCK = 2 #External clock source
|
||||
ADQAPI.ADQ_SetClockSource(self.adq_cu, self.adq_num, ADQ_CLOCK_EXT_REF);
|
||||
|
||||
##########################
|
||||
# Test pattern
|
||||
#ADQAPI.ADQ_SetTestPatternMode(self.adq_cu, self.adq_num, 4)
|
||||
##########################
|
||||
# Sample skip
|
||||
#ADQAPI.ADQ_SetSampleSkip(self.adq_cu, self.adq_num, 1)
|
||||
##########################
|
||||
|
||||
# Set trig mode
|
||||
self.trigger = EXT_TRIG_1
|
||||
#trigger = LVL_TRIG
|
||||
success = ADQAPI.ADQ_SetTriggerMode(self.adq_cu, self.adq_num, self.trigger)
|
||||
if (success == 0):
|
||||
print('ADQ_SetTriggerMode failed.')
|
||||
if (self.trigger == LVL_TRIG):
|
||||
success = ADQAPI.ADQ_SetLvlTrigLevel(self.adq_cu, self.adq_num, -100)
|
||||
if (success == 0):
|
||||
print('ADQ_SetLvlTrigLevel failed.')
|
||||
success = ADQAPI.ADQ_SetTrigLevelResetValue(self.adq_cu, self.adq_num, 1000)
|
||||
if (success == 0):
|
||||
print('ADQ_SetTrigLevelResetValue failed.')
|
||||
success = ADQAPI.ADQ_SetLvlTrigChannel(self.adq_cu, self.adq_num, 1)
|
||||
if (success == 0):
|
||||
print('ADQ_SetLvlTrigChannel failed.')
|
||||
success = ADQAPI.ADQ_SetLvlTrigEdge(self.adq_cu, self.adq_num, LVL_RISING)
|
||||
if (success == 0):
|
||||
print('ADQ_SetLvlTrigEdge failed.')
|
||||
elif (self.trigger == EXT_TRIG_1) :
|
||||
success = ADQAPI.ADQ_SetExternTrigEdge(self.adq_cu, self.adq_num,2)
|
||||
if (success == 0):
|
||||
print('ADQ_SetLvlTrigEdge failed.')
|
||||
# success = ADQAPI.ADQ_SetTriggerThresholdVoltage(self.adq_cu, self.adq_num, trigger, ct.c_double(0.2))
|
||||
# if (success == 0):
|
||||
# print('SetTriggerThresholdVoltage failed.')
|
||||
print("CHANNEL:"+str(ct.c_int(ADQAPI.ADQ_GetLvlTrigChannel(self.adq_cu, self.adq_num))))
|
||||
self.setup_target_buffers()
|
||||
|
||||
def setup_target_buffers(self):
|
||||
# Setup target buffers for data
|
||||
self.target_buffers=(ct.POINTER(ct.c_int16 * self.samples_per_record * self.number_of_records)
|
||||
* self.max_number_of_channels)()
|
||||
for bufp in self.target_buffers:
|
||||
bufp.contents = (ct.c_int16 * self.samples_per_record * self.number_of_records)()
|
||||
|
||||
def deletecu(self):
|
||||
# Only disarm trigger after data is collected
|
||||
ADQAPI.ADQ_DisarmTrigger(self.adq_cu, self.adq_num)
|
||||
ADQAPI.ADQ_MultiRecordClose(self.adq_cu, self.adq_num);
|
||||
# Delete ADQControlunit
|
||||
ADQAPI.DeleteADQControlUnit(self.adq_cu)
|
||||
|
||||
def start(self):
|
||||
"""start datat acquisition"""
|
||||
# samples_per_records = samples_per_record/number_of_records
|
||||
# Change number of pulses to be acquired acording to how many records are taken
|
||||
# Start acquisition
|
||||
ADQAPI.ADQ_MultiRecordSetup(self.adq_cu, self.adq_num,
|
||||
self.number_of_records,
|
||||
self.samples_per_record)
|
||||
|
||||
ADQAPI.ADQ_DisarmTrigger(self.adq_cu, self.adq_num)
|
||||
ADQAPI.ADQ_ArmTrigger(self.adq_cu, self.adq_num)
|
||||
|
||||
def getdata(self):
|
||||
"""wait for aquisition to be finished and get data"""
|
||||
#start = time.time()
|
||||
while(ADQAPI.ADQ_GetAcquiredAll(self.adq_cu,self.adq_num) == 0):
|
||||
time.sleep(0.001)
|
||||
#if (self.trigger == SW_TRIG):
|
||||
# ADQAPI.ADQ_SWTrig(self.adq_cu, self.adq_num)
|
||||
#mid = time.time()
|
||||
status = ADQAPI.ADQ_GetData(self.adq_cu, self.adq_num, self.target_buffers,
|
||||
self.samples_per_record * self.number_of_records, 2,
|
||||
0, self.number_of_records, ADQ_CHANNELS_MASK,
|
||||
0, self.samples_per_record, ADQ_TRANSFER_MODE_NORMAL);
|
||||
#print(time.time()-mid,mid-start)
|
||||
if not status:
|
||||
raise ValueError('no succesS from ADQ_GetDATA')
|
||||
# Now this is an array with all records, but the time is artificial
|
||||
data = []
|
||||
for ch in range(2):
|
||||
onedim = np.frombuffer(self.target_buffers[ch].contents, dtype=np.int16)
|
||||
data.append(onedim.reshape(self.number_of_records, self.samples_per_record) / float(2**14)) # 14 bits ADC
|
||||
return data
|
||||
|
||||
def acquire(self):
|
||||
self.start()
|
||||
return self.getdata()
|
||||
'''
|
||||
def average(self, data):
|
||||
#Average over records
|
||||
return [data[ch].sum(axis=0) / self.number_of_records for ch in range(2)]
|
||||
|
||||
def iq(self, channel, f_LO):
|
||||
newx = np.linspace(0, self.samples_per_record /2, self.samples_per_record)
|
||||
s0 = channel /((2**16)/2)*0.5*np.exp(1j*2*np.pi*f_LO/(1e3)*newx)
|
||||
I0 = s0.real
|
||||
Q0 = s0.imag
|
||||
return I0, Q0
|
||||
|
||||
|
||||
def fitting(self, data, f_LO, ti, tf):
|
||||
# As long as data[0] is the pulse
|
||||
si = 2*ti #Those are for fitting the pulse
|
||||
sf = 2*tf
|
||||
phase = np.zeros(self.number_of_records)
|
||||
amplitude = np.zeros(self.number_of_records)
|
||||
offset = np.zeros(self.number_of_records)
|
||||
|
||||
for i in range(self.number_of_records):
|
||||
phase[i], amplitude[i] = sineW(data[0][i][si:sf],f_LO*1e-9,ti,tf)
|
||||
offset[i] = np.average(data[0][i][si:sf])
|
||||
return phase, amplitude, offset
|
||||
|
||||
|
||||
def waveIQ(self, channel,ti,f_LO):
|
||||
#channel is not the sample data
|
||||
t = np.linspace(0, self.samples_per_record /2, self.samples_per_record + 1)[:-1]
|
||||
si = 2*ti # Again that is where the wave pulse starts
|
||||
cwi = np.zeros((self.number_of_records,self.samples_per_record))
|
||||
cwq = np.zeros((self.number_of_records,self.samples_per_record))
|
||||
iq = np.zeros((self.number_of_records,self.samples_per_record))
|
||||
q = np.zeros((self.number_of_records,self.samples_per_record))
|
||||
for i in range(self.number_of_records):
|
||||
cwi[i] = np.zeros(self.samples_per_record)
|
||||
cwq[i] = np.zeros(self.samples_per_record)
|
||||
cwi[i] = amplitude[i]*sin(t[si:]*f_LO*1e-9*2*np.pi+phase[i]*np.pi/180)+bias[i]
|
||||
cwq[i] = amplitude[i]*sin(t[si:]*f_LO*1e-9*(2*np.pi+(phase[i]+90)*np.pi/180))+bias[i]
|
||||
|
||||
iq[i] = channel[i]*cwi[i]
|
||||
q[i] = channel[i]*cwq[i]
|
||||
|
||||
return iq,q
|
||||
'''
|
||||
def sinW(self,sig,freq,ti,tf):
|
||||
# sig: signal array
|
||||
# freq
|
||||
# ti, tf: initial and end time
|
||||
si = int(ti * self.samp_freq)
|
||||
nperiods = freq * (tf - ti)
|
||||
n = int(round(max(2, int(nperiods)) / nperiods * (tf-ti) * self.samp_freq))
|
||||
self.nperiods = n
|
||||
t = np.arange(si, len(sig)) / self.samp_freq
|
||||
t = t[:n]
|
||||
self.pulselen = n / self.samp_freq
|
||||
sig = sig[si:si+n]
|
||||
a = 2*np.sum(sig*np.cos(2*np.pi*freq*t))/len(sig)
|
||||
b = 2*np.sum(sig*np.sin(2*np.pi*freq*t))/len(sig)
|
||||
return a, b
|
||||
|
||||
def mix(self, sigin, sigout, freq, ti, tf):
|
||||
# sigin, sigout: signal array, incomping, output
|
||||
# freq
|
||||
# ti, tf: initial and end time if sigin
|
||||
a, b = self.sinW(sigin, freq, ti, tf)
|
||||
phase = arctan2(a,b) * 180 / np.pi
|
||||
amp = sqrt(a**2 + b**2)
|
||||
a, b = a/amp, b/amp
|
||||
#si = int(ti * self.samp_freq)
|
||||
t = np.arange(len(sigout)) / self.samp_freq
|
||||
wave1 = sigout * (a * cos(2*np.pi*freq*t) + b * sin(2*np.pi*freq*t))
|
||||
wave2 = sigout * (a * sin(2*np.pi*freq*t) - b * cos(2*np.pi*freq*t))
|
||||
return wave1, wave2
|
||||
|
||||
def averageiq(self, data, freq, ti, tf):
|
||||
'''Average over records'''
|
||||
iorq = np.array([self.mix(data[0][i], data[1][i], freq, ti, tf) for i in range(self.number_of_records)])
|
||||
# iorq = np.array([self.mix(data[0][:], data[1][:], freq, ti, tf)])
|
||||
return iorq.sum(axis=0) / self.number_of_records
|
||||
|
||||
def filtro(self, iorq, cutoff):
|
||||
b, a = butter_lowpass(cutoff, self.samp_freq*1e9)
|
||||
|
||||
#ifi = np.array(signal.filtfilt(b,a,iorq[0]))
|
||||
#qf = np.array(signal.filtfilt(b,a,iorq[1]))
|
||||
iqf = [signal.filtfilt(b,a,iorq[i]) for i in np.arange(len(iorq))]
|
||||
|
||||
return iqf
|
||||
|
||||
def box(self, iorq, ti, tf):
|
||||
si = int(self.samp_freq * ti)
|
||||
sf = int(self.samp_freq * tf)
|
||||
bxa = [sum(iorq[i][si:sf])/(sf-si) for i in np.arange(len(iorq))]
|
||||
return bxa
|
||||
|
||||
def gates_and_curves(self, data, freq, pulse, roi):
|
||||
"""return iq values of rois and prepare plottable curves for iq"""
|
||||
times = []
|
||||
times.append(('aviq', time.time()))
|
||||
iq = self.averageiq(data,freq*1e-9,*pulse)
|
||||
times.append(('filtro', time.time()))
|
||||
iqf = self.filtro(iq,self.bw_cutoff)
|
||||
m = len(iqf[0]) // self.ndecimate
|
||||
times.append(('iqdec', time.time()))
|
||||
iqd = np.average(np.resize(iqf, (2, m, self.ndecimate)), axis=2)
|
||||
t_axis = np.arange(m) * self.ndecimate / self.samp_freq
|
||||
pulsig = np.abs(data[0][0])
|
||||
times.append(('pulsig', time.time()))
|
||||
pulsig = np.average(np.resize(pulsig, (m, self.ndecimate)), axis=1)
|
||||
self.curves = (t_axis, iqd[0], iqd[1], pulsig)
|
||||
#print(times)
|
||||
return [self.box(iqf,*r) for r in roi]
|
||||
|
||||
# *****************************************************************************
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free Software
|
||||
# Foundation; either version 2 of the License, or (at your option) any later
|
||||
# version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#
|
||||
# Module authors:
|
||||
# Damaris Tartarotti Maimone
|
||||
# Markus Zolliker <markus.zolliker@psi.ch>
|
||||
# *****************************************************************************
|
||||
"""Wrapper for the ADQ data acquisition card for ultrasound"""
|
||||
import sys
|
||||
import atexit
|
||||
import signal
|
||||
import time
|
||||
import numpy as np
|
||||
import ctypes as ct
|
||||
from scipy.signal import butter, filtfilt
|
||||
|
||||
|
||||
# For different trigger modes
|
||||
SW_TRIG = 1
|
||||
# The following external trigger does not work if the level of the trigger is very close to 0.5V.
|
||||
# Now we have it close to 3V, and it works
|
||||
EXT_TRIG_1 = 2
|
||||
EXT_TRIG_2 = 7
|
||||
EXT_TRIG_3 = 8
|
||||
LVL_TRIG = 3
|
||||
INT_TRIG = 4
|
||||
LVL_FALLING = 0
|
||||
LVL_RISING = 1
|
||||
|
||||
ADQ_CLOCK_INT_INTREF = 0 # internal clock source
|
||||
ADQ_CLOCK_EXT_REF = 1 # internal clock source, external reference
|
||||
ADQ_CLOCK_EXT_CLOCK = 2 # External clock source
|
||||
|
||||
ADQ_TRANSFER_MODE_NORMAL = 0x00
|
||||
ADQ_CHANNELS_MASK = 0x3
|
||||
|
||||
GHz = 1e9
|
||||
|
||||
|
||||
class Adq:
|
||||
sample_rate = 2 * GHz
|
||||
max_number_of_channels = 2
|
||||
ndecimate = 50 # decimation ratio (2GHz / 40 MHz)
|
||||
number_of_records = 1
|
||||
samples_per_record = 16384
|
||||
bw_cutoff = 10E6
|
||||
trigger = EXT_TRIG_1
|
||||
adq_num = 1
|
||||
UNDEFINED = -1
|
||||
IDLE = 0
|
||||
BUSY = 1
|
||||
READY = 2
|
||||
status = UNDEFINED
|
||||
data = None
|
||||
|
||||
def __init__(self):
|
||||
global ADQAPI
|
||||
ADQAPI = ct.cdll.LoadLibrary("libadq.so.0")
|
||||
|
||||
ADQAPI.ADQAPI_GetRevision()
|
||||
|
||||
# Manually set return type from some ADQAPI functions
|
||||
ADQAPI.CreateADQControlUnit.restype = ct.c_void_p
|
||||
ADQAPI.ADQ_GetRevision.restype = ct.c_void_p
|
||||
ADQAPI.ADQ_GetPtrStream.restype = ct.POINTER(ct.c_int16)
|
||||
ADQAPI.ADQControlUnit_FindDevices.argtypes = [ct.c_void_p]
|
||||
# Create ADQControlUnit
|
||||
self.adq_cu = ct.c_void_p(ADQAPI.CreateADQControlUnit())
|
||||
ADQAPI.ADQControlUnit_EnableErrorTrace(self.adq_cu, 3, '.')
|
||||
|
||||
# Find ADQ devices
|
||||
ADQAPI.ADQControlUnit_FindDevices(self.adq_cu)
|
||||
n_of_adq = ADQAPI.ADQControlUnit_NofADQ(self.adq_cu)
|
||||
if n_of_adq != 1:
|
||||
raise RuntimeError('number of ADQs must be 1, not %d' % n_of_adq)
|
||||
|
||||
rev = ADQAPI.ADQ_GetRevision(self.adq_cu, self.adq_num)
|
||||
revision = ct.cast(rev, ct.POINTER(ct.c_int))
|
||||
print('\nConnected to ADQ #1')
|
||||
# Print revision information
|
||||
print('FPGA Revision: {}'.format(revision[0]))
|
||||
if revision[1]:
|
||||
print('Local copy')
|
||||
else:
|
||||
print('SVN Managed')
|
||||
if revision[2]:
|
||||
print('Mixed Revision')
|
||||
else:
|
||||
print('SVN Updated')
|
||||
print('')
|
||||
|
||||
ADQAPI.ADQ_SetClockSource(self.adq_cu, self.adq_num, ADQ_CLOCK_EXT_REF)
|
||||
|
||||
##########################
|
||||
# Test pattern
|
||||
# ADQAPI.ADQ_SetTestPatternMode(self.adq_cu, self.adq_num, 4)
|
||||
##########################
|
||||
# Sample skip
|
||||
# ADQAPI.ADQ_SetSampleSkip(self.adq_cu, self.adq_num, 1)
|
||||
##########################
|
||||
|
||||
# set trigger mode
|
||||
if not ADQAPI.ADQ_SetTriggerMode(self.adq_cu, self.adq_num, self.trigger):
|
||||
raise RuntimeError('ADQ_SetTriggerMode failed.')
|
||||
if self.trigger == LVL_TRIG:
|
||||
if not ADQAPI.ADQ_SetLvlTrigLevel(self.adq_cu, self.adq_num, -100):
|
||||
raise RuntimeError('ADQ_SetLvlTrigLevel failed.')
|
||||
if not ADQAPI.ADQ_SetTrigLevelResetValue(self.adq_cu, self.adq_num, 1000):
|
||||
raise RuntimeError('ADQ_SetTrigLevelResetValue failed.')
|
||||
if not ADQAPI.ADQ_SetLvlTrigChannel(self.adq_cu, self.adq_num, 1):
|
||||
raise RuntimeError('ADQ_SetLvlTrigChannel failed.')
|
||||
if not ADQAPI.ADQ_SetLvlTrigEdge(self.adq_cu, self.adq_num, LVL_RISING):
|
||||
raise RuntimeError('ADQ_SetLvlTrigEdge failed.')
|
||||
elif self.trigger == EXT_TRIG_1:
|
||||
if not ADQAPI.ADQ_SetExternTrigEdge(self.adq_cu, self.adq_num, 2):
|
||||
raise RuntimeError('ADQ_SetLvlTrigEdge failed.')
|
||||
# if not ADQAPI.ADQ_SetTriggerThresholdVoltage(self.adq_cu, self.adq_num, trigger, ct.c_double(0.2)):
|
||||
# raise RuntimeError('SetTriggerThresholdVoltage failed.')
|
||||
print("CHANNEL:"+str(ct.c_int(ADQAPI.ADQ_GetLvlTrigChannel(self.adq_cu, self.adq_num))))
|
||||
atexit.register(self.deletecu)
|
||||
signal.signal(signal.SIGTERM, lambda *_: sys.exit(0))
|
||||
|
||||
def init(self, samples_per_record=None, number_of_records=None):
|
||||
"""initialize dimensions"""
|
||||
if samples_per_record:
|
||||
self.samples_per_record = samples_per_record
|
||||
if number_of_records:
|
||||
self.number_of_records = number_of_records
|
||||
# Setup target buffers for data
|
||||
self.target_buffers = (ct.POINTER(ct.c_int16 * self.samples_per_record * self.number_of_records)
|
||||
* self.max_number_of_channels)()
|
||||
for bufp in self.target_buffers:
|
||||
bufp.contents = (ct.c_int16 * self.samples_per_record * self.number_of_records)()
|
||||
|
||||
def deletecu(self):
|
||||
# Only disarm trigger after data is collected
|
||||
ADQAPI.ADQ_DisarmTrigger(self.adq_cu, self.adq_num)
|
||||
ADQAPI.ADQ_MultiRecordClose(self.adq_cu, self.adq_num)
|
||||
# Delete ADQControlunit
|
||||
ADQAPI.DeleteADQControlUnit(self.adq_cu)
|
||||
|
||||
def start(self):
|
||||
# Start acquisition
|
||||
ADQAPI.ADQ_MultiRecordSetup(self.adq_cu, self.adq_num,
|
||||
self.number_of_records,
|
||||
self.samples_per_record)
|
||||
|
||||
ADQAPI.ADQ_DisarmTrigger(self.adq_cu, self.adq_num)
|
||||
ADQAPI.ADQ_ArmTrigger(self.adq_cu, self.adq_num)
|
||||
self.status = self.BUSY
|
||||
|
||||
def get_status(self):
|
||||
"""check if ADQ card is busy"""
|
||||
if self.status == self.BUSY:
|
||||
if ADQAPI.ADQ_GetAcquiredAll(self.adq_cu, self.adq_num):
|
||||
self.status = self.READY
|
||||
else:
|
||||
if self.trigger == SW_TRIG:
|
||||
ADQAPI.ADQ_SWTrig(self.adq_cu, self.adq_num)
|
||||
return self.status
|
||||
|
||||
def get_data(self, dataclass, **kwds):
|
||||
"""when ready, get raw data from card, else return cached data
|
||||
|
||||
return
|
||||
"""
|
||||
if self.get_status() == self.READY:
|
||||
# Get data from ADQ
|
||||
if not ADQAPI.ADQ_GetData(self.adq_cu, self.adq_num, self.target_buffers,
|
||||
self.samples_per_record * self.number_of_records, 2,
|
||||
0, self.number_of_records, ADQ_CHANNELS_MASK,
|
||||
0, self.samples_per_record, ADQ_TRANSFER_MODE_NORMAL):
|
||||
raise RuntimeError('no success from ADQ_GetDATA')
|
||||
self.data = dataclass(self, **kwds)
|
||||
self.status = self.IDLE
|
||||
if self.status == self.UNDEFINED:
|
||||
raise RuntimeError('no data available yet')
|
||||
return self.data
|
||||
|
||||
|
||||
class PEdata:
|
||||
def __init__(self, adq):
|
||||
self.sample_rate = adq.sample_rate
|
||||
self.samp_freq = self.sample_rate / GHz
|
||||
self.number_of_records = adq.number_of_records
|
||||
data = []
|
||||
for ch in range(2):
|
||||
onedim = np.frombuffer(adq.target_buffers[ch].contents, dtype=np.int16)
|
||||
data.append(onedim.reshape(adq.number_of_records, adq.samples_per_record) / float(2**14)) # 14 bits ADC
|
||||
# Now this is an array with all records, but the time is artificial
|
||||
self.data = data
|
||||
|
||||
def sinW(self, sig, freq, ti, tf):
|
||||
# sig: signal array
|
||||
# freq
|
||||
# ti, tf: initial and end time
|
||||
si = int(ti * self.samp_freq)
|
||||
nperiods = freq * (tf - ti)
|
||||
n = int(round(max(2, int(nperiods)) / nperiods * (tf-ti) * self.samp_freq))
|
||||
self.nperiods = n
|
||||
t = np.arange(si, len(sig)) / self.samp_freq
|
||||
t = t[:n]
|
||||
self.pulselen = n / self.samp_freq
|
||||
sig = sig[si:si+n]
|
||||
a = 2*np.sum(sig*np.cos(2*np.pi*freq*t))/len(sig)
|
||||
b = 2*np.sum(sig*np.sin(2*np.pi*freq*t))/len(sig)
|
||||
return a, b
|
||||
|
||||
def mix(self, sigin, sigout, freq, ti, tf):
|
||||
# sigin, sigout: signal array, incomping, output
|
||||
# freq
|
||||
# ti, tf: initial and end time of sigin
|
||||
a, b = self.sinW(sigin, freq, ti, tf)
|
||||
amp = np.sqrt(a**2 + b**2)
|
||||
a, b = a/amp, b/amp
|
||||
# si = int(ti * self.samp_freq)
|
||||
t = np.arange(len(sigout)) / self.samp_freq
|
||||
wave1 = sigout * (a * np.cos(2*np.pi*freq*t) + b * np.sin(2*np.pi*freq*t))
|
||||
wave2 = sigout * (a * np.sin(2*np.pi*freq*t) - b * np.cos(2*np.pi*freq*t))
|
||||
return wave1, wave2
|
||||
|
||||
def averageiq(self, data, freq, ti, tf):
|
||||
"""Average over records"""
|
||||
iorq = np.array([self.mix(data[0][i], data[1][i], freq, ti, tf) for i in range(self.number_of_records)])
|
||||
return iorq.sum(axis=0) / self.number_of_records
|
||||
|
||||
def filtro(self, iorq, cutoff):
|
||||
# butter lowpass
|
||||
nyq = 0.5 * self.sample_rate
|
||||
normal_cutoff = cutoff / nyq
|
||||
order = 5
|
||||
b, a = butter(order, normal_cutoff, btype='low', analog=False)
|
||||
iqf = [filtfilt(b, a, iorq[i]) for i in np.arange(len(iorq))]
|
||||
return iqf
|
||||
|
||||
def box(self, iorq, ti, tf):
|
||||
si = int(self.samp_freq * ti)
|
||||
sf = int(self.samp_freq * tf)
|
||||
bxa = [sum(iorq[i][si:sf])/(sf-si) for i in np.arange(len(iorq))]
|
||||
return bxa
|
||||
|
||||
def gates_and_curves(self, freq, pulse, roi, bw_cutoff):
|
||||
"""return iq values of rois and prepare plottable curves for iq"""
|
||||
self.ndecimate = int(round(self.sample_rate / freq))
|
||||
# times = []
|
||||
# times.append(('aviq', time.time()))
|
||||
iq = self.averageiq(self.data, freq / GHz, *pulse)
|
||||
# times.append(('filtro', time.time()))
|
||||
iqf = self.filtro(iq, bw_cutoff)
|
||||
m = len(iqf[0]) // self.ndecimate
|
||||
ll = m * self.ndecimate
|
||||
iqf = [iqfx[0:ll] for iqfx in iqf]
|
||||
# times.append(('iqdec', time.time()))
|
||||
iqd = np.average(np.resize(iqf, (2, m, self.ndecimate)), axis=2)
|
||||
t_axis = np.arange(m) * self.ndecimate / self.samp_freq
|
||||
pulsig = np.abs(self.data[0][0])
|
||||
# times.append(('pulsig', time.time()))
|
||||
pulsig = np.average(np.resize(pulsig, (m, self.ndecimate)), axis=1)
|
||||
self.curves = (t_axis, iqd[0], iqd[1], pulsig)
|
||||
# print(times)
|
||||
return [self.box(iqf, *r) for r in roi]
|
||||
|
||||
|
||||
class RUSdata:
|
||||
def __init__(self, adq, freq, periods):
|
||||
self.sample_rate = adq.sample_rate
|
||||
self.freq = freq
|
||||
self.periods = periods
|
||||
self.samples_per_record = adq.samples_per_record
|
||||
input_signal = np.frombuffer(adq.target_buffers[0].contents, dtype=np.int16)
|
||||
output_signal = np.frombuffer(adq.target_buffers[1].contents, dtype=np.int16)
|
||||
complex_sinusoid = np.exp(1j * 2 * np.pi * self.freq / self.sample_rate * np.arange(len(input_signal)))
|
||||
self.input_mixed = input_signal * complex_sinusoid
|
||||
self.output_mixed = output_signal * complex_sinusoid
|
||||
self.input_mean = self.input_mixed.mean()
|
||||
self.output_mean = self.output_mixed.mean()
|
||||
self.iq = self.output_mean / self.input_mean
|
||||
|
||||
def get_reduced(self, mixed):
|
||||
"""get reduced array and normalize"""
|
||||
nper = self.samples_per_record // self.periods
|
||||
mean = mixed.mean()
|
||||
return mixed[:self.period * nper].reshape((-1, nper)).mean(axis=0) / mean
|
||||
|
||||
def calc_quality(self):
|
||||
"""get signal quality info
|
||||
|
||||
quality info (small values indicate good quality):
|
||||
- input_std and output_std:
|
||||
the imaginary part indicates deviations in phase
|
||||
the real part indicates deviations in amplitude
|
||||
- input_slope and output_slope:
|
||||
the imaginary part indicates a turning phase (rad/sec)
|
||||
the real part indicates changes in amplitude (0.01 ~= 1%/sec)
|
||||
"""
|
||||
reduced = self.get_reduced(self.input_mixed)
|
||||
self.input_stdev = reduced.std()
|
||||
|
||||
reduced = self.get_reduced(self.output_mixed)
|
||||
timeaxis = np.arange(len(reduced)) * self.sample_rate / self.freq
|
||||
self.output_slope = np.polyfit(timeaxis, reduced, 1)[0]
|
||||
|
@ -17,51 +17,80 @@
|
||||
# Markus Zolliker <markus.zolliker@psi.ch>
|
||||
# Jael Celia Lorenzana <jael-celia.lorenzana@psi.ch>
|
||||
# *****************************************************************************
|
||||
"""Powersupply B&K Precision BK168xB"""
|
||||
"""Powersupply B&K Precision BK168xB
|
||||
|
||||
The following lines are part of a config file for the frappy-server process
|
||||
The frappy server creates the following modules and refreshes the values with a refresh rate of 1 sec.
|
||||
The communication with the powersupply is established via serial over USB.
|
||||
The manual can be found here https://www.vectortechnologies.gr/images/products/2022/02/168xB_programming_manual.pdf
|
||||
|
||||
Mod('htr_io',
|
||||
'frappy_psi.bkpower.IO',
|
||||
'powersupply communicator',
|
||||
uri = 'serial:///dev/ttyUSBupper',
|
||||
)
|
||||
|
||||
Mod('htr',
|
||||
'frappy_psi.bkpower.Power',
|
||||
'heater power',
|
||||
io= 'htr_io',
|
||||
)
|
||||
|
||||
Mod('out',
|
||||
'frappy_psi.bkpower.Output',
|
||||
'heater output',
|
||||
io = 'htr_io',
|
||||
maxvolt = 50,
|
||||
maxcurrent = 2,
|
||||
)
|
||||
"""
|
||||
|
||||
|
||||
from frappy.core import StringIO, Readable, Parameter, FloatRange, Writable, HasIO, BoolType
|
||||
|
||||
|
||||
# define the IO class
|
||||
class IO(StringIO):
|
||||
end_of_line = ('OK\r', '\r')
|
||||
default_settings = {'baudrate': 9600}
|
||||
|
||||
|
||||
|
||||
|
||||
class Power(HasIO, Readable):
|
||||
value = Parameter(datatype=FloatRange(0,300,unit='W'))
|
||||
|
||||
|
||||
def read_value(self):
|
||||
reply = self.communicate('GETD')
|
||||
volt = float(reply[0:4])/100
|
||||
current = float(reply[4:8])/100
|
||||
return volt*current
|
||||
|
||||
|
||||
|
||||
class Output(HasIO, Writable):
|
||||
value = Parameter(datatype=FloatRange(0,100,unit='%'))
|
||||
target = Parameter(datatype=FloatRange(0,100,unit='%'))
|
||||
maxvolt = Parameter('voltage at 100%',datatype=FloatRange(0,60,unit='V'),default=50,readonly=False)
|
||||
maxcurrent = Parameter('current at 100%',datatype=FloatRange(0,5,unit='A'),default=2,readonly=False)
|
||||
output_enable = Parameter('control on/off', BoolType(), readonly=False)
|
||||
|
||||
|
||||
def initModule(self):
|
||||
super().initModule()
|
||||
self.write_output_enable(False)
|
||||
|
||||
|
||||
def write_target(self, target):
|
||||
self.write_output_enable(target != 0)
|
||||
self.communicate(f'VOLT{round(max(8,target*self.maxvolt/10)):03d}')
|
||||
self.communicate(f'CURR{round(target*self.maxcurrent):03d}')
|
||||
self.value = target
|
||||
|
||||
|
||||
def write_output_enable(self, value):
|
||||
self.communicate(f'SOUT{int(not value)}')
|
||||
|
||||
|
||||
def write_maxvolt(self, maxvolt):
|
||||
self.communicate(f'SOVP{round(maxvolt*10):03d}')
|
||||
|
||||
|
||||
def write_maxcurrent(self, maxcurrent):
|
||||
self.communicate(f'SOCP{round(maxcurrent*100):03d}')
|
||||
|
||||
|
||||
def shutdown(self):
|
||||
self.write_target(0)
|
||||
|
||||
|
||||
|
@ -22,6 +22,7 @@
|
||||
"""drivers for CCU4, the cryostat control unit at SINQ"""
|
||||
import time
|
||||
import math
|
||||
from frappy.lib.enum import Enum
|
||||
# the most common Frappy classes can be imported from frappy.core
|
||||
from frappy.core import HasIO, Parameter, Command, Readable, Writable, Drivable, \
|
||||
Property, StringIO, BUSY, IDLE, WARN, ERROR, DISABLED, Attached
|
||||
@ -32,6 +33,10 @@ from frappy.errors import CommunicationFailedError
|
||||
from frappy.states import HasStates, status_code, Retry
|
||||
|
||||
|
||||
M = Enum(idle=0, opening=1, closing=2, opened=3, closed=5, no_motor=6)
|
||||
A = Enum(disabled=0, manual=1, auto=2)
|
||||
|
||||
|
||||
class CCU4IO(StringIO):
|
||||
"""communication with CCU4"""
|
||||
# for completeness: (not needed, as it is the default)
|
||||
@ -43,25 +48,34 @@ class CCU4IO(StringIO):
|
||||
class CCU4Base(HasIO):
|
||||
ioClass = CCU4IO
|
||||
|
||||
def command(self, *args, **kwds):
|
||||
def command(self, **kwds):
|
||||
"""send a command and get the response
|
||||
|
||||
:param args: the name of the parameters to query
|
||||
:param kwds: <parameter>=<value> for changing a parameter
|
||||
:param kwds: <parameter>=<value> for changing a parameter <parameter>=<type> for querying a parameter
|
||||
:returns: the (new) values of the parameters
|
||||
"""
|
||||
cmds = [f'{k}={v:g}' for k, v in kwds.items()] + list(args)
|
||||
types = {}
|
||||
cmds = []
|
||||
for key, value in kwds.items():
|
||||
if isinstance(value, type):
|
||||
types[key] = value
|
||||
cmds.append(key)
|
||||
elif isinstance(value, str):
|
||||
types[key] = str
|
||||
cmds.append(f'{key}={value}')
|
||||
else:
|
||||
types[key] = float
|
||||
cmds.append(f'{key}={value:g}')
|
||||
reply = self.io.communicate(' '.join(cmds)).split()
|
||||
names = list(args) + list(kwds)
|
||||
if len(reply) != len(names):
|
||||
if len(reply) != len(types):
|
||||
raise CommunicationFailedError('number of reply items does not match')
|
||||
result = []
|
||||
for given, item in zip(names, reply):
|
||||
name, txtvalue = item.split('=')
|
||||
for (given, typ), res in zip(types.items(), reply):
|
||||
name, txtvalue = res.split('=')
|
||||
if given != name:
|
||||
raise CommunicationFailedError('result keys do not match given keys')
|
||||
result.append(float(txtvalue))
|
||||
if len(result) == 1:
|
||||
result.append(typ(txtvalue))
|
||||
if len(kwds) == 1:
|
||||
return result[0]
|
||||
return result
|
||||
|
||||
@ -89,13 +103,13 @@ class HeLevel(CCU4Base, Readable):
|
||||
}
|
||||
|
||||
def read_value(self):
|
||||
return self.command('h')
|
||||
return self.command(h=float)
|
||||
|
||||
def read_status(self):
|
||||
return self.STATUS_MAP[int(self.command('hsf'))]
|
||||
return self.STATUS_MAP[int(self.command(hsf=int))]
|
||||
|
||||
def read_sample_rate(self):
|
||||
value, self.empty_length, self.full_length = self.command('hf', 'hem', 'hfu')
|
||||
value, self.empty_length, self.full_length = self.command(hf=int, hem=float, hfu=float)
|
||||
return value
|
||||
|
||||
def write_sample_rate(self, value):
|
||||
@ -138,13 +152,13 @@ class Valve(CCU4Base, Writable):
|
||||
class HeFillValve(Valve):
|
||||
_open_command = {'hcd': 1, 'hf': 1}
|
||||
_close_command = {'hcd': 0, 'hf': 0}
|
||||
_query_state = 'hv'
|
||||
_query_state = {'hv': int}
|
||||
|
||||
|
||||
class N2FillValve(Valve):
|
||||
_open_command = {'nc': 1}
|
||||
_close_command = {'nc': 0}
|
||||
_query_state = 'nv'
|
||||
_query_state = {'nv': int}
|
||||
|
||||
|
||||
class AuxValve(Valve):
|
||||
@ -153,7 +167,7 @@ class AuxValve(Valve):
|
||||
def initModule(self):
|
||||
self._open_command = {f'vc{self.channel}': 1}
|
||||
self._close_command = {f'vc{self.channel}': 0}
|
||||
self._query_state = f'v{self.channel}'
|
||||
self._query_state = {f'v{self.channel}': int}
|
||||
|
||||
|
||||
class N2TempSensor(Readable):
|
||||
@ -167,7 +181,7 @@ class N2Level(CCU4Base, Pinata, Readable):
|
||||
|
||||
value = Parameter('vessel state', EnumType(empty=0, ok=1, full=2))
|
||||
status = Parameter(datatype=StatusType(Readable, 'BUSY'))
|
||||
mode = Parameter('auto mode', EnumType(disabled=0, manual=1, auto=2), readonly=False)
|
||||
mode = Parameter('auto mode', EnumType(A), readonly=False)
|
||||
|
||||
threshold = Parameter('threshold triggering start/stop filling',
|
||||
FloatRange(unit='K'), readonly=False)
|
||||
@ -206,17 +220,17 @@ class N2Level(CCU4Base, Pinata, Readable):
|
||||
super().initialReads()
|
||||
|
||||
def read_status(self):
|
||||
auto, nstate = self.command('na', 'ns')
|
||||
auto, nstate = self.command(na=int, ns=int)
|
||||
if not self.valve or not auto:
|
||||
if self.mode == 'auto':
|
||||
if self.mode == A.auto:
|
||||
# no valve assigned
|
||||
self.mode = 'manual'
|
||||
if self.mode == 'disabled':
|
||||
self.mode = A.manual
|
||||
if self.mode == A.disabled:
|
||||
return DISABLED, ''
|
||||
status = self.STATUS_MAP[nstate]
|
||||
if status[0] // 100 != IDLE // 100:
|
||||
return status
|
||||
if self.mode == 'manual':
|
||||
if self.mode == A.manual:
|
||||
return IDLE, ''
|
||||
vstatus = self.valve.status
|
||||
if vstatus[0] // 100 == WARN // 100:
|
||||
@ -229,7 +243,7 @@ class N2Level(CCU4Base, Pinata, Readable):
|
||||
|
||||
def read_value(self):
|
||||
# read sensors
|
||||
lower, upper = self.command('nl', 'nu')
|
||||
lower, upper = self.command(nl=float, nu=float)
|
||||
if self.lower:
|
||||
self.lower.value = lower
|
||||
if self.upper:
|
||||
@ -241,7 +255,7 @@ class N2Level(CCU4Base, Pinata, Readable):
|
||||
return 'empty'
|
||||
|
||||
def write_mode(self, mode):
|
||||
if mode == 'auto':
|
||||
if mode == A.auto:
|
||||
if self.isBusy():
|
||||
return mode
|
||||
# set to watching
|
||||
@ -252,7 +266,7 @@ class N2Level(CCU4Base, Pinata, Readable):
|
||||
return mode
|
||||
|
||||
def read_threshold(self):
|
||||
value, self.cool_delay, self.fill_timeout = self.command('nth', 'ntc', 'ntm')
|
||||
value, self.cool_delay, self.fill_timeout = self.command(nth=float, ntc=float, ntm=float)
|
||||
return value
|
||||
|
||||
def write_threshold(self, value):
|
||||
@ -266,12 +280,12 @@ class N2Level(CCU4Base, Pinata, Readable):
|
||||
|
||||
@Command()
|
||||
def fill(self):
|
||||
self.mode = 'auto'
|
||||
self.mode = A.auto
|
||||
self.io.write(nc=1)
|
||||
|
||||
@Command()
|
||||
def stop(self):
|
||||
if self.mode == 'auto':
|
||||
if self.mode == A.auto:
|
||||
# set to watching
|
||||
self.command(nc=3)
|
||||
else:
|
||||
@ -285,7 +299,7 @@ class FlowPressure(CCU4Base, Readable):
|
||||
pollinterval = Parameter(default=0.25)
|
||||
|
||||
def read_value(self):
|
||||
return self.filter(self.command('f')) - self.mbar_offset
|
||||
return self.filter(self.command(f=float)) - self.mbar_offset
|
||||
|
||||
|
||||
class NeedleValve(HasStates, CCU4Base, Drivable):
|
||||
@ -298,9 +312,7 @@ class NeedleValve(HasStates, CCU4Base, Drivable):
|
||||
lnm_per_mbar = Parameter(unit='ln/min/mbar', default=0.6, readonly=False)
|
||||
use_pressure = Parameter('use flow from pressure', BoolType(),
|
||||
default=False, readonly=False)
|
||||
motor_state = Parameter('motor_state',
|
||||
EnumType(idle=0, opening=1, closing=2,
|
||||
opened=3, closed=5, no_motor=6))
|
||||
motor_state = Parameter('motor_state', EnumType(M))
|
||||
tolerance = Parameter('tolerance', FloatRange(0), value=0.25, readonly=False)
|
||||
tolerance2 = Parameter('tolerance limit above 2 lnm', FloatRange(0), value=0.5, readonly=False)
|
||||
prop = Parameter('proportional term', FloatRange(unit='s/lnm'), readonly=False)
|
||||
@ -341,12 +353,12 @@ class NeedleValve(HasStates, CCU4Base, Drivable):
|
||||
def update_flow(self, value):
|
||||
if not self.use_pressure:
|
||||
self.value = value
|
||||
self.doPoll()
|
||||
self.cycle_machine()
|
||||
|
||||
def update_flow_pressure(self, value):
|
||||
if self.use_pressure:
|
||||
self.value = value * self.lnm_per_mbar
|
||||
self.doPoll()
|
||||
self.cycle_machine()
|
||||
|
||||
def write_target(self, value):
|
||||
self.start_machine(self.controlling, in_tol_time=0,
|
||||
@ -354,7 +366,7 @@ class NeedleValve(HasStates, CCU4Base, Drivable):
|
||||
|
||||
@status_code(BUSY)
|
||||
def unblock_from_open(self, state):
|
||||
self.motor_state = self.command('fm')
|
||||
self.motor_state = self.command(fm=int)
|
||||
if self.motor_state == 'opened':
|
||||
self.command(mp=-60)
|
||||
return Retry
|
||||
@ -372,7 +384,7 @@ class NeedleValve(HasStates, CCU4Base, Drivable):
|
||||
|
||||
@status_code(BUSY)
|
||||
def unblock_open(self, state):
|
||||
self.motor_state = self.command('fm')
|
||||
self.motor_state = self.command(fm=int)
|
||||
if self.value < state.flow_before:
|
||||
state.flow_before_open = self.value
|
||||
elif self.value > state.flow_before + 1:
|
||||
@ -393,7 +405,7 @@ class NeedleValve(HasStates, CCU4Base, Drivable):
|
||||
|
||||
@status_code(BUSY)
|
||||
def unblock_close(self, state):
|
||||
self.motor_state = self.command('fm')
|
||||
self.motor_state = self.command(fm=int)
|
||||
if self.value > state.flow_before:
|
||||
state.flow_before_open = self.value
|
||||
elif self.value < state.flow_before - 1:
|
||||
@ -435,7 +447,7 @@ class NeedleValve(HasStates, CCU4Base, Drivable):
|
||||
dif = self.target - self.value
|
||||
difdif = dif - state.prev_dif
|
||||
state.prev_dif = dif
|
||||
self.motor_state = self.command('fm')
|
||||
self.motor_state = self.command(fm=int)
|
||||
if self.motor_state == 'closed':
|
||||
if dif < 0 or difdif < 0:
|
||||
return Retry
|
||||
@ -467,5 +479,3 @@ class NeedleValve(HasStates, CCU4Base, Drivable):
|
||||
return Retry
|
||||
self.command(mp=state.step)
|
||||
return Retry
|
||||
|
||||
|
||||
|
@ -63,7 +63,12 @@ def parse_result(reply):
|
||||
|
||||
class LakeShoreIO(HasIO):
|
||||
def set_param(self, cmd, *args):
|
||||
head = ','.join([cmd] + [f'{a:g}' for a in args])
|
||||
args = [f'{a:g}' for a in args]
|
||||
if ' ' in cmd.strip():
|
||||
args.insert(0, cmd)
|
||||
else:
|
||||
args[0] = cmd + args[0]
|
||||
head = ','.join(args)
|
||||
tail = cmd.replace(' ', '?')
|
||||
reply = self.io.communicate(f'{head};{tail}')
|
||||
return parse_result(reply)
|
||||
@ -99,7 +104,7 @@ class Switcher(LakeShoreIO, ChannelSwitcher):
|
||||
if channelno is None:
|
||||
self.status = 'ERROR', 'no enabled channel'
|
||||
return
|
||||
self.set_param(f'SCAN {channelno},0')
|
||||
self.set_param('SCAN ', channelno, 0)
|
||||
|
||||
def doPoll(self):
|
||||
"""poll buttons
|
||||
@ -160,7 +165,7 @@ class Switcher(LakeShoreIO, ChannelSwitcher):
|
||||
self.measure_delay = chan.dwell
|
||||
|
||||
def set_active_channel(self, chan):
|
||||
self.set_param(f'SCAN {chan.channel},0')
|
||||
self.set_param('SCAN ', chan.channel, 0)
|
||||
chan._last_range_change = time.monotonic()
|
||||
self.set_delays(chan)
|
||||
|
||||
@ -227,7 +232,7 @@ class ResChannel(LakeShoreIO, Channel):
|
||||
now = time.monotonic()
|
||||
if now + 0.5 < max(self._last_range_change, self.switcher._start_switch) + self.pause:
|
||||
return None
|
||||
result = self.get_param(f'RDGR{self.channel}')
|
||||
result = self.get_param(f'RDGR?{self.channel}')
|
||||
if self.autorange:
|
||||
self.fix_autorange()
|
||||
if now + 0.5 > self._last_range_change + self.pause:
|
||||
@ -251,7 +256,7 @@ class ResChannel(LakeShoreIO, Channel):
|
||||
|
||||
def read_value(self):
|
||||
if self.channel == self.switcher.value == self.switcher.target:
|
||||
value = self._read_value()
|
||||
value = self.get_value()
|
||||
if value is not None:
|
||||
return value
|
||||
return self.value # return previous value
|
||||
@ -264,7 +269,7 @@ class ResChannel(LakeShoreIO, Channel):
|
||||
|
||||
@CommonReadHandler(rdgrng_params)
|
||||
def read_rdgrng(self):
|
||||
iscur, exc, rng, autorange, excoff = self.get_param(f'RDGRNG{self.channel}')
|
||||
iscur, exc, rng, autorange, excoff = self.get_param(f'RDGRNG?{self.channel}')
|
||||
self._prev_rdgrng = iscur, exc
|
||||
if autorange: # pressed autorange button
|
||||
if not self._toggle_autorange:
|
||||
@ -293,8 +298,7 @@ class ResChannel(LakeShoreIO, Channel):
|
||||
excoff = 1
|
||||
rng = change['range']
|
||||
if self.autorange:
|
||||
if rng < self.minrange:
|
||||
rng = self.minrange
|
||||
rng = max(rng, self.minrange)
|
||||
self.set_param(f'RDGRNG {self.channel}', iscur, exc, rng, 0, excoff)
|
||||
self.read_range()
|
||||
|
||||
|
@ -16,84 +16,38 @@
|
||||
# Module authors:
|
||||
# Markus Zolliker <markus.zolliker@psi.ch>
|
||||
# *****************************************************************************
|
||||
"""a very simple simulator for a LakeShore Model 370"""
|
||||
"""a very simple simulator for LakeShore Models 370 and 372
|
||||
|
||||
reduced to the functionality actually used in e.g. frappy_psi.ls370res
|
||||
"""
|
||||
|
||||
import time
|
||||
from frappy.modules import Communicator
|
||||
|
||||
|
||||
class Ls370Sim(Communicator):
|
||||
CHANNEL_COMMANDS = [
|
||||
('RDGR?%d', '1.0'),
|
||||
('RDGST?%d', '0'),
|
||||
('RDGRNG?%d', '0,5,5,0,0'),
|
||||
('INSET?%d', '1,5,5,0,0'),
|
||||
('FILTER?%d', '1,5,80'),
|
||||
]
|
||||
OTHER_COMMANDS = [
|
||||
('*IDN?', 'LSCI,MODEL370,370184,05302003'),
|
||||
('SCAN?', '3,1'),
|
||||
('*OPC?', '1'),
|
||||
]
|
||||
|
||||
def earlyInit(self):
|
||||
super().earlyInit()
|
||||
self._data = dict(self.OTHER_COMMANDS)
|
||||
for fmt, v in self.CHANNEL_COMMANDS:
|
||||
for chan in range(1,17):
|
||||
self._data[fmt % chan] = v
|
||||
|
||||
def communicate(self, command):
|
||||
self.comLog('> %s' % command)
|
||||
# simulation part, time independent
|
||||
for channel in range(1,17):
|
||||
_, _, _, _, excoff = self._data['RDGRNG?%d' % channel].split(',')
|
||||
if excoff == '1':
|
||||
self._data['RDGST?%d' % channel] = '6'
|
||||
else:
|
||||
self._data['RDGST?%d' % channel] = '0'
|
||||
|
||||
chunks = command.split(';')
|
||||
reply = []
|
||||
for chunk in chunks:
|
||||
if '?' in chunk:
|
||||
reply.append(self._data[chunk])
|
||||
else:
|
||||
for nqarg in (1,0):
|
||||
if nqarg == 0:
|
||||
qcmd, arg = chunk.split(' ', 1)
|
||||
qcmd += '?'
|
||||
else:
|
||||
qcmd, arg = chunk.split(',', nqarg)
|
||||
qcmd = qcmd.replace(' ', '?', 1)
|
||||
if qcmd in self._data:
|
||||
self._data[qcmd] = arg
|
||||
break
|
||||
reply = ';'.join(reply)
|
||||
self.comLog('< %s' % reply)
|
||||
return reply
|
||||
|
||||
|
||||
class Ls372Sim(Communicator):
|
||||
class _Ls37xSim(Communicator):
|
||||
# commands containing %d for the channel number
|
||||
CHANNEL_COMMANDS = [
|
||||
('RDGR?%d', '1.0'),
|
||||
('RDGK?%d', '1.5'),
|
||||
('RDGST?%d', '0'),
|
||||
('RDGRNG?%d', '0,5,5,0,0'),
|
||||
('INSET?%d', '1,5,5,0,0'),
|
||||
('INSET?%d', '1,3,3,0,0'),
|
||||
('FILTER?%d', '1,5,80'),
|
||||
]
|
||||
# commands not related to a channel
|
||||
OTHER_COMMANDS = [
|
||||
('*IDN?', 'LSCI,MODEL372,372184,05302003'),
|
||||
('SCAN?', '3,1'),
|
||||
('PID?1', '10,10,0'),
|
||||
('*OPC?', '1'),
|
||||
]
|
||||
|
||||
def earlyInit(self):
|
||||
super().earlyInit()
|
||||
self._res = {}
|
||||
self._start = time.time()
|
||||
self._data = dict(self.OTHER_COMMANDS)
|
||||
for fmt, v in self.CHANNEL_COMMANDS:
|
||||
for chan in range(1,17):
|
||||
for chan in range(1, 17):
|
||||
self._data[fmt % chan] = v
|
||||
|
||||
def communicate(self, command):
|
||||
@ -105,6 +59,10 @@ class Ls372Sim(Communicator):
|
||||
self._data['RDGST?%d' % channel] = '6'
|
||||
else:
|
||||
self._data['RDGST?%d' % channel] = '0'
|
||||
channel = int(self._data['SCAN?'].split(',', 1)[0])
|
||||
self._res[channel] = channel + (time.time() - self._start) / 3600
|
||||
strvalue = f'{self._res[channel]:g}'
|
||||
self._data['RDGR?%d' % channel] = self._data['RDGK?%d' % channel] = strvalue
|
||||
|
||||
chunks = command.split(';')
|
||||
reply = []
|
||||
@ -112,7 +70,7 @@ class Ls372Sim(Communicator):
|
||||
if '?' in chunk:
|
||||
reply.append(self._data[chunk])
|
||||
else:
|
||||
for nqarg in (1,0):
|
||||
for nqarg in (1, 0):
|
||||
if nqarg == 0:
|
||||
qcmd, arg = chunk.split(' ', 1)
|
||||
qcmd += '?'
|
||||
@ -125,3 +83,16 @@ class Ls372Sim(Communicator):
|
||||
reply = ';'.join(reply)
|
||||
self.comLog('< %s' % reply)
|
||||
return reply
|
||||
|
||||
|
||||
class Ls370Sim(_Ls37xSim):
|
||||
OTHER_COMMANDS = _Ls37xSim.OTHER_COMMANDS + [
|
||||
('*IDN?', 'LSCI,MODEL370,370184,05302003'),
|
||||
]
|
||||
|
||||
|
||||
class Ls372Sim(_Ls37xSim):
|
||||
OTHER_COMMANDS = _Ls37xSim.OTHER_COMMANDS + [
|
||||
('*IDN?', 'LSCI,MODEL372,372184,05302003'),
|
||||
('PID?1', '10,10,0'),
|
||||
]
|
||||
|
@ -140,6 +140,15 @@ class SimpleMagfield(HasStates, Drivable):
|
||||
self.setFastPoll(True, 1.0)
|
||||
return self.start_ramp_to_target
|
||||
|
||||
@status_code(Status.RAMPING)
|
||||
def start_ramp_to_target(self, sm):
|
||||
"""start ramping current to target field
|
||||
|
||||
initiate ramp to target
|
||||
the implementation should return ramp_to_target
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
@status_code(BUSY, 'ramping field')
|
||||
def ramp_to_target(self, sm):
|
||||
if sm.init:
|
||||
@ -324,15 +333,6 @@ class Magfield(SimpleMagfield):
|
||||
self._last_target = sm.target
|
||||
return self.start_ramp_to_target
|
||||
|
||||
@status_code(Status.RAMPING)
|
||||
def start_ramp_to_target(self, sm):
|
||||
"""start ramping current to target field
|
||||
|
||||
initiate ramp to target
|
||||
the implementation should return ramp_to_target
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
@status_code(Status.RAMPING)
|
||||
def ramp_to_target(self, sm):
|
||||
dif = abs(self.value - sm.target)
|
||||
|
276
frappy_psi/oxinst.py
Normal file
276
frappy_psi/oxinst.py
Normal file
@ -0,0 +1,276 @@
|
||||
# *****************************************************************************
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free Software
|
||||
# Foundation; either version 2 of the License, or (at your option) any later
|
||||
# version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#
|
||||
# Module authors:
|
||||
# Markus Zolliker <markus.zolliker@psi.ch>
|
||||
# *****************************************************************************
|
||||
"""oxford instruments old devices (ILM, IGH, IPS)"""
|
||||
|
||||
import re
|
||||
import time
|
||||
from frappy.core import StringIO, HasIO, Readable, Parameter, ERROR, IDLE, PREPARING
|
||||
from frappy.datatypes import FloatRange, BoolType, EnumType
|
||||
from frappy.errors import HardwareError, RangeError
|
||||
from frappy.lib import formatStatusBits, clamp
|
||||
from frappy.lib.enum import Enum
|
||||
from frappy_psi.magfield import Magfield
|
||||
from frappy.states import Retry
|
||||
|
||||
|
||||
class IlmIO(StringIO):
|
||||
end_of_line = '\r'
|
||||
identification = [('V', r'ILM200.*')]
|
||||
timeout = 5
|
||||
|
||||
|
||||
class OxiBase(HasIO):
|
||||
def query(self, cmd, dig=0):
|
||||
reply = self.communicate(cmd)
|
||||
if reply[0] == cmd[0]:
|
||||
if '.' not in reply and dig > 0:
|
||||
# add decimal point if not already there (for older systems)
|
||||
reply = f'{reply[1:-dig]}.{reply[-dig:]}'
|
||||
try:
|
||||
value = float(reply)
|
||||
return value
|
||||
except Exception:
|
||||
pass
|
||||
raise HardwareError(f'bad reply {reply!r} to {cmd!r}')
|
||||
|
||||
def command(self, *cmds):
|
||||
try:
|
||||
self.communicate('C3')
|
||||
for cmd in cmds:
|
||||
self.communicate(cmd)
|
||||
finally:
|
||||
self.communicate('C0')
|
||||
|
||||
def change(self, cmd, query, value):
|
||||
try:
|
||||
self.communicate('C3')
|
||||
self.communicate(f'{cmd}{value:g}')
|
||||
return self.query(query)
|
||||
finally:
|
||||
self.communicate('C0')
|
||||
|
||||
|
||||
class Level(OxiBase, Readable):
|
||||
ioClass = IlmIO
|
||||
value = Parameter(datatype=FloatRange(unit='%'))
|
||||
CHANNEL = None
|
||||
XPAT = re.compile(r'X(\d)(\d)(\d)S([0-9A-F]{2}){3}R\d\d$')
|
||||
FLUID = None
|
||||
_statusbits = None
|
||||
|
||||
def read_value(self):
|
||||
return self.query(f'R{self.CHANNEL}', 1)
|
||||
|
||||
def write_fast(self, fast):
|
||||
self.command(f'T{self.CHANNEL}' if fast else f'S{self.CHANNEL}')
|
||||
|
||||
def get_status(self):
|
||||
reply = self.communicate('X')
|
||||
match = self.XPAT.match(reply)
|
||||
if match:
|
||||
statuslist = match.groups()
|
||||
if statuslist[self.CHANNEL] == '9':
|
||||
return ERROR, f'error on {self.FLUID} level channel (not connected?)'
|
||||
if statuslist[self.CHANNEL] != '2':
|
||||
return ERROR, f'{self.FLUID} level channel not configured properly'
|
||||
self._statusbits = int(statuslist[self.CHANNEL + 3], 16)
|
||||
return None
|
||||
return ERROR, f'bad status message {reply}'
|
||||
|
||||
|
||||
class HeLevel:
|
||||
CHANNEL = 1
|
||||
FLUID = 'He'
|
||||
fast = Parameter('measuring mode: True is fast', BoolType())
|
||||
|
||||
def read_status(self):
|
||||
status = self.get_status()
|
||||
if status is not None:
|
||||
return status
|
||||
return IDLE, formatStatusBits(self._statusbits, ['meas', 'fast', 'slow'])
|
||||
|
||||
|
||||
class N2Level:
|
||||
CHANNEL = 2
|
||||
MEDIUM = 'N2'
|
||||
|
||||
def read_status(self):
|
||||
status = self.get_status()
|
||||
if status is not None:
|
||||
return status
|
||||
return IDLE, ''
|
||||
|
||||
|
||||
A = Enum(hold=0, run_to_set=1, run_to_zero=2, clamped=4)
|
||||
|
||||
|
||||
class Field(OxiBase, Magfield):
|
||||
action = Parameter('action', EnumType(A), readonly=False)
|
||||
setpoint = Parameter('field setpoint', FloatRange(unit='T'), default=0)
|
||||
voltage = Parameter('leads voltage', FloatRange(unit='V'), default=0)
|
||||
atob = Parameter('field to amp', FloatRange(0, unit='A/T'), default=0)
|
||||
working_ramp = Parameter('effective ramp', FloatRange(0, unit='T/min'), default=0)
|
||||
persistent_field = Parameter(
|
||||
'persistent field at last switch off', FloatRange(unit='$'), readonly=False)
|
||||
wait_switch_on = Parameter(default=60)
|
||||
wait_switch_off = Parameter(default=60)
|
||||
forced_persistent_field = Parameter(
|
||||
'manual indication that persistent field is bad', BoolType(), readonly=False, default=False)
|
||||
|
||||
XPAT = re.compile(r'X(\d)(\d)A(\d)C\dH(\d)M(\d\d)P\d\d$')
|
||||
|
||||
def startModule(self, start_events):
|
||||
# on restart, assume switch is changed long time ago, if not, the mercury
|
||||
# will complain and this will be handled in start_ramp_to_field
|
||||
self.switch_on_time = 0
|
||||
self.switch_off_time = 0
|
||||
super().startModule(start_events)
|
||||
|
||||
def read_value(self):
|
||||
current = self.query('R7')
|
||||
if self.switch_heater == self.switch_heater.on:
|
||||
self.__persistent_field = current
|
||||
self.forced_persistent_field = False
|
||||
self._field_mismatch = False
|
||||
return current
|
||||
pf = self.query('R18')
|
||||
if self.__persistent_field is None:
|
||||
self.__persistent_field = pf
|
||||
self._field_mismatch = False
|
||||
else:
|
||||
self._field_mismatch = abs(self.__persistent_field - pf) > self.tolerance * 10
|
||||
self.persistent_field = self.__persistent_field
|
||||
return self.__persistent_field
|
||||
|
||||
def read_current(self):
|
||||
current = self.query('R2')
|
||||
return current / self.atob
|
||||
|
||||
def write_persistent_field(self, value):
|
||||
if self.forced_persistent_field or abs(self.__persistent_field - value) <= self.tolerance * 10:
|
||||
self._field_mismatch = False
|
||||
self.__persistent_field = value
|
||||
return value
|
||||
raise RangeError('changing persistent field needs forced_persistent_field=True')
|
||||
|
||||
def write_target(self, target):
|
||||
if self._field_mismatch:
|
||||
self.forced_persistent_field = True
|
||||
raise RangeError('persistent field does not match - set persistent field to guessed value first')
|
||||
return super().write_target(target)
|
||||
|
||||
def read_status(self):
|
||||
status = super().read_status() # from HasStates
|
||||
reply = self.communicate('X')
|
||||
match = self.XPAT.match(reply)
|
||||
statuslist = match.group()
|
||||
if statuslist[0] != '0':
|
||||
return ERROR, formatStatusBits(int(statuslist[0]),
|
||||
['quenched', 'overheated', 'warming up', 'fault'])
|
||||
# TODO: statuslist[1]: voltage / current limit status
|
||||
self.action = int(statuslist[2])
|
||||
if statuslist[3] >= '4':
|
||||
return ERROR, 'auto run-down'
|
||||
self.switch_heater = statuslist[3] == '1'
|
||||
if statuslist[3] == '5':
|
||||
return ERROR, 'switch heater failure'
|
||||
# TODO: sweep mode (fast, slow), sweep limits
|
||||
return status
|
||||
|
||||
def read_ramp(self):
|
||||
return self.query('R9')
|
||||
|
||||
def write_ramp(self, value):
|
||||
return self.change('T', 'R9', value)
|
||||
|
||||
def write_action(self, value):
|
||||
return self.change(f'A{int(value)}')
|
||||
|
||||
def read_voltage(self):
|
||||
return self.query('R1')
|
||||
|
||||
def read_working_ramp(self):
|
||||
return self.query('R6')
|
||||
|
||||
def read_setpoint(self):
|
||||
return self.query('R8')
|
||||
|
||||
def write_setpoint(self, value):
|
||||
return self.change('J', 'R8', value)
|
||||
|
||||
def write_switch_heater(self, value):
|
||||
self.read_status()
|
||||
if value == self.switch_heater:
|
||||
self.log.info('switch heater already %r', value)
|
||||
# we do not want to restart the timer
|
||||
return value
|
||||
self.command('H1')
|
||||
|
||||
# inherit Magfield.start_field_change
|
||||
|
||||
def start_ramp_to_field(self, sm):
|
||||
if abs(self.current - self.__persistent_field) <= self.tolerance:
|
||||
self.log.info('leads %g are already at %g', self.current, self.__persistent_field)
|
||||
return self.ramp_to_field
|
||||
self.read_value()
|
||||
self.write_setpoint(self.__persistent_field)
|
||||
self.write_action(A.run_to_set)
|
||||
return self.ramp_to_field
|
||||
|
||||
# inherit from Magfield: ramp_to_field, stabilize_current, start_switch_on
|
||||
|
||||
def wait_for_switch_on(self, sm):
|
||||
self.read_status()
|
||||
if self.switch_heater == self.switch_heater.off:
|
||||
if sm.init: # avoid too many states chained
|
||||
return Retry
|
||||
self.log.warning('switch turned off manually?')
|
||||
return self.start_switch_on
|
||||
return super().wait_for_switch_on(sm) # will eventually return start_ramp_to_target
|
||||
|
||||
def start_ramp_to_target(self, sm):
|
||||
self.write_action(A.run_to_set)
|
||||
return self.ramp_to_target
|
||||
|
||||
def ramp_to_target(self, sm):
|
||||
step = self.ramp / 4 # step to be done in 15 seconds
|
||||
# change the setpoint only gradually, ramping stoppes soon after connection is lost
|
||||
self.write_setpoint(clamp(self.target, self.setpoint + step, self.setpoint - step))
|
||||
return super().ramp_to_target() # will eventually return stabilize_field
|
||||
|
||||
# inherit from Magfield: stabilize_field, check_switch_off, start_switch_off
|
||||
|
||||
def wait_for_switch_off(self, sm):
|
||||
self.read_status()
|
||||
if self.switch_heater == self.switch_heater.on:
|
||||
if sm.init: # avoid too many states chained
|
||||
return Retry
|
||||
self.log.warning('switch turned on manually?')
|
||||
return self.start_switch_off
|
||||
return super().wait_for_switch_off(sm) # will eventually return start_ramp_to_zero
|
||||
|
||||
def start_ramp_to_zero(self, sm):
|
||||
self.write_action(A.run_to_zero)
|
||||
return self.ramp_to_zero
|
||||
|
||||
# inherit from Magfield: ramp_to_zero
|
||||
|
||||
def final_status(self, *args, **kwds):
|
||||
self.write_action(A.hold)
|
||||
return super().final_status(*args, **kwds)
|
@ -166,7 +166,7 @@ class Motor(HasOffset, HasStates, PersistentMixin, HasIO, Drivable):
|
||||
def write_target(self, value):
|
||||
self.read_alive_time()
|
||||
if self._blocking_error:
|
||||
self.status = ERROR, 'clear_errors needed after ' + self._blocking_error
|
||||
self.status = ERROR, '<motor>.clear_errors() needed after ' + self._blocking_error
|
||||
raise HardwareError(self.status[1])
|
||||
self.saveParameters()
|
||||
self.start_machine(self.starting, target=value)
|
||||
|
@ -48,7 +48,7 @@ class Base(HasIO):
|
||||
return self.communicate(f'smua.measure.{cmd} = {val}')
|
||||
|
||||
|
||||
class Create_Pulse(Base, Readable, Writable):
|
||||
class Create_Pulse(Base, Writable):
|
||||
target = Parameter('source target', FloatRange, unit='A', readonly=False)
|
||||
width = Parameter('pulse width', FloatRange, unit="s", readonly=False)
|
||||
resistance = Parameter('resistance', FloatRange)
|
||||
@ -72,3 +72,4 @@ class Create_Pulse(Base, Readable, Writable):
|
||||
|
||||
|
||||
class Script(Create_Pulse):
|
||||
pass
|
||||
|
@ -36,11 +36,12 @@ import time
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
from frappy.client import ProxyClient
|
||||
from frappy.client import ProxyClient, CacheItem
|
||||
from frappy.datatypes import ArrayOf, BoolType, \
|
||||
EnumType, FloatRange, IntRange, StringType, StatusType
|
||||
from frappy.core import IDLE, BUSY, WARN, ERROR, DISABLED
|
||||
from frappy.errors import ConfigError, HardwareError, ReadFailedError, CommunicationFailedError
|
||||
from frappy.errors import ConfigError, HardwareError, ReadFailedError, \
|
||||
CommunicationFailedError, ProgrammingError
|
||||
from frappy.lib import generalConfig, mkthread, lazy_property
|
||||
from frappy.lib.asynconn import AsynConn, ConnectionClosed
|
||||
from frappy.modulebase import Done
|
||||
@ -332,9 +333,10 @@ class SeaClient(ProxyClient, Module):
|
||||
self.secNode.srv.shutdown()
|
||||
else:
|
||||
for module, param in mplist:
|
||||
oldv, oldt, oldr = self.cache.get((module, param), [None, None, None])
|
||||
oldv, oldt, oldr = self.cache[module, param]
|
||||
if value is None:
|
||||
value = oldv
|
||||
self.cache[module, param] = CacheItem(value, now, readerror)
|
||||
if value != oldv or str(readerror) != str(oldr) or abs(now - oldt) > 60:
|
||||
# do not update unchanged values within 60 sec
|
||||
self.updateValue(module, param, value, now, readerror)
|
||||
@ -417,11 +419,24 @@ class SeaConfigCreator(SeaClient):
|
||||
return reply
|
||||
|
||||
|
||||
class SeaBool(BoolType):
|
||||
"""some sea enum nodes used as boolean have text type -> accept '<integer>' also"""
|
||||
def copy(self):
|
||||
return SeaBool()
|
||||
|
||||
def __call__(self, value):
|
||||
try:
|
||||
value = int(value)
|
||||
return super().__call__(value)
|
||||
except Exception as e:
|
||||
raise ReadFailedError(e) from e
|
||||
|
||||
|
||||
SEA_TO_SECOPTYPE = {
|
||||
'float': FloatRange(),
|
||||
'text': StringType(),
|
||||
'int': IntRange(-1 << 63, 1 << 63 - 1),
|
||||
'bool': BoolType(),
|
||||
'bool': SeaBool(),
|
||||
'none': None,
|
||||
'floatvarar': ArrayOf(FloatRange(), 0, 400), # 400 is the current limit for proper notify events in SEA
|
||||
}
|
||||
@ -451,13 +466,19 @@ def get_datatype(paramdesc):
|
||||
raise ValueError('unknown SEA type %r' % typ)
|
||||
|
||||
|
||||
def get_cfg(cfgdict, *args):
|
||||
result = cfgdict.get(*args)
|
||||
return result['value'] if isinstance(result, dict) else result
|
||||
|
||||
|
||||
def pop_cfg(cfgdict, *args):
|
||||
result = cfgdict.pop(*args)
|
||||
return result['value'] if isinstance(result, dict) else result
|
||||
|
||||
|
||||
class SeaModule(Module):
|
||||
io = Attached()
|
||||
|
||||
path2param = None
|
||||
sea_object = None
|
||||
hdbpath = None # hdbpath for main writable
|
||||
|
||||
# pylint: disable=too-many-statements,arguments-differ,too-many-branches
|
||||
def __new__(cls, name, logger, cfgdict, srv):
|
||||
if hasattr(srv, 'extra_sea_modules'):
|
||||
@ -465,90 +486,100 @@ class SeaModule(Module):
|
||||
else:
|
||||
extra_modules = {}
|
||||
srv.extra_sea_modules = extra_modules
|
||||
for k, v in cfgdict.items():
|
||||
try:
|
||||
cfgdict[k] = v['value']
|
||||
except (KeyError, TypeError):
|
||||
pass
|
||||
json_file = cfgdict.pop('json_file', None) or SeaClient.default_json_file[cfgdict['io']]
|
||||
visibility_level = cfgdict.pop('visibility_level', 2)
|
||||
|
||||
single_module = cfgdict.pop('single_module', None)
|
||||
json_file = pop_cfg(cfgdict, 'json_file', None) or SeaClient.default_json_file[get_cfg(cfgdict, 'io')]
|
||||
visibility_level = pop_cfg(cfgdict, 'visibility_level', 2)
|
||||
|
||||
single_module = pop_cfg(cfgdict, 'single_module', None)
|
||||
if single_module:
|
||||
sea_object, base, paramdesc = extra_modules[single_module]
|
||||
params = [paramdesc]
|
||||
paramdesc['key'] = 'value'
|
||||
if issubclass(cls, SeaWritable):
|
||||
if issubclass(cls, SeaWritable): # and not SeaDrivable!
|
||||
if paramdesc.get('readonly', True):
|
||||
raise ConfigError(f"{sea_object}/{paramdesc['path']} is not writable")
|
||||
params.insert(0, paramdesc.copy()) # copy value
|
||||
paramdesc['key'] = 'target'
|
||||
paramdesc['readonly'] = False
|
||||
extra_module_set = ()
|
||||
if 'description' not in cfgdict:
|
||||
if not get_cfg(cfgdict, 'description'):
|
||||
cfgdict['description'] = f'{single_module}@{json_file}'
|
||||
else:
|
||||
sea_object = cfgdict.pop('sea_object')
|
||||
rel_paths = cfgdict.pop('rel_paths', '.')
|
||||
if 'description' not in cfgdict:
|
||||
sea_object = pop_cfg(cfgdict, 'sea_object', None)
|
||||
sea_path = pop_cfg(cfgdict, 'sea_path', None)
|
||||
if sea_object:
|
||||
if sea_path:
|
||||
raise ConfigError(f'module {name}: superfluous sea_object property (sea_path is given)')
|
||||
sea_path = sea_object
|
||||
rel_paths = get_cfg(cfgdict, 'rel_paths', None)
|
||||
if rel_paths is None:
|
||||
sea_object, *rel_paths = sea_path.split('/', 1)
|
||||
if not rel_paths:
|
||||
rel_paths = None
|
||||
else:
|
||||
if '/' in sea_path:
|
||||
raise ConfigError(f'module {name}: superfluous rel_paths property (sea_path is given)')
|
||||
sea_object = sea_path
|
||||
# rel_paths:
|
||||
# a list of sub nodes to look for parameters
|
||||
# '.' denotes the main path
|
||||
# Readable: the main value is taken from the first subpath
|
||||
# Writable:
|
||||
# - read the target value: <sicsobj> target
|
||||
# - write target value: command from first subpath
|
||||
# Drivable:
|
||||
# - write target value: run <sea_object> <target>
|
||||
if not get_cfg(cfgdict, 'description'):
|
||||
cfgdict['description'] = '%s@%s%s' % (
|
||||
name, json_file, '' if rel_paths == '.' else f' (rel_paths={rel_paths})')
|
||||
name, json_file, '' if rel_paths is None else f' (rel_paths={rel_paths})')
|
||||
|
||||
with (seaconfig.dir / json_file).open(encoding='utf-8') as fp:
|
||||
content = json.load(fp)
|
||||
descr = content[sea_object]
|
||||
if rel_paths == '*' or not rel_paths:
|
||||
# take all
|
||||
main = descr['params'][0]
|
||||
if issubclass(cls, Readable):
|
||||
# assert main['path'] == '' # TODO: check cases where this fails
|
||||
main['key'] = 'value'
|
||||
else:
|
||||
descr['params'].pop(0)
|
||||
|
||||
# filter by relative paths
|
||||
if rel_paths:
|
||||
result = {k: [] for k in rel_paths}
|
||||
else:
|
||||
# filter by relative paths
|
||||
result = []
|
||||
is_running = None
|
||||
for rpath in rel_paths:
|
||||
include = True
|
||||
for paramdesc in descr['params']:
|
||||
path = paramdesc['path']
|
||||
if path.endswith('is_running') and issubclass(cls, Drivable):
|
||||
# take this independent of visibility
|
||||
is_running = paramdesc
|
||||
continue
|
||||
if paramdesc.get('visibility', 1) > visibility_level:
|
||||
continue
|
||||
sub = path.split('/', 1)
|
||||
if rpath == '.': # take all except subpaths with readonly node at top
|
||||
if len(sub) == 1:
|
||||
include = paramdesc.get('kids', 0) == 0 or not paramdesc.get('readonly', True)
|
||||
if include or path == '':
|
||||
result.append(paramdesc)
|
||||
elif sub[0] == rpath:
|
||||
result.append(paramdesc)
|
||||
if is_running: # take this at end
|
||||
result.append(is_running)
|
||||
descr['params'] = result
|
||||
rel0 = '' if rel_paths[0] == '.' else rel_paths[0]
|
||||
if result[0]['path'] == rel0:
|
||||
if issubclass(cls, Readable):
|
||||
result[0]['key'] = 'value'
|
||||
else:
|
||||
result.pop(0)
|
||||
result = {True: []}
|
||||
is_running = None
|
||||
for paramdesc in descr['params']:
|
||||
path = paramdesc['path']
|
||||
pathlist = path.split('/')
|
||||
if pathlist[-1] == 'is_running' and issubclass(cls, Drivable):
|
||||
# take this independent of visibility
|
||||
is_running = paramdesc
|
||||
continue
|
||||
if paramdesc.get('visibility', 1) > visibility_level:
|
||||
continue
|
||||
if rel_paths is None:
|
||||
result[True].append(paramdesc)
|
||||
else:
|
||||
logger.error('%s: no value found', name)
|
||||
cls.paramFilter(result, paramdesc)
|
||||
cfgdict.pop('rel_paths', None)
|
||||
params = sum(result.values(), [])
|
||||
if is_running: # take this at end
|
||||
params.append(is_running)
|
||||
|
||||
main_value = params[0]
|
||||
if issubclass(cls, Readable):
|
||||
if 'key' in main_value:
|
||||
raise ProgrammingError(f'main_value {main_value!r}')
|
||||
main_value['key'] = 'value'
|
||||
else:
|
||||
params.pop(0)
|
||||
base = descr['base']
|
||||
params = descr['params']
|
||||
if issubclass(cls, SeaWritable):
|
||||
if issubclass(cls, SeaWritable): # and not SeaDrivable!
|
||||
paramdesc = params[0]
|
||||
assert paramdesc['key'] == 'value'
|
||||
if paramdesc.get('key') != 'value':
|
||||
raise ProgrammingError(f"key of first parameter of {name} must be 'value'")
|
||||
params.append(paramdesc.copy()) # copy value?
|
||||
if paramdesc.get('readonly', True):
|
||||
raise ConfigError(f"{sea_object}/{paramdesc['path']} is not writable")
|
||||
paramdesc['key'] = 'target'
|
||||
paramdesc['readonly'] = False
|
||||
extra_module_set = set(cfgdict.pop('extra_modules', ()))
|
||||
|
||||
extra_module_set = set(pop_cfg(cfgdict, 'extra_modules', ()))
|
||||
path2param = {}
|
||||
attributes = {'sea_object': sea_object, 'path2param': path2param}
|
||||
|
||||
@ -559,14 +590,14 @@ class SeaModule(Module):
|
||||
attributes['visibility'] = 2
|
||||
# check for ambiguous names. candidates are either the last item
|
||||
# of the path or the full path (underscore separated)
|
||||
simple_names = {k: 1 for k in cls.accessibles}
|
||||
duplicates = {k: [k] for k in cls.accessibles}
|
||||
for paramdesc in params:
|
||||
path = paramdesc['path']
|
||||
if path:
|
||||
pathlist = path.split('/')
|
||||
if 'key' not in paramdesc:
|
||||
pname = pathlist[-1]
|
||||
simple_names[pname] = simple_names.get(pname, 0) + 1
|
||||
duplicates.setdefault(pname, pathlist)
|
||||
for paramdesc in params:
|
||||
path = paramdesc['path']
|
||||
readonly = paramdesc.get('readonly', True)
|
||||
@ -583,11 +614,11 @@ class SeaModule(Module):
|
||||
if len(pathlist) > 0:
|
||||
if len(pathlist) == 1:
|
||||
if issubclass(cls, Readable):
|
||||
kwds['group'] = 'more'
|
||||
kwds['group'] = 'more_'
|
||||
else:
|
||||
kwds['group'] = pathlist[-2]
|
||||
kwds['group'] = pathlist[-2] + '_'
|
||||
# take short name if unique
|
||||
if simple_names[pathlist[-1]] == 1:
|
||||
if duplicates[pathlist[-1]] == pathlist:
|
||||
key = pathlist[-1]
|
||||
else:
|
||||
key = '_'.join(pathlist)
|
||||
@ -595,31 +626,41 @@ class SeaModule(Module):
|
||||
kwds['export'] = False
|
||||
if key == 'target' and kwds.get('group') == 'more':
|
||||
kwds.pop('group')
|
||||
prev = cls.accessibles.get(key)
|
||||
if key in cls.accessibles:
|
||||
if key == 'target':
|
||||
kwds['readonly'] = False
|
||||
prev = cls.accessibles[key]
|
||||
if key == 'status':
|
||||
# special case: status from sea is a string, not the status tuple
|
||||
pobj = prev.copy()
|
||||
else:
|
||||
pobj = Parameter(**kwds)
|
||||
merged_properties = prev.propertyValues.copy()
|
||||
pobj.updateProperties(merged_properties)
|
||||
pobj.merge(merged_properties)
|
||||
else:
|
||||
pobj = Parameter(**kwds)
|
||||
datatype = pobj.datatype
|
||||
if issubclass(cls, SeaWritable) and key == 'target':
|
||||
kwds['readonly'] = False
|
||||
attributes['value'] = Parameter(**kwds)
|
||||
attributes['target'] = pobj = Parameter(**kwds)
|
||||
if prev:
|
||||
merged_properties = prev.propertyValues.copy()
|
||||
pobj.updateProperties(merged_properties)
|
||||
pobj.merge(merged_properties)
|
||||
|
||||
if key in ('value', 'target'):
|
||||
unit = get_cfg(cfgdict, 'unit')
|
||||
if unit is not None:
|
||||
pcfg = cfgdict.get(key, None)
|
||||
if not isinstance(pcfg, dict):
|
||||
cfgdict[key] = pcfg = {} if pcfg is None else {'value': pcfg}
|
||||
pcfg['unit'] = unit
|
||||
|
||||
hdbpath = '/'.join([base] + pathlist)
|
||||
if key in extra_module_set:
|
||||
extra_modules[name + '.' + key] = sea_object, base, paramdesc
|
||||
continue # skip this parameter
|
||||
path2param.setdefault(hdbpath, []).append((name, key))
|
||||
attributes[key] = pobj
|
||||
if key is not None:
|
||||
path2param.setdefault(hdbpath, []).append((name, key))
|
||||
attributes[key] = pobj
|
||||
|
||||
def rfunc(self, cmd=f'hval {base}/{path}'):
|
||||
reply = self.io.query(cmd, True)
|
||||
@ -631,7 +672,7 @@ class SeaModule(Module):
|
||||
return reply
|
||||
|
||||
rfunc.poll = False
|
||||
if key != 'status':
|
||||
if key != 'status' and key is not None:
|
||||
attributes['read_' + key] = rfunc
|
||||
|
||||
if not readonly:
|
||||
@ -645,7 +686,7 @@ class SeaModule(Module):
|
||||
self.io.query(cmd)
|
||||
return Done
|
||||
|
||||
attributes['write_' + key] = wfunc
|
||||
attributes['write_' + (key or 'target')] = wfunc
|
||||
|
||||
# create standard parameters like value and status, if not yet there
|
||||
for pname, pobj in cls.accessibles.items():
|
||||
@ -659,10 +700,23 @@ class SeaModule(Module):
|
||||
pobj.__set_name__(cls, pname)
|
||||
|
||||
classname = f'{cls.__name__}_{name}'
|
||||
newcls = type(classname, (cls,), attributes)
|
||||
try:
|
||||
newcls = type(classname, (cls,), attributes)
|
||||
except Exception as e:
|
||||
raise
|
||||
# newcls = type(classname, (cls,), attributes)
|
||||
result = Module.__new__(newcls)
|
||||
return result
|
||||
|
||||
@classmethod
|
||||
def paramFilter(cls, result, paramdesc):
|
||||
sub = paramdesc['path'].split('/', 1)
|
||||
sublist = result.get(sub[0])
|
||||
if sublist is None:
|
||||
return False
|
||||
sublist.append(paramdesc)
|
||||
return True
|
||||
|
||||
def updateEvent(self, module, parameter, value, timestamp, readerror):
|
||||
upd = getattr(self, 'update_' + parameter, None)
|
||||
if upd:
|
||||
@ -679,6 +733,7 @@ class SeaReadable(SeaModule, Readable):
|
||||
_readerror = None
|
||||
_status = IDLE, ''
|
||||
|
||||
unit = Property('physical unit', StringType(isUTF8=True), default='')
|
||||
status = Parameter(datatype=StatusType(Readable, 'DISABLED'))
|
||||
|
||||
def update_value(self, value, timestamp, readerror):
|
||||
@ -778,11 +833,6 @@ class SeaDrivable(SeaReadable, Drivable):
|
||||
return IDLE, f'started, but not running'
|
||||
return IDLE, ''
|
||||
|
||||
def update_target(self, module, parameter, value, timestamp, readerror):
|
||||
# TODO: check if this is needed
|
||||
if value is not None:
|
||||
self.target = value
|
||||
|
||||
@Command()
|
||||
def stop(self):
|
||||
"""propagate to SEA
|
||||
@ -791,3 +841,20 @@ class SeaDrivable(SeaReadable, Drivable):
|
||||
- on EaseDriv this will set the stopped state
|
||||
"""
|
||||
self.io.query(f'{self.sea_object} is_running 0')
|
||||
|
||||
|
||||
class LscDrivable(SeaDrivable):
|
||||
def __new__(cls, name, logger, cfgdict, srv):
|
||||
cfgdict['rel_paths'] = [pop_cfg(cfgdict, 'sensor_path', 'tm'), '.',
|
||||
pop_cfg(cfgdict, 'set_path', 'set'), 'dblctrl']
|
||||
return super().__new__(cls, name, logger, cfgdict, srv)
|
||||
|
||||
@classmethod
|
||||
def paramFilter(cls, result, paramdesc):
|
||||
if super().paramFilter(result, paramdesc):
|
||||
return True
|
||||
pathlist = paramdesc['path'].split('/')
|
||||
if len(pathlist) == 1 and paramdesc.get('kids', 0) == 0:
|
||||
result['.'].append(paramdesc)
|
||||
return True
|
||||
return False
|
||||
|
@ -21,7 +21,7 @@
|
||||
|
||||
from frappy.core import Command, StringIO, Parameter, HasIO, \
|
||||
Drivable, FloatRange, IDLE, BUSY, ERROR, WARN, BoolType
|
||||
from frappy.structparam import StructParam
|
||||
from frappy.extparams import StructParam
|
||||
from frappy_psi.convergence import HasConvergence
|
||||
|
||||
|
||||
|
@ -19,17 +19,19 @@
|
||||
"""frappy support for ultrasound"""
|
||||
|
||||
import math
|
||||
#import serial
|
||||
import os
|
||||
import time
|
||||
|
||||
import numpy as np
|
||||
|
||||
import frappy_psi.iqplot as iqplot
|
||||
from frappy_psi.adq_mr import Adq
|
||||
from frappy_psi.adq_mr import Adq, PEdata, RUSdata
|
||||
from frappy.core import Attached, BoolType, Done, FloatRange, HasIO, \
|
||||
IntRange, Module, Parameter, Readable, StringIO, StringType
|
||||
IntRange, Module, Parameter, Readable, Writable, Drivable, StringIO, StringType, \
|
||||
IDLE, BUSY, DISABLED, ERROR, TupleOf, ArrayOf, Command
|
||||
from frappy.properties import Property
|
||||
#from frappy.modules import Collector
|
||||
|
||||
Collector = Readable
|
||||
|
||||
|
||||
def fname_from_time(t, extension):
|
||||
@ -52,10 +54,9 @@ class Roi(Readable):
|
||||
time = Parameter('start time', FloatRange(unit='nsec'), readonly=False)
|
||||
size = Parameter('interval (symmetric around time)', FloatRange(unit='nsec'), readonly=False)
|
||||
enable = Parameter('calculate this roi', BoolType(), readonly=False, default=True)
|
||||
#status = Parameter(export=False)
|
||||
pollinterval = Parameter(export=False)
|
||||
|
||||
interval = (0,0)
|
||||
interval = (0, 0)
|
||||
|
||||
def initModule(self):
|
||||
super().initModule()
|
||||
@ -65,6 +66,9 @@ class Roi(Readable):
|
||||
def calc_interval(self):
|
||||
self.interval = (self.time - 0.5 * self.size, self.time + 0.5 * self.size)
|
||||
|
||||
def read_status(self):
|
||||
return (IDLE, '') if self.enable else (DISABLED, 'disabled')
|
||||
|
||||
def write_time(self, value):
|
||||
self.time = value
|
||||
self.calc_interval()
|
||||
@ -82,81 +86,29 @@ class Pars(Module):
|
||||
timestamp = Parameter('unix timestamp', StringType(), default='0', readonly=False)
|
||||
temperature = Parameter('T', FloatRange(unit='K'), default=0, readonly=False)
|
||||
mf = Parameter('field', FloatRange(unit='T'), default=0, readonly=False)
|
||||
sr = Parameter('rotaion angle', FloatRange(unit='deg'), default=0, readonly=False)
|
||||
sr = Parameter('rotation angle', FloatRange(unit='deg'), default=0, readonly=False)
|
||||
|
||||
|
||||
class FreqStringIO(StringIO):
|
||||
end_of_line = '\r'
|
||||
|
||||
|
||||
class Frequency(HasIO, Readable):
|
||||
pars = Attached()
|
||||
sr = Property('samples per record', datatype=IntRange(), default=16384)
|
||||
maxy = Property('plot y scale', datatype=FloatRange(), default=0.5)
|
||||
|
||||
value = Parameter('frequency@I,q', datatype=FloatRange(unit='Hz'), default=0)
|
||||
basefreq = Parameter('base frequency', FloatRange(unit='Hz'), readonly=False)
|
||||
nr = Parameter('number of records', datatype=IntRange(1,10000), default=500)
|
||||
freq = Parameter('target frequency', FloatRange(unit='Hz'), readonly=False)
|
||||
bw = Parameter('bandwidth lowpassfilter', datatype=FloatRange(unit='Hz'),default=10E6)
|
||||
class Frequency(HasIO, Writable):
|
||||
value = Parameter('frequency', unit='Hz')
|
||||
amp = Parameter('amplitude', FloatRange(unit='dBm'), readonly=False)
|
||||
control = Parameter('control loop on?', BoolType(), readonly=False, default=True)
|
||||
time = Parameter('pulse start time', FloatRange(unit='nsec'),
|
||||
readonly=False)
|
||||
size = Parameter('pulse length (starting from time)', FloatRange(unit='nsec'),
|
||||
readonly=False)
|
||||
pulselen = Parameter('adjusted pulse length (integer number of periods)', FloatRange(unit='nsec'), default=1)
|
||||
maxstep = Parameter('max frequency step', FloatRange(unit='Hz'), readonly=False,
|
||||
default=10000)
|
||||
minstep = Parameter('min frequency step for slope calculation', FloatRange(unit='Hz'),
|
||||
readonly=False, default=4000)
|
||||
slope = Parameter('inphase/frequency slope', FloatRange(), readonly=False,
|
||||
default=1e6)
|
||||
plot = Parameter('create plot images', BoolType(), readonly=False, default=True)
|
||||
save = Parameter('save data', BoolType(), readonly=False, default=True)
|
||||
pollinterval = Parameter(datatype=FloatRange(0,120))
|
||||
|
||||
last_change = 0
|
||||
ioClass = FreqStringIO
|
||||
dif = None
|
||||
|
||||
lastfreq = None
|
||||
old = None
|
||||
starttime = None
|
||||
interval = (0,0)
|
||||
def register_dif(self, dif):
|
||||
self.dif = dif
|
||||
|
||||
def earlyInit(self):
|
||||
super().earlyInit()
|
||||
self.adq = Adq(self.nr, self.sr, self.bw)
|
||||
self.roilist = []
|
||||
self.write_nr(self.nr)
|
||||
self.skipctrl = 0
|
||||
self.plotter = iqplot.Plot(self.maxy)
|
||||
self.calc_interval()
|
||||
|
||||
def calc_interval(self):
|
||||
self.interval = (self.time, self.time + self.size)
|
||||
|
||||
def write_time(self, value):
|
||||
self.time = value
|
||||
self.calc_interval()
|
||||
return Done
|
||||
|
||||
def write_size(self, value):
|
||||
self.size = value
|
||||
self.calc_interval()
|
||||
return Done
|
||||
|
||||
def write_nr(self, value):
|
||||
# self.pollinterval = value * 0.0001
|
||||
return value
|
||||
|
||||
def register_roi(self, roi):
|
||||
self.roilist.append(roi)
|
||||
|
||||
def set_freq(self):
|
||||
freq = self.freq + self.basefreq
|
||||
self.communicate('FREQ %.15g;FREQ?' % freq)
|
||||
#self._iodev.readline().decode('ascii')
|
||||
return freq
|
||||
def write_target(self, value):
|
||||
self.communicate('FREQ %.15g;FREQ?' % value)
|
||||
self.last_change = time.time()
|
||||
if self.dif:
|
||||
self.dif.read_value()
|
||||
|
||||
def write_amp(self, amp):
|
||||
reply = self.communicate('AMPR %g;AMPR?' % amp)
|
||||
@ -166,94 +118,196 @@ class Frequency(HasIO, Readable):
|
||||
reply = self.communicate('AMPR?')
|
||||
return float(reply)
|
||||
|
||||
def write_freq(self, value):
|
||||
self.skipctrl = 2 # suppress control for the 2 next steps
|
||||
return value
|
||||
|
||||
def doPoll(self):
|
||||
"""main poll loop body"""
|
||||
if self.lastfreq is None:
|
||||
self.lastfreq = self.set_freq()
|
||||
self.adq.start()
|
||||
if self.starttime is None:
|
||||
self.starttime = time.time()
|
||||
times = []
|
||||
times.append(('init', time.time()))
|
||||
seadata = {p: float(getattr(self.pars, p)) for p in self.pars.parameters}
|
||||
data = self.adq.getdata() # this will wait, if not yet finished
|
||||
#save sample
|
||||
#np.save('sample.dat',data)
|
||||
times.append(('wait',time.time()))
|
||||
if self.control:
|
||||
freq = self.lastfreq # data was acquired at this freq
|
||||
else:
|
||||
freq = self.set_freq()
|
||||
seadata['frequency'] = freq
|
||||
if self.control:
|
||||
self.lastfreq = self.set_freq()
|
||||
times.append(('setf',time.time()))
|
||||
self.adq.start() # start next acq
|
||||
times.append(('start',time.time()))
|
||||
roilist = [r for r in self.roilist if r.enable]
|
||||
class FrequencyDif(Readable):
|
||||
freq = Attached(Frequency)
|
||||
base = Parameter('base frequency', FloatRange(unit='Hz'), default=0)
|
||||
value = Parameter('difference to base frequency', FloatRange(unit='Hz'), default=0)
|
||||
|
||||
gates = self.adq.gates_and_curves(data, freq, self.interval,
|
||||
[r.interval for r in roilist])
|
||||
if self.save:
|
||||
times.append(('save',time.time()))
|
||||
tdata, idata, qdata, pdata = self.adq.curves
|
||||
seadata['timestep'] = tdata[1] - tdata[0]
|
||||
iqdata = np.array((idata, qdata, pdata), dtype='f4')
|
||||
ts = seadata['timestamp']
|
||||
if ts:
|
||||
filename = fname_from_time(ts, '.npz')
|
||||
seanp = np.array(list(seadata.items()), dtype=[('name', 'U16'), ('value', 'f8')])
|
||||
np.savez(filename, seadata=seanp, iqdata=iqdata)
|
||||
# can be load back via
|
||||
# content = np.load(filename)
|
||||
# content['seadata'], content['iqdata']
|
||||
self.pulselen = self.adq.pulselen
|
||||
times.append(('ana',time.time()))
|
||||
if self.plot:
|
||||
# get reduced interval from adq.sinW
|
||||
pulseint = (self.interval[0], self.interval[0] + self.pulselen)
|
||||
try:
|
||||
self.plotter.plot(
|
||||
self.adq.curves,
|
||||
rois=[pulseint] + [r.interval for r in roilist],
|
||||
average=([r.time for r in roilist],
|
||||
[r.i for r in roilist],
|
||||
[r.q for r in roilist]))
|
||||
except Exception as e:
|
||||
self.log.warning('can not plot %r' % e)
|
||||
else:
|
||||
self.plotter.close()
|
||||
now = time.time()
|
||||
times.append(('plot',now))
|
||||
# print(' '.join('%s %5.3f' % (label, t - self.starttime) for label, t in times))
|
||||
self.starttime = now
|
||||
self.value = freq - self.basefreq
|
||||
for i, roi in enumerate(roilist):
|
||||
roi.i = a = gates[i][0]
|
||||
roi.q = b = gates[i][1]
|
||||
roi.value = math.sqrt(a ** 2 + b ** 2)
|
||||
roi.phase = math.atan2(a,b) * 180 / math.pi
|
||||
inphase = self.roilist[0].i
|
||||
if self.control:
|
||||
newfreq = freq + inphase * self.slope - self.basefreq
|
||||
# step = sorted((-self.maxstep, inphase * self.slope, self.maxstep))[1]
|
||||
if self.old:
|
||||
fdif = freq - self.old[0]
|
||||
idif = inphase - self.old[1]
|
||||
if abs(fdif) >= self.minstep:
|
||||
self.slope = - fdif / idif
|
||||
else:
|
||||
fdif = 0
|
||||
idif = 0
|
||||
newfreq = freq + self.minstep
|
||||
self.old = (freq, inphase)
|
||||
if self.skipctrl > 0: # do no control for some time after changing frequency
|
||||
self.skipctrl -= 1
|
||||
elif self.control:
|
||||
self.freq = sorted((self.freq - self.maxstep, newfreq, self.freq + self.maxstep))[1]
|
||||
#print(times)
|
||||
return Done
|
||||
def initModule(self):
|
||||
super().initModule()
|
||||
self.freq.register_dif(self)
|
||||
|
||||
def read_value(self):
|
||||
return self.freq - self.base
|
||||
|
||||
|
||||
class Base(Collector):
|
||||
freq = Attached()
|
||||
adq = Attached(Adq)
|
||||
sr = Parameter('samples per record', datatype=IntRange(1, 1E9), default=16384)
|
||||
pollinterval = Parameter(datatype=FloatRange(0, 120)) # allow pollinterval = 0
|
||||
_data = None
|
||||
_data_args = None
|
||||
|
||||
def read_status(self):
|
||||
adqstate = self.adq.get_status()
|
||||
if adqstate == Adq.BUSY:
|
||||
return BUSY, 'acquiring'
|
||||
if adqstate == Adq.UNDEFINED:
|
||||
return ERROR, 'no data yet'
|
||||
if adqstate == Adq.READY:
|
||||
return IDLE, 'new data available'
|
||||
return IDLE, ''
|
||||
|
||||
def get_data(self):
|
||||
data = self.adq.get_data(*self._data_args)
|
||||
if id(data) != id(self._data):
|
||||
self._data = data
|
||||
return data
|
||||
return None
|
||||
|
||||
|
||||
class PulseEcho(Base):
|
||||
value = Parameter("t, i, q, pulse curves",
|
||||
TupleOf(*[ArrayOf(FloatRange(), 0, 16283) for _ in range(4)]), default=[[]] * 4)
|
||||
nr = Parameter('number of records', datatype=IntRange(1, 9999), default=500)
|
||||
bw = Parameter('bandwidth lowpassfilter', datatype=FloatRange(unit='Hz'), default=10E6)
|
||||
control = Parameter('control loop on?', BoolType(), readonly=False, default=True)
|
||||
time = Parameter('pulse start time', FloatRange(unit='nsec'),
|
||||
readonly=False)
|
||||
size = Parameter('pulse length (starting from time)', FloatRange(unit='nsec'),
|
||||
readonly=False)
|
||||
pulselen = Parameter('adjusted pulse length (integer number of periods)', FloatRange(unit='nsec'), default=1)
|
||||
|
||||
starttime = None
|
||||
|
||||
def initModule(self):
|
||||
super().initModule()
|
||||
self.adq = Adq()
|
||||
self.adq.init(self.sr, self.nr)
|
||||
self.roilist = []
|
||||
|
||||
def write_nr(self, value):
|
||||
self.adq.init(self.sr, value)
|
||||
|
||||
def write_sr(self, value):
|
||||
self.adq.init(value, self.nr)
|
||||
|
||||
def write_bw(self, value):
|
||||
self.adq.bw_cutoff = value
|
||||
|
||||
def register_roi(self, roi):
|
||||
self.roilist.append(roi)
|
||||
|
||||
def go(self):
|
||||
self.starttime = time.time()
|
||||
self.adq.start()
|
||||
|
||||
def read_value(self):
|
||||
if self.get_rawdata(): # new data available
|
||||
roilist = [r for r in self.roilist if r.enable]
|
||||
freq = self.freq.value
|
||||
gates = self.adq.gates_and_curves(self._data, freq,
|
||||
(self.time, self.time + self.size),
|
||||
[r.interval for r in roilist])
|
||||
for i, roi in enumerate(roilist):
|
||||
roi.i = a = gates[i][0]
|
||||
roi.q = b = gates[i][1]
|
||||
roi.value = math.sqrt(a ** 2 + b ** 2)
|
||||
roi.phase = math.atan2(a, b) * 180 / math.pi
|
||||
return self.adq.curves
|
||||
|
||||
# TODO: CONTROL
|
||||
# inphase = self.roilist[0].i
|
||||
# if self.control:
|
||||
# newfreq = freq + inphase * self.slope - self.basefreq
|
||||
# # step = sorted((-self.maxstep, inphase * self.slope, self.maxstep))[1]
|
||||
# if self.old:
|
||||
# fdif = freq - self.old[0]
|
||||
# idif = inphase - self.old[1]
|
||||
# if abs(fdif) >= self.minstep:
|
||||
# self.slope = - fdif / idif
|
||||
# else:
|
||||
# fdif = 0
|
||||
# idif = 0
|
||||
# newfreq = freq + self.minstep
|
||||
# self.old = (freq, inphase)
|
||||
# if self.skipctrl > 0: # do no control for some time after changing frequency
|
||||
# self.skipctrl -= 1
|
||||
# elif self.control:
|
||||
# self.freq = sorted((self.freq - self.maxstep, newfreq, self.freq + self.maxstep))[1]
|
||||
|
||||
|
||||
class RUS(Base):
|
||||
value = Parameter('averaged (I, Q) tuple', TupleOf(FloatRange(), FloatRange()))
|
||||
periods = Parameter('number of periods', IntRange(1, 9999), default=12)
|
||||
scale = Parameter('scale,taking into account input attenuation', FloatRange(), default=0.1)
|
||||
input_phase_stddev = Parameter('input signal quality', FloatRange(unit='rad'))
|
||||
output_phase_slope = Parameter('output signal phase slope', FloatRange(unit='rad/sec'))
|
||||
output_amp_slope = Parameter('output signal amplitude change', FloatRange(unit='1/sec'))
|
||||
phase = Parameter('phase', FloatRange(unit='deg'))
|
||||
amp = Parameter('amplitude', FloatRange())
|
||||
|
||||
starttime = None
|
||||
_data_args = None
|
||||
|
||||
def initModule(self):
|
||||
super().initModule()
|
||||
self.adq = Adq()
|
||||
# self.write_periods(self.periods)
|
||||
|
||||
def read_value(self):
|
||||
if self._data_args is None:
|
||||
return self.value # or may we raise as no value is defined yet?
|
||||
data = self.get_data(RUSdata, *self._data_args)
|
||||
if data:
|
||||
# data available
|
||||
data.calc_quality()
|
||||
self.input_phase_stddev = data.input_stddev.imag
|
||||
self.output_phase_slope = data.output_slope.imag
|
||||
self.output_amp_slope = data.output_slope.real
|
||||
|
||||
iq = data.iq * self.scale
|
||||
self.phase = np.arctan2(iq.imag, iq.real) * 180 / np.pi
|
||||
self.amp = np.abs(iq.imag, iq.real)
|
||||
return iq.real, iq.imag
|
||||
return self.value
|
||||
|
||||
def go(self):
|
||||
self.starttime = time.time()
|
||||
freq = self.freq.value
|
||||
self._data_args = (RUSdata, freq, self.periods)
|
||||
self.sr = round(self.periods * self.adq.sample_rate / freq)
|
||||
self.adq.init(self.sr, 1)
|
||||
self.adq.start()
|
||||
self.read_status()
|
||||
|
||||
|
||||
class ControlLoop:
|
||||
maxstep = Parameter('max frequency step', FloatRange(unit='Hz'), readonly=False,
|
||||
default=10000)
|
||||
minstep = Parameter('min frequency step for slope calculation', FloatRange(unit='Hz'),
|
||||
readonly=False, default=4000)
|
||||
slope = Parameter('inphase/frequency slope', FloatRange(), readonly=False,
|
||||
default=1e6)
|
||||
|
||||
|
||||
# class Frequency(HasIO, Readable):
|
||||
# pars = Attached()
|
||||
# curves = Attached(mandatory=False)
|
||||
# maxy = Property('plot y scale', datatype=FloatRange(), default=0.5)
|
||||
#
|
||||
# value = Parameter('frequency@I,q', datatype=FloatRange(unit='Hz'), default=0)
|
||||
# basefreq = Parameter('base frequency', FloatRange(unit='Hz'), readonly=False)
|
||||
# nr = Parameter('number of records', datatype=IntRange(1,10000), default=500)
|
||||
# sr = Parameter('samples per record', datatype=IntRange(1,1E9), default=16384)
|
||||
# freq = Parameter('target frequency', FloatRange(unit='Hz'), readonly=False)
|
||||
# bw = Parameter('bandwidth lowpassfilter', datatype=FloatRange(unit='Hz'),default=10E6)
|
||||
# amp = Parameter('amplitude', FloatRange(unit='dBm'), readonly=False)
|
||||
# control = Parameter('control loop on?', BoolType(), readonly=False, default=True)
|
||||
# rusmode = Parameter('RUS mode on?', BoolType(), readonly=False, default=False)
|
||||
# time = Parameter('pulse start time', FloatRange(unit='nsec'),
|
||||
# readonly=False)
|
||||
# size = Parameter('pulse length (starting from time)', FloatRange(unit='nsec'),
|
||||
# readonly=False)
|
||||
# pulselen = Parameter('adjusted pulse length (integer number of periods)', FloatRange(unit='nsec'), default=1)
|
||||
# maxstep = Parameter('max frequency step', FloatRange(unit='Hz'), readonly=False,
|
||||
# default=10000)
|
||||
# minstep = Parameter('min frequency step for slope calculation', FloatRange(unit='Hz'),
|
||||
# readonly=False, default=4000)
|
||||
# slope = Parameter('inphase/frequency slope', FloatRange(), readonly=False,
|
||||
# default=1e6)
|
||||
# plot = Parameter('create plot images', BoolType(), readonly=False, default=True)
|
||||
# save = Parameter('save data', BoolType(), readonly=False, default=True)
|
||||
# pollinterval = Parameter(datatype=FloatRange(0,120))
|
||||
|
@ -96,12 +96,14 @@ def print_commit(line):
|
||||
print(' '.join(output), title)
|
||||
cnt[0] += 1
|
||||
if cnt[0] % 50 == 0:
|
||||
input(f' {br0:11s} {br1:11s}')
|
||||
if input(f' {br0:11s} {br1:11s}'):
|
||||
raise StopIteration()
|
||||
|
||||
|
||||
(br0, log0), (br1, log1) = list(log_no.items())
|
||||
no1 = 0
|
||||
for no0, line0 in enumerate(log0):
|
||||
try:
|
||||
for no0, line0 in enumerate(log0):
|
||||
if line0[1]: # line not yet printed
|
||||
infodict = commits[line0[1]]
|
||||
if len(infodict) > 1: # found a match
|
||||
@ -114,3 +116,5 @@ for no0, line0 in enumerate(log0):
|
||||
print_commit(line1)
|
||||
no1 = no1end
|
||||
print_commit(line0)
|
||||
except StopIteration:
|
||||
pass
|
||||
|
@ -1,2 +1,3 @@
|
||||
2024-01-29 wip develop
|
||||
2024-01-29 wip mlz
|
||||
2024-10-01 wip sinq
|
||||
|
@ -1,21 +0,0 @@
|
||||
# content of conftest.py
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def constants():
|
||||
# setup
|
||||
class Constants:
|
||||
ONE = 1
|
||||
TWO = 2
|
||||
c = Constants()
|
||||
yield c
|
||||
# teardown
|
||||
del c
|
||||
|
||||
|
||||
# pylint: disable=redefined-builtin
|
||||
@pytest.fixture(scope="session")
|
||||
def globals():
|
||||
return {}
|
13
test/test_cfg_editor.py
Normal file
13
test/test_cfg_editor.py
Normal file
@ -0,0 +1,13 @@
|
||||
from pathlib import Path
|
||||
|
||||
from frappy.gui.cfg_editor.utils import get_modules, get_interfaces
|
||||
from frappy.lib import generalConfig
|
||||
|
||||
basedir = Path(__file__).parent.parent.absolute()
|
||||
|
||||
|
||||
def test_imports():
|
||||
generalConfig.testinit(basedir=basedir)
|
||||
|
||||
get_modules()
|
||||
get_interfaces()
|
@ -137,5 +137,5 @@ def test_process_file(direc, log):
|
||||
|
||||
|
||||
def test_full(direc, log):
|
||||
ret = load_config('pyfile_cfg.py', log)
|
||||
ret = load_config(['pyfile_cfg.py'], log)
|
||||
do_asserts(ret)
|
||||
|
62
test/test_discovery.py
Normal file
62
test/test_discovery.py
Normal file
@ -0,0 +1,62 @@
|
||||
# *****************************************************************************
|
||||
#
|
||||
# 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>
|
||||
#
|
||||
# *****************************************************************************
|
||||
"""Test discovery messages"""
|
||||
|
||||
from test.test_modules import LoggerStub
|
||||
from frappy.protocol.discovery import UDPListener, MAX_MESSAGE_LEN
|
||||
from frappy.version import get_version
|
||||
|
||||
|
||||
def test_empty():
|
||||
logger = LoggerStub()
|
||||
udp = UDPListener('', '', ['tcp://0'], logger)
|
||||
udp.firmware = ''
|
||||
# 78 is the maximum overhead
|
||||
assert 78 == len(udp._getMessage(2**16-1))
|
||||
|
||||
|
||||
def test_basic():
|
||||
logger = LoggerStub()
|
||||
udp = UDPListener('eq', 'desc', ['tcp://1234'], logger)
|
||||
assert udp.description == 'desc'
|
||||
assert udp.equipment_id == 'eq'
|
||||
assert udp.ports == [1234]
|
||||
assert udp.firmware == 'FRAPPY ' + get_version()
|
||||
|
||||
|
||||
def test_ascii_truncation():
|
||||
logger = LoggerStub()
|
||||
desc = 'a' * MAX_MESSAGE_LEN
|
||||
udp = UDPListener('eq', desc, ['tcp://1234'], logger)
|
||||
assert MAX_MESSAGE_LEN == len(udp._getMessage(65535))
|
||||
fw = len(('FRAPPY ' + get_version()).encode('utf-8'))
|
||||
expected_length = 430 - fw - 2
|
||||
assert expected_length == len(udp.description)
|
||||
|
||||
|
||||
def test_unicode_truncation():
|
||||
logger = LoggerStub()
|
||||
desc = '\U0001f604' * 400
|
||||
udp = UDPListener('eq', desc, ['tcp://1234'], logger)
|
||||
fw = len(('FRAPPY ' + get_version()).encode('utf-8'))
|
||||
# 4 bytes per symbol, rounded down for the potential cut
|
||||
expected_length = (430 - fw - 2) // 4
|
||||
assert expected_length == len(udp.description)
|
@ -28,7 +28,7 @@ from glob import glob
|
||||
import pytest
|
||||
|
||||
from frappy.datatypes import BoolType, FloatRange, StringType, IntRange, ScaledInteger
|
||||
from frappy.errors import ProgrammingError, ConfigError, RangeError
|
||||
from frappy.errors import ProgrammingError, ConfigError, RangeError, HardwareError
|
||||
from frappy.modules import Communicator, Drivable, Readable, Module, Writable
|
||||
from frappy.params import Command, Parameter, Limit
|
||||
from frappy.rwhandler import ReadHandler, WriteHandler, nopoll
|
||||
@ -141,12 +141,10 @@ def test_ModuleMagic():
|
||||
|
||||
|
||||
# first inherited accessibles
|
||||
sortcheck1 = ['value', 'status', 'pollinterval', 'target', 'stop',
|
||||
'param1', 'param2', 'cmd', 'a1', 'a2', 'cmd2']
|
||||
sortcheck1 = ['value', 'status', 'target', 'pollinterval', 'stop',
|
||||
'param1', 'param2', 'cmd', 'a1', 'a2', 'cmd2']
|
||||
|
||||
class Newclass2(Newclass1):
|
||||
paramOrder = 'param1', 'param2', 'cmd', 'value'
|
||||
|
||||
@Command(description='another stuff')
|
||||
def cmd2(self, arg):
|
||||
return arg
|
||||
@ -171,9 +169,9 @@ def test_ModuleMagic():
|
||||
def read_value(self):
|
||||
return 0
|
||||
|
||||
# first inherited items not mentioned, then the ones mentioned in paramOrder, then the other new ones
|
||||
sortcheck2 = ['status', 'pollinterval', 'target', 'stop',
|
||||
'a1', 'a2', 'cmd2', 'param1', 'param2', 'cmd', 'value', 'b2']
|
||||
# first predefined parameters, then in the order of inheritance
|
||||
sortcheck2 = ['value', 'status', 'target', 'pollinterval', 'stop',
|
||||
'param1', 'param2', 'cmd', 'a1', 'a2', 'cmd2', 'b2']
|
||||
|
||||
updates = {}
|
||||
srv = ServerStub(updates)
|
||||
@ -245,7 +243,7 @@ def test_ModuleMagic():
|
||||
'export', 'group', 'description', 'features',
|
||||
'meaning', 'visibility', 'implementation', 'interface_classes', 'target', 'stop',
|
||||
'status', 'param1', 'param2', 'cmd', 'a2', 'pollinterval', 'slowinterval', 'b2',
|
||||
'cmd2', 'value', 'a1', 'omit_unchanged_within'}
|
||||
'cmd2', 'value', 'a1', 'omit_unchanged_within', 'original_id'}
|
||||
assert set(cfg['value'].keys()) == {
|
||||
'group', 'export', 'relative_resolution',
|
||||
'visibility', 'unit', 'default', 'value', 'datatype', 'fmtstr',
|
||||
@ -943,3 +941,36 @@ def test_stop_doc(modcls):
|
||||
if (modcls.stop.description == Drivable.stop.description
|
||||
and modcls.stop.func != Drivable.stop.func):
|
||||
assert modcls.stop.func.__doc__ # stop method needs a doc string
|
||||
|
||||
|
||||
def test_write_error():
|
||||
updates = {}
|
||||
srv = ServerStub(updates)
|
||||
|
||||
class Mod(Module):
|
||||
par = Parameter('', FloatRange(), readonly=False, default=0)
|
||||
|
||||
def read_par(self):
|
||||
raise HardwareError('failure')
|
||||
|
||||
def write_par(self, value):
|
||||
if value < 0:
|
||||
raise RangeError('outside range')
|
||||
|
||||
a = Mod('a', LoggerStub(), {'description': 'test'}, srv)
|
||||
|
||||
# behaviour on read errors:
|
||||
with pytest.raises(HardwareError):
|
||||
a.read_par()
|
||||
assert updates == {'a': {('error', 'par'): 'failure'}}
|
||||
updates.clear()
|
||||
|
||||
# behaviour on normal write
|
||||
a.write_par(1)
|
||||
assert updates['a']['par'] == 1
|
||||
updates.clear()
|
||||
|
||||
# behaviour when write failed
|
||||
with pytest.raises(RangeError):
|
||||
a.write_par(-1)
|
||||
assert not updates # no error update!
|
||||
|
@ -148,6 +148,8 @@ def test_Property_override():
|
||||
o2 = co()
|
||||
assert o1.a == 1
|
||||
assert o2.a == 3
|
||||
o2.setProperty('a', 4)
|
||||
assert o2.a == 4
|
||||
|
||||
with pytest.raises(ProgrammingError) as e:
|
||||
class cx(c): # pylint: disable=unused-variable
|
||||
|
@ -57,13 +57,13 @@ def test_name_only(direc, log):
|
||||
|
||||
def test_file(direc, log):
|
||||
"""only see that this does not throw. get config from cfgfiles."""
|
||||
s = Server('foo', log, cfgfiles='pyfile_cfg.py')
|
||||
s = Server('foo', log, cfgfiles=['pyfile_cfg.py'])
|
||||
s._processCfg()
|
||||
|
||||
|
||||
def test_basic_description(direc, log):
|
||||
"""only see that this does not throw. get config from cfgfiles."""
|
||||
s = Server('foo', log, cfgfiles='pyfile_cfg.py')
|
||||
s = Server('foo', log, cfgfiles=['pyfile_cfg.py'])
|
||||
s._processCfg()
|
||||
desc = s.secnode.get_descriptive_data('')
|
||||
# secnode properties correctly exported
|
||||
|
@ -1,14 +0,0 @@
|
||||
|
||||
from frappy.gui.cfg_editor.utils import get_modules
|
||||
|
||||
def test_assert():
|
||||
assert 1
|
||||
|
||||
|
||||
def test_constants(constants):
|
||||
assert constants.ONE == 1
|
||||
assert constants.TWO == 2
|
||||
|
||||
|
||||
def test_imports():
|
||||
get_modules()
|
Loading…
x
Reference in New Issue
Block a user