Compare commits
147 Commits
Author | SHA1 | Date | |
---|---|---|---|
38b8a7a118 | |||
d73b8bc774 | |||
ceb6e487a8 | |||
eb08e31b62 | |||
4b88ece3be | |||
c22e41c8e2 | |||
701d1f1735 | |||
400b314fc7 | |||
ce3b3ddfd6 | |||
3045a11ff9 | |||
de773f084d | |||
564bea63e4 | |||
bd8a417112 | |||
e6263cbcd0 | |||
7c06bbeec2 | |||
d6947711c6 | |||
0938049e89 | |||
0053b7847c | |||
5c93953183 | |||
![]() |
b60ad38fd0 | ||
7b0356b4ee | |||
7faf328141 | |||
d07406c2fd | |||
dbd4667c87 | |||
![]() |
6e7514b718 | ||
c49f15ce64 | |||
3bae6f8d7f | |||
e46291eef6 | |||
af746f59bb | |||
a2a4d4332a | |||
aff6a2381a | |||
eb8eee02d1 | |||
9a818ab40b | |||
![]() |
dc87a50c76 | ||
5974d759b0 | |||
396bbe9982 | |||
ae98fa174c | |||
83ad7b1638 | |||
257b24a480 | |||
29f0e18c1e | |||
144083a54a | |||
748a39f932 | |||
8cea974f49 | |||
5768f096a5 | |||
94859fe2ef | |||
fcf867675e | |||
09dce1aabd | |||
eefe271cba | |||
c42dd41dec | |||
c545e36633 | |||
ef985f094c | |||
![]() |
55d399241c | ||
aea56bb16a | |||
3b8f3586cf | |||
56bee015b3 | |||
1febb7bacf | |||
df4dec6db5 | |||
7feee607e0 | |||
7c938a3bb1 | |||
904dd96f95 | |||
![]() |
04fd8743a2 | ||
![]() |
b59a98e4dc | ||
![]() |
285c5c330f | ||
![]() |
e3e27881d3 | ||
![]() |
d26434f797 | ||
c3b6aca7bc | |||
![]() |
71372a450b | ||
![]() |
79dbfdfad0 | ||
5c7fe37807 | |||
e75b8b0b10 | |||
8d62375483 | |||
7c60daa568 | |||
fdc868c2d7 | |||
b66acd4d73 | |||
![]() |
da24c6244e | ||
738c4c7a51 | |||
93fa2c818b | |||
![]() |
88eb6e93bd | ||
![]() |
5242c903ae | ||
4aec05c86b | |||
a43590a3ff | |||
![]() |
76e46f7c84 | ||
![]() |
baf55baffc | ||
![]() |
f7f77b168f | ||
ebcf95e31e | |||
ee29bea821 | |||
4fc4cd3687 | |||
1145493e81 | |||
3f7ef438e3 | |||
90dc5359b9 | |||
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 |
@ -24,14 +24,12 @@
|
||||
|
||||
import sys
|
||||
import argparse
|
||||
import socket
|
||||
from pathlib import Path
|
||||
|
||||
# Add import path for inplace usage
|
||||
sys.path.insert(0, str(Path(__file__).absolute().parents[1]))
|
||||
|
||||
from frappy.client.interactive import init, run, clientenv, interact
|
||||
from frappy.protocol.discovery import scan
|
||||
|
||||
|
||||
def parseArgv(argv):
|
||||
@ -39,9 +37,6 @@ def parseArgv(argv):
|
||||
parser.add_argument('-i', '--include',
|
||||
help='file to execute after connecting to the clients', metavar='file',
|
||||
type=Path, action='append', default=[])
|
||||
parser.add_argument('-s', '--scan',
|
||||
help='hosts to scan for (-s subnet for all nodes in subnet)',
|
||||
action='append', default=[])
|
||||
parser.add_argument('-o', '--only-execute',
|
||||
help='Do not go into interactive mode after executing files. \
|
||||
Has no effect without --include.', action='store_true')
|
||||
@ -51,38 +46,9 @@ def parseArgv(argv):
|
||||
return parser.parse_args(argv)
|
||||
|
||||
|
||||
def own_ip():
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
s.settimeout(0)
|
||||
try:
|
||||
# doesn't even have to be reachable
|
||||
s.connect(('10.254.254.254', 1))
|
||||
return s.getsockname()[0]
|
||||
except Exception:
|
||||
return '127.0.0.1'
|
||||
finally:
|
||||
s.close()
|
||||
|
||||
|
||||
args = parseArgv(sys.argv[1:])
|
||||
|
||||
nodes = args.node
|
||||
hosts = args.scan
|
||||
if not nodes and not hosts:
|
||||
hosts = ['localhost']
|
||||
if hosts:
|
||||
answers = []
|
||||
for host in hosts:
|
||||
ans = scan()
|
||||
if host == 'subnet': # all in subnet
|
||||
answers.extend(ans)
|
||||
else: # filter by ip
|
||||
ip = socket.gethostbyname(host)
|
||||
if ip == '127.0.0.1':
|
||||
ip = own_ip()
|
||||
answers.extend(a for a in ans if a.address == ip)
|
||||
nodes.extend(f'{h.hostname}:{h.port}' for h in answers)
|
||||
success = init(*nodes)
|
||||
success = init(*args.node)
|
||||
|
||||
run_error = ''
|
||||
file_success = False
|
||||
|
@ -23,17 +23,54 @@
|
||||
"""SEC node autodiscovery tool."""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import select
|
||||
import socket
|
||||
import sys
|
||||
from frappy.protocol.discovery import scan, listen
|
||||
from collections import namedtuple
|
||||
from time import time as currenttime
|
||||
|
||||
UDP_PORT = 10767
|
||||
|
||||
Answer = namedtuple('Answer',
|
||||
'address, port, equipment_id, firmware, description')
|
||||
|
||||
|
||||
def decode(msg, addr):
|
||||
msg = msg.decode('utf-8')
|
||||
try:
|
||||
data = json.loads(msg)
|
||||
except Exception:
|
||||
return None
|
||||
if not isinstance(data, dict):
|
||||
return None
|
||||
if data.get('SECoP') != 'node':
|
||||
return None
|
||||
try:
|
||||
eq_id = data['equipment_id']
|
||||
fw = data['firmware']
|
||||
desc = data['description']
|
||||
port = data['port']
|
||||
except KeyError:
|
||||
return None
|
||||
addr, _scanport = addr
|
||||
return Answer(addr, port, eq_id, fw, desc)
|
||||
|
||||
|
||||
def print_answer(answer, *, short=False):
|
||||
try:
|
||||
hostname = socket.gethostbyaddr(answer.address)[0]
|
||||
address = hostname
|
||||
numeric = f' ({answer.address})'
|
||||
except Exception:
|
||||
address = answer.address
|
||||
numeric = ''
|
||||
if short:
|
||||
# NOTE: keep this easily parseable!
|
||||
print(f'{answer.equipment_id} {answer.hostname}:{answer.port}')
|
||||
print(f'{answer.equipment_id} {address}:{answer.port}')
|
||||
return
|
||||
numeric = f' ({answer.address})' if answer.address == answer.hostname else ''
|
||||
print(f'Found {answer.equipment_id} at {answer.hostname}{numeric}:')
|
||||
print(f'Found {answer.equipment_id} at {address}{numeric}:')
|
||||
print(f' Port: {answer.port}')
|
||||
print(f' Firmware: {answer.firmware}')
|
||||
desc = answer.description.replace('\n', '\n ')
|
||||
@ -41,6 +78,51 @@ def print_answer(answer, *, short=False):
|
||||
print('-' * 80)
|
||||
|
||||
|
||||
def scan(max_wait=1.0):
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
|
||||
# send a general broadcast
|
||||
try:
|
||||
s.sendto(json.dumps(dict(SECoP='discover')).encode('utf-8'),
|
||||
('255.255.255.255', UDP_PORT))
|
||||
except OSError as e:
|
||||
print('could not send the broadcast:', e)
|
||||
# we still keep listening for self-announcements
|
||||
start = currenttime()
|
||||
seen = set()
|
||||
while currenttime() < start + max_wait:
|
||||
res = select.select([s], [], [], 0.1)
|
||||
if res[0]:
|
||||
try:
|
||||
msg, addr = s.recvfrom(1024)
|
||||
except socket.error: # pragma: no cover
|
||||
continue
|
||||
answer = decode(msg, addr)
|
||||
if answer is None:
|
||||
continue
|
||||
if (answer.address, answer.equipment_id, answer.port) in seen:
|
||||
continue
|
||||
seen.add((answer.address, answer.equipment_id, answer.port))
|
||||
yield answer
|
||||
|
||||
|
||||
def listen(*, short=False):
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
if os.name == 'nt':
|
||||
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
else:
|
||||
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
|
||||
s.bind(('0.0.0.0', UDP_PORT))
|
||||
while True:
|
||||
try:
|
||||
msg, addr = s.recvfrom(1024)
|
||||
except KeyboardInterrupt:
|
||||
break
|
||||
answer = decode(msg, addr)
|
||||
if answer:
|
||||
print_answer(answer, short=short)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('-l', '--listen', action='store_true',
|
||||
@ -54,5 +136,4 @@ if __name__ == '__main__':
|
||||
for answer in scan():
|
||||
print_answer(answer, short=short)
|
||||
if args.listen:
|
||||
for answer in listen():
|
||||
print_answer(short=short)
|
||||
listen(short=short)
|
||||
|
218
cfg/dil5_cfg.py
218
cfg/dil5_cfg.py
@ -11,13 +11,13 @@ lsc_uri = '192.168.1.2:7777'
|
||||
#lsc_uri='serial:///dev/ttyACM1?baudrate=57600+parity=odd+bytesize=7+stopbits=1',
|
||||
|
||||
|
||||
Node('dil5_logo.psi.ch',
|
||||
'dil5 logo test',
|
||||
Node('dil5.psi.ch',
|
||||
'dil5 with state machine for condensing and removing',
|
||||
interface='tcp://5000',
|
||||
secondary = ['ws://8010']
|
||||
)
|
||||
|
||||
Mod('logo',
|
||||
Mod('io',
|
||||
'frappy_psi.logo.IO',
|
||||
'',
|
||||
ip_address = "192.168.0.3",
|
||||
@ -26,101 +26,155 @@ Mod('logo',
|
||||
)
|
||||
|
||||
Mod('V1',
|
||||
'frappy_psi.logo.DigitalActuator',
|
||||
'frappy_psi.logo.Valve',
|
||||
'Valves',
|
||||
io = 'logo',
|
||||
feedback_addr ="V1025.0",
|
||||
output_addr ="V1064.3"
|
||||
io = 'io',
|
||||
vm_address_input ="V1025.0",
|
||||
vm_address_output ="V1064.3"
|
||||
)
|
||||
|
||||
Mod('V2',
|
||||
'frappy_psi.logo.DigitalActuator',
|
||||
'dil bypass',
|
||||
io = 'logo',
|
||||
feedback_addr ="V1024.2",
|
||||
output_addr ="V1064.0",
|
||||
'frappy_psi.logo.Valve',
|
||||
'Valves',
|
||||
io = 'io',
|
||||
vm_address_input ="V1024.2",
|
||||
vm_address_output ="V1064.0",
|
||||
)
|
||||
|
||||
Mod('V4',
|
||||
'frappy_psi.logo.DigitalActuator',
|
||||
'compressor to dump',
|
||||
io = 'logo',
|
||||
# feedback_addr ="V1024.5", # not verified
|
||||
output_addr ="V1064.7",
|
||||
target_addr ="V404.1",
|
||||
'frappy_psi.logo.Valve',
|
||||
'Valves',
|
||||
io = 'io',
|
||||
vm_address_input ="V1024.5",
|
||||
vm_address_output ="V1064.7",
|
||||
)
|
||||
|
||||
Mod('V5',
|
||||
'frappy_psi.logo.DigitalActuator',
|
||||
'compressor input',
|
||||
io = 'logo',
|
||||
feedback_addr ="V1024.4",
|
||||
output_addr ="V1064.2",
|
||||
'frappy_psi.logo.Valve',
|
||||
'Valves',
|
||||
io = 'io',
|
||||
vm_address_input ="V1024.4",
|
||||
vm_address_output ="V1064.2"
|
||||
)
|
||||
|
||||
Mod('V9',
|
||||
'frappy_psi.logo.DelayedActuator',
|
||||
'dump output',
|
||||
io = 'logo',
|
||||
delay_addr = 'VW24',
|
||||
feedback_addr ="V1024.3",
|
||||
output_addr ="V1064.5",
|
||||
target_addr ="V404.3",
|
||||
'frappy_psi.logo.Valve',
|
||||
'Valves',
|
||||
io = 'io',
|
||||
vm_address_input ="V1024.3",
|
||||
vm_address_output ="V404.1",
|
||||
)
|
||||
|
||||
Mod('forepump',
|
||||
'frappy_psi.logo.DigitalActuator',
|
||||
'forepump',
|
||||
io = 'logo',
|
||||
output_addr ="V1064.6",
|
||||
target_addr ="V404.4",
|
||||
Mod('pump',
|
||||
'frappy_psi.logo.FluidMachines',
|
||||
'Pump',
|
||||
io = 'io',
|
||||
vm_address_output ="V414.1"
|
||||
)
|
||||
|
||||
Mod('compressor',
|
||||
'frappy_psi.logo.DigitalActuator',
|
||||
'',
|
||||
io = 'logo',
|
||||
output_addr ="V1064.4",
|
||||
target_addr ="V404.2",
|
||||
'frappy_psi.logo.FluidMachines',
|
||||
'Compressor',
|
||||
io = 'io',
|
||||
vm_address_output ="V400.1"
|
||||
)
|
||||
|
||||
Mod('p2',
|
||||
'frappy_psi.logo.Value',
|
||||
'pressure after compressor',
|
||||
io = 'logo',
|
||||
addr ="VW0",
|
||||
value = Param(unit='mbar'),
|
||||
'frappy_psi.logo.Pressure',
|
||||
'Pressure in mBar',
|
||||
io = 'io',
|
||||
vm_address ="VW0",
|
||||
)
|
||||
|
||||
Mod('p1',
|
||||
'frappy_psi.logo.Value',
|
||||
'dump pressure',
|
||||
io = 'logo',
|
||||
addr ="VW28",
|
||||
value = Param(unit='mbar'),
|
||||
'frappy_psi.logo.Pressure',
|
||||
'Pressure in mBar',
|
||||
io = 'io',
|
||||
vm_address ="VW2",
|
||||
)
|
||||
|
||||
Mod('p5',
|
||||
'frappy_psi.logo.Value',
|
||||
'pressure after forepump',
|
||||
io = 'logo',
|
||||
addr ="VW4",
|
||||
value = Param(unit='mbar'),
|
||||
'frappy_psi.logo.Pressure',
|
||||
'Pressure in mBar',
|
||||
io = 'io',
|
||||
vm_address ="VW4",
|
||||
)
|
||||
|
||||
Mod('airpressure',
|
||||
'frappy_psi.logo.DigitalValue',
|
||||
Mod('Druckluft',
|
||||
'frappy_psi.logo.Airpressure',
|
||||
'Airpressure state',
|
||||
io = 'logo',
|
||||
addr ="V1024.7",
|
||||
io = 'io',
|
||||
vm_address ="VW6",
|
||||
)
|
||||
|
||||
|
||||
|
||||
Mod('SF1',
|
||||
'frappy_psi.logo.safetyfeatureState',
|
||||
'Safety Feature',
|
||||
io = 'io',
|
||||
vm_address ="V410.1",
|
||||
)
|
||||
|
||||
Mod('SF2',
|
||||
'frappy_psi.logo.safetyfeatureState',
|
||||
'Safety Feature',
|
||||
io = 'io',
|
||||
vm_address ="V406.1",
|
||||
)
|
||||
|
||||
Mod('SF3',
|
||||
'frappy_psi.logo.safetyfeatureState',
|
||||
'Safety Feature',
|
||||
io = 'io',
|
||||
vm_address ="V408.1",
|
||||
)
|
||||
|
||||
Mod('SF4',
|
||||
'frappy_psi.logo.safetyfeatureState',
|
||||
'Safety Feature',
|
||||
io = 'io',
|
||||
vm_address ="V412.1",
|
||||
)
|
||||
|
||||
Mod('p2max',
|
||||
'frappy_psi.logo.safetyfeatureParam',
|
||||
'Safety Feature Param',
|
||||
io = 'io',
|
||||
target = 2000,
|
||||
vm_address ="VW8",
|
||||
)
|
||||
|
||||
Mod('pcond',
|
||||
'frappy_psi.logo.safetyfeatureParam',
|
||||
'Safety Feature Param',
|
||||
io = 'io',
|
||||
target = 1800,
|
||||
vm_address ="VW10",
|
||||
)
|
||||
|
||||
Mod('p5min',
|
||||
'frappy_psi.logo.safetyfeatureParam',
|
||||
'Safety Feature Param',
|
||||
io = 'io',
|
||||
target = 0,
|
||||
vm_address ="VW12",
|
||||
)
|
||||
|
||||
Mod('p5max',
|
||||
'frappy_psi.logo.safetyfeatureParam',
|
||||
'Safety Feature Param',
|
||||
io = 'io',
|
||||
target = 900,
|
||||
vm_address ="VW14",
|
||||
)
|
||||
|
||||
|
||||
Mod('io_ls273',
|
||||
'frappy_psi.ls372.StringIO',
|
||||
'io for Ls372',
|
||||
uri=lsc_uri,
|
||||
)
|
||||
|
||||
Mod('sw',
|
||||
'frappy_psi.ls372.Switcher',
|
||||
'channel switcher',
|
||||
@ -307,31 +361,41 @@ Mod('T_cond',
|
||||
io='itc',
|
||||
)
|
||||
|
||||
Mod('safety',
|
||||
'frappy_psi.dilution.Interlock',
|
||||
'interlock mechanism',
|
||||
io='logo',
|
||||
dil='dil',
|
||||
)
|
||||
|
||||
Mod('dil',
|
||||
'frappy_psi.dilution.DIL5',
|
||||
'dilution state machine and parameters',
|
||||
Mod('stateMachine',
|
||||
'frappy_psi.dilution_statemachine.DIL5',
|
||||
'Statemachine',
|
||||
|
||||
condenseline_pressure = "p2",
|
||||
condense_valve = "V9",
|
||||
dump_valve = "V4",
|
||||
forepump = "forepump",
|
||||
circulate_pump = "pump",
|
||||
compressor = "compressor",
|
||||
|
||||
turbopump = "turbopump",
|
||||
condenseline_valve = "V1",
|
||||
circuitshort_valve = "V2",
|
||||
still_pressure = "p4",
|
||||
still_pressure_turbo = "p3",
|
||||
still_pressure = "p3",
|
||||
#ls372 = "res1",
|
||||
dump_pressure = "p1",
|
||||
condensing_p_low = 1200,
|
||||
condensing_p_high = 1500,
|
||||
V5 = "V5",
|
||||
p1 = "p1",
|
||||
|
||||
MV10 = 'MV10',
|
||||
MV13 ='MV13',
|
||||
MV8 = 'MV8',
|
||||
MVB = 'MVB',
|
||||
MV2 = 'MV2',
|
||||
|
||||
MV1 = 'MV1',
|
||||
MV3a = 'MV3a',
|
||||
MV3b = 'MV3b',
|
||||
GV1 = 'GV1',
|
||||
MV14 = 'MV14',
|
||||
MV12 = 'MV12',
|
||||
MV11 = 'MV11',
|
||||
MV9 = 'MV9',
|
||||
GV2 = 'GV2',
|
||||
condensing_p_low = 150,
|
||||
condensing_p_high = 250
|
||||
)
|
||||
|
||||
|
||||
|
@ -14,7 +14,7 @@ Mod('tt',
|
||||
io='sea_main',
|
||||
meaning=['temperature_regulation', 27],
|
||||
sea_object='tt',
|
||||
rel_paths=['main', '.', 'set'],
|
||||
rel_paths=['tt', 'set'],
|
||||
value=Param(unit='K'),
|
||||
)
|
||||
|
||||
|
@ -9,21 +9,12 @@ Mod('sea_main',
|
||||
service='main',
|
||||
)
|
||||
|
||||
#Mod('tt',
|
||||
# 'frappy_psi.sea.SeaDrivable', '',
|
||||
# io='sea_main',
|
||||
# meaning=['temperature_regulation', 27],
|
||||
# sea_object='tt',
|
||||
# rel_paths=['tm', '.', 'set', 'dblctrl'],
|
||||
#)
|
||||
|
||||
Mod('tt',
|
||||
'frappy_psi.sea.LscDrivable', '',
|
||||
'frappy_psi.sea.SeaDrivable', '',
|
||||
io='sea_main',
|
||||
meaning=['temperature_regulation', 27],
|
||||
sea_object='tt',
|
||||
sensor_path='tm',
|
||||
set_path='set',
|
||||
rel_paths=['.', 'tm', 'set', 'dblctrl'],
|
||||
)
|
||||
|
||||
Mod('cc',
|
||||
|
@ -1,50 +1,50 @@
|
||||
{"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": "status", "type": "text", "readonly": false, "cmd": "run tt", "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": "target", "type": "float", "readonly": false, "cmd": "run tt"},
|
||||
{"path": "running", "type": "int", "readonly": false, "cmd": "run tt"},
|
||||
{"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": "log/mean", "type": "float", "readonly": false, "cmd": "run tt", "visibility": 3},
|
||||
{"path": "log/m2", "type": "float", "readonly": false, "cmd": "run tt", "visibility": 3},
|
||||
{"path": "log/stddev", "type": "float", "readonly": false, "cmd": "run tt", "visibility": 3},
|
||||
{"path": "log/n", "type": "float", "readonly": false, "cmd": "run tt", "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/shift_up", "type": "float", "readonly": false, "cmd": "run tt"},
|
||||
{"path": "dblctrl/shift_lo", "type": "float", "readonly": false, "cmd": "run tt"},
|
||||
{"path": "dblctrl/t_min", "type": "float", "readonly": false, "cmd": "run tt"},
|
||||
{"path": "dblctrl/t_max", "type": "float", "readonly": false, "cmd": "run tt"},
|
||||
{"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", "type": "float", "readonly": false, "cmd": "run tt", "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": "tm/stddev", "type": "float", "readonly": false, "cmd": "run tt"},
|
||||
{"path": "tm/raw", "type": "float", "readonly": false, "cmd": "run tt"},
|
||||
{"path": "ts", "type": "float", "readonly": false, "cmd": "run tt", "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/stddev", "type": "float", "readonly": false, "cmd": "run tt"},
|
||||
{"path": "ts/raw", "type": "float", "readonly": false, "cmd": "run tt"},
|
||||
{"path": "ts_2", "type": "float", "readonly": false, "cmd": "run tt", "kids": 4},
|
||||
{"path": "ts_2/curve", "type": "text", "readonly": false, "cmd": "tt ts_2/curve", "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": "ts_2/alarm", "type": "float", "readonly": false, "cmd": "tt ts_2/alarm"},
|
||||
{"path": "ts_2/stddev", "type": "float", "readonly": false, "cmd": "run tt"},
|
||||
{"path": "ts_2/raw", "type": "float", "readonly": false, "cmd": "run tt"},
|
||||
{"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/reg", "type": "float", "readonly": false, "cmd": "run tt"},
|
||||
{"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)"},
|
||||
@ -53,17 +53,17 @@
|
||||
{"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/maxpowerlim", "type": "float", "readonly": false, "cmd": "run tt", "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/maxcurrent", "type": "float", "readonly": false, "cmd": "run tt", "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/power", "type": "float", "readonly": false, "cmd": "run tt"},
|
||||
{"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": "setsamp", "type": "float", "readonly": false, "cmd": "tt setsamp", "kids": 18},
|
||||
{"path": "setsamp/mode", "type": "enum", "enum": {"disabled": -1, "off": 0, "controlling": 1, "manual": 2}, "readonly": false, "cmd": "tt setsamp/mode"},
|
||||
{"path": "setsamp/reg", "type": "float"},
|
||||
{"path": "setsamp/reg", "type": "float", "readonly": false, "cmd": "run tt"},
|
||||
{"path": "setsamp/ramp", "type": "float", "readonly": false, "cmd": "tt setsamp/ramp", "description": "maximum ramp in K/min (0: ramp off)"},
|
||||
{"path": "setsamp/wramp", "type": "float", "readonly": false, "cmd": "tt setsamp/wramp"},
|
||||
{"path": "setsamp/smooth", "type": "float", "readonly": false, "cmd": "tt setsamp/smooth", "description": "smooth time (minutes)"},
|
||||
@ -72,16 +72,16 @@
|
||||
{"path": "setsamp/resist", "type": "float", "readonly": false, "cmd": "tt setsamp/resist"},
|
||||
{"path": "setsamp/maxheater", "type": "text", "readonly": false, "cmd": "tt setsamp/maxheater", "description": "maximum heater limit, units should be given without space: W, mW, A, mA"},
|
||||
{"path": "setsamp/linearpower", "type": "float", "readonly": false, "cmd": "tt setsamp/linearpower", "description": "when not 0, it is the maximum effective power, and the power is linear to the heater output"},
|
||||
{"path": "setsamp/maxpowerlim", "type": "float", "description": "the maximum power limit (before any booster or converter)"},
|
||||
{"path": "setsamp/maxpowerlim", "type": "float", "readonly": false, "cmd": "run tt", "description": "the maximum power limit (before any booster or converter)"},
|
||||
{"path": "setsamp/maxpower", "type": "float", "readonly": false, "cmd": "tt setsamp/maxpower", "description": "maximum power [W]"},
|
||||
{"path": "setsamp/maxcurrent", "type": "float", "description": "the maximum current before any booster or converter"},
|
||||
{"path": "setsamp/maxcurrent", "type": "float", "readonly": false, "cmd": "run tt", "description": "the maximum current before any booster or converter"},
|
||||
{"path": "setsamp/manualpower", "type": "float", "readonly": false, "cmd": "tt setsamp/manualpower"},
|
||||
{"path": "setsamp/power", "type": "float"},
|
||||
{"path": "setsamp/power", "type": "float", "readonly": false, "cmd": "run tt"},
|
||||
{"path": "setsamp/prop", "type": "float", "readonly": false, "cmd": "tt setsamp/prop", "description": "bigger means more gain"},
|
||||
{"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"}]},
|
||||
{"path": "remote", "type": "bool", "readonly": false, "cmd": "run tt"}]},
|
||||
|
||||
"cc": {"base": "/cc", "params": [
|
||||
{"path": "", "type": "bool", "kids": 96},
|
||||
@ -108,7 +108,7 @@
|
||||
{"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", "enum": {"none": 0, "int": 1, "ext": 2}, "readonly": false, "cmd": "cc hav"},
|
||||
{"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"},
|
||||
@ -132,26 +132,26 @@
|
||||
{"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": 6}, "readonly": false, "cmd": "cc hea"},
|
||||
{"path": "hch", "type": "int", "readonly": false, "cmd": "cc hch"},
|
||||
{"path": "hwr0", "type": "float", "readonly": false, "cmd": "cc hwr0"},
|
||||
{"path": "hem0", "type": "float", "readonly": false, "cmd": "cc hem0", "description": "sensor length in mm from top to empty pos."},
|
||||
{"path": "hfu0", "type": "float", "readonly": false, "cmd": "cc hfu0", "description": "sensor length in mm from top to full pos."},
|
||||
{"path": "hd0", "type": "float", "readonly": false, "cmd": "cc hd0", "description": "external sensor drive current (mA)"},
|
||||
{"path": "h0", "type": "float"},
|
||||
{"path": "hs0", "type": "enum", "enum": {"sens_ok": 0, "sens_warm": 1, "no_sens": 2, "timeout": 3, "not_yet_read": 4, "disabled": 5}},
|
||||
{"path": "h1", "type": "float"},
|
||||
{"path": "hs1", "type": "enum", "enum": {"sens_ok": 0, "sens_warm": 1, "no_sens": 2, "timeout": 3, "not_yet_read": 4, "disabled": 5}},
|
||||
{"path": "h2", "type": "float"},
|
||||
{"path": "hs2", "type": "enum", "enum": {"sens_ok": 0, "sens_warm": 1, "no_sens": 2, "timeout": 3, "not_yet_read": 4, "disabled": 5}},
|
||||
{"path": "h3", "type": "float"},
|
||||
{"path": "hs3", "type": "enum", "enum": {"sens_ok": 0, "sens_warm": 1, "no_sens": 2, "timeout": 3, "not_yet_read": 4, "disabled": 5}},
|
||||
{"path": "h4", "type": "float"},
|
||||
{"path": "hs4", "type": "enum", "enum": {"sens_ok": 0, "sens_warm": 1, "no_sens": 2, "timeout": 3, "not_yet_read": 4, "disabled": 5}},
|
||||
{"path": "h5", "type": "float"},
|
||||
{"path": "hs5", "type": "enum", "enum": {"sens_ok": 0, "sens_warm": 1, "no_sens": 2, "timeout": 3, "not_yet_read": 4, "disabled": 5}},
|
||||
{"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": "bool", "readonly": false, "cmd": "cc nav"},
|
||||
{"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"},
|
||||
@ -183,16 +183,15 @@
|
||||
{"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": 12},
|
||||
{"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", "description": "flow calculated from pressure before pump"},
|
||||
{"path": "flowp", "type": "float"},
|
||||
{"path": "span", "type": "float"},
|
||||
{"path": "use_pressure", "type": "bool", "readonly": false, "cmd": "nv use_pressure", "description": "use pressure instead of flow meter for control"},
|
||||
{"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"},
|
||||
@ -236,34 +235,15 @@
|
||||
{"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"}]},
|
||||
|
||||
"hefill": {"base": "/hefill", "params": [
|
||||
{"path": "", "type": "enum", "enum": {"watching": 0, "filling": 1, "inactive": 2, "manualfill": 3}, "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"},
|
||||
{"path": "readpath", "type": "text", "readonly": false, "cmd": "hefill readpath", "visibility": 3},
|
||||
{"path": "lowlevel", "type": "float", "readonly": false, "cmd": "hefill lowlevel"},
|
||||
{"path": "highlevel", "type": "float", "readonly": false, "cmd": "hefill highlevel"},
|
||||
{"path": "smooth", "type": "float"},
|
||||
{"path": "minfillminutes", "type": "float", "readonly": false, "cmd": "hefill minfillminutes"},
|
||||
{"path": "maxfillminutes", "type": "float", "readonly": false, "cmd": "hefill maxfillminutes"},
|
||||
{"path": "minholdhours", "type": "float", "readonly": false, "cmd": "hefill minholdhours"},
|
||||
{"path": "maxholdhours", "type": "float", "readonly": false, "cmd": "hefill maxholdhours"},
|
||||
{"path": "tolerance", "type": "float", "readonly": false, "cmd": "hefill tolerance"},
|
||||
{"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"}]},
|
||||
|
||||
"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": "eco", "type": "bool", "readonly": false, "cmd": "hepump eco"},
|
||||
{"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", "visibility": 3},
|
||||
{"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},
|
||||
{"path": "health", "type": "float"}]},
|
||||
|
||||
@ -311,11 +291,11 @@
|
||||
{"path": "save", "type": "bool", "readonly": false, "cmd": "nvflow save", "description": "unchecked: current calib is not saved. set checked: save calib"}]},
|
||||
|
||||
"ln2fill": {"base": "/ln2fill", "params": [
|
||||
{"path": "", "type": "enum", "enum": {"watching": 0, "filling": 1, "inactive": 2, "manualfill": 3}, "readonly": false, "cmd": "ln2fill", "kids": 14},
|
||||
{"path": "", "type": "enum", "enum": {"watching": 0, "fill": 1, "inactive": 2}, "readonly": false, "cmd": "ln2fill", "kids": 14},
|
||||
{"path": "send", "type": "text", "readonly": false, "cmd": "ln2fill send", "visibility": 3},
|
||||
{"path": "status", "type": "text", "visibility": 3},
|
||||
{"path": "state", "type": "text"},
|
||||
{"path": "readpath", "type": "text", "readonly": false, "cmd": "ln2fill readpath", "visibility": 3},
|
||||
{"path": "readlevel", "type": "text", "readonly": false, "cmd": "ln2fill readlevel", "visibility": 3},
|
||||
{"path": "lowlevel", "type": "float", "readonly": false, "cmd": "ln2fill lowlevel"},
|
||||
{"path": "highlevel", "type": "float", "readonly": false, "cmd": "ln2fill highlevel"},
|
||||
{"path": "smooth", "type": "float"},
|
||||
@ -327,33 +307,52 @@
|
||||
{"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": "send", "type": "text", "readonly": false, "cmd": "hefill send", "visibility": 3},
|
||||
{"path": "status", "type": "text", "visibility": 3},
|
||||
{"path": "state", "type": "text"},
|
||||
{"path": "readlevel", "type": "text", "readonly": false, "cmd": "hefill readlevel", "visibility": 3},
|
||||
{"path": "lowlevel", "type": "float", "readonly": false, "cmd": "hefill lowlevel"},
|
||||
{"path": "highlevel", "type": "float", "readonly": false, "cmd": "hefill highlevel"},
|
||||
{"path": "smooth", "type": "float"},
|
||||
{"path": "minfillminutes", "type": "float", "readonly": false, "cmd": "hefill minfillminutes"},
|
||||
{"path": "maxfillminutes", "type": "float", "readonly": false, "cmd": "hefill maxfillminutes"},
|
||||
{"path": "minholdhours", "type": "float", "readonly": false, "cmd": "hefill minholdhours"},
|
||||
{"path": "maxholdhours", "type": "float", "readonly": false, "cmd": "hefill maxholdhours"},
|
||||
{"path": "tolerance", "type": "float", "readonly": false, "cmd": "hefill tolerance"},
|
||||
{"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"}]},
|
||||
|
||||
"mf": {"base": "/mf", "params": [
|
||||
{"path": "", "type": "float", "readonly": false, "cmd": "run mf", "kids": 26},
|
||||
{"path": "persmode", "type": "int", "readonly": false, "cmd": "mf persmode"},
|
||||
{"path": "perswitch", "type": "int"},
|
||||
{"path": "perswitch", "type": "int", "readonly": false, "cmd": "run mf"},
|
||||
{"path": "nowait", "type": "int", "readonly": false, "cmd": "mf nowait"},
|
||||
{"path": "maxlimit", "type": "float", "visibility": 3},
|
||||
{"path": "maxlimit", "type": "float", "readonly": false, "cmd": "run mf", "visibility": 3},
|
||||
{"path": "limit", "type": "float", "readonly": false, "cmd": "mf limit"},
|
||||
{"path": "ramp", "type": "float", "readonly": false, "cmd": "mf ramp"},
|
||||
{"path": "perscurrent", "type": "float", "readonly": false, "cmd": "mf perscurrent"},
|
||||
{"path": "perslimit", "type": "float", "readonly": false, "cmd": "mf perslimit"},
|
||||
{"path": "perswait", "type": "int", "readonly": false, "cmd": "mf perswait"},
|
||||
{"path": "persdelay", "type": "int", "readonly": false, "cmd": "mf persdelay"},
|
||||
{"path": "current", "type": "float"},
|
||||
{"path": "measured", "type": "float"},
|
||||
{"path": "voltage", "type": "float"},
|
||||
{"path": "lastfield", "type": "float", "visibility": 3},
|
||||
{"path": "ampRamp", "type": "float", "visibility": 3},
|
||||
{"path": "inductance", "type": "float", "visibility": 3},
|
||||
{"path": "current", "type": "float", "readonly": false, "cmd": "run mf"},
|
||||
{"path": "measured", "type": "float", "readonly": false, "cmd": "run mf"},
|
||||
{"path": "voltage", "type": "float", "readonly": false, "cmd": "run mf"},
|
||||
{"path": "lastfield", "type": "float", "readonly": false, "cmd": "run mf", "visibility": 3},
|
||||
{"path": "ampRamp", "type": "float", "readonly": false, "cmd": "run mf", "visibility": 3},
|
||||
{"path": "inductance", "type": "float", "readonly": false, "cmd": "run mf", "visibility": 3},
|
||||
{"path": "trainedTo", "type": "float", "readonly": false, "cmd": "mf trainedTo"},
|
||||
{"path": "trainMode", "type": "int"},
|
||||
{"path": "trainMode", "type": "int", "readonly": false, "cmd": "run mf"},
|
||||
{"path": "external", "type": "int", "readonly": false, "cmd": "mf external"},
|
||||
{"path": "startScript", "type": "text", "readonly": false, "cmd": "mf startScript", "visibility": 3},
|
||||
{"path": "is_running", "type": "int", "readonly": false, "cmd": "mf is_running", "visibility": 3},
|
||||
{"path": "is_running", "type": "int", "readonly": false, "cmd": "run mf", "visibility": 3},
|
||||
{"path": "verbose", "type": "int", "readonly": false, "cmd": "mf verbose", "visibility": 3},
|
||||
{"path": "driver", "type": "text", "visibility": 3},
|
||||
{"path": "creationCmd", "type": "text", "visibility": 3},
|
||||
{"path": "targetValue", "type": "float"},
|
||||
{"path": "driver", "type": "text", "readonly": false, "cmd": "run mf", "visibility": 3},
|
||||
{"path": "creationCmd", "type": "text", "readonly": false, "cmd": "run mf", "visibility": 3},
|
||||
{"path": "targetValue", "type": "float", "readonly": false, "cmd": "run mf"},
|
||||
{"path": "status", "type": "text", "readonly": false, "cmd": "mf status", "visibility": 3}]},
|
||||
|
||||
"lev": {"base": "/lev", "params": [
|
||||
@ -363,22 +362,7 @@
|
||||
{"path": "mode", "type": "enum", "enum": {"slow": 0, "fast (switches to slow automatically after filling)": 1}, "readonly": false, "cmd": "lev mode"},
|
||||
{"path": "n2", "type": "float"}]},
|
||||
|
||||
"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 ..."}]}}
|
||||
"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}]}}
|
||||
|
@ -1,75 +0,0 @@
|
||||
# *****************************************************************************
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free Software
|
||||
# Foundation; either version 2 of the License, or (at your option) any later
|
||||
# version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#
|
||||
# Module authors:
|
||||
# Markus Zolliker <markus.zolliker@psi.ch>
|
||||
#
|
||||
# *****************************************************************************
|
||||
|
||||
from frappy.core import Parameter, Property
|
||||
from frappy.datatypes import ValueType
|
||||
|
||||
|
||||
class AddrParam(Parameter):
|
||||
"""parameter with an address field
|
||||
|
||||
instead of implementing read_<param> and write_<param>, just implement
|
||||
addressed_read and addressed_write.
|
||||
"""
|
||||
addr = Property('address', ValueType())
|
||||
|
||||
|
||||
class AddrMixin:
|
||||
"""mixin for addressed parameters
|
||||
|
||||
in case a read_<param> and/or write_<param> are not implemented,
|
||||
they are created with a call to addressed_read and/or addressed_write
|
||||
"""
|
||||
def __init_subclass__(cls):
|
||||
for aname, aobj in list(cls.__dict__.items()):
|
||||
if isinstance(aobj, AddrParam):
|
||||
methodname = f'read_{aname}'
|
||||
|
||||
if not hasattr(cls, methodname):
|
||||
def rfunc(self, pname=aname):
|
||||
return self.addressed_read(self.accessibles[pname])
|
||||
|
||||
setattr(cls, methodname, rfunc)
|
||||
|
||||
if not aobj.readonly:
|
||||
methodname = f'write_{aname}'
|
||||
if not hasattr(cls, methodname):
|
||||
def wfunc(self, value, pname=aname):
|
||||
return self.addressed_write(self.accessibles[pname], value)
|
||||
|
||||
setattr(cls, methodname, wfunc)
|
||||
super().__init_subclass__()
|
||||
|
||||
def addressed_read(self, pobj):
|
||||
"""addressed read
|
||||
|
||||
:param pobj: the AddrParam
|
||||
:return: the value read
|
||||
"""
|
||||
return getattr(self, pobj.name)
|
||||
|
||||
def addressed_write(self, pobj, value):
|
||||
"""addressed write
|
||||
|
||||
:param pobj: the AddrParam
|
||||
:param value: the value to be written
|
||||
:return: the value written or None
|
||||
"""
|
@ -233,13 +233,13 @@ class Parameter(Accessible):
|
||||
try:
|
||||
return instance.parameters[self.name].value
|
||||
except KeyError:
|
||||
raise ProgrammingError(f'optional parameter {self.name} is not implemented') from None
|
||||
raise ProgrammingError(f'optional parameter {self.name} it is not implemented') from None
|
||||
|
||||
def __set__(self, obj, value):
|
||||
try:
|
||||
obj.announceUpdate(self.name, value)
|
||||
except KeyError:
|
||||
raise ProgrammingError(f'optional parameter {self.name} is not implemented') from None
|
||||
raise ProgrammingError(f'optional parameter {self.name} it is not implemented') from None
|
||||
|
||||
def __set_name__(self, owner, name):
|
||||
self.name = name
|
||||
|
@ -94,7 +94,6 @@ logger = MainLogger()
|
||||
|
||||
class Playground(Server):
|
||||
def __init__(self, **kwds): # pylint: disable=super-init-not-called
|
||||
self.name = 'playground'
|
||||
for modname, cfg in kwds.items():
|
||||
cfg.setdefault('description', modname)
|
||||
self.log = logger.log
|
||||
|
@ -23,9 +23,6 @@
|
||||
import os
|
||||
import json
|
||||
import socket
|
||||
import select
|
||||
from time import monotonic
|
||||
from collections import namedtuple
|
||||
|
||||
from frappy.lib import closeSocket
|
||||
from frappy.protocol.interface.tcp import format_address
|
||||
@ -35,79 +32,6 @@ UDP_PORT = 10767
|
||||
MAX_MESSAGE_LEN = 508
|
||||
|
||||
|
||||
Answer = namedtuple('Answer',
|
||||
'address, hostname, 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
|
||||
try:
|
||||
hostname = socket.gethostbyaddr(addr[0])[0]
|
||||
except Exception:
|
||||
hostname = addr[0]
|
||||
return Answer(addr[0], hostname, port, eq_id, fw, desc)
|
||||
|
||||
|
||||
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
|
||||
seen = set()
|
||||
start = monotonic()
|
||||
while monotonic() < 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():
|
||||
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:
|
||||
yield answer
|
||||
|
||||
|
||||
class UDPListener:
|
||||
def __init__(self, equipment_id, description, ifaces, logger, *,
|
||||
startup_broadcast=True):
|
||||
|
@ -1,451 +0,0 @@
|
||||
# *****************************************************************************
|
||||
#
|
||||
# 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:
|
||||
# Andrea Plank <andrea.plank@psi.ch>
|
||||
#
|
||||
# *****************************************************************************
|
||||
|
||||
import time
|
||||
from frappy.core import Readable, Drivable, Parameter, Attached, FloatRange, \
|
||||
Command, IDLE, BUSY, WARN, ERROR, Property
|
||||
from frappy.datatypes import EnumType, IntRange, BoolType, StructOf, StringType
|
||||
from frappy.states import Retry, Finish, status_code, HasStates
|
||||
from frappy.lib.enum import Enum
|
||||
from frappy.errors import ImpossibleError, HardwareError
|
||||
from frappy.addrparam import AddrParam, AddrMixin
|
||||
from frappy.lib import formatStatusBits
|
||||
from frappy.persistent import PersistentMixin, PersistentParam
|
||||
from frappy_psi.logo import LogoMixin, DigitalActuator
|
||||
|
||||
T = Enum( # target states
|
||||
off = 0,
|
||||
sorbpumped = 2,
|
||||
condense = 5,
|
||||
remove = 7,
|
||||
remove_and_sorbpump = 9,
|
||||
remove_and_condense = 10,
|
||||
manual = 11,
|
||||
test = 12,
|
||||
)
|
||||
|
||||
V = Enum(T, # value status inherits from target status
|
||||
sorbpumping=1,
|
||||
condensing=4,
|
||||
circulating=6,
|
||||
removing=8,
|
||||
)
|
||||
|
||||
|
||||
class Dilution(HasStates, Drivable):
|
||||
condenseline_pressure = Attached()
|
||||
condense_valve = Attached()
|
||||
dump_valve = Attached()
|
||||
|
||||
forepump = Attached()
|
||||
compressor = Attached(mandatory=False)
|
||||
turbopump = Attached(mandatory=False)
|
||||
condenseline_valve = Attached()
|
||||
circuitshort_valve = Attached()
|
||||
still_pressure = Attached()
|
||||
still_pressure_turbo = Attached(mandatory=False)
|
||||
|
||||
value = Parameter('current state', EnumType(T), default=0)
|
||||
target = Parameter('target state', EnumType(T), default=0)
|
||||
|
||||
sorbpumped = Parameter('sorb pump done', BoolType(), default=False)
|
||||
dump_pressure = Attached()
|
||||
|
||||
#ls372 = Attached()
|
||||
|
||||
condensing_p_low = Parameter('Lower limit for condenseline pressure',
|
||||
FloatRange(unit='mbar'), readonly=False, default=1200)
|
||||
condensing_p_high = Parameter('Higher limit for condenseline pressure',
|
||||
FloatRange(unit='mbar'), readonly=False, default=1500)
|
||||
dump_target = Parameter('low dump pressure limit indicating end of condensation phase',
|
||||
FloatRange(unit='mbar * min'), readonly=False, default=100)
|
||||
pulse_factor = Parameter('factor for calculating V9 pulse length',
|
||||
FloatRange(unit='mbar'), readonly=False, default=20)
|
||||
end_condense_pressure = Parameter('low condense pressure indicating end of condensation phase',
|
||||
FloatRange(unit='mbar'), readonly=False, default=500)
|
||||
turbo_condense_pressure = Parameter('low condense pressure before turbo start',
|
||||
FloatRange(unit='mbar'), readonly=False, default=900)
|
||||
safe_turbo_pressure = Parameter('low still pressure before turbo start',
|
||||
FloatRange(unit='mbar'), readonly=False, default=10)
|
||||
turbo_off_speed = Parameter('speed to wait for after switching turbo off',
|
||||
FloatRange(unit='Hz'), readonly=False, default=200)
|
||||
end_remove_turbo_pressure = Parameter('pressure reached before end of remove (before turbo)',
|
||||
FloatRange(unit='mbar'), readonly=False, default=1e-4)
|
||||
end_remove_pressure = Parameter('pressure reached before end of remove (before fore pump)',
|
||||
FloatRange(unit='mbar'), readonly=False, default=0.02)
|
||||
st = StringType()
|
||||
valve_set = StructOf(close=st, open=st, check_open=st, check_closed=st)
|
||||
condense_valves = Parameter('valve to act when condensing', valve_set)
|
||||
valves_after_remove = Parameter('valve to act after remove', valve_set)
|
||||
check_after_remove = Parameter('check for manual valves after remove', valve_set)
|
||||
_start_time = 0
|
||||
init = True
|
||||
_warn_manual_work = None
|
||||
|
||||
def write_target(self, target):
|
||||
"""
|
||||
if (target == Targetstates.SORBPUMP):
|
||||
if self.value == target:
|
||||
return self.target
|
||||
self.start_machine(self.sorbpump)
|
||||
self.value = Targetstates.SORBPUMP
|
||||
return self.value
|
||||
"""
|
||||
self.log.info('start %s', target.name)
|
||||
if self.value == target:
|
||||
return target # not sure if this is correct. may be a step wants to be repeated?
|
||||
|
||||
try:
|
||||
self.start_machine(getattr(self, target.name, None))
|
||||
except Exception as e:
|
||||
self.log.exception('error')
|
||||
self.log.info('started %s', target.name)
|
||||
|
||||
return target
|
||||
|
||||
"""
|
||||
@status_code(BUSY, 'sorbpump state')
|
||||
def sorbpump(self, state):
|
||||
#Heizt Tsorb auf und wartet ab.
|
||||
if self.init:
|
||||
self.ls372.write_target(40) #Setze Tsorb auf 40K
|
||||
self.start_time = self.now
|
||||
self.init = false
|
||||
return Retry
|
||||
|
||||
if self.now - self.start_time < 2400: # 40 Minuten warten
|
||||
return Retry
|
||||
|
||||
self.ls372.write_target(0)
|
||||
|
||||
if self.ls372.read_value() > 10: # Warten bis Tsorb unter 10K
|
||||
return Retry
|
||||
|
||||
return self.condense
|
||||
"""
|
||||
|
||||
@status_code(BUSY, 'start test')
|
||||
def test(self, state):
|
||||
"""Nur zum testen, ob UI funktioniert"""
|
||||
self.init = False
|
||||
if state.init:
|
||||
state._start = state.now
|
||||
return self.wait_test
|
||||
|
||||
@status_code(BUSY)
|
||||
def wait_test(self, state):
|
||||
if state.now < state.start + 20:
|
||||
return Retry
|
||||
return self.final_status(IDLE, 'end test')
|
||||
|
||||
@status_code(BUSY)
|
||||
def condense(self, state):
|
||||
"""Führt das Kondensationsverfahren durch."""
|
||||
if state.init:
|
||||
# self.value = V.condensing
|
||||
pumpstate = self.forepump.read_value()
|
||||
if self.turbopump:
|
||||
self.turbopump.write_target(0)
|
||||
self.handle_valves(**self.condense_valves)
|
||||
self._start_time = state.now
|
||||
if not pumpstate: # wait longer for starting fore pump
|
||||
self._start_time += 10
|
||||
return Retry
|
||||
if self.wait_valves():
|
||||
return Retry
|
||||
self.check_valve_result()
|
||||
return self.condensing
|
||||
|
||||
@status_code(BUSY)
|
||||
def condensing(self, state):
|
||||
pdump = self.dump_pressure.value # or self.dump_pressure.read_value() ?
|
||||
pcond = self.condenseline_pressure.read_value()
|
||||
v9 = self.condense_valve.read_value()
|
||||
if v9:
|
||||
if pcond > self.condensing_p_high:
|
||||
self.log.info('shut V9')
|
||||
self.condense_valve.write_target(0)
|
||||
elif pcond < self.condensing_p_low and state.now > self._start_time + 5:
|
||||
pulse_time = 60 * self.pulse_factor / pdump
|
||||
if pulse_time > 59:
|
||||
self.log.info('open V9')
|
||||
self.condense_value.write_target(1)
|
||||
else:
|
||||
self.log.info('V9 pulse %r', pulse_time)
|
||||
self._start_time = state.now
|
||||
self.condense_valve.pulse(pulse_time)
|
||||
|
||||
if pdump > self.dump_target:
|
||||
return Retry
|
||||
|
||||
self.condense_valve.write_target(1)
|
||||
|
||||
if self.turbopump is not None:
|
||||
return self.condense_wait_before_turbo_start
|
||||
|
||||
return self.wait_for_condense_line_pressure
|
||||
|
||||
@status_code(BUSY, 'condense (wait before starting turbo)')
|
||||
def condense_wait_before_turbo_start(self, state):
|
||||
if (self.condenseline_pressure.read_value() > self.turbo_condense_pressure
|
||||
and self.still_pressure.read_value() > self.safe_turbo_pressure):
|
||||
return Retry
|
||||
self.turbopump.write_target(1)
|
||||
return self.wait_for_condense_line_pressure
|
||||
|
||||
@status_code(BUSY)
|
||||
def wait_for_condense_line_pressure(self, state):
|
||||
if self.condenseline_pressure.read_value() > self.end_condense_pressure:
|
||||
return Retry
|
||||
self.condense_valve.write_target(0)
|
||||
return self.circulate
|
||||
|
||||
@status_code(BUSY)
|
||||
def circulate(self, state):
|
||||
"""Zirkuliert die Mischung."""
|
||||
if state.init:
|
||||
self.handle_valves(**self.condense_valves)
|
||||
if self.wait_valves():
|
||||
return Retry
|
||||
self.check_valve_result()
|
||||
self.value = V.circulating
|
||||
return Finish
|
||||
|
||||
@status_code(BUSY, 'remove (wait for turbo shut down)')
|
||||
def remove(self, state):
|
||||
"""Entfernt die Mischung."""
|
||||
|
||||
if state.init:
|
||||
self.handle_valves(**self.remove_valves)
|
||||
if self.turbopump is not None:
|
||||
self._start_time = state.now
|
||||
self.turbopump.write_target(0)
|
||||
return Retry
|
||||
|
||||
if self.turbopump is not None:
|
||||
# if (state.now - self._start_time < self.turbo_off_delay or
|
||||
if self.turbopump.read_speed() > self.turbo_off_speed:
|
||||
return Retry
|
||||
|
||||
self.circuitshort_valve.write_target(1)
|
||||
|
||||
if self.turbopump is not None:
|
||||
return self.remove_wait_for_still_pressure
|
||||
|
||||
return self.remove_endsequence
|
||||
|
||||
@status_code(BUSY, 'remove (wait for still pressure low)')
|
||||
def remove_wait_for_still_pressure(self, state):
|
||||
if self.still_pressure.read_value() > self.safe_turbo_pressure:
|
||||
return Retry
|
||||
self.turbopump.write_target(1)
|
||||
return self.remove_endsequence
|
||||
|
||||
@status_code(BUSY)
|
||||
def remove_endsequence(self, state):
|
||||
if (self.still_pressure_turbo and
|
||||
self.still_pressure_turbo.read_value() > self.end_remove_turbo_pressure):
|
||||
return Retry
|
||||
if self.still_pressure.read_value() > self.end_remove_pressure:
|
||||
return Retry
|
||||
self.circuitshort_valve.write_target(0)
|
||||
self.dump_valve.write_target(0)
|
||||
|
||||
if self.compressor is not None:
|
||||
self.compressor.write_target(0)
|
||||
return self.close_valves_after_remove
|
||||
|
||||
@status_code(BUSY)
|
||||
def close_valves_after_remove(self, state):
|
||||
if state.init:
|
||||
self.handle_valves(**self.valves_after_remove)
|
||||
self.turbopump.write_target(0)
|
||||
if self.wait_valves():
|
||||
return Retry
|
||||
self.check_valve_result()
|
||||
self._warn_manual_work = True
|
||||
return self.final_status(WARN, 'please check manual valves')
|
||||
|
||||
def read_status(self):
|
||||
status = super().read_status()
|
||||
if status[0] < ERROR and self._warn_manual_work:
|
||||
try:
|
||||
self.handle_valves(**self.check_after_remove)
|
||||
self._warn_manual_work = False
|
||||
except ImpossibleError:
|
||||
return WARN, f'please close manual valves {",".join(self._valves_failed[False])}'
|
||||
return status
|
||||
|
||||
def handle_valves(self, check_closed=(), check_open=(), close=(), open=()):
|
||||
"""check ot set given valves
|
||||
|
||||
raises ImpossibleError, when checks fails
|
||||
"""
|
||||
self._valves_to_wait_for = {}
|
||||
self._valves_failed = {True: [], False: []}
|
||||
for flag, valves in enumerate([check_closed, check_open]):
|
||||
for vname in valves.split():
|
||||
if self.secNode.modules[vname].read_value() != flag:
|
||||
self._valves_failed[flag].append(vname)
|
||||
for flag, valves in enumerate([close, open]):
|
||||
for vname in valves.split():
|
||||
valve = self.secNode.modules[vname]
|
||||
valve.write_target(flag)
|
||||
if valve.isBusy():
|
||||
self._valves_to_wait_for[vname] = (valve, flag)
|
||||
elif valve.read_value() != flag:
|
||||
self._valves_failed[flag].append(vname)
|
||||
|
||||
def wait_valves(self):
|
||||
busy = False
|
||||
for vname, (valve, flag) in dict(self._valves_to_wait_for.items()):
|
||||
statuscode = valve.read_status()[0]
|
||||
if statuscode == BUSY:
|
||||
busy = True
|
||||
continue
|
||||
if valve.read_value() == flag and statuscode == IDLE:
|
||||
self._valves_to_wait_for.pop(vname)
|
||||
else:
|
||||
self._valves_failed[flag].append(vname)
|
||||
return busy
|
||||
|
||||
def check_valve_result(self):
|
||||
result = []
|
||||
for flag, valves in self._valves_failed.items():
|
||||
if valves:
|
||||
result.append(f"{','.join(valves)} not {'open' if flag else 'closed'}")
|
||||
if result:
|
||||
raise ImpossibleError(f"failed: {', '.join(result)}")
|
||||
|
||||
|
||||
class DIL5(Dilution):
|
||||
condense_valves = {
|
||||
'close': 'V2 V4 V9',
|
||||
'check_closed': 'MV10 MV13 MV8 MVB MV2',
|
||||
'check_open': 'MV1 MV3a MV3b GV1 MV9 MV11 MV12 MV14',
|
||||
'open': 'V1 V5 compressor forepump',
|
||||
}
|
||||
remove_valves = {
|
||||
'close': 'V1 V2 V9',
|
||||
'check_closed': 'MV10 MV13 MV8 MVB MV2',
|
||||
'check_open': 'MV1 MV3a MV3b GV1 MV9 MV11 MV12 MV14',
|
||||
'open': 'V4 V5 compressor forepump',
|
||||
}
|
||||
valves_after_remove = {
|
||||
'close': 'V1 V2 V4 V5 V9',
|
||||
'check_closed': 'MV10 MV13 MV8 MVB MV2',
|
||||
'open': '',
|
||||
'check_open': '',
|
||||
}
|
||||
check_after_remove = {
|
||||
'close': '',
|
||||
'check_closed': 'MV1 MV9 MV10 MV11 MV12',
|
||||
'open': '',
|
||||
'check_open': '',
|
||||
}
|
||||
|
||||
|
||||
class Interlock(LogoMixin, AddrMixin, Readable):
|
||||
dil = Attached()
|
||||
value = AddrParam('interlock state (bitmap)',
|
||||
IntRange(0, 31), addr='V414', readonly=False)
|
||||
p5lim = AddrParam('safety limit on p5 to protect forepump',
|
||||
FloatRange(), value=1300, addr='VW16 VW18', readonly=False)
|
||||
p2lim = AddrParam('safety limit on p2 to protect compressor',
|
||||
FloatRange(), value=4000, addr='VW8 VW10', readonly=False)
|
||||
p1lim = AddrParam('safety limit to protect dump',
|
||||
FloatRange(), value=1300, addr='VW12 VW14', readonly=False)
|
||||
p2max = AddrParam('limit pn p2 for mechanism to put mix to dump',
|
||||
FloatRange(), value=3000, addr='VW20 VW22', readonly=False)
|
||||
conditions = { # starting with bit 1
|
||||
'off (p5>p5lim)': {'forepump': False},
|
||||
'off (p2>p2lim)': {'compressor': False},
|
||||
'off (p1>p2lim)': {'forepump': False, 'compressor': False},
|
||||
'open (p2>p2max)': {'V4': True}}
|
||||
|
||||
reset_param = Property('addr for reset', StringType(), default='V418.1')
|
||||
_mismatch = None
|
||||
_prefix = ''
|
||||
_actuators = None
|
||||
|
||||
def initModule(self):
|
||||
super().initModule()
|
||||
self._actuators = {}
|
||||
for actions in self.conditions.values():
|
||||
for modname in actions:
|
||||
if modname not in self._actuators:
|
||||
self._actuators[modname] = self.secNode.modules[modname]
|
||||
|
||||
def doPoll(self):
|
||||
self.read_status() # this includes read_value
|
||||
|
||||
def initialReads(self):
|
||||
super().initialReads()
|
||||
self.reset()
|
||||
|
||||
@Command
|
||||
def reset(self):
|
||||
"""reset the interlock"""
|
||||
self._prefix = ''
|
||||
self.set_vm_value(self.reset_param, 1)
|
||||
for actuator in self._actuators.values():
|
||||
actuator.reset_fault()
|
||||
if self.read_value() != 0:
|
||||
raise HardwareError('can not clear status byte')
|
||||
self.set_vm_value(self.reset_param, 0)
|
||||
self.read_status() # update status (this may trigger ERROR again)
|
||||
|
||||
def read_status(self):
|
||||
if self._mismatch is None: # init
|
||||
self._mismatch = set()
|
||||
bits = self.read_value()
|
||||
if bits:
|
||||
self.dil.stop()
|
||||
keys = formatStatusBits(bits, self.conditions, 1)
|
||||
statustext = []
|
||||
for key in keys:
|
||||
actions = self.conditions[key]
|
||||
statustext.append(f"{' and '.join(actions)} {key}")
|
||||
for module, value in actions.items():
|
||||
modobj = self._actuators[module]
|
||||
if modobj.target != value:
|
||||
self._prefix = 'switched '
|
||||
modobj.set_fault(value, f'switched {key}')
|
||||
return ERROR, f"{self._prefix}{', '.join(statustext)}"
|
||||
if self._mismatch:
|
||||
return ERROR, f"mismatch on values for {', '.join(self._mismatch)}"
|
||||
return IDLE, ''
|
||||
|
||||
def addressed_read(self, pobj):
|
||||
values = [self.get_vm_value(a) for a in pobj.addr.split()]
|
||||
if any(v != values[0] for v in values):
|
||||
self._mismatch.add(pobj.name)
|
||||
self.read_status()
|
||||
else:
|
||||
self._mismatch.discard(pobj.name)
|
||||
return values[0]
|
||||
|
||||
def addressed_write(self, pobj, value):
|
||||
for addr in pobj.addr.split():
|
||||
self.set_vm_value(addr, value)
|
||||
self.read_status()
|
||||
|
||||
|
320
frappy_psi/dilution_statemachine.py
Normal file
320
frappy_psi/dilution_statemachine.py
Normal file
@ -0,0 +1,320 @@
|
||||
# *****************************************************************************
|
||||
#
|
||||
# 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:
|
||||
# Andrea Plank <andrea.plank@psi.ch>
|
||||
#
|
||||
# *****************************************************************************
|
||||
|
||||
from frappy.core import Drivable, Parameter, EnumType, Attached, FloatRange, \
|
||||
Command, IDLE, BUSY, WARN, ERROR, Property
|
||||
from frappy.datatypes import StatusType, EnumType, ArrayOf, BoolType, IntRange
|
||||
from frappy.states import StateMachine, Retry, Finish, status_code, HasStates
|
||||
from frappy.lib.enum import Enum
|
||||
from frappy.errors import ImpossibleError
|
||||
import time
|
||||
Targetstates = Enum(
|
||||
SORBPUMP = 0,
|
||||
CONDENSE = 1,
|
||||
CIRCULATE = 2,
|
||||
REMOVE = 3,
|
||||
MANUAL = 4,
|
||||
TEST = 5,
|
||||
STOP = 6,
|
||||
)
|
||||
|
||||
class Dilution(HasStates, Drivable):
|
||||
|
||||
condenseline_pressure = Attached()
|
||||
condense_valve = Attached()
|
||||
dump_valve = Attached()
|
||||
|
||||
circulate_pump = Attached()
|
||||
compressor = Attached(mandatory=False)
|
||||
turbopump = Attached(mandatory=False)
|
||||
condenseline_valve = Attached()
|
||||
circuitshort_valve = Attached()
|
||||
still_pressure = Attached()
|
||||
#ls372 = Attached()
|
||||
V5 = Attached() #Name noch ändern!!!
|
||||
p1 = Attached() #Name noch ändern!!!
|
||||
|
||||
condensing_p_low = Property('Lower limit for condenseline pressure', IntRange())
|
||||
|
||||
condensing_p_high = Property('Higher limit for condenseline pressure', IntRange())
|
||||
|
||||
target = Parameter('target state', EnumType(Targetstates))
|
||||
|
||||
value = Parameter('current state', EnumType(Targetstates))
|
||||
|
||||
init = True
|
||||
|
||||
|
||||
|
||||
def write_target(self, target):
|
||||
"""
|
||||
if (target == Targetstates.SORBPUMP):
|
||||
if self.value == target:
|
||||
return self.target
|
||||
self.start_machine(self.sorbpump)
|
||||
self.value = Targetstates.SORBPUMP
|
||||
return self.value
|
||||
"""
|
||||
if (target == Targetstates.TEST):
|
||||
self.value = Targetstates.TEST
|
||||
self.init = True
|
||||
self.start_machine(self.test)
|
||||
|
||||
if (target == Targetstates.REMOVE):
|
||||
if self.value == target:
|
||||
return target
|
||||
if self.value != Teststates.CIRCULATE:
|
||||
self.final_status(WARN, "state before is not circulate")
|
||||
return self.value
|
||||
self.value = Targetstates.REMOVE
|
||||
self.init = True
|
||||
self.start_machine(self.remove)
|
||||
|
||||
elif (target == Targetstates.CIRCULATE):
|
||||
if self.value == target:
|
||||
return target
|
||||
self.value = Targetstates.CIRCULATE
|
||||
self.init = True
|
||||
self.start_machine(self.circulate)
|
||||
|
||||
elif (target == Targetstates.CONDENSE):
|
||||
if self.value == target:
|
||||
return target
|
||||
self.value = Targetstates.CONDENSE
|
||||
self.init = True
|
||||
self.start_machine(self.condense)
|
||||
|
||||
elif(target == Targetstates.MANUAL):
|
||||
self.value = Targetstates.MANUAL
|
||||
self.stop_machine()
|
||||
|
||||
elif (target == Targetstates.STOP):
|
||||
self.value = Targetstates.STOP
|
||||
self.stop_machine()
|
||||
return self.value
|
||||
|
||||
"""
|
||||
@status_code(BUSY, 'sorbpump state')
|
||||
def sorbpump(self, state):
|
||||
#Heizt Tsorb auf und wartet ab.
|
||||
if self.init:
|
||||
self.ls372.write_target(40) #Setze Tsorb auf 40K
|
||||
self.start_time = self.now
|
||||
self.init = false
|
||||
return Retry
|
||||
|
||||
if self.now - self.start_time < 2400: # 40 Minuten warten
|
||||
return Retry
|
||||
|
||||
self.ls372.write_target(0)
|
||||
|
||||
if self.ls372.read_value() > 10: # Warten bis Tsorb unter 10K
|
||||
return Retry
|
||||
|
||||
return self.condense
|
||||
"""
|
||||
|
||||
@status_code(BUSY, 'test mode')
|
||||
def test(self, state):
|
||||
"Nur zum testen, ob UI funktioniert"
|
||||
self.init = False
|
||||
self.condense_valve.write_target(1)
|
||||
time.sleep(1)
|
||||
self.condense_valve.write_target(0)
|
||||
self.dump_valve.write_target(1)
|
||||
time.sleep(1)
|
||||
self.dump_valve.write_target(0)
|
||||
self.compressor.write_target(1)
|
||||
return True
|
||||
|
||||
@status_code(BUSY)
|
||||
def wait_for_condense_line_pressure(self, state):
|
||||
if (self.condenseline_pressure.read_value > 500):
|
||||
return Retry
|
||||
self.condense_valve.write_target(0)
|
||||
return self.circulate
|
||||
|
||||
def initialize_condense_valves(self, state):
|
||||
raise NotImplementedError
|
||||
|
||||
@status_code(BUSY)
|
||||
def condense(self, state):
|
||||
"""Führt das Kondensationsverfahren durch."""
|
||||
if state.init:
|
||||
self.initialize_condense_valves()
|
||||
self.circuitshort_valve.write_target(0)
|
||||
self.dump_valve.write_target(0)
|
||||
self.condense_valve.write_target(0)
|
||||
|
||||
self.condenseline_valve.write_target(1)
|
||||
self.V5.write_target(1)
|
||||
|
||||
if (self.compressor is not None):
|
||||
self.compressor.write_target(1)
|
||||
|
||||
self.circulate_pump.write_target(1)
|
||||
return Retry
|
||||
|
||||
if self.condenseline_pressure.read_value() < self.condensing_p_low:
|
||||
self.condense_valve.write_target(1)
|
||||
elif (self.condenseline_pressure.read_value() > self.condensing_p_high):
|
||||
self.condense_valve.write_target(0)
|
||||
|
||||
if (self.p1.read_value() > 20):
|
||||
return Retry
|
||||
|
||||
self.condense_valve.write_target(1)
|
||||
|
||||
if (self.turbopump is not None):
|
||||
return self.condense_wait_before_turbo_start
|
||||
|
||||
return self.wait_for_condense_line_pressure
|
||||
|
||||
@status_code(BUSY, 'condense (wait before starting turbo)')
|
||||
def condense_wait_before_turbo_start(self, state):
|
||||
if (self.condenseline_pressure.read_value() > 900 and self.still_pressure.read_value() > 10):
|
||||
return Retry
|
||||
else:
|
||||
self.turbopump.write_target(1)
|
||||
return self.wait_for_condense_line_pressure
|
||||
|
||||
def initialize_circulation_valves(self, state):
|
||||
raise NotImplementedError
|
||||
|
||||
@status_code(BUSY)
|
||||
def circulate(self, state):
|
||||
"""Zirkuliert die Mischung."""
|
||||
if state.init:
|
||||
self.initialize_circulation_valves()
|
||||
return Retry
|
||||
|
||||
@status_code(BUSY, 'remove (wait for turbo shut down)')
|
||||
def remove(self, state):
|
||||
"""Entfernt die Mischung."""
|
||||
|
||||
if state.init:
|
||||
self.condenseline_valve.write_target(0)
|
||||
self.dump_valve.write_target(1)
|
||||
self.start_time = self.now
|
||||
return Retry
|
||||
|
||||
if self.turbopump is not None:
|
||||
self.turbopump.write_target(0)
|
||||
|
||||
if (self.now - self.start_time < 300 or self.turbopump.read_speed() > 60):
|
||||
return Retry
|
||||
|
||||
self.circuitshort_valve.write_target(1)
|
||||
|
||||
if self.turbopump is not None:
|
||||
return self.remove_wait_for_still_pressure
|
||||
|
||||
return remove_endsequence
|
||||
|
||||
@status_code(BUSY, 'remove (wait for still pressure low)')
|
||||
def remove_wait_for_still_pressure(self, state):
|
||||
if self.still_pressure.read_value() > 20:
|
||||
return Retry
|
||||
self.turbopump.write_target(1)
|
||||
return self.remove_endsequence
|
||||
|
||||
@status_code(BUSY)
|
||||
def remove_endsequence(self, state):
|
||||
if self.still_pressure.read_value() > 1e-4:
|
||||
return Retry
|
||||
self.circuitshort_valve.write_target(0)
|
||||
self.dump_valve.write_target(0)
|
||||
|
||||
if self.compressor is not None:
|
||||
self.compressor.write_target(0)
|
||||
self.remove_check_manual_valves()
|
||||
self.remove_close_valves()
|
||||
self.circulate_pump.write_target(0)
|
||||
return Finish
|
||||
|
||||
def remove_check_manual_valves(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def remove_close_valves(self):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class DIL5(Dilution):
|
||||
|
||||
MV10 = Attached()
|
||||
MV13 = Attached()
|
||||
MV8 = Attached()
|
||||
MVB = Attached()
|
||||
MV2 = Attached()
|
||||
MV1 = Attached()
|
||||
MV3a = Attached()
|
||||
MV3b = Attached()
|
||||
GV1 = Attached()
|
||||
MV14 = Attached()
|
||||
MV12 = Attached()
|
||||
MV11 = Attached()
|
||||
MV9 = Attached()
|
||||
GV2 = Attached()
|
||||
|
||||
def earlyInit(self):
|
||||
self.circulate_closed_valves = [self.condense_valve, self.dump_valve, self.circuitshort_valve, self.MV10, self.MV13, self.MV8, self.MVB, self.MV2]
|
||||
self.circulate_open_valves = [self.MV11, self.circulate_pump, self.GV2, self.V5, self.compressor, self.condenseline_valve, self.MV1, self.MV3a, self.MV3b, self.GV1, self.MV9, self.MV14]
|
||||
self.condense_closed_valves = [self.MV10, self.MV13, self.MV8, self.MVB, self.MV2]
|
||||
self.condense_open_valves = [self.MV1, self.MV3a, self.MV3b, self.GV1, self.MV9, self.MV14, self.MV12, self.MV11]
|
||||
self.remove_check_closed_valves = [self.MV11, self.MV9, self.MV12, self.MV1]
|
||||
self.remove_closed_valves = [self.condenseline_valve, self.circuitshort_valve, self.V5, self.condense_valve, self.dump_valve]
|
||||
super().earlyInit()
|
||||
|
||||
def initialize_condense_valves(self):
|
||||
#Anfangszustand der Ventile überprüfen
|
||||
for valve in self.condense_open_valves:
|
||||
if valve.read_value() == 0:
|
||||
self.stop_machine()
|
||||
raise ImpossibleError(f'valve {valve.name} must be open')
|
||||
|
||||
for valve in self.condense_closed_valves:
|
||||
if valve.read_value == 1:
|
||||
self.stop_machine()
|
||||
return ImpossibleError(f'valve {valve.name} must be closed')
|
||||
|
||||
def initialize_circulation_valves(self):
|
||||
#Anfangszustand der Ventile überprüfen
|
||||
self.value = Targetstates.CIRCULATE
|
||||
for valve in self.circulate_closed_valves:
|
||||
if (valve.read_value() == 1):
|
||||
self.stop_machine()
|
||||
raise ImpossibleError(f'valve {valve.name} must be open')
|
||||
|
||||
for valve in self.circulate_open_valves:
|
||||
if (valve.read_value() == 0):
|
||||
valve.write_target(1)
|
||||
self.stop_machine()
|
||||
raise ImpossibleError(f'valve {valve.name} must be open')
|
||||
|
||||
def remove_check_manual_valves(self):
|
||||
for valve in self.remove_check_closed_valves:
|
||||
if (valve.read_value() == 1):
|
||||
self.final_status(WARN, "manual valve {valve.name} must be closed")
|
||||
|
||||
def remove_close_valves(self):
|
||||
for valve in self.remove_closed_valves:
|
||||
valve.write_target(0)
|
||||
|
@ -17,17 +17,17 @@
|
||||
#
|
||||
#
|
||||
# *****************************************************************************
|
||||
import sys
|
||||
from time import monotonic
|
||||
from ast import literal_eval
|
||||
import snap7
|
||||
from frappy.core import Attached, Command, Readable, Parameter, FloatRange, HasIO, Property, StringType, \
|
||||
IDLE, BUSY, WARN, ERROR, Writable, Drivable, BoolType, IntRange, Communicator, StatusType
|
||||
from frappy.errors import CommunicationFailedError, ConfigError
|
||||
from frappy.core import Readable, Parameter, FloatRange, HasIO, StringIO, Property, StringType,IDLE, BUSY, WARN, ERROR,Writable, Drivable, BoolType, IntRange, Communicator
|
||||
from frappy.errors import CommunicationFailedError
|
||||
from threading import RLock
|
||||
|
||||
import sys
|
||||
import time
|
||||
|
||||
class IO(Communicator):
|
||||
|
||||
|
||||
tcap_client = Property('tcap_client', IntRange())
|
||||
tsap_server = Property('tcap_server', IntRange())
|
||||
ip_address = Property('numeric ip address', StringType())
|
||||
@ -37,11 +37,12 @@ class IO(Communicator):
|
||||
def initModule(self):
|
||||
self._lock = RLock()
|
||||
super().initModule()
|
||||
|
||||
def _init(self):
|
||||
if monotonic() < self._last_try + 10:
|
||||
if not self._plc:
|
||||
if time.time() < self._last_try + 10:
|
||||
raise CommunicationFailedError('logo PLC not reachable')
|
||||
self._plc = snap7.logo.Logo()
|
||||
prev_stderr = sys.stdout
|
||||
sys.stderr = open('/dev/null', 'w') # suppress output of snap7
|
||||
try:
|
||||
self._plc.connect(self.ip_address, self.tcap_client, self.tsap_server)
|
||||
@ -50,14 +51,15 @@ class IO(Communicator):
|
||||
except Exception:
|
||||
pass
|
||||
finally:
|
||||
sys.stderr = sys.stdout
|
||||
sys.stderr = prev_stderr
|
||||
self._plc = None
|
||||
self._last_try = monotonic()
|
||||
self._last_try = time.time()
|
||||
raise CommunicationFailedError('logo PLC not reachable')
|
||||
|
||||
|
||||
|
||||
def communicate(self, cmd):
|
||||
with self._lock:
|
||||
if not self._plc:
|
||||
self._init()
|
||||
cmd = cmd.split(maxsplit=1)
|
||||
if len(cmd) == 2:
|
||||
@ -77,169 +79,22 @@ class IO(Communicator):
|
||||
raise
|
||||
|
||||
|
||||
class LogoMixin(HasIO):
|
||||
|
||||
class Snap7Mixin(HasIO):
|
||||
ioclass = IO
|
||||
|
||||
def get_vm_value(self, vm_address):
|
||||
return literal_eval(self.io.communicate(vm_address))
|
||||
|
||||
|
||||
def set_vm_value(self, vm_address, value):
|
||||
return literal_eval(self.io.communicate(f'{vm_address} {round(value)}'))
|
||||
return literal_eval(self.io.communicate(f'{vm_address} {value}'))
|
||||
|
||||
class Pressure(Snap7Mixin, Readable):
|
||||
vm_address = Property('VM address', datatype= StringType())
|
||||
value = Parameter('pressure', datatype = FloatRange(unit = 'mbar'))
|
||||
|
||||
class DigitalActuator(LogoMixin, Writable):
|
||||
"""output with or without feedback"""
|
||||
output_addr = Property('VM address output', datatype=StringType(), default='')
|
||||
target_addr = Property('VM address target', datatype=StringType(), default='')
|
||||
feedback_addr = Property('VM address feedback', datatype=StringType(), default='')
|
||||
target = Parameter('target', datatype=BoolType())
|
||||
value = Parameter('feedback or output', datatype=BoolType())
|
||||
_input = 'output'
|
||||
_value_addr = None
|
||||
_target_addr = None
|
||||
_fault = None
|
||||
|
||||
def doPoll(self):
|
||||
self.read_status() # this calls also read_value
|
||||
|
||||
def checkProperties(self):
|
||||
super().checkProperties()
|
||||
if self.feedback_addr:
|
||||
self._input = 'feedback'
|
||||
self._value_addr = self.feedback_addr
|
||||
else:
|
||||
self._input = 'output'
|
||||
self._value_addr = self.output_addr
|
||||
self._target_addr = self.target_addr or self.output_addr
|
||||
if self._target_addr and self._value_addr:
|
||||
self._check_feedback = self.feedback_addr and self.output_addr
|
||||
return
|
||||
raise ConfigError('need either output_addr or both feedback_addr and target_addr')
|
||||
|
||||
def initialReads(self):
|
||||
super().initialReads()
|
||||
self.target = self.read_value()
|
||||
|
||||
def set_fault(self, value, statustext):
|
||||
"""on a fault condition, set target to value
|
||||
|
||||
and status to statustext
|
||||
"""
|
||||
self.write_target(value)
|
||||
self._fault = statustext
|
||||
self.read_status()
|
||||
|
||||
def reset_fault(self):
|
||||
"""reset fault condition"""
|
||||
self._fault = None
|
||||
self.read_status()
|
||||
|
||||
def read_value(self):
|
||||
return self.get_vm_value(self._value_addr)
|
||||
|
||||
def write_target(self, target):
|
||||
self._fault = None
|
||||
self.set_vm_value(self._target_addr, target)
|
||||
value = self.read_value()
|
||||
if value != target and self.feedback_addr:
|
||||
# retry only if we have a feedback and the feedback did not change yet
|
||||
for i in range(20):
|
||||
if self.read_value() == target:
|
||||
self.log.debug('tried %d times', i)
|
||||
break
|
||||
self.set_vm_value(self._target_addr, target)
|
||||
|
||||
def read_status(self):
|
||||
if self._fault:
|
||||
return ERROR, self._fault
|
||||
value = self.read_value()
|
||||
if value != self.target:
|
||||
return ERROR, 'value and target do not match'
|
||||
if self._check_feedback:
|
||||
if value != self.get_vm_value(self._check_feedback):
|
||||
return ERROR, f'feedback does not match output'
|
||||
if self.feedback_addr:
|
||||
return IDLE, 'feedback confirmed'
|
||||
return IDLE, ''
|
||||
|
||||
|
||||
class DelayedActuator(DigitalActuator, Drivable):
|
||||
delay_addr = Property('address of delay value', StringType())
|
||||
_pulse_start = 0
|
||||
_pulse_end = 0
|
||||
_fault = None
|
||||
|
||||
def read_status(self):
|
||||
if self._fault:
|
||||
return ERROR, self._fault
|
||||
value = self.read_value()
|
||||
fberror = None
|
||||
if self._pulse_start:
|
||||
now = monotonic()
|
||||
if now < self._pulse_start + 1:
|
||||
value = 1
|
||||
elif now < self._pulse_end - 1:
|
||||
if not value:
|
||||
self._pulse_start = 0
|
||||
return WARN, f'{self._input} is not on during pulse - due to interlock?'
|
||||
if value:
|
||||
if now < self._pulse_end + 1:
|
||||
return BUSY, 'pulsing'
|
||||
self.log.warn('pulse timeout')
|
||||
self.set_vm_value(self._target_addr, 0)
|
||||
self._pulse_start = 0
|
||||
self.set_vm_value(self.delay_addr, 0)
|
||||
elif self._check_feedback and value != self.get_vm_value(self._check_feedback):
|
||||
fberror = ERROR, f'feedback does not match output'
|
||||
if value != self.target:
|
||||
return ERROR, 'value does not match target'
|
||||
self.setFastPoll(False)
|
||||
if fberror:
|
||||
return fberror
|
||||
if self.feedback_addr:
|
||||
return IDLE, 'feedback confirmed'
|
||||
return IDLE, ''
|
||||
|
||||
def write_target(self, value):
|
||||
self._pulse_start = 0
|
||||
if not value:
|
||||
self.set_vm_value(self.delay_addr, 0)
|
||||
return super().write_target(value)
|
||||
|
||||
@Command(argument=FloatRange(0))
|
||||
def pulse(self, delay):
|
||||
"""open for delay seconds"""
|
||||
self.set_vm_value(self.delay_addr, delay)
|
||||
self.set_vm_value(self._target_addr, 1)
|
||||
self.set_vm_value(self._target_addr, 0)
|
||||
self.setFastPoll(True, 0.5)
|
||||
self.status = BUSY, 'pulsing'
|
||||
now = monotonic()
|
||||
self._pulse_start = now
|
||||
self._pulse_end = now + delay
|
||||
|
||||
|
||||
class Value(LogoMixin, Readable):
|
||||
addr = Property('VM address', datatype=StringType())
|
||||
|
||||
def read_value(self):
|
||||
return self.get_vm_value(self.addr)
|
||||
|
||||
def read_status(self):
|
||||
return IDLE, ''
|
||||
|
||||
|
||||
class DigitalValue(Value):
|
||||
value = Parameter('airpressure state', datatype=BoolType())
|
||||
|
||||
|
||||
# TODO: the following classes are too specific, they have to be moved
|
||||
|
||||
class Pressure(LogoMixin, Drivable):
|
||||
vm_address = Property('VM address', datatype=StringType())
|
||||
value = Parameter('pressure', datatype=FloatRange(unit='mbar'))
|
||||
|
||||
# pollinterval = 0.5
|
||||
#pollinterval = 0.5
|
||||
|
||||
def read_value(self):
|
||||
return self.get_vm_value(self.vm_address)
|
||||
@ -248,11 +103,11 @@ class Pressure(LogoMixin, Drivable):
|
||||
return IDLE, ''
|
||||
|
||||
|
||||
class Airpressure(LogoMixin, Readable):
|
||||
vm_address = Property('VM address', datatype=StringType())
|
||||
value = Parameter('airpressure state', datatype=BoolType())
|
||||
class Airpressure(Snap7Mixin, Readable):
|
||||
vm_address = Property('VM address', datatype= StringType())
|
||||
value = Parameter('airpressure state', datatype = BoolType())
|
||||
|
||||
# pollinterval = 0.5
|
||||
#pollinterval = 0.5
|
||||
|
||||
def read_value(self):
|
||||
if (self.get_vm_value(self.vm_address) > 500):
|
||||
@ -263,13 +118,12 @@ class Airpressure(LogoMixin, Readable):
|
||||
def read_status(self):
|
||||
return IDLE, ''
|
||||
|
||||
class Valve(Snap7Mixin, Drivable):
|
||||
vm_address_input = Property('VM address input', datatype= StringType())
|
||||
vm_address_output = Property('VM address output', datatype= StringType())
|
||||
|
||||
class Valve(LogoMixin, Drivable):
|
||||
vm_address_input = Property('VM address input', datatype=StringType())
|
||||
vm_address_output = Property('VM address output', datatype=StringType())
|
||||
|
||||
target = Parameter('Valve target', datatype=BoolType())
|
||||
value = Parameter('Value state', datatype=BoolType())
|
||||
target = Parameter('Valve target', datatype = BoolType())
|
||||
value = Parameter('Value state', datatype = BoolType())
|
||||
_remaining_tries = None
|
||||
|
||||
def read_value(self):
|
||||
@ -288,7 +142,7 @@ class Valve(LogoMixin, Drivable):
|
||||
if value != self.target:
|
||||
if self._remaining_tries is None:
|
||||
self.target = self.read_value()
|
||||
return IDLE, ''
|
||||
return IDLE,''
|
||||
self._remaining_tries -= 1
|
||||
if self._remaining_tries < 0:
|
||||
self.setFastPoll(False)
|
||||
@ -298,12 +152,11 @@ class Valve(LogoMixin, Drivable):
|
||||
self.setFastPoll(False)
|
||||
return IDLE, ''
|
||||
|
||||
class FluidMachines(Snap7Mixin, Drivable):
|
||||
vm_address_output = Property('VM address output', datatype= StringType())
|
||||
|
||||
class FluidMachines(LogoMixin, Drivable):
|
||||
vm_address_output = Property('VM address output', datatype=StringType())
|
||||
|
||||
target = Parameter('Valve target', datatype=BoolType())
|
||||
value = Parameter('Valve state', datatype=BoolType())
|
||||
target = Parameter('Valve target', datatype = BoolType())
|
||||
value = Parameter('Value state', datatype = BoolType())
|
||||
|
||||
def read_value(self):
|
||||
return self.get_vm_value(self.vm_address_output)
|
||||
@ -314,10 +167,10 @@ class FluidMachines(LogoMixin, Drivable):
|
||||
def read_status(self):
|
||||
return IDLE, ''
|
||||
|
||||
class TempSensor(Snap7Mixin, Readable):
|
||||
vm_address = Property('VM address', datatype= StringType())
|
||||
value = Parameter('resistance', datatype = FloatRange(unit = 'Ohm'))
|
||||
|
||||
class TempSensor(LogoMixin, Readable):
|
||||
vm_address = Property('VM address', datatype=StringType())
|
||||
value = Parameter('resistance', datatype=FloatRange(unit='Ohm'))
|
||||
|
||||
def read_value(self):
|
||||
return self.get_vm_value(self.vm_address)
|
||||
@ -325,13 +178,12 @@ class TempSensor(LogoMixin, Readable):
|
||||
def read_status(self):
|
||||
return IDLE, ''
|
||||
|
||||
class HeaterParam(Snap7Mixin, Writable):
|
||||
vm_address = Property('VM address output', datatype= StringType())
|
||||
|
||||
class HeaterParam(LogoMixin, Writable):
|
||||
vm_address = Property('VM address output', datatype=StringType())
|
||||
target = Parameter('Heater target', datatype = IntRange())
|
||||
|
||||
target = Parameter('Heater target', datatype=IntRange())
|
||||
|
||||
value = Parameter('Heater Param', datatype=IntRange())
|
||||
value = Parameter('Heater Param', datatype = IntRange())
|
||||
|
||||
def read_value(self):
|
||||
return self.get_vm_value(self.vm_address)
|
||||
@ -343,12 +195,13 @@ class HeaterParam(LogoMixin, Writable):
|
||||
return IDLE, ''
|
||||
|
||||
|
||||
class controlHeater(LogoMixin, Writable):
|
||||
vm_address = Property('VM address on switch', datatype=StringType())
|
||||
class controlHeater(Snap7Mixin, Writable):
|
||||
|
||||
target = Parameter('Heater state', datatype=BoolType())
|
||||
vm_address = Property('VM address on switch', datatype= StringType())
|
||||
|
||||
value = Parameter('Heater state', datatype=BoolType())
|
||||
target = Parameter('Heater state', datatype = BoolType())
|
||||
|
||||
value = Parameter('Heater state', datatype = BoolType())
|
||||
|
||||
def read_value(self):
|
||||
return self.get_vm_value(self.vm_address_on)
|
||||
@ -363,12 +216,11 @@ class controlHeater(LogoMixin, Writable):
|
||||
return IDLE, ''
|
||||
|
||||
|
||||
class safetyfeatureState(Snap7Mixin, Readable):
|
||||
|
||||
vm_address = Property('VM address state', datatype= StringType())
|
||||
|
||||
class safetyfeatureState(LogoMixin, Readable):
|
||||
vm_address = Property('VM address state', datatype=StringType())
|
||||
|
||||
value = Parameter('safety Feature state', datatype=BoolType())
|
||||
value = Parameter('safety Feature state', datatype = BoolType())
|
||||
|
||||
def read_value(self):
|
||||
return self.get_vm_value(self.vm_address)
|
||||
@ -377,12 +229,12 @@ class safetyfeatureState(LogoMixin, Readable):
|
||||
return IDLE, ''
|
||||
|
||||
|
||||
class safetyfeatureParam(LogoMixin, Writable):
|
||||
vm_address = Property('VM address output', datatype=StringType())
|
||||
class safetyfeatureParam(Snap7Mixin, Writable):
|
||||
vm_address = Property('VM address output', datatype= StringType())
|
||||
|
||||
target = Parameter('safety Feature target', datatype=IntRange())
|
||||
target = Parameter('safety Feature target', datatype = IntRange())
|
||||
|
||||
value = Parameter('safety Feature Param', datatype=IntRange())
|
||||
value = Parameter('safety Feature Param', datatype = IntRange())
|
||||
|
||||
def read_value(self):
|
||||
return self.get_vm_value(self.vm_address)
|
||||
@ -394,12 +246,12 @@ class safetyfeatureParam(LogoMixin, Writable):
|
||||
return IDLE, ''
|
||||
|
||||
|
||||
class comparatorgekoppeltParam(LogoMixin, Writable):
|
||||
vm_address_1 = Property('VM address output', datatype=StringType())
|
||||
vm_address_2 = Property('VM address output', datatype=StringType())
|
||||
class comparatorgekoppeltParam(Snap7Mixin, Writable):
|
||||
vm_address_1 = Property('VM address output', datatype= StringType())
|
||||
vm_address_2 = Property('VM address output', datatype= StringType())
|
||||
|
||||
target = Parameter('safety Feature target', datatype=IntRange())
|
||||
value = Parameter('safety Feature Param', datatype=IntRange())
|
||||
target = Parameter('safety Feature target', datatype = IntRange())
|
||||
value = Parameter('safety Feature Param', datatype = IntRange())
|
||||
|
||||
def read_value(self):
|
||||
return self.get_vm_value(self.vm_address_1)
|
||||
@ -411,3 +263,6 @@ class comparatorgekoppeltParam(LogoMixin, Writable):
|
||||
def read_status(self):
|
||||
return IDLE, ''
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -19,14 +19,20 @@
|
||||
#
|
||||
# *****************************************************************************
|
||||
|
||||
from frappy.core import Parameter, BoolType, Writable
|
||||
from frappy.persistent import PersistentMixin, PersistentParam
|
||||
from frappy.core import Readable, Parameter, FloatRange, BoolType, StringIO, HasIO, \
|
||||
Property, StringType, Writable, IntRange, IDLE, BUSY, ERROR
|
||||
from frappy.errors import CommunicationFailedError
|
||||
|
||||
class ManualValve(Writable):
|
||||
target = Parameter('Valve target', datatype = BoolType())
|
||||
value = Parameter('Valve state', datatype = BoolType())
|
||||
|
||||
class ManualValve(PersistentMixin, Writable):
|
||||
target = PersistentParam('Valve target', datatype=BoolType(), persistent='auto')
|
||||
value = Parameter('Valve state', datatype=BoolType())
|
||||
def read_value(self):
|
||||
return self.value
|
||||
|
||||
def write_target(self, target):
|
||||
self.value = target
|
||||
return self.value
|
||||
|
||||
def read_status(self):
|
||||
return IDLE, ''
|
||||
|
@ -129,7 +129,7 @@ class PfeifferMixin(HasIO):
|
||||
|
||||
|
||||
class RPT200(PfeifferMixin, Readable):
|
||||
value = Parameter('Pressure', FloatRange(unit='mbar'))
|
||||
value = Parameter('Pressure', FloatRange(unit='hPa'))
|
||||
|
||||
def read_value(self):
|
||||
return self.data_request_u_expo_new(740)
|
||||
@ -143,11 +143,11 @@ class RPT200(PfeifferMixin, Readable):
|
||||
|
||||
|
||||
class TCP400(PfeifferMixin, Drivable, Readable):
|
||||
speed= Parameter('Rotational speed', FloatRange(unit='Hz'), readonly = False)
|
||||
speed= Parameter('Rotational speed', FloatRange(unit = 'Hz'), readonly = False)
|
||||
target= Parameter('Pumping station', BoolType())
|
||||
current= Parameter('Current consumption', FloatRange(unit='%'))
|
||||
current= Parameter('Current consumption', FloatRange(unit = '%'))
|
||||
value = Parameter('Turbopump state', BoolType())
|
||||
temp = Parameter('temp', FloatRange(unit='C'))
|
||||
temp = Parameter('temp', FloatRange(unit = 'C'))
|
||||
|
||||
def read_temp (self):
|
||||
return self.data_request_u_int(326)
|
||||
|
@ -213,10 +213,7 @@ class SeaClient(ProxyClient, Module):
|
||||
if not self._connected.is_set():
|
||||
if self._connect_thread is None:
|
||||
# let doPoll do the reconnect
|
||||
if self.pollInfo: # is None in playground
|
||||
self.pollInfo.trigger(True)
|
||||
else:
|
||||
self.log.info('sea_main.doPoll() will connect')
|
||||
raise ConnectionClosed('disconnected - reconnect is tried later')
|
||||
return self.raw_request(command, quiet)
|
||||
|
||||
@ -560,12 +557,10 @@ class SeaModule(Module):
|
||||
else:
|
||||
cls.paramFilter(result, paramdesc)
|
||||
cfgdict.pop('rel_paths', None)
|
||||
params = []
|
||||
for key, plist in result.items():
|
||||
params.extend(plist)
|
||||
params = sum(result.values(), [])
|
||||
if is_running: # take this at end
|
||||
params.append(is_running)
|
||||
# this needs some care in the configuration: the main value must be the first!
|
||||
|
||||
main_value = params[0]
|
||||
if issubclass(cls, Readable):
|
||||
if 'key' in main_value:
|
||||
@ -680,7 +675,7 @@ class SeaModule(Module):
|
||||
if key != 'status' and key is not None:
|
||||
attributes['read_' + key] = rfunc
|
||||
|
||||
if not readonly and key:
|
||||
if not readonly:
|
||||
|
||||
def wfunc(self, value, datatype=datatype, command=paramdesc['cmd']):
|
||||
value = datatype.export_value(value)
|
||||
@ -716,15 +711,9 @@ class SeaModule(Module):
|
||||
@classmethod
|
||||
def paramFilter(cls, result, paramdesc):
|
||||
sub = paramdesc['path'].split('/', 1)
|
||||
sublist = result.get(sub[0] or '.')
|
||||
if sublist is None:
|
||||
sublist = result.get('.')
|
||||
sublist = result.get(sub[0])
|
||||
if sublist is None:
|
||||
return False
|
||||
if len(sub) > 1 or 'kids' in paramdesc:
|
||||
# avoid params from subtrees
|
||||
return False
|
||||
# this is a top paramerter without kids
|
||||
sublist.append(paramdesc)
|
||||
return True
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user