Compare commits
63 Commits
Author | SHA1 | Date | |
---|---|---|---|
4c71e44cd2 | |||
5d0c980e22 | |||
f1bbdec2b3 | |||
2bdf9c6542 | |||
1fba0fad42 | |||
0a5de1ebc2 | |||
92c53ad3ba | |||
802d4e0c99 | |||
ee040ce98a | |||
b1f9c74269 | |||
63210c9924 | |||
47c5e297d4 | |||
6cd83eabcc | |||
caaefec6db | |||
6dfb3bcee8 | |||
d492f7326f | |||
7929c37027 | |||
76349e38f9 | |||
647d87f70f | |||
798c268eb4 | |||
9467f5233c | |||
a7ff73a34d | |||
bf018e74ba | |||
801f80af7f | |||
26fa5371e9 | |||
2bad1ffee5 | |||
b2b77c70ea | |||
192cde605e | |||
c7d23e40e9 | |||
2561e82086 | |||
55c96ffe4f | |||
17a44ef42a | |||
fcdee8e3ec | |||
dddf74df9e | |||
ac251ea515 | |||
9e4f9b7b95 | |||
4f65ae7e46 | |||
a73b7e7d88 | |||
76a78871b4 | |||
118e22ee44 | |||
c63f98f3cb | |||
6514a1b2ee | |||
aeec940659 | |||
4571af8534 | |||
adfb561308 | |||
70a31b5cae | |||
8ee97ade63 | |||
1715f95dd4 | |||
db29776dd5 | |||
a2905d9fbc | |||
16b826394f | |||
ea8570d422 | |||
1169e0cd09 | |||
7d02498b3d | |||
694b121c01 | |||
0f50de9a7f | |||
b454f47a12 | |||
6e7be6b4c7 | |||
af28511403 | |||
9d9d31693b | |||
3a7fff713d | |||
2acab33faa | |||
8c589cc138 |
19
cfg/attocube_cfg.py
Normal file
19
cfg/attocube_cfg.py
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
Node('attocube_test.psi.ch',
|
||||||
|
'a single attocube axis',
|
||||||
|
interface='tcp://5000',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('r',
|
||||||
|
'frappy_psi.attocube.Axis',
|
||||||
|
'ANRv220-F3-02882',
|
||||||
|
axis = 1,
|
||||||
|
value = Param(unit='deg'),
|
||||||
|
tolerance = 0.1,
|
||||||
|
target_min = 0,
|
||||||
|
target_max = 360,
|
||||||
|
steps_fwd = 45,
|
||||||
|
steps_bwd = 85,
|
||||||
|
step_mode = True,
|
||||||
|
# gear = 1.2,
|
||||||
|
)
|
||||||
|
|
231
cfg/dilsc_cfg.py
Normal file
231
cfg/dilsc_cfg.py
Normal file
@ -0,0 +1,231 @@
|
|||||||
|
Node('cfg/dilsc1.cfg',
|
||||||
|
'triton test',
|
||||||
|
interface='5000',
|
||||||
|
name='dilsc1',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('triton',
|
||||||
|
'frappy_psi.mercury.IO',
|
||||||
|
'connection to triton software',
|
||||||
|
uri='tcp://192.168.2.33:33576',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('T_mix',
|
||||||
|
'frappy_psi.triton.TemperatureSensor',
|
||||||
|
'mix. chamber temperature',
|
||||||
|
slot='T8',
|
||||||
|
io='triton',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('T_pt2head',
|
||||||
|
'frappy_psi.triton.TemperatureSensor',
|
||||||
|
'PTR2 head temperature',
|
||||||
|
slot='T1',
|
||||||
|
io='triton',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('T_pt2plate',
|
||||||
|
'frappy_psi.triton.TemperatureSensor',
|
||||||
|
'PTR2 plate temperature',
|
||||||
|
slot='T2',
|
||||||
|
io='triton',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('T_still',
|
||||||
|
'frappy_psi.triton.TemperatureSensor',
|
||||||
|
'still temperature',
|
||||||
|
slot='T3',
|
||||||
|
io='triton',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('htr_still',
|
||||||
|
'frappy_psi.triton.HeaterOutput',
|
||||||
|
'still heater',
|
||||||
|
slot='H2',
|
||||||
|
io='triton',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('T_coldpl',
|
||||||
|
'frappy_psi.triton.TemperatureSensor',
|
||||||
|
'cold plate temperature',
|
||||||
|
slot='T4',
|
||||||
|
io='triton',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('T_mixcx',
|
||||||
|
'frappy_psi.triton.TemperatureSensor',
|
||||||
|
'mix. chamber cernox',
|
||||||
|
slot='T5',
|
||||||
|
io='triton',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('T_pt1head',
|
||||||
|
'frappy_psi.triton.TemperatureSensor',
|
||||||
|
'PTR1 head temperature',
|
||||||
|
slot='T6',
|
||||||
|
io='triton',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('T_pt1plate',
|
||||||
|
'frappy_psi.triton.TemperatureSensor',
|
||||||
|
'PTR1 plate temperature',
|
||||||
|
slot='T7',
|
||||||
|
io='triton',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('T_pucksensor',
|
||||||
|
'frappy_psi.triton.TemperatureLoop',
|
||||||
|
'puck sensor temperature',
|
||||||
|
output_module='htr_pucksensor',
|
||||||
|
slot='TA',
|
||||||
|
io='triton',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('htr_pucksensor',
|
||||||
|
'frappy_psi.triton.HeaterOutputWithRange',
|
||||||
|
'mix. chamber heater',
|
||||||
|
slot='H1,TA',
|
||||||
|
io='triton',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('T_magnet',
|
||||||
|
'frappy_psi.triton.TemperatureSensor',
|
||||||
|
'magnet temperature',
|
||||||
|
slot='T13',
|
||||||
|
io='triton',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('action',
|
||||||
|
'frappy_psi.triton.Action',
|
||||||
|
'higher level scripts',
|
||||||
|
io='triton',
|
||||||
|
slot='DR',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('p_dump',
|
||||||
|
'frappy_psi.mercury.PressureSensor',
|
||||||
|
'dump pressure',
|
||||||
|
slot='P1',
|
||||||
|
io='triton',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('p_cond',
|
||||||
|
'frappy_psi.mercury.PressureSensor',
|
||||||
|
'condenser pressure',
|
||||||
|
slot='P2',
|
||||||
|
io='triton',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('p_still',
|
||||||
|
'frappy_psi.mercury.PressureSensor',
|
||||||
|
'still pressure',
|
||||||
|
slot='P3',
|
||||||
|
io='triton',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('p_fore',
|
||||||
|
'frappy_psi.mercury.PressureSensor',
|
||||||
|
'pressure on the pump side',
|
||||||
|
slot='P5',
|
||||||
|
io='triton',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('p_back',
|
||||||
|
'frappy_psi.mercury.PressureSensor',
|
||||||
|
'pressure on the back side of the pump',
|
||||||
|
slot='P4',
|
||||||
|
io='triton',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('p_ovc',
|
||||||
|
'frappy_psi.mercury.PressureSensor',
|
||||||
|
'outer vacuum pressure',
|
||||||
|
slot='P6',
|
||||||
|
io='triton',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('V1',
|
||||||
|
'frappy_psi.triton.Valve',
|
||||||
|
'valve V1',
|
||||||
|
slot='V1',
|
||||||
|
io='triton',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('V2',
|
||||||
|
'frappy_psi.triton.Valve',
|
||||||
|
'valve V2',
|
||||||
|
slot='V2',
|
||||||
|
io='triton',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('V4',
|
||||||
|
'frappy_psi.triton.Valve',
|
||||||
|
'valve V4',
|
||||||
|
slot='V4',
|
||||||
|
io='triton',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('V5',
|
||||||
|
'frappy_psi.triton.Valve',
|
||||||
|
'valve V5',
|
||||||
|
slot='V5',
|
||||||
|
io='triton',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('V9',
|
||||||
|
'frappy_psi.triton.Valve',
|
||||||
|
'valve V9',
|
||||||
|
slot='V9',
|
||||||
|
io='triton',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('ips',
|
||||||
|
'frappy_psi.mercury.IO',
|
||||||
|
'IPS for magnet',
|
||||||
|
uri='192.168.127.254:3001',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('mf',
|
||||||
|
'frappy_psi.dilsc.VectorField',
|
||||||
|
'vector field',
|
||||||
|
x='mfx',
|
||||||
|
y='mfy',
|
||||||
|
z='mfz',
|
||||||
|
sphere_radius=0.6,
|
||||||
|
cylinders=((0.23, 5.2), (0.45, 0.8)),
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('mfx',
|
||||||
|
'frappy_psi.ips_mercury.SimpleField',
|
||||||
|
'magnetic field, x-axis',
|
||||||
|
slot='GRPX',
|
||||||
|
io='ips',
|
||||||
|
tolerance=0.0001,
|
||||||
|
wait_stable_field=0.0,
|
||||||
|
nunits=2,
|
||||||
|
target=Param(max=0.6),
|
||||||
|
ramp=0.225,
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('mfy',
|
||||||
|
'frappy_psi.ips_mercury.SimpleField',
|
||||||
|
'magnetic field, y axis',
|
||||||
|
slot='GRPY',
|
||||||
|
io='ips',
|
||||||
|
tolerance=0.0001,
|
||||||
|
wait_stable_field=0.0,
|
||||||
|
nunits=2,
|
||||||
|
target=Param(max=0.6),
|
||||||
|
ramp=0.225,
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('mfz',
|
||||||
|
'frappy_psi.ips_mercury.Field',
|
||||||
|
'magnetic field, z-axis',
|
||||||
|
slot='GRPZ',
|
||||||
|
io='ips',
|
||||||
|
tolerance=0.0001,
|
||||||
|
target=Param(max=5.2),
|
||||||
|
mode='DRIVEN',
|
||||||
|
ramp=0.52,
|
||||||
|
)
|
52
cfg/flowsas_cfg.py
Normal file
52
cfg/flowsas_cfg.py
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
Node('flowsas.psi.ch',
|
||||||
|
'flowsas test motors',
|
||||||
|
'tcp://5000',
|
||||||
|
)
|
||||||
|
|
||||||
|
#Mod('mot_io',
|
||||||
|
# 'frappy_psi.phytron.PhytronIO',
|
||||||
|
# 'io for motor control',
|
||||||
|
# uri = 'serial:///dev/ttyUSB0',
|
||||||
|
# )
|
||||||
|
|
||||||
|
#Mod('hmot',
|
||||||
|
# 'frappy_psi.phytron.Motor',
|
||||||
|
# 'horizontal axis',
|
||||||
|
# axis = 'X',
|
||||||
|
# io = 'mot_io',
|
||||||
|
# encoder_mode= 'NO',
|
||||||
|
# )
|
||||||
|
|
||||||
|
#Mod('vmot',
|
||||||
|
# 'frappy_psi.phytron.Motor',
|
||||||
|
# 'vertical axis',
|
||||||
|
# axis = 'Y',
|
||||||
|
# io = 'mot_io',
|
||||||
|
# encoder_mode= 'NO',
|
||||||
|
# )
|
||||||
|
|
||||||
|
Mod('syr_io',
|
||||||
|
'frappy_psi.cetoni_pump.LabCannBus',
|
||||||
|
'Module for bus',
|
||||||
|
deviceconfig = "/home/l_samenv/frappy/cetoniSDK/CETONI_SDK_Raspi_64bit_v20220627/config/dual_pumps",
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('syr1',
|
||||||
|
'frappy_psi.cetoni_pump.SyringePump',
|
||||||
|
'First syringe pump',
|
||||||
|
io='syr_io',
|
||||||
|
pump_name = "Nemesys_S_1_Pump",
|
||||||
|
valve_name = "Nemesys_S_1_Valve",
|
||||||
|
inner_diameter_set = 10,
|
||||||
|
piston_stroke_set = 60,
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('syr2',
|
||||||
|
'frappy_psi.cetoni_pump.SyringePump',
|
||||||
|
'Second syringe pump',
|
||||||
|
io='syr_io',
|
||||||
|
pump_name = "Nemesys_S_2_Pump",
|
||||||
|
valve_name = "Nemesys_S_2_Valve",
|
||||||
|
inner_diameter_set = 1,
|
||||||
|
piston_stroke_set = 60,
|
||||||
|
)
|
@ -3,3 +3,5 @@
|
|||||||
logdir = ./log
|
logdir = ./log
|
||||||
piddir = ./pid
|
piddir = ./pid
|
||||||
confdir = ./cfg
|
confdir = ./cfg
|
||||||
|
comlog = True
|
||||||
|
|
||||||
|
24
cfg/main/fw_cfg.py
Normal file
24
cfg/main/fw_cfg.py
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
Node('ft.config.sea.psi.ch',
|
||||||
|
'FW ILL furnace with W5 thermnocouple (1800 K), old power rack',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('sea_main',
|
||||||
|
'frappy_psi.sea.SeaClient',
|
||||||
|
'main sea connection for fw.config',
|
||||||
|
config='fw.config',
|
||||||
|
service='main',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('ts',
|
||||||
|
'frappy_psi.sea.SeaDrivable', '',
|
||||||
|
io='sea_main',
|
||||||
|
sea_object='tt',
|
||||||
|
rel_paths=['.', 'ts'],
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('t2',
|
||||||
|
'frappy_psi.sea.SeaReadable', '',
|
||||||
|
io='sea_main',
|
||||||
|
sea_object='tt',
|
||||||
|
rel_paths=['t2'],
|
||||||
|
)
|
@ -83,4 +83,5 @@ Mod('om',
|
|||||||
target_min = -180,
|
target_min = -180,
|
||||||
target_max = 360,
|
target_max = 360,
|
||||||
encoder_mode='READ',
|
encoder_mode='READ',
|
||||||
|
backlash=-1,
|
||||||
)
|
)
|
||||||
|
101
cfg/main/ma10heat_cfg.py
Normal file
101
cfg/main/ma10heat_cfg.py
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
Node('ma10.config.sea.psi.ch',
|
||||||
|
'10 Tesla vertical cryomagnet',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('sea_main',
|
||||||
|
'frappy_psi.sea.SeaClient',
|
||||||
|
'main sea connection for ma10.config',
|
||||||
|
config='ma10heat.config',
|
||||||
|
service='main',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('ts',
|
||||||
|
'frappy_psi.sea.SeaDrivable', '',
|
||||||
|
io='sea_main',
|
||||||
|
sea_object='tt',
|
||||||
|
rel_paths=['ts', 'set']
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('tm',
|
||||||
|
'frappy_psi.sea.SeaReadable', '',
|
||||||
|
io='sea_main',
|
||||||
|
sea_object='tt',
|
||||||
|
rel_paths=['tm', 'setvti']
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('ts2',
|
||||||
|
'frappy_psi.sea.SeaReadable', '',
|
||||||
|
io='sea_main',
|
||||||
|
sea_object='tt',
|
||||||
|
rel_paths=['ts_2']
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('cc',
|
||||||
|
'frappy_psi.sea.SeaReadable', '',
|
||||||
|
io='sea_main',
|
||||||
|
sea_object='cc',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('nv',
|
||||||
|
'frappy_psi.sea.SeaWritable', '',
|
||||||
|
io='sea_main',
|
||||||
|
sea_object='nv',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('hepump',
|
||||||
|
'frappy_psi.sea.SeaWritable', '',
|
||||||
|
io='sea_main',
|
||||||
|
sea_object='hepump',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('hemot',
|
||||||
|
'frappy_psi.sea.SeaDrivable', '',
|
||||||
|
io='sea_main',
|
||||||
|
sea_object='hemot',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('mf',
|
||||||
|
'frappy_psi.sea.SeaDrivable', '',
|
||||||
|
io='sea_main',
|
||||||
|
sea_object='mf',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('lev',
|
||||||
|
'frappy_psi.sea.SeaReadable', '',
|
||||||
|
io='sea_main',
|
||||||
|
sea_object='lev',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('ln2fill',
|
||||||
|
'frappy_psi.sea.SeaWritable', '',
|
||||||
|
io='sea_main',
|
||||||
|
sea_object='ln2fill',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('hefill',
|
||||||
|
'frappy_psi.sea.SeaWritable', '',
|
||||||
|
io='sea_main',
|
||||||
|
sea_object='hefill',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('table',
|
||||||
|
'frappy_psi.sea.SeaReadable', '',
|
||||||
|
io='sea_main',
|
||||||
|
sea_object='table',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('om_io',
|
||||||
|
'frappy_psi.phytron.PhytronIO',
|
||||||
|
'dom motor IO',
|
||||||
|
uri='ma10-ts.psi.ch:3004',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('om',
|
||||||
|
'frappy_psi.phytron.Motor',
|
||||||
|
'stick rotation, typically used for omega',
|
||||||
|
io='om_io',
|
||||||
|
sign=-1,
|
||||||
|
target_min = -180,
|
||||||
|
target_max = 360,
|
||||||
|
encoder_mode='READ',
|
||||||
|
)
|
108
cfg/main/ma10high_t_cfg.py
Normal file
108
cfg/main/ma10high_t_cfg.py
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
Node('ma10.config.sea.psi.ch',
|
||||||
|
'10 Tesla vertical cryomagnet',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('sea_main',
|
||||||
|
'frappy_psi.sea.SeaClient',
|
||||||
|
'main sea connection for ma10.config',
|
||||||
|
config='ma10high_t.config',
|
||||||
|
service='main',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('th',
|
||||||
|
'frappy_psi.sea.SeaReadable',
|
||||||
|
'sample heater temperature',
|
||||||
|
io='sea_main',
|
||||||
|
sea_object='tt',
|
||||||
|
rel_paths=['ts', 'setsamp']
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('tm',
|
||||||
|
'frappy_psi.sea.SeaDrivable', '',
|
||||||
|
io='sea_main',
|
||||||
|
sea_object='tt',
|
||||||
|
rel_paths=['tm', 'set']
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('ts',
|
||||||
|
'frappy_psi.parmod.Converging',
|
||||||
|
'test for parmod',
|
||||||
|
unit='K',
|
||||||
|
read='th.value',
|
||||||
|
write='th.setsamp',
|
||||||
|
meaning=['temperature', 20],
|
||||||
|
settling_time=20,
|
||||||
|
tolerance=1,
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('ts2',
|
||||||
|
'frappy_psi.sea.SeaReadable', '',
|
||||||
|
io='sea_main',
|
||||||
|
sea_object='tt',
|
||||||
|
rel_paths=['ts_2']
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('cc',
|
||||||
|
'frappy_psi.sea.SeaReadable', '',
|
||||||
|
io='sea_main',
|
||||||
|
sea_object='cc',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('nv',
|
||||||
|
'frappy_psi.sea.SeaWritable', '',
|
||||||
|
io='sea_main',
|
||||||
|
sea_object='nv',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('hepump',
|
||||||
|
'frappy_psi.sea.SeaWritable', '',
|
||||||
|
io='sea_main',
|
||||||
|
sea_object='hepump',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('hemot',
|
||||||
|
'frappy_psi.sea.SeaDrivable', '',
|
||||||
|
io='sea_main',
|
||||||
|
sea_object='hemot',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('mf',
|
||||||
|
'frappy_psi.sea.SeaDrivable', '',
|
||||||
|
io='sea_main',
|
||||||
|
sea_object='mf',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('lev',
|
||||||
|
'frappy_psi.sea.SeaReadable', '',
|
||||||
|
io='sea_main',
|
||||||
|
sea_object='lev',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('ln2fill',
|
||||||
|
'frappy_psi.sea.SeaWritable', '',
|
||||||
|
io='sea_main',
|
||||||
|
sea_object='ln2fill',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('hefill',
|
||||||
|
'frappy_psi.sea.SeaWritable', '',
|
||||||
|
io='sea_main',
|
||||||
|
sea_object='hefill',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('om_io',
|
||||||
|
'frappy_psi.phytron.PhytronIO',
|
||||||
|
'dom motor IO',
|
||||||
|
uri='ma10-ts.psi.ch:3004',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('om',
|
||||||
|
'frappy_psi.phytron.Motor',
|
||||||
|
'stick rotation, typically used for omega',
|
||||||
|
io='om_io',
|
||||||
|
sign=-1,
|
||||||
|
target_min = -180,
|
||||||
|
target_max = 360,
|
||||||
|
encoder_mode='READ',
|
||||||
|
)
|
||||||
|
|
@ -13,7 +13,7 @@ Mod('tt',
|
|||||||
'frappy_psi.sea.SeaDrivable', '',
|
'frappy_psi.sea.SeaDrivable', '',
|
||||||
io='sea_main',
|
io='sea_main',
|
||||||
sea_object='tt',
|
sea_object='tt',
|
||||||
rel_paths=['.', 'tm'],
|
rel_paths=['tm', 'set', 'dblctrl'],
|
||||||
)
|
)
|
||||||
|
|
||||||
Mod('cc',
|
Mod('cc',
|
||||||
|
107
cfg/main/ma7_piezo_cfg.py
Normal file
107
cfg/main/ma7_piezo_cfg.py
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
Node('ma7.config.sea.psi.ch',
|
||||||
|
'6.8 Tesla horizontal cryomagnet',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('sea_main',
|
||||||
|
'frappy_psi.sea.SeaClient',
|
||||||
|
'main sea connection for ma7.config',
|
||||||
|
config='ma7_piezo.config',
|
||||||
|
service='main',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('tt',
|
||||||
|
'frappy_psi.sea.SeaDrivable', '',
|
||||||
|
io='sea_main',
|
||||||
|
sea_object='tt',
|
||||||
|
rel_paths=['tm', 'set', 'dblctrl', 'voltage'],
|
||||||
|
extra_modules=['manualpower'],
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('cc',
|
||||||
|
'frappy_psi.sea.SeaReadable', '',
|
||||||
|
io='sea_main',
|
||||||
|
sea_object='cc',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('nv',
|
||||||
|
'frappy_psi.sea.SeaWritable', '',
|
||||||
|
io='sea_main',
|
||||||
|
sea_object='nv',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('hefill',
|
||||||
|
'frappy_psi.sea.SeaWritable', '',
|
||||||
|
io='sea_main',
|
||||||
|
sea_object='hefill',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('hepump',
|
||||||
|
'frappy_psi.sea.SeaWritable', '',
|
||||||
|
io='sea_main',
|
||||||
|
sea_object='hepump',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('hemot',
|
||||||
|
'frappy_psi.sea.SeaDrivable', '',
|
||||||
|
io='sea_main',
|
||||||
|
sea_object='hemot',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('ln2fill',
|
||||||
|
'frappy_psi.sea.SeaWritable', '',
|
||||||
|
io='sea_main',
|
||||||
|
sea_object='ln2fill',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('mf',
|
||||||
|
'frappy_psi.sea.SeaDrivable', '',
|
||||||
|
io='sea_main',
|
||||||
|
sea_object='mf',
|
||||||
|
value=Param(unit='T'),
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('lev',
|
||||||
|
'frappy_psi.sea.SeaReadable', '',
|
||||||
|
io='sea_main',
|
||||||
|
sea_object='lev',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('tcoil1',
|
||||||
|
'frappy_psi.sea.SeaReadable', '',
|
||||||
|
io='sea_main',
|
||||||
|
sea_object='tcoil',
|
||||||
|
rel_paths=['ta'],
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('tcoil2',
|
||||||
|
'frappy_psi.sea.SeaReadable', '',
|
||||||
|
io='sea_main',
|
||||||
|
sea_object='tcoil',
|
||||||
|
rel_paths=['tb'],
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('table',
|
||||||
|
'frappy_psi.sea.SeaReadable', '',
|
||||||
|
io='sea_main',
|
||||||
|
sea_object='table',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('stick_io',
|
||||||
|
'frappy_psi.phytron.PhytronIO',
|
||||||
|
'dom motor IO',
|
||||||
|
uri='ma7-ts.psi.ch:3007',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('stickrot',
|
||||||
|
'frappy_psi.phytron.Motor',
|
||||||
|
'stick rotation, typically not used as omega',
|
||||||
|
io='stick_io',
|
||||||
|
encoder_mode='CHECK',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('V',
|
||||||
|
'frappy_psi.sea.SeaWritable',
|
||||||
|
'voltage',
|
||||||
|
io='sea_main',
|
||||||
|
single_module='tt.manualpower',
|
||||||
|
)
|
@ -36,8 +36,8 @@ Mod('ts',
|
|||||||
'frappy_psi.parmod.Converging',
|
'frappy_psi.parmod.Converging',
|
||||||
'test for parmod',
|
'test for parmod',
|
||||||
unit='K',
|
unit='K',
|
||||||
read='th.value',
|
value_param='th.value',
|
||||||
write='th.setsamp',
|
target_param='th.setsamp',
|
||||||
meaning=['temperature', 20],
|
meaning=['temperature', 20],
|
||||||
settling_time=20,
|
settling_time=20,
|
||||||
tolerance=1,
|
tolerance=1,
|
||||||
|
128
cfg/main/ma7_thermalc_cfg.py
Normal file
128
cfg/main/ma7_thermalc_cfg.py
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
Node('ma7_thermalc.config.sea.psi.ch',
|
||||||
|
'''6.8 Tesla horizontal cryomagnet for thrermalcond''',
|
||||||
|
)
|
||||||
|
Mod('sea_main',
|
||||||
|
'frappy_psi.sea.SeaClient',
|
||||||
|
'main sea connection for ma7_thermalc.config',
|
||||||
|
config = 'ma7_thermalc.config',
|
||||||
|
service = 'main',
|
||||||
|
)
|
||||||
|
#Mod('tt',
|
||||||
|
# 'frappy_psi.sea.SeaDrivable', '',
|
||||||
|
# io='sea_main',
|
||||||
|
# sea_object='tt',
|
||||||
|
# rel_paths=['.', 'tm'],
|
||||||
|
#)
|
||||||
|
|
||||||
|
Mod('tt',
|
||||||
|
'frappy_psi.sea.SeaDrivable', '',
|
||||||
|
io='sea_main',
|
||||||
|
sea_object='tt',
|
||||||
|
rel_paths=['tm', 'set'],
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('th',
|
||||||
|
'frappy_psi.sea.SeaReadable',
|
||||||
|
'sample heater temperature',
|
||||||
|
io='sea_main',
|
||||||
|
sea_object='tt',
|
||||||
|
rel_paths=['ts', 'setsamp']
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('ts',
|
||||||
|
'frappy_psi.parmod.Converging',
|
||||||
|
'test for parmod',
|
||||||
|
unit='K',
|
||||||
|
value_param='th.value',
|
||||||
|
target_param='th.setsamp',
|
||||||
|
meaning=['temperature', 20],
|
||||||
|
settling_time=20,
|
||||||
|
tolerance=1,
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('samph',
|
||||||
|
'frappy_psi.sea.SeaWritable', '',
|
||||||
|
io='sea_main',
|
||||||
|
sea_object='tt',
|
||||||
|
rel_paths=['setsamp','power'],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
Mod('cc',
|
||||||
|
'frappy_psi.sea.SeaReadable', '',
|
||||||
|
io = 'sea_main',
|
||||||
|
sea_object = 'cc',
|
||||||
|
)
|
||||||
|
Mod('nv',
|
||||||
|
'frappy_psi.sea.SeaWritable', '',
|
||||||
|
io = 'sea_main',
|
||||||
|
sea_object = 'nv',
|
||||||
|
)
|
||||||
|
Mod('hefill',
|
||||||
|
'frappy_psi.sea.SeaWritable', '',
|
||||||
|
io = 'sea_main',
|
||||||
|
sea_object = 'hefill',
|
||||||
|
)
|
||||||
|
Mod('hepump',
|
||||||
|
'frappy_psi.sea.SeaWritable', '',
|
||||||
|
io = 'sea_main',
|
||||||
|
sea_object = 'hepump',
|
||||||
|
)
|
||||||
|
Mod('hemot',
|
||||||
|
'frappy_psi.sea.SeaDrivable', '',
|
||||||
|
io = 'sea_main',
|
||||||
|
sea_object = 'hemot',
|
||||||
|
)
|
||||||
|
Mod('nvflow',
|
||||||
|
'frappy_psi.sea.SeaReadable', '',
|
||||||
|
io = 'sea_main',
|
||||||
|
sea_object = 'nvflow',
|
||||||
|
)
|
||||||
|
Mod('ln2fill',
|
||||||
|
'frappy_psi.sea.SeaWritable', '',
|
||||||
|
io = 'sea_main',
|
||||||
|
sea_object = 'ln2fill',
|
||||||
|
)
|
||||||
|
Mod('mf',
|
||||||
|
'frappy_psi.sea.SeaDrivable', '',
|
||||||
|
io = 'sea_main',
|
||||||
|
sea_object = 'mf',
|
||||||
|
)
|
||||||
|
Mod('lev',
|
||||||
|
'frappy_psi.sea.SeaReadable', '',
|
||||||
|
io = 'sea_main',
|
||||||
|
sea_object = 'lev',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('tcoil1',
|
||||||
|
'frappy_psi.sea.SeaReadable', '',
|
||||||
|
io='sea_main',
|
||||||
|
sea_object='tcoil',
|
||||||
|
rel_paths=['ta'],
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('tcoil2',
|
||||||
|
'frappy_psi.sea.SeaReadable', '',
|
||||||
|
io='sea_main',
|
||||||
|
sea_object='tcoil',
|
||||||
|
rel_paths=['tb'],
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('table',
|
||||||
|
'frappy_psi.sea.SeaReadable', '',
|
||||||
|
io = 'sea_main',
|
||||||
|
sea_object = 'table',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('stick_io',
|
||||||
|
'frappy_psi.phytron.PhytronIO',
|
||||||
|
'dom motor IO',
|
||||||
|
uri='ma7-ts.psi.ch:3007',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('stickrot',
|
||||||
|
'frappy_psi.phytron.Motor',
|
||||||
|
'stick rotation, typically not used as omega',
|
||||||
|
io='stick_io',
|
||||||
|
encoder_mode='CHECK',
|
||||||
|
)
|
139
cfg/razorbillUC220T_cfg.py
Normal file
139
cfg/razorbillUC220T_cfg.py
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
# call $ bin/frappy-server razorbillUC220T
|
||||||
|
# in frappy directory, with python with frappy libraries installed.
|
||||||
|
|
||||||
|
Node('UC220T.psi.ch',
|
||||||
|
'A Razorbill UC220T controlled by a RP100 high voltage powersupply and a ACM1219 (AD7746) capacitance meter',
|
||||||
|
interface='tcp://3000')
|
||||||
|
|
||||||
|
Mod('io1',
|
||||||
|
'frappy_psi.RP100.RP100IO',
|
||||||
|
'communication',
|
||||||
|
uri='serial:///dev/ttyACM1?baudrate=9600+bytesize=8+parity=none+stopbits=1',
|
||||||
|
visibility=2)
|
||||||
|
Mod('Tension',
|
||||||
|
'frappy_psi.RP100.VoltageChannel',
|
||||||
|
'Voltage Channel 1',
|
||||||
|
temp='T',
|
||||||
|
io='io1',
|
||||||
|
target=Param(min=-200, max=200),
|
||||||
|
max_target=120,
|
||||||
|
min_target=-20,
|
||||||
|
slew_rate=5,
|
||||||
|
channel=1)
|
||||||
|
Mod('Compression',
|
||||||
|
'frappy_psi.RP100.VoltageChannel',
|
||||||
|
'Voltage Channel 2',
|
||||||
|
temp='T',
|
||||||
|
io='io1',
|
||||||
|
target=Param(min=-200, max=200),
|
||||||
|
max_target=120,
|
||||||
|
min_target=-20,
|
||||||
|
slew_rate=5,
|
||||||
|
channel=2)
|
||||||
|
|
||||||
|
Mod('io2',
|
||||||
|
'frappy_psi.ACM1219.ACM1219IO',
|
||||||
|
'communication',
|
||||||
|
uri='serial:///dev/ttyUSB1?baudrate=9600+bytesize=8+parity=none+stopbits=1',
|
||||||
|
visibility=2)
|
||||||
|
Mod('C1',
|
||||||
|
'frappy_psi.ACM1219.OneChannel',
|
||||||
|
'channel 1',
|
||||||
|
channel_enabled=True,
|
||||||
|
channel=1,
|
||||||
|
io='io2',
|
||||||
|
group='cap')
|
||||||
|
|
||||||
|
Mod('io3',
|
||||||
|
'frappy_psi.ACM1219.ACM1219IO',
|
||||||
|
'communication',
|
||||||
|
uri='serial:///dev/ttyUSB2?baudrate=9600+bytesize=8+parity=none+stopbits=1',
|
||||||
|
visibility=2)
|
||||||
|
Mod('C2',
|
||||||
|
'frappy_psi.ACM1219.OneChannel',
|
||||||
|
'channel 1',
|
||||||
|
channel_enabled=True,
|
||||||
|
channel=1,
|
||||||
|
io='io3',
|
||||||
|
group='cap')
|
||||||
|
|
||||||
|
# Mod('C1',
|
||||||
|
# 'frappy_psi.ACM1219.Channel',
|
||||||
|
# 'channel 1',
|
||||||
|
# group='cap')
|
||||||
|
# Mod('C2',
|
||||||
|
# 'frappy_psi.ACM1219.Channel',
|
||||||
|
# 'channel 2',
|
||||||
|
# group='cap')
|
||||||
|
# Mod('C1C2',
|
||||||
|
# 'frappy_psi.ACM1219.BothChannels',
|
||||||
|
# 'Capacitance channels 1 and 2',
|
||||||
|
# chan1='C1',
|
||||||
|
# chan2='C2',
|
||||||
|
# channels_enabled=True,
|
||||||
|
# io='io2',
|
||||||
|
# group='cap')
|
||||||
|
Mod('d',
|
||||||
|
'frappy_psi.razorbill.Displacement',
|
||||||
|
'razorbill displacement from capacitance',
|
||||||
|
cap='C1',
|
||||||
|
alpha290K=56.710,
|
||||||
|
d0=95.443,
|
||||||
|
Cp=0.01883,
|
||||||
|
d0_curve={'a':4.21,'b':-0.00157,'c':-3.38e-5,'d':5.28e-8,'e':-6.93e-11},
|
||||||
|
temp='T')
|
||||||
|
Mod('strain',
|
||||||
|
'frappy_psi.razorbill.Strain',
|
||||||
|
'Sample strain from force',
|
||||||
|
displacement='d',
|
||||||
|
L=3,
|
||||||
|
)
|
||||||
|
Mod('F',
|
||||||
|
'frappy_psi.razorbill.Force',
|
||||||
|
'razorbill force from capacitance',
|
||||||
|
cap='C2',
|
||||||
|
alpha290K=374.23,
|
||||||
|
f0=315.63,
|
||||||
|
Cp=0.0755,
|
||||||
|
f0_curve={'a':38.9,'b':-0.0147,'c':-0.000346,'d':8.96e-7,'e':-1.58e-9},
|
||||||
|
temp='T')
|
||||||
|
Mod('stress',
|
||||||
|
'frappy_psi.razorbill.Stress',
|
||||||
|
'Sample stress from force',
|
||||||
|
force='F',
|
||||||
|
area=0.1,
|
||||||
|
)
|
||||||
|
Mod('YM',
|
||||||
|
'frappy_psi.razorbill.YoungsModulus',
|
||||||
|
'Sample youngs modulus from stress and strain',
|
||||||
|
stress='stress',
|
||||||
|
strain='strain',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('T',
|
||||||
|
'frappy_psi.razorbill.Temp',
|
||||||
|
'dummy T written from client',
|
||||||
|
target=Param(value=300, min=1, max=325),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
Mod('io4',
|
||||||
|
'frappy_psi.ls372.StringIO',
|
||||||
|
'the communication device',
|
||||||
|
uri='tcp://192.168.3.3:7777',
|
||||||
|
visibility=2
|
||||||
|
)
|
||||||
|
Mod('lsswitcher',
|
||||||
|
'frappy_psi.ls372.Switcher',
|
||||||
|
'Switcher control of Lsc controller',
|
||||||
|
uri='tcp://192.168.3.3:7777',
|
||||||
|
io='io4',
|
||||||
|
)
|
||||||
|
Mod('res',
|
||||||
|
'frappy_psi.ls372.ResChannel',
|
||||||
|
'resistivity',
|
||||||
|
iexc='100uA',
|
||||||
|
range='63.2mOhm',
|
||||||
|
channel=1,
|
||||||
|
switcher='lsswitcher',
|
||||||
|
)
|
@ -1,10 +1,10 @@
|
|||||||
Node('ori7.config.sea.psi.ch',
|
Node('ccrpe.config.sea.psi.ch',
|
||||||
'''orange cryostat with 50 mm sample space for ULT''',
|
'''4 K closed cycle cryostat (PE cell)''',
|
||||||
)
|
)
|
||||||
Mod('sea_main',
|
Mod('sea_main',
|
||||||
'frappy_psi.sea.SeaClient',
|
'frappy_psi.sea.SeaClient',
|
||||||
'main sea connection for ori7.config',
|
'main sea connection for ccrpe.config',
|
||||||
config = 'ori7.config',
|
config = 'ccrpe.config',
|
||||||
service = 'main',
|
service = 'main',
|
||||||
)
|
)
|
||||||
Mod('tt',
|
Mod('tt',
|
||||||
@ -22,16 +22,6 @@ Mod('nv',
|
|||||||
io = 'sea_main',
|
io = 'sea_main',
|
||||||
sea_object = 'nv',
|
sea_object = 'nv',
|
||||||
)
|
)
|
||||||
Mod('ln2fill',
|
|
||||||
'frappy_psi.sea.SeaWritable', '',
|
|
||||||
io = 'sea_main',
|
|
||||||
sea_object = 'ln2fill',
|
|
||||||
)
|
|
||||||
Mod('hefill',
|
|
||||||
'frappy_psi.sea.SeaWritable', '',
|
|
||||||
io = 'sea_main',
|
|
||||||
sea_object = 'hefill',
|
|
||||||
)
|
|
||||||
Mod('hepump',
|
Mod('hepump',
|
||||||
'frappy_psi.sea.SeaWritable', '',
|
'frappy_psi.sea.SeaWritable', '',
|
||||||
io = 'sea_main',
|
io = 'sea_main',
|
||||||
@ -47,8 +37,13 @@ Mod('nvflow',
|
|||||||
io = 'sea_main',
|
io = 'sea_main',
|
||||||
sea_object = 'nvflow',
|
sea_object = 'nvflow',
|
||||||
)
|
)
|
||||||
Mod('table',
|
Mod('warmup',
|
||||||
|
'frappy_psi.sea.SeaDrivable', '',
|
||||||
|
io = 'sea_main',
|
||||||
|
sea_object = 'warmup',
|
||||||
|
)
|
||||||
|
Mod('p',
|
||||||
'frappy_psi.sea.SeaReadable', '',
|
'frappy_psi.sea.SeaReadable', '',
|
||||||
io = 'sea_main',
|
io = 'sea_main',
|
||||||
sea_object = 'table',
|
sea_object = 'p',
|
||||||
)
|
)
|
@ -72,8 +72,7 @@
|
|||||||
{"path": "min_cpl", "type": "float", "readonly": false, "cmd": "pauto min_cpl"},
|
{"path": "min_cpl", "type": "float", "readonly": false, "cmd": "pauto min_cpl"},
|
||||||
{"path": "max_cpl", "type": "float", "readonly": false, "cmd": "pauto max_cpl"},
|
{"path": "max_cpl", "type": "float", "readonly": false, "cmd": "pauto max_cpl"},
|
||||||
{"path": "fact_cpl", "type": "float", "readonly": false, "cmd": "pauto fact_cpl"},
|
{"path": "fact_cpl", "type": "float", "readonly": false, "cmd": "pauto fact_cpl"},
|
||||||
{"path": "max_ramp", "type": "float", "readonly": false, "cmd": "pauto max_ramp"},
|
{"path": "max_ramp", "type": "float", "readonly": false, "cmd": "pauto max_ramp"}]},
|
||||||
{"path": "target", "type": "float"}]},
|
|
||||||
|
|
||||||
"tc": {"base": "/tc", "params": [
|
"tc": {"base": "/tc", "params": [
|
||||||
{"path": "", "type": "float", "readonly": false, "cmd": "run tc", "description": "tc", "kids": 15},
|
{"path": "", "type": "float", "readonly": false, "cmd": "run tc", "description": "tc", "kids": 15},
|
||||||
|
395
cfg/sea/ma10heat.config.json
Normal file
395
cfg/sea/ma10heat.config.json
Normal file
@ -0,0 +1,395 @@
|
|||||||
|
{"tt": {"base": "/tt", "params": [
|
||||||
|
{"path": "", "type": "float", "readonly": false, "cmd": "run tt", "description": "tt", "kids": 18},
|
||||||
|
{"path": "send", "type": "text", "readonly": false, "cmd": "tt send", "visibility": 3},
|
||||||
|
{"path": "status", "type": "text", "visibility": 3},
|
||||||
|
{"path": "is_running", "type": "int", "readonly": false, "cmd": "tt is_running", "visibility": 3},
|
||||||
|
{"path": "mainloop", "type": "text", "readonly": false, "cmd": "tt mainloop", "visibility": 3},
|
||||||
|
{"path": "target", "type": "float"},
|
||||||
|
{"path": "running", "type": "int"},
|
||||||
|
{"path": "tolerance", "type": "float", "readonly": false, "cmd": "tt tolerance"},
|
||||||
|
{"path": "maxwait", "type": "float", "readonly": false, "cmd": "tt maxwait"},
|
||||||
|
{"path": "settle", "type": "float", "readonly": false, "cmd": "tt settle"},
|
||||||
|
{"path": "log", "type": "text", "readonly": false, "cmd": "tt log", "visibility": 3, "kids": 4},
|
||||||
|
{"path": "log/mean", "type": "float", "visibility": 3},
|
||||||
|
{"path": "log/m2", "type": "float", "visibility": 3},
|
||||||
|
{"path": "log/stddev", "type": "float", "visibility": 3},
|
||||||
|
{"path": "log/n", "type": "float", "visibility": 3},
|
||||||
|
{"path": "dblctrl", "type": "bool", "readonly": false, "cmd": "tt dblctrl", "kids": 9},
|
||||||
|
{"path": "dblctrl/tshift", "type": "float", "readonly": false, "cmd": "tt dblctrl/tshift"},
|
||||||
|
{"path": "dblctrl/mode", "type": "enum", "enum": {"disabled": -1, "inactive": 0, "stable": 1, "up": 2, "down": 3}, "readonly": false, "cmd": "tt dblctrl/mode"},
|
||||||
|
{"path": "dblctrl/shift_up", "type": "float"},
|
||||||
|
{"path": "dblctrl/shift_lo", "type": "float"},
|
||||||
|
{"path": "dblctrl/t_min", "type": "float"},
|
||||||
|
{"path": "dblctrl/t_max", "type": "float"},
|
||||||
|
{"path": "dblctrl/int2", "type": "float", "readonly": false, "cmd": "tt dblctrl/int2"},
|
||||||
|
{"path": "dblctrl/prop_up", "type": "float", "readonly": false, "cmd": "tt dblctrl/prop_up"},
|
||||||
|
{"path": "dblctrl/prop_lo", "type": "float", "readonly": false, "cmd": "tt dblctrl/prop_lo"},
|
||||||
|
{"path": "tm", "type": "float", "kids": 4},
|
||||||
|
{"path": "tm/curve", "type": "text", "readonly": false, "cmd": "tt tm/curve", "kids": 1},
|
||||||
|
{"path": "tm/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt tm/curve/points", "visibility": 3},
|
||||||
|
{"path": "tm/alarm", "type": "float", "readonly": false, "cmd": "tt tm/alarm"},
|
||||||
|
{"path": "tm/stddev", "type": "float"},
|
||||||
|
{"path": "tm/raw", "type": "float"},
|
||||||
|
{"path": "ts", "type": "float", "kids": 4},
|
||||||
|
{"path": "ts/curve", "type": "text", "readonly": false, "cmd": "tt ts/curve", "kids": 1},
|
||||||
|
{"path": "ts/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt ts/curve/points", "visibility": 3},
|
||||||
|
{"path": "ts/alarm", "type": "float", "readonly": false, "cmd": "tt ts/alarm"},
|
||||||
|
{"path": "ts/stddev", "type": "float"},
|
||||||
|
{"path": "ts/raw", "type": "float"},
|
||||||
|
{"path": "ts_2", "type": "float", "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"},
|
||||||
|
{"path": "ts_2/stddev", "type": "float"},
|
||||||
|
{"path": "ts_2/raw", "type": "float"},
|
||||||
|
{"path": "set", "type": "float", "readonly": false, "cmd": "tt set", "kids": 18},
|
||||||
|
{"path": "set/mode", "type": "enum", "enum": {"disabled": -1, "off": 0, "controlling": 1, "manual": 2}, "readonly": false, "cmd": "tt set/mode"},
|
||||||
|
{"path": "set/reg", "type": "float"},
|
||||||
|
{"path": "set/ramp", "type": "float", "readonly": false, "cmd": "tt set/ramp", "description": "maximum ramp in K/min (0: ramp off)"},
|
||||||
|
{"path": "set/wramp", "type": "float", "readonly": false, "cmd": "tt set/wramp"},
|
||||||
|
{"path": "set/smooth", "type": "float", "readonly": false, "cmd": "tt set/smooth", "description": "smooth time (minutes)"},
|
||||||
|
{"path": "set/channel", "type": "text", "readonly": false, "cmd": "tt set/channel"},
|
||||||
|
{"path": "set/limit", "type": "float", "readonly": false, "cmd": "tt set/limit"},
|
||||||
|
{"path": "set/resist", "type": "float", "readonly": false, "cmd": "tt set/resist"},
|
||||||
|
{"path": "set/maxheater", "type": "text", "readonly": false, "cmd": "tt set/maxheater", "description": "maximum heater limit, units should be given without space: W, mW, A, mA"},
|
||||||
|
{"path": "set/linearpower", "type": "float", "readonly": false, "cmd": "tt set/linearpower", "description": "when not 0, it is the maximum effective power, and the power is linear to the heater output"},
|
||||||
|
{"path": "set/maxpowerlim", "type": "float", "description": "the maximum power limit (before any booster or converter)"},
|
||||||
|
{"path": "set/maxpower", "type": "float", "readonly": false, "cmd": "tt set/maxpower", "description": "maximum power [W]"},
|
||||||
|
{"path": "set/maxcurrent", "type": "float", "description": "the maximum current before any booster or converter"},
|
||||||
|
{"path": "set/manualpower", "type": "float", "readonly": false, "cmd": "tt set/manualpower"},
|
||||||
|
{"path": "set/power", "type": "float"},
|
||||||
|
{"path": "set/prop", "type": "float", "readonly": false, "cmd": "tt set/prop", "description": "bigger means more gain"},
|
||||||
|
{"path": "set/integ", "type": "float", "readonly": false, "cmd": "tt set/integ", "description": "bigger means faster"},
|
||||||
|
{"path": "set/deriv", "type": "float", "readonly": false, "cmd": "tt set/deriv"},
|
||||||
|
{"path": "setvti", "type": "float", "readonly": false, "cmd": "tt setvti", "kids": 18},
|
||||||
|
{"path": "setvti/mode", "type": "enum", "enum": {"disabled": -1, "off": 0, "controlling": 1, "manual": 2}, "readonly": false, "cmd": "tt setvti/mode"},
|
||||||
|
{"path": "setvti/reg", "type": "float"},
|
||||||
|
{"path": "setvti/ramp", "type": "float", "readonly": false, "cmd": "tt setvti/ramp", "description": "maximum ramp in K/min (0: ramp off)"},
|
||||||
|
{"path": "setvti/wramp", "type": "float", "readonly": false, "cmd": "tt setvti/wramp"},
|
||||||
|
{"path": "setvti/smooth", "type": "float", "readonly": false, "cmd": "tt setvti/smooth", "description": "smooth time (minutes)"},
|
||||||
|
{"path": "setvti/channel", "type": "text", "readonly": false, "cmd": "tt setvti/channel"},
|
||||||
|
{"path": "setvti/limit", "type": "float", "readonly": false, "cmd": "tt setvti/limit"},
|
||||||
|
{"path": "setvti/resist", "type": "float", "readonly": false, "cmd": "tt setvti/resist"},
|
||||||
|
{"path": "setvti/maxheater", "type": "text", "readonly": false, "cmd": "tt setvti/maxheater", "description": "maximum heater limit, units should be given without space: W, mW, A, mA"},
|
||||||
|
{"path": "setvti/linearpower", "type": "float", "readonly": false, "cmd": "tt setvti/linearpower", "description": "when not 0, it is the maximum effective power, and the power is linear to the heater output"},
|
||||||
|
{"path": "setvti/maxpowerlim", "type": "float", "description": "the maximum power limit (before any booster or converter)"},
|
||||||
|
{"path": "setvti/maxpower", "type": "float", "readonly": false, "cmd": "tt setvti/maxpower", "description": "maximum power [W]"},
|
||||||
|
{"path": "setvti/maxcurrent", "type": "float", "description": "the maximum current before any booster or converter"},
|
||||||
|
{"path": "setvti/manualpower", "type": "float", "readonly": false, "cmd": "tt setvti/manualpower"},
|
||||||
|
{"path": "setvti/power", "type": "float"},
|
||||||
|
{"path": "setvti/prop", "type": "float", "readonly": false, "cmd": "tt setvti/prop", "description": "bigger means more gain"},
|
||||||
|
{"path": "setvti/integ", "type": "float", "readonly": false, "cmd": "tt setvti/integ", "description": "bigger means faster"},
|
||||||
|
{"path": "setvti/deriv", "type": "float", "readonly": false, "cmd": "tt setvti/deriv"},
|
||||||
|
{"path": "display", "type": "text", "readonly": false, "cmd": "tt display"},
|
||||||
|
{"path": "remote", "type": "bool"}]},
|
||||||
|
|
||||||
|
"cc": {"base": "/cc", "params": [
|
||||||
|
{"path": "", "type": "bool", "kids": 96},
|
||||||
|
{"path": "send", "type": "text", "readonly": false, "cmd": "cc send", "visibility": 3},
|
||||||
|
{"path": "status", "type": "text", "visibility": 3},
|
||||||
|
{"path": "autodevice", "type": "bool", "readonly": false, "cmd": "cc autodevice"},
|
||||||
|
{"path": "fav", "type": "bool", "readonly": false, "cmd": "cc fav"},
|
||||||
|
{"path": "f", "type": "float"},
|
||||||
|
{"path": "fs", "type": "enum", "enum": {"ok": 0, "no_sens": 1}, "readonly": false, "cmd": "cc fs"},
|
||||||
|
{"path": "mav", "type": "bool", "readonly": false, "cmd": "cc mav"},
|
||||||
|
{"path": "fm", "type": "enum", "enum": {"idle": 0, "opening": 1, "closing": 2, "opened": 3, "closed": 4, "no_motor": 5}},
|
||||||
|
{"path": "fa", "type": "enum", "enum": {"fixed": 0, "controlled": 1, "automatic": 2, "offline": 3}, "readonly": false, "cmd": "cc fa"},
|
||||||
|
{"path": "mp", "type": "float", "readonly": false, "cmd": "cc mp"},
|
||||||
|
{"path": "msp", "type": "float"},
|
||||||
|
{"path": "mmp", "type": "float"},
|
||||||
|
{"path": "mc", "type": "float", "readonly": false, "cmd": "cc mc"},
|
||||||
|
{"path": "mfc", "type": "float", "readonly": false, "cmd": "cc mfc"},
|
||||||
|
{"path": "moc", "type": "float", "readonly": false, "cmd": "cc moc"},
|
||||||
|
{"path": "mtc", "type": "float", "readonly": false, "cmd": "cc mtc"},
|
||||||
|
{"path": "mtl", "type": "float"},
|
||||||
|
{"path": "mft", "type": "float", "readonly": false, "cmd": "cc mft"},
|
||||||
|
{"path": "mt", "type": "float"},
|
||||||
|
{"path": "mo", "type": "float"},
|
||||||
|
{"path": "mcr", "type": "float"},
|
||||||
|
{"path": "mot", "type": "float"},
|
||||||
|
{"path": "mw", "type": "float", "readonly": false, "cmd": "cc mw", "description": "correction pulse after automatic open"},
|
||||||
|
{"path": "hav", "type": "bool", "readonly": false, "cmd": "cc hav"},
|
||||||
|
{"path": "h", "type": "float"},
|
||||||
|
{"path": "hr", "type": "float"},
|
||||||
|
{"path": "hc", "type": "float"},
|
||||||
|
{"path": "hu", "type": "float"},
|
||||||
|
{"path": "hh", "type": "float", "readonly": false, "cmd": "cc hh"},
|
||||||
|
{"path": "hl", "type": "float", "readonly": false, "cmd": "cc hl"},
|
||||||
|
{"path": "htf", "type": "float", "readonly": false, "cmd": "cc htf", "description": "meas. period in fast mode"},
|
||||||
|
{"path": "hts", "type": "float", "readonly": false, "cmd": "cc hts", "description": "meas. period in slow mode"},
|
||||||
|
{"path": "hd", "type": "float", "readonly": false, "cmd": "cc hd"},
|
||||||
|
{"path": "hwr", "type": "float", "readonly": false, "cmd": "cc hwr"},
|
||||||
|
{"path": "hem", "type": "float", "readonly": false, "cmd": "cc hem", "description": "sensor length in mm from top to empty pos."},
|
||||||
|
{"path": "hfu", "type": "float", "readonly": false, "cmd": "cc hfu", "description": "sensor length in mm from top to full pos."},
|
||||||
|
{"path": "hcd", "type": "enum", "enum": {"stop": 0, "fill": 1, "off": 2, "auto": 3, "manual": 7}, "readonly": false, "cmd": "cc hcd"},
|
||||||
|
{"path": "hv", "type": "enum", "enum": {"fill_valve_off": 0, "filling": 1, "no_fill_valve": 2, "timeout": 3, "timeout1": 4}},
|
||||||
|
{"path": "hsf", "type": "enum", "enum": {"sens_ok": 0, "sens_warm": 1, "no_sens": 2, "timeout": 3, "not_yet_read": 4, "disabled": 5}},
|
||||||
|
{"path": "ha", "type": "bool", "readonly": false, "cmd": "cc ha"},
|
||||||
|
{"path": "hm", "type": "bool"},
|
||||||
|
{"path": "hf", "type": "enum", "enum": {"slow": 0, "fast": 1}, "readonly": false, "cmd": "cc hf"},
|
||||||
|
{"path": "hbe", "type": "bool", "readonly": false, "cmd": "cc hbe"},
|
||||||
|
{"path": "hmf", "type": "float"},
|
||||||
|
{"path": "hms", "type": "float"},
|
||||||
|
{"path": "hit", "type": "float", "readonly": false, "cmd": "cc hit"},
|
||||||
|
{"path": "hft", "type": "int", "readonly": false, "cmd": "cc hft"},
|
||||||
|
{"path": "hea", "type": "enum", "enum": {"0": 0, "1": 1, "6": 6}, "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": "nu", "type": "float"},
|
||||||
|
{"path": "nl", "type": "float"},
|
||||||
|
{"path": "nth", "type": "float", "readonly": false, "cmd": "cc nth"},
|
||||||
|
{"path": "ntc", "type": "float", "readonly": false, "cmd": "cc ntc"},
|
||||||
|
{"path": "ntm", "type": "float", "readonly": false, "cmd": "cc ntm"},
|
||||||
|
{"path": "ns", "type": "enum", "enum": {"sens_ok": 0, "no_sens": 1, "short_circuit": 2, "upside_down": 3, "sens_warm": 4, "empty": 5}},
|
||||||
|
{"path": "na", "type": "bool", "readonly": false, "cmd": "cc na"},
|
||||||
|
{"path": "nv", "type": "enum", "enum": {"fill_valve_off": 0, "filling": 1, "no_fill_valve": 2, "timeout": 3, "timeout1": 4, "boost": 5}},
|
||||||
|
{"path": "nc", "type": "enum", "enum": {"stop": 0, "fill": 1, "off": 2, "auto": 3}, "readonly": false, "cmd": "cc nc"},
|
||||||
|
{"path": "nfb", "type": "float"},
|
||||||
|
{"path": "cda", "type": "float"},
|
||||||
|
{"path": "cdb", "type": "float"},
|
||||||
|
{"path": "cba", "type": "float"},
|
||||||
|
{"path": "cbb", "type": "float"},
|
||||||
|
{"path": "cvs", "type": "int"},
|
||||||
|
{"path": "csp", "type": "int"},
|
||||||
|
{"path": "cdv", "type": "text", "readonly": false, "cmd": "cc cdv"},
|
||||||
|
{"path": "cic", "type": "text", "readonly": false, "cmd": "cc cic"},
|
||||||
|
{"path": "cin", "type": "text"},
|
||||||
|
{"path": "cds", "type": "enum", "enum": {"local": 0, "remote": 1, "loading": 2, "by_code": 3, "by_touch": 4}, "readonly": false, "cmd": "cc cds"},
|
||||||
|
{"path": "timing", "type": "bool", "readonly": false, "cmd": "cc timing"},
|
||||||
|
{"path": "tc", "type": "float", "visibility": 3},
|
||||||
|
{"path": "tn", "type": "float", "visibility": 3},
|
||||||
|
{"path": "th", "type": "float", "visibility": 3},
|
||||||
|
{"path": "tf", "type": "float", "visibility": 3},
|
||||||
|
{"path": "tm", "type": "float", "visibility": 3},
|
||||||
|
{"path": "tv", "type": "float", "visibility": 3},
|
||||||
|
{"path": "tq", "type": "float", "visibility": 3},
|
||||||
|
{"path": "bdl", "type": "float", "readonly": false, "cmd": "cc bdl"}]},
|
||||||
|
|
||||||
|
"nv": {"base": "/nv", "params": [
|
||||||
|
{"path": "", "type": "enum", "enum": {"fixed": 0, "controlled": 1, "automatic": 2, "close": 3, "open": 4}, "readonly": false, "cmd": "nv", "kids": 11},
|
||||||
|
{"path": "send", "type": "text", "readonly": false, "cmd": "nv send", "visibility": 3},
|
||||||
|
{"path": "status", "type": "text", "visibility": 3},
|
||||||
|
{"path": "motstat", "type": "enum", "enum": {"idle": 0, "opening": 1, "closing": 2, "opened": 3, "closed": 4, "no_motor": 5}},
|
||||||
|
{"path": "flow", "type": "float"},
|
||||||
|
{"path": "set", "type": "float", "readonly": false, "cmd": "nv set"},
|
||||||
|
{"path": "flowmax", "type": "float", "readonly": false, "cmd": "nv flowmax"},
|
||||||
|
{"path": "flowp", "type": "float"},
|
||||||
|
{"path": "span", "type": "float"},
|
||||||
|
{"path": "ctrl", "type": "none", "kids": 13},
|
||||||
|
{"path": "ctrl/regtext", "type": "text"},
|
||||||
|
{"path": "ctrl/prop_o", "type": "float", "readonly": false, "cmd": "nv ctrl/prop_o", "description": "prop [sec/mbar] when opening. above 4 mbar a 10 times lower value is used"},
|
||||||
|
{"path": "ctrl/prop_c", "type": "float", "readonly": false, "cmd": "nv ctrl/prop_c", "description": "prop [sec/mbar] when closing. above 4 mbar a 10 times lower value is used"},
|
||||||
|
{"path": "ctrl/deriv_o", "type": "float", "readonly": false, "cmd": "nv ctrl/deriv_o", "description": "convergence target time [sec] when opening"},
|
||||||
|
{"path": "ctrl/deriv_c", "type": "float", "readonly": false, "cmd": "nv ctrl/deriv_c", "description": "convergence target time [sec] when closing"},
|
||||||
|
{"path": "ctrl/minpulse_o", "type": "float", "readonly": false, "cmd": "nv ctrl/minpulse_o", "description": "minimum close pulse [sec]"},
|
||||||
|
{"path": "ctrl/minpulse_c", "type": "float", "readonly": false, "cmd": "nv ctrl/minpulse_c", "description": "standard close pulse [sec]"},
|
||||||
|
{"path": "ctrl/hystpulse_o", "type": "float", "readonly": false, "cmd": "nv ctrl/hystpulse_o", "description": "motor pulse to overcome hysteresis when opening"},
|
||||||
|
{"path": "ctrl/hystpulse_c", "type": "float", "readonly": false, "cmd": "nv ctrl/hystpulse_c", "description": "motor pulse to overcome hysteresis when closing"},
|
||||||
|
{"path": "ctrl/tol", "type": "float", "readonly": false, "cmd": "nv ctrl/tol", "description": "valid below 3 mbar"},
|
||||||
|
{"path": "ctrl/tolhigh", "type": "float", "readonly": false, "cmd": "nv ctrl/tolhigh", "description": "valid above 4 mbar"},
|
||||||
|
{"path": "ctrl/openpulse", "type": "float", "readonly": false, "cmd": "nv ctrl/openpulse", "description": "time to open from completely closed to a significant opening"},
|
||||||
|
{"path": "ctrl/adjust_minpulse", "type": "bool", "readonly": false, "cmd": "nv ctrl/adjust_minpulse", "description": "adjust minpulse automatically"},
|
||||||
|
{"path": "autoflow", "type": "none", "kids": 24},
|
||||||
|
{"path": "autoflow/suspended", "type": "bool", "readonly": false, "cmd": "nv autoflow/suspended"},
|
||||||
|
{"path": "autoflow/prop", "type": "float", "readonly": false, "cmd": "nv autoflow/prop"},
|
||||||
|
{"path": "autoflow/flowstd", "type": "float", "readonly": false, "cmd": "nv autoflow/flowstd"},
|
||||||
|
{"path": "autoflow/flowlim", "type": "float", "readonly": false, "cmd": "nv autoflow/flowlim"},
|
||||||
|
{"path": "autoflow/smooth", "type": "float", "readonly": false, "cmd": "nv autoflow/smooth"},
|
||||||
|
{"path": "autoflow/difSize", "type": "float", "readonly": false, "cmd": "nv autoflow/difSize"},
|
||||||
|
{"path": "autoflow/difRange", "type": "float", "readonly": false, "cmd": "nv autoflow/difRange"},
|
||||||
|
{"path": "autoflow/flowSize", "type": "float", "readonly": false, "cmd": "nv autoflow/flowSize"},
|
||||||
|
{"path": "autoflow/convTime", "type": "float", "readonly": false, "cmd": "nv autoflow/convTime"},
|
||||||
|
{"path": "autoflow/Tmin", "type": "float", "readonly": false, "cmd": "nv autoflow/Tmin"},
|
||||||
|
{"path": "autoflow/script", "type": "text", "readonly": false, "cmd": "nv autoflow/script"},
|
||||||
|
{"path": "autoflow/getTemp", "type": "text", "readonly": false, "cmd": "nv autoflow/getTemp"},
|
||||||
|
{"path": "autoflow/getTset", "type": "text", "readonly": false, "cmd": "nv autoflow/getTset"},
|
||||||
|
{"path": "autoflow/getFlow", "type": "text", "readonly": false, "cmd": "nv autoflow/getFlow"},
|
||||||
|
{"path": "autoflow/difBuf", "type": "text"},
|
||||||
|
{"path": "autoflow/flowBuf", "type": "text"},
|
||||||
|
{"path": "autoflow/flowset", "type": "float"},
|
||||||
|
{"path": "autoflow/flowmin", "type": "float"},
|
||||||
|
{"path": "autoflow/flowmax", "type": "float"},
|
||||||
|
{"path": "autoflow/difmin", "type": "float"},
|
||||||
|
{"path": "autoflow/difmax", "type": "float"},
|
||||||
|
{"path": "autoflow/setmin", "type": "float"},
|
||||||
|
{"path": "autoflow/setmax", "type": "float"},
|
||||||
|
{"path": "autoflow/flowtarget", "type": "float"},
|
||||||
|
{"path": "calib", "type": "none", "kids": 2},
|
||||||
|
{"path": "calib/ln_per_min_per_mbar", "type": "float", "readonly": false, "cmd": "nv calib/ln_per_min_per_mbar"},
|
||||||
|
{"path": "calib/mbar_offset", "type": "float", "readonly": false, "cmd": "nv calib/mbar_offset"}]},
|
||||||
|
|
||||||
|
"hepump": {"base": "/hepump", "params": [
|
||||||
|
{"path": "", "type": "enum", "enum": {"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"},
|
||||||
|
{"path": "auto", "type": "bool", "readonly": false, "cmd": "hepump auto"},
|
||||||
|
{"path": "valve", "type": "enum", "enum": {"closed": 0, "closing": 1, "opening": 2, "opened": 3, "undefined": 4}, "readonly": false, "cmd": "hepump valve"},
|
||||||
|
{"path": "eco_t_lim", "type": "float", "readonly": false, "cmd": "hepump eco_t_lim", "description": "switch off eco mode when T_set < eco_t_lim and T < eco_t_lim * 2"},
|
||||||
|
{"path": "calib", "type": "float", "readonly": false, "cmd": "hepump calib", "visibility": 3},
|
||||||
|
{"path": "health", "type": "float"}]},
|
||||||
|
|
||||||
|
"hemot": {"base": "/hepump/hemot", "params": [
|
||||||
|
{"path": "", "type": "float", "readonly": false, "cmd": "run hemot", "kids": 30},
|
||||||
|
{"path": "send", "type": "text", "readonly": false, "cmd": "hemot send", "visibility": 3},
|
||||||
|
{"path": "status", "type": "text", "visibility": 3},
|
||||||
|
{"path": "is_running", "type": "int", "readonly": false, "cmd": "hemot is_running", "visibility": 3},
|
||||||
|
{"path": "pos", "type": "float"},
|
||||||
|
{"path": "encoder", "type": "float"},
|
||||||
|
{"path": "zero", "type": "float", "readonly": false, "cmd": "hemot zero"},
|
||||||
|
{"path": "lowerlimit", "type": "float", "readonly": false, "cmd": "hemot lowerlimit"},
|
||||||
|
{"path": "upperlimit", "type": "float", "readonly": false, "cmd": "hemot upperlimit"},
|
||||||
|
{"path": "disablelimits", "type": "bool", "readonly": false, "cmd": "hemot disablelimits"},
|
||||||
|
{"path": "verbose", "type": "bool", "readonly": false, "cmd": "hemot verbose"},
|
||||||
|
{"path": "target", "type": "float"},
|
||||||
|
{"path": "runstate", "type": "enum", "enum": {"idle": 0, "running": 1, "finished": 2, "error": 3}},
|
||||||
|
{"path": "precision", "type": "float", "readonly": false, "cmd": "hemot precision"},
|
||||||
|
{"path": "maxencdif", "type": "float", "readonly": false, "cmd": "hemot maxencdif"},
|
||||||
|
{"path": "id", "type": "float", "readonly": false, "cmd": "hemot id"},
|
||||||
|
{"path": "pump_number", "type": "float", "readonly": false, "cmd": "hemot pump_number"},
|
||||||
|
{"path": "init", "type": "float", "readonly": false, "cmd": "hemot init"},
|
||||||
|
{"path": "maxspeed", "type": "float", "readonly": false, "cmd": "hemot maxspeed"},
|
||||||
|
{"path": "acceleration", "type": "float", "readonly": false, "cmd": "hemot acceleration"},
|
||||||
|
{"path": "maxcurrent", "type": "float", "readonly": false, "cmd": "hemot maxcurrent"},
|
||||||
|
{"path": "standbycurrent", "type": "float", "readonly": false, "cmd": "hemot standbycurrent"},
|
||||||
|
{"path": "freewheeling", "type": "bool", "readonly": false, "cmd": "hemot freewheeling"},
|
||||||
|
{"path": "output0", "type": "bool", "readonly": false, "cmd": "hemot output0"},
|
||||||
|
{"path": "output1", "type": "bool", "readonly": false, "cmd": "hemot output1"},
|
||||||
|
{"path": "input3", "type": "bool"},
|
||||||
|
{"path": "pullup", "type": "float", "readonly": false, "cmd": "hemot pullup"},
|
||||||
|
{"path": "nopumpfeedback", "type": "bool", "readonly": false, "cmd": "hemot nopumpfeedback"},
|
||||||
|
{"path": "eeprom", "type": "enum", "enum": {"ok": 0, "dirty": 1, "save": 2, "load": 3}, "readonly": false, "cmd": "hemot eeprom"},
|
||||||
|
{"path": "customadr", "type": "text", "readonly": false, "cmd": "hemot customadr"},
|
||||||
|
{"path": "custompar", "type": "float", "readonly": false, "cmd": "hemot custompar"}]},
|
||||||
|
|
||||||
|
"nvflow": {"base": "/nvflow", "params": [
|
||||||
|
{"path": "", "type": "float", "kids": 7},
|
||||||
|
{"path": "send", "type": "text", "readonly": false, "cmd": "nvflow send", "visibility": 3},
|
||||||
|
{"path": "status", "type": "text", "visibility": 3},
|
||||||
|
{"path": "stddev", "type": "float"},
|
||||||
|
{"path": "nsamples", "type": "int", "readonly": false, "cmd": "nvflow nsamples"},
|
||||||
|
{"path": "offset", "type": "float", "readonly": false, "cmd": "nvflow offset"},
|
||||||
|
{"path": "scale", "type": "float", "readonly": false, "cmd": "nvflow scale"},
|
||||||
|
{"path": "save", "type": "bool", "readonly": false, "cmd": "nvflow save", "description": "unchecked: current calib is not saved. set checked: save calib"}]},
|
||||||
|
|
||||||
|
"mf": {"base": "/mf", "params": [
|
||||||
|
{"path": "", "type": "float", "readonly": false, "cmd": "run mf", "kids": 8},
|
||||||
|
{"path": "send", "type": "text", "readonly": false, "cmd": "mf send", "visibility": 3},
|
||||||
|
{"path": "status", "type": "text", "visibility": 3},
|
||||||
|
{"path": "is_running", "type": "int", "readonly": false, "cmd": "mf is_running", "visibility": 3},
|
||||||
|
{"path": "statustext", "type": "text"},
|
||||||
|
{"path": "ramp", "type": "float", "readonly": false, "cmd": "mf ramp"},
|
||||||
|
{"path": "persistent_mode", "type": "enum", "enum": {"forever_off": -1, "off": 0, "on": 1}, "readonly": false, "cmd": "mf persistent_mode", "description": "hidden mode -1: completely off"},
|
||||||
|
{"path": "gen", "type": "none", "kids": 13},
|
||||||
|
{"path": "gen/persistent_delay", "type": "float", "readonly": false, "cmd": "mf gen/persistent_delay", "description": "timeout for going automatically into persistent mode"},
|
||||||
|
{"path": "gen/tolerance", "type": "float", "readonly": false, "cmd": "mf gen/tolerance"},
|
||||||
|
{"path": "gen/wait_switch_on", "type": "float", "readonly": false, "cmd": "mf gen/wait_switch_on"},
|
||||||
|
{"path": "gen/wait_switch_off", "type": "float", "readonly": false, "cmd": "mf gen/wait_switch_off"},
|
||||||
|
{"path": "gen/wait_stable_leads", "type": "float", "readonly": false, "cmd": "mf gen/wait_stable_leads"},
|
||||||
|
{"path": "gen/wait_stable_field", "type": "float", "readonly": false, "cmd": "mf gen/wait_stable_field"},
|
||||||
|
{"path": "gen/expectend", "type": "text"},
|
||||||
|
{"path": "gen/trained_pos", "type": "float", "readonly": false, "cmd": "mf gen/trained_pos"},
|
||||||
|
{"path": "gen/trained_neg", "type": "float", "readonly": false, "cmd": "mf gen/trained_neg"},
|
||||||
|
{"path": "gen/profile", "type": "text", "readonly": false, "cmd": "mf gen/profile", "description": "syntax: <field1>:<ramp1> <field2>:<ramp2> ... (<ramp2> is the ramp limit from <field1> to <field2>)"},
|
||||||
|
{"path": "gen/profile_training", "type": "text", "readonly": false, "cmd": "mf gen/profile_training", "description": "syntax: <field1>:<ramp1> <field2>:<ramp2> ... (<ramp2> is the ramp limit from <field1> to <field2>)"},
|
||||||
|
{"path": "gen/limit", "type": "float", "readonly": false, "cmd": "mf gen/limit"},
|
||||||
|
{"path": "gen/bipolar", "type": "bool", "readonly": false, "cmd": "mf gen/bipolar"},
|
||||||
|
{"path": "ips", "type": "float", "kids": 17},
|
||||||
|
{"path": "ips/ramp_slow", "type": "float", "readonly": false, "cmd": "mf ips/ramp_slow", "description": "ramp rate for coils Tesla/min."},
|
||||||
|
{"path": "ips/ramp_fast", "type": "float", "description": "ramp rate for leads Tesla/min."},
|
||||||
|
{"path": "ips/set_field", "type": "float", "readonly": false, "cmd": "mf ips/set_field"},
|
||||||
|
{"path": "ips/heater", "type": "bool", "readonly": false, "cmd": "mf ips/heater"},
|
||||||
|
{"path": "ips/ramp_state", "type": "enum", "enum": {"hold": 0, "to_zero": 1, "to_set": 2, "clamp": 3}, "readonly": false, "cmd": "mf ips/ramp_state"},
|
||||||
|
{"path": "ips/leads_set", "type": "float", "description": "calculated current in the leads, converted to Tesla"},
|
||||||
|
{"path": "ips/show_internals", "type": "bool", "readonly": false, "cmd": "mf ips/show_internals"},
|
||||||
|
{"path": "ips/leads_meas", "type": "float", "description": "measured current in the leads, converted to Tesla"},
|
||||||
|
{"path": "ips/slave1", "type": "float"},
|
||||||
|
{"path": "ips/slave2", "type": "float"},
|
||||||
|
{"path": "ips/slave3", "type": "float"},
|
||||||
|
{"path": "ips/volt", "type": "float"},
|
||||||
|
{"path": "ips/symode", "type": "text"},
|
||||||
|
{"path": "ips/engineering_password", "type": "text", "readonly": false, "cmd": "mf ips/engineering_password"},
|
||||||
|
{"path": "ips/atob", "type": "float", "readonly": false, "cmd": "mf ips/atob", "description": "Amp/Tesla"},
|
||||||
|
{"path": "ips/inductance", "type": "float", "readonly": false, "cmd": "mf ips/inductance", "description": "henries"},
|
||||||
|
{"path": "ips/switch_heater_current", "type": "float", "readonly": false, "cmd": "mf ips/switch_heater_current", "description": "switch heater current [mA]"}]},
|
||||||
|
|
||||||
|
"lev": {"base": "/lev", "params": [
|
||||||
|
{"path": "", "type": "float", "kids": 4},
|
||||||
|
{"path": "send", "type": "text", "readonly": false, "cmd": "lev send", "visibility": 3},
|
||||||
|
{"path": "status", "type": "text", "visibility": 3},
|
||||||
|
{"path": "mode", "type": "enum", "enum": {"slow": 0, "fast": 1}, "readonly": false, "cmd": "lev mode"},
|
||||||
|
{"path": "n2", "type": "float"}]},
|
||||||
|
|
||||||
|
"ln2fill": {"base": "/ln2fill", "params": [
|
||||||
|
{"path": "", "type": "enum", "enum": {"watching": 0, "filling": 1, "inactive": 2, "manualfill": 3}, "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": "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"},
|
||||||
|
{"path": "minfillminutes", "type": "float", "readonly": false, "cmd": "ln2fill minfillminutes"},
|
||||||
|
{"path": "maxfillminutes", "type": "float", "readonly": false, "cmd": "ln2fill maxfillminutes"},
|
||||||
|
{"path": "minholdhours", "type": "float", "readonly": false, "cmd": "ln2fill minholdhours"},
|
||||||
|
{"path": "maxholdhours", "type": "float", "readonly": false, "cmd": "ln2fill maxholdhours"},
|
||||||
|
{"path": "tolerance", "type": "float", "readonly": false, "cmd": "ln2fill tolerance"},
|
||||||
|
{"path": "badreadingminutes", "type": "float", "readonly": false, "cmd": "ln2fill badreadingminutes"},
|
||||||
|
{"path": "tubecoolingminutes", "type": "float", "readonly": false, "cmd": "ln2fill tubecoolingminutes"}]},
|
||||||
|
|
||||||
|
"hefill": {"base": "/hefill", "params": [
|
||||||
|
{"path": "", "type": "enum", "enum": {"watching": 0, "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": "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"}]},
|
||||||
|
|
||||||
|
"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 ..."}]}}
|
395
cfg/sea/ma10high_t.config.json
Normal file
395
cfg/sea/ma10high_t.config.json
Normal file
@ -0,0 +1,395 @@
|
|||||||
|
{"tt": {"base": "/tt", "params": [
|
||||||
|
{"path": "", "type": "float", "readonly": false, "cmd": "run tt", "description": "tt", "kids": 18},
|
||||||
|
{"path": "send", "type": "text", "readonly": false, "cmd": "tt send", "visibility": 3},
|
||||||
|
{"path": "status", "type": "text", "visibility": 3},
|
||||||
|
{"path": "is_running", "type": "int", "readonly": false, "cmd": "tt is_running", "visibility": 3},
|
||||||
|
{"path": "mainloop", "type": "text", "readonly": false, "cmd": "tt mainloop", "visibility": 3},
|
||||||
|
{"path": "target", "type": "float"},
|
||||||
|
{"path": "running", "type": "int"},
|
||||||
|
{"path": "tolerance", "type": "float", "readonly": false, "cmd": "tt tolerance"},
|
||||||
|
{"path": "maxwait", "type": "float", "readonly": false, "cmd": "tt maxwait"},
|
||||||
|
{"path": "settle", "type": "float", "readonly": false, "cmd": "tt settle"},
|
||||||
|
{"path": "log", "type": "text", "readonly": false, "cmd": "tt log", "visibility": 3, "kids": 4},
|
||||||
|
{"path": "log/mean", "type": "float", "visibility": 3},
|
||||||
|
{"path": "log/m2", "type": "float", "visibility": 3},
|
||||||
|
{"path": "log/stddev", "type": "float", "visibility": 3},
|
||||||
|
{"path": "log/n", "type": "float", "visibility": 3},
|
||||||
|
{"path": "dblctrl", "type": "bool", "readonly": false, "cmd": "tt dblctrl", "kids": 9},
|
||||||
|
{"path": "dblctrl/tshift", "type": "float", "readonly": false, "cmd": "tt dblctrl/tshift"},
|
||||||
|
{"path": "dblctrl/mode", "type": "enum", "enum": {"disabled": -1, "inactive": 0, "stable": 1, "up": 2, "down": 3}, "readonly": false, "cmd": "tt dblctrl/mode"},
|
||||||
|
{"path": "dblctrl/shift_up", "type": "float"},
|
||||||
|
{"path": "dblctrl/shift_lo", "type": "float"},
|
||||||
|
{"path": "dblctrl/t_min", "type": "float"},
|
||||||
|
{"path": "dblctrl/t_max", "type": "float"},
|
||||||
|
{"path": "dblctrl/int2", "type": "float", "readonly": false, "cmd": "tt dblctrl/int2"},
|
||||||
|
{"path": "dblctrl/prop_up", "type": "float", "readonly": false, "cmd": "tt dblctrl/prop_up"},
|
||||||
|
{"path": "dblctrl/prop_lo", "type": "float", "readonly": false, "cmd": "tt dblctrl/prop_lo"},
|
||||||
|
{"path": "tm", "type": "float", "kids": 4},
|
||||||
|
{"path": "tm/curve", "type": "text", "readonly": false, "cmd": "tt tm/curve", "kids": 1},
|
||||||
|
{"path": "tm/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt tm/curve/points", "visibility": 3},
|
||||||
|
{"path": "tm/alarm", "type": "float", "readonly": false, "cmd": "tt tm/alarm"},
|
||||||
|
{"path": "tm/stddev", "type": "float"},
|
||||||
|
{"path": "tm/raw", "type": "float"},
|
||||||
|
{"path": "ts", "type": "float", "kids": 4},
|
||||||
|
{"path": "ts/curve", "type": "text", "readonly": false, "cmd": "tt ts/curve", "kids": 1},
|
||||||
|
{"path": "ts/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt ts/curve/points", "visibility": 3},
|
||||||
|
{"path": "ts/alarm", "type": "float", "readonly": false, "cmd": "tt ts/alarm"},
|
||||||
|
{"path": "ts/stddev", "type": "float"},
|
||||||
|
{"path": "ts/raw", "type": "float"},
|
||||||
|
{"path": "ts_2", "type": "float", "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"},
|
||||||
|
{"path": "ts_2/stddev", "type": "float"},
|
||||||
|
{"path": "ts_2/raw", "type": "float"},
|
||||||
|
{"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/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)"},
|
||||||
|
{"path": "setsamp/channel", "type": "text", "readonly": false, "cmd": "tt setsamp/channel"},
|
||||||
|
{"path": "setsamp/limit", "type": "float", "readonly": false, "cmd": "tt setsamp/limit"},
|
||||||
|
{"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/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/manualpower", "type": "float", "readonly": false, "cmd": "tt setsamp/manualpower"},
|
||||||
|
{"path": "setsamp/power", "type": "float"},
|
||||||
|
{"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": "set", "type": "float", "readonly": false, "cmd": "tt set", "kids": 18},
|
||||||
|
{"path": "set/mode", "type": "enum", "enum": {"disabled": -1, "off": 0, "controlling": 1, "manual": 2}, "readonly": false, "cmd": "tt set/mode"},
|
||||||
|
{"path": "set/reg", "type": "float"},
|
||||||
|
{"path": "set/ramp", "type": "float", "readonly": false, "cmd": "tt set/ramp", "description": "maximum ramp in K/min (0: ramp off)"},
|
||||||
|
{"path": "set/wramp", "type": "float", "readonly": false, "cmd": "tt set/wramp"},
|
||||||
|
{"path": "set/smooth", "type": "float", "readonly": false, "cmd": "tt set/smooth", "description": "smooth time (minutes)"},
|
||||||
|
{"path": "set/channel", "type": "text", "readonly": false, "cmd": "tt set/channel"},
|
||||||
|
{"path": "set/limit", "type": "float", "readonly": false, "cmd": "tt set/limit"},
|
||||||
|
{"path": "set/resist", "type": "float", "readonly": false, "cmd": "tt set/resist"},
|
||||||
|
{"path": "set/maxheater", "type": "text", "readonly": false, "cmd": "tt set/maxheater", "description": "maximum heater limit, units should be given without space: W, mW, A, mA"},
|
||||||
|
{"path": "set/linearpower", "type": "float", "readonly": false, "cmd": "tt set/linearpower", "description": "when not 0, it is the maximum effective power, and the power is linear to the heater output"},
|
||||||
|
{"path": "set/maxpowerlim", "type": "float", "description": "the maximum power limit (before any booster or converter)"},
|
||||||
|
{"path": "set/maxpower", "type": "float", "readonly": false, "cmd": "tt set/maxpower", "description": "maximum power [W]"},
|
||||||
|
{"path": "set/maxcurrent", "type": "float", "description": "the maximum current before any booster or converter"},
|
||||||
|
{"path": "set/manualpower", "type": "float", "readonly": false, "cmd": "tt set/manualpower"},
|
||||||
|
{"path": "set/power", "type": "float"},
|
||||||
|
{"path": "set/prop", "type": "float", "readonly": false, "cmd": "tt set/prop", "description": "bigger means more gain"},
|
||||||
|
{"path": "set/integ", "type": "float", "readonly": false, "cmd": "tt set/integ", "description": "bigger means faster"},
|
||||||
|
{"path": "set/deriv", "type": "float", "readonly": false, "cmd": "tt set/deriv"},
|
||||||
|
{"path": "display", "type": "text", "readonly": false, "cmd": "tt display"},
|
||||||
|
{"path": "remote", "type": "bool"}]},
|
||||||
|
|
||||||
|
"cc": {"base": "/cc", "params": [
|
||||||
|
{"path": "", "type": "bool", "kids": 96},
|
||||||
|
{"path": "send", "type": "text", "readonly": false, "cmd": "cc send", "visibility": 3},
|
||||||
|
{"path": "status", "type": "text", "visibility": 3},
|
||||||
|
{"path": "autodevice", "type": "bool", "readonly": false, "cmd": "cc autodevice"},
|
||||||
|
{"path": "fav", "type": "bool", "readonly": false, "cmd": "cc fav"},
|
||||||
|
{"path": "f", "type": "float"},
|
||||||
|
{"path": "fs", "type": "enum", "enum": {"ok": 0, "no_sens": 1}, "readonly": false, "cmd": "cc fs"},
|
||||||
|
{"path": "mav", "type": "bool", "readonly": false, "cmd": "cc mav"},
|
||||||
|
{"path": "fm", "type": "enum", "enum": {"idle": 0, "opening": 1, "closing": 2, "opened": 3, "closed": 4, "no_motor": 5}},
|
||||||
|
{"path": "fa", "type": "enum", "enum": {"fixed": 0, "controlled": 1, "automatic": 2, "offline": 3}, "readonly": false, "cmd": "cc fa"},
|
||||||
|
{"path": "mp", "type": "float", "readonly": false, "cmd": "cc mp"},
|
||||||
|
{"path": "msp", "type": "float"},
|
||||||
|
{"path": "mmp", "type": "float"},
|
||||||
|
{"path": "mc", "type": "float", "readonly": false, "cmd": "cc mc"},
|
||||||
|
{"path": "mfc", "type": "float", "readonly": false, "cmd": "cc mfc"},
|
||||||
|
{"path": "moc", "type": "float", "readonly": false, "cmd": "cc moc"},
|
||||||
|
{"path": "mtc", "type": "float", "readonly": false, "cmd": "cc mtc"},
|
||||||
|
{"path": "mtl", "type": "float"},
|
||||||
|
{"path": "mft", "type": "float", "readonly": false, "cmd": "cc mft"},
|
||||||
|
{"path": "mt", "type": "float"},
|
||||||
|
{"path": "mo", "type": "float"},
|
||||||
|
{"path": "mcr", "type": "float"},
|
||||||
|
{"path": "mot", "type": "float"},
|
||||||
|
{"path": "mw", "type": "float", "readonly": false, "cmd": "cc mw", "description": "correction pulse after automatic open"},
|
||||||
|
{"path": "hav", "type": "bool", "readonly": false, "cmd": "cc hav"},
|
||||||
|
{"path": "h", "type": "float"},
|
||||||
|
{"path": "hr", "type": "float"},
|
||||||
|
{"path": "hc", "type": "float"},
|
||||||
|
{"path": "hu", "type": "float"},
|
||||||
|
{"path": "hh", "type": "float", "readonly": false, "cmd": "cc hh"},
|
||||||
|
{"path": "hl", "type": "float", "readonly": false, "cmd": "cc hl"},
|
||||||
|
{"path": "htf", "type": "float", "readonly": false, "cmd": "cc htf", "description": "meas. period in fast mode"},
|
||||||
|
{"path": "hts", "type": "float", "readonly": false, "cmd": "cc hts", "description": "meas. period in slow mode"},
|
||||||
|
{"path": "hd", "type": "float", "readonly": false, "cmd": "cc hd"},
|
||||||
|
{"path": "hwr", "type": "float", "readonly": false, "cmd": "cc hwr"},
|
||||||
|
{"path": "hem", "type": "float", "readonly": false, "cmd": "cc hem", "description": "sensor length in mm from top to empty pos."},
|
||||||
|
{"path": "hfu", "type": "float", "readonly": false, "cmd": "cc hfu", "description": "sensor length in mm from top to full pos."},
|
||||||
|
{"path": "hcd", "type": "enum", "enum": {"stop": 0, "fill": 1, "off": 2, "auto": 3, "manual": 7}, "readonly": false, "cmd": "cc hcd"},
|
||||||
|
{"path": "hv", "type": "enum", "enum": {"fill_valve_off": 0, "filling": 1, "no_fill_valve": 2, "timeout": 3, "timeout1": 4}},
|
||||||
|
{"path": "hsf", "type": "enum", "enum": {"sens_ok": 0, "sens_warm": 1, "no_sens": 2, "timeout": 3, "not_yet_read": 4, "disabled": 5}},
|
||||||
|
{"path": "ha", "type": "bool", "readonly": false, "cmd": "cc ha"},
|
||||||
|
{"path": "hm", "type": "bool"},
|
||||||
|
{"path": "hf", "type": "enum", "enum": {"slow": 0, "fast": 1}, "readonly": false, "cmd": "cc hf"},
|
||||||
|
{"path": "hbe", "type": "bool", "readonly": false, "cmd": "cc hbe"},
|
||||||
|
{"path": "hmf", "type": "float"},
|
||||||
|
{"path": "hms", "type": "float"},
|
||||||
|
{"path": "hit", "type": "float", "readonly": false, "cmd": "cc hit"},
|
||||||
|
{"path": "hft", "type": "int", "readonly": false, "cmd": "cc hft"},
|
||||||
|
{"path": "hea", "type": "enum", "enum": {"0": 0, "1": 1, "6": 6}, "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": "nu", "type": "float"},
|
||||||
|
{"path": "nl", "type": "float"},
|
||||||
|
{"path": "nth", "type": "float", "readonly": false, "cmd": "cc nth"},
|
||||||
|
{"path": "ntc", "type": "float", "readonly": false, "cmd": "cc ntc"},
|
||||||
|
{"path": "ntm", "type": "float", "readonly": false, "cmd": "cc ntm"},
|
||||||
|
{"path": "ns", "type": "enum", "enum": {"sens_ok": 0, "no_sens": 1, "short_circuit": 2, "upside_down": 3, "sens_warm": 4, "empty": 5}},
|
||||||
|
{"path": "na", "type": "bool", "readonly": false, "cmd": "cc na"},
|
||||||
|
{"path": "nv", "type": "enum", "enum": {"fill_valve_off": 0, "filling": 1, "no_fill_valve": 2, "timeout": 3, "timeout1": 4, "boost": 5}},
|
||||||
|
{"path": "nc", "type": "enum", "enum": {"stop": 0, "fill": 1, "off": 2, "auto": 3}, "readonly": false, "cmd": "cc nc"},
|
||||||
|
{"path": "nfb", "type": "float"},
|
||||||
|
{"path": "cda", "type": "float"},
|
||||||
|
{"path": "cdb", "type": "float"},
|
||||||
|
{"path": "cba", "type": "float"},
|
||||||
|
{"path": "cbb", "type": "float"},
|
||||||
|
{"path": "cvs", "type": "int"},
|
||||||
|
{"path": "csp", "type": "int"},
|
||||||
|
{"path": "cdv", "type": "text", "readonly": false, "cmd": "cc cdv"},
|
||||||
|
{"path": "cic", "type": "text", "readonly": false, "cmd": "cc cic"},
|
||||||
|
{"path": "cin", "type": "text"},
|
||||||
|
{"path": "cds", "type": "enum", "enum": {"local": 0, "remote": 1, "loading": 2, "by_code": 3, "by_touch": 4}, "readonly": false, "cmd": "cc cds"},
|
||||||
|
{"path": "timing", "type": "bool", "readonly": false, "cmd": "cc timing"},
|
||||||
|
{"path": "tc", "type": "float", "visibility": 3},
|
||||||
|
{"path": "tn", "type": "float", "visibility": 3},
|
||||||
|
{"path": "th", "type": "float", "visibility": 3},
|
||||||
|
{"path": "tf", "type": "float", "visibility": 3},
|
||||||
|
{"path": "tm", "type": "float", "visibility": 3},
|
||||||
|
{"path": "tv", "type": "float", "visibility": 3},
|
||||||
|
{"path": "tq", "type": "float", "visibility": 3},
|
||||||
|
{"path": "bdl", "type": "float", "readonly": false, "cmd": "cc bdl"}]},
|
||||||
|
|
||||||
|
"nv": {"base": "/nv", "params": [
|
||||||
|
{"path": "", "type": "enum", "enum": {"fixed": 0, "controlled": 1, "automatic": 2, "close": 3, "open": 4}, "readonly": false, "cmd": "nv", "kids": 11},
|
||||||
|
{"path": "send", "type": "text", "readonly": false, "cmd": "nv send", "visibility": 3},
|
||||||
|
{"path": "status", "type": "text", "visibility": 3},
|
||||||
|
{"path": "motstat", "type": "enum", "enum": {"idle": 0, "opening": 1, "closing": 2, "opened": 3, "closed": 4, "no_motor": 5}},
|
||||||
|
{"path": "flow", "type": "float"},
|
||||||
|
{"path": "set", "type": "float", "readonly": false, "cmd": "nv set"},
|
||||||
|
{"path": "flowmax", "type": "float", "readonly": false, "cmd": "nv flowmax"},
|
||||||
|
{"path": "flowp", "type": "float"},
|
||||||
|
{"path": "span", "type": "float"},
|
||||||
|
{"path": "ctrl", "type": "none", "kids": 13},
|
||||||
|
{"path": "ctrl/regtext", "type": "text"},
|
||||||
|
{"path": "ctrl/prop_o", "type": "float", "readonly": false, "cmd": "nv ctrl/prop_o", "description": "prop [sec/mbar] when opening. above 4 mbar a 10 times lower value is used"},
|
||||||
|
{"path": "ctrl/prop_c", "type": "float", "readonly": false, "cmd": "nv ctrl/prop_c", "description": "prop [sec/mbar] when closing. above 4 mbar a 10 times lower value is used"},
|
||||||
|
{"path": "ctrl/deriv_o", "type": "float", "readonly": false, "cmd": "nv ctrl/deriv_o", "description": "convergence target time [sec] when opening"},
|
||||||
|
{"path": "ctrl/deriv_c", "type": "float", "readonly": false, "cmd": "nv ctrl/deriv_c", "description": "convergence target time [sec] when closing"},
|
||||||
|
{"path": "ctrl/minpulse_o", "type": "float", "readonly": false, "cmd": "nv ctrl/minpulse_o", "description": "minimum close pulse [sec]"},
|
||||||
|
{"path": "ctrl/minpulse_c", "type": "float", "readonly": false, "cmd": "nv ctrl/minpulse_c", "description": "standard close pulse [sec]"},
|
||||||
|
{"path": "ctrl/hystpulse_o", "type": "float", "readonly": false, "cmd": "nv ctrl/hystpulse_o", "description": "motor pulse to overcome hysteresis when opening"},
|
||||||
|
{"path": "ctrl/hystpulse_c", "type": "float", "readonly": false, "cmd": "nv ctrl/hystpulse_c", "description": "motor pulse to overcome hysteresis when closing"},
|
||||||
|
{"path": "ctrl/tol", "type": "float", "readonly": false, "cmd": "nv ctrl/tol", "description": "valid below 3 mbar"},
|
||||||
|
{"path": "ctrl/tolhigh", "type": "float", "readonly": false, "cmd": "nv ctrl/tolhigh", "description": "valid above 4 mbar"},
|
||||||
|
{"path": "ctrl/openpulse", "type": "float", "readonly": false, "cmd": "nv ctrl/openpulse", "description": "time to open from completely closed to a significant opening"},
|
||||||
|
{"path": "ctrl/adjust_minpulse", "type": "bool", "readonly": false, "cmd": "nv ctrl/adjust_minpulse", "description": "adjust minpulse automatically"},
|
||||||
|
{"path": "autoflow", "type": "none", "kids": 24},
|
||||||
|
{"path": "autoflow/suspended", "type": "bool", "readonly": false, "cmd": "nv autoflow/suspended"},
|
||||||
|
{"path": "autoflow/prop", "type": "float", "readonly": false, "cmd": "nv autoflow/prop"},
|
||||||
|
{"path": "autoflow/flowstd", "type": "float", "readonly": false, "cmd": "nv autoflow/flowstd"},
|
||||||
|
{"path": "autoflow/flowlim", "type": "float", "readonly": false, "cmd": "nv autoflow/flowlim"},
|
||||||
|
{"path": "autoflow/smooth", "type": "float", "readonly": false, "cmd": "nv autoflow/smooth"},
|
||||||
|
{"path": "autoflow/difSize", "type": "float", "readonly": false, "cmd": "nv autoflow/difSize"},
|
||||||
|
{"path": "autoflow/difRange", "type": "float", "readonly": false, "cmd": "nv autoflow/difRange"},
|
||||||
|
{"path": "autoflow/flowSize", "type": "float", "readonly": false, "cmd": "nv autoflow/flowSize"},
|
||||||
|
{"path": "autoflow/convTime", "type": "float", "readonly": false, "cmd": "nv autoflow/convTime"},
|
||||||
|
{"path": "autoflow/Tmin", "type": "float", "readonly": false, "cmd": "nv autoflow/Tmin"},
|
||||||
|
{"path": "autoflow/script", "type": "text", "readonly": false, "cmd": "nv autoflow/script"},
|
||||||
|
{"path": "autoflow/getTemp", "type": "text", "readonly": false, "cmd": "nv autoflow/getTemp"},
|
||||||
|
{"path": "autoflow/getTset", "type": "text", "readonly": false, "cmd": "nv autoflow/getTset"},
|
||||||
|
{"path": "autoflow/getFlow", "type": "text", "readonly": false, "cmd": "nv autoflow/getFlow"},
|
||||||
|
{"path": "autoflow/difBuf", "type": "text"},
|
||||||
|
{"path": "autoflow/flowBuf", "type": "text"},
|
||||||
|
{"path": "autoflow/flowset", "type": "float"},
|
||||||
|
{"path": "autoflow/flowmin", "type": "float"},
|
||||||
|
{"path": "autoflow/flowmax", "type": "float"},
|
||||||
|
{"path": "autoflow/difmin", "type": "float"},
|
||||||
|
{"path": "autoflow/difmax", "type": "float"},
|
||||||
|
{"path": "autoflow/setmin", "type": "float"},
|
||||||
|
{"path": "autoflow/setmax", "type": "float"},
|
||||||
|
{"path": "autoflow/flowtarget", "type": "float"},
|
||||||
|
{"path": "calib", "type": "none", "kids": 2},
|
||||||
|
{"path": "calib/ln_per_min_per_mbar", "type": "float", "readonly": false, "cmd": "nv calib/ln_per_min_per_mbar"},
|
||||||
|
{"path": "calib/mbar_offset", "type": "float", "readonly": false, "cmd": "nv calib/mbar_offset"}]},
|
||||||
|
|
||||||
|
"hepump": {"base": "/hepump", "params": [
|
||||||
|
{"path": "", "type": "enum", "enum": {"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"},
|
||||||
|
{"path": "auto", "type": "bool", "readonly": false, "cmd": "hepump auto"},
|
||||||
|
{"path": "valve", "type": "enum", "enum": {"closed": 0, "closing": 1, "opening": 2, "opened": 3, "undefined": 4}, "readonly": false, "cmd": "hepump valve"},
|
||||||
|
{"path": "eco_t_lim", "type": "float", "readonly": false, "cmd": "hepump eco_t_lim", "description": "switch off eco mode when T_set < eco_t_lim and T < eco_t_lim * 2"},
|
||||||
|
{"path": "calib", "type": "float", "readonly": false, "cmd": "hepump calib", "visibility": 3},
|
||||||
|
{"path": "health", "type": "float"}]},
|
||||||
|
|
||||||
|
"hemot": {"base": "/hepump/hemot", "params": [
|
||||||
|
{"path": "", "type": "float", "readonly": false, "cmd": "run hemot", "kids": 30},
|
||||||
|
{"path": "send", "type": "text", "readonly": false, "cmd": "hemot send", "visibility": 3},
|
||||||
|
{"path": "status", "type": "text", "visibility": 3},
|
||||||
|
{"path": "is_running", "type": "int", "readonly": false, "cmd": "hemot is_running", "visibility": 3},
|
||||||
|
{"path": "pos", "type": "float"},
|
||||||
|
{"path": "encoder", "type": "float"},
|
||||||
|
{"path": "zero", "type": "float", "readonly": false, "cmd": "hemot zero"},
|
||||||
|
{"path": "lowerlimit", "type": "float", "readonly": false, "cmd": "hemot lowerlimit"},
|
||||||
|
{"path": "upperlimit", "type": "float", "readonly": false, "cmd": "hemot upperlimit"},
|
||||||
|
{"path": "disablelimits", "type": "bool", "readonly": false, "cmd": "hemot disablelimits"},
|
||||||
|
{"path": "verbose", "type": "bool", "readonly": false, "cmd": "hemot verbose"},
|
||||||
|
{"path": "target", "type": "float"},
|
||||||
|
{"path": "runstate", "type": "enum", "enum": {"idle": 0, "running": 1, "finished": 2, "error": 3}},
|
||||||
|
{"path": "precision", "type": "float", "readonly": false, "cmd": "hemot precision"},
|
||||||
|
{"path": "maxencdif", "type": "float", "readonly": false, "cmd": "hemot maxencdif"},
|
||||||
|
{"path": "id", "type": "float", "readonly": false, "cmd": "hemot id"},
|
||||||
|
{"path": "pump_number", "type": "float", "readonly": false, "cmd": "hemot pump_number"},
|
||||||
|
{"path": "init", "type": "float", "readonly": false, "cmd": "hemot init"},
|
||||||
|
{"path": "maxspeed", "type": "float", "readonly": false, "cmd": "hemot maxspeed"},
|
||||||
|
{"path": "acceleration", "type": "float", "readonly": false, "cmd": "hemot acceleration"},
|
||||||
|
{"path": "maxcurrent", "type": "float", "readonly": false, "cmd": "hemot maxcurrent"},
|
||||||
|
{"path": "standbycurrent", "type": "float", "readonly": false, "cmd": "hemot standbycurrent"},
|
||||||
|
{"path": "freewheeling", "type": "bool", "readonly": false, "cmd": "hemot freewheeling"},
|
||||||
|
{"path": "output0", "type": "bool", "readonly": false, "cmd": "hemot output0"},
|
||||||
|
{"path": "output1", "type": "bool", "readonly": false, "cmd": "hemot output1"},
|
||||||
|
{"path": "input3", "type": "bool"},
|
||||||
|
{"path": "pullup", "type": "float", "readonly": false, "cmd": "hemot pullup"},
|
||||||
|
{"path": "nopumpfeedback", "type": "bool", "readonly": false, "cmd": "hemot nopumpfeedback"},
|
||||||
|
{"path": "eeprom", "type": "enum", "enum": {"ok": 0, "dirty": 1, "save": 2, "load": 3}, "readonly": false, "cmd": "hemot eeprom"},
|
||||||
|
{"path": "customadr", "type": "text", "readonly": false, "cmd": "hemot customadr"},
|
||||||
|
{"path": "custompar", "type": "float", "readonly": false, "cmd": "hemot custompar"}]},
|
||||||
|
|
||||||
|
"nvflow": {"base": "/nvflow", "params": [
|
||||||
|
{"path": "", "type": "float", "kids": 7},
|
||||||
|
{"path": "send", "type": "text", "readonly": false, "cmd": "nvflow send", "visibility": 3},
|
||||||
|
{"path": "status", "type": "text", "visibility": 3},
|
||||||
|
{"path": "stddev", "type": "float"},
|
||||||
|
{"path": "nsamples", "type": "int", "readonly": false, "cmd": "nvflow nsamples"},
|
||||||
|
{"path": "offset", "type": "float", "readonly": false, "cmd": "nvflow offset"},
|
||||||
|
{"path": "scale", "type": "float", "readonly": false, "cmd": "nvflow scale"},
|
||||||
|
{"path": "save", "type": "bool", "readonly": false, "cmd": "nvflow save", "description": "unchecked: current calib is not saved. set checked: save calib"}]},
|
||||||
|
|
||||||
|
"mf": {"base": "/mf", "params": [
|
||||||
|
{"path": "", "type": "float", "readonly": false, "cmd": "run mf", "kids": 8},
|
||||||
|
{"path": "send", "type": "text", "readonly": false, "cmd": "mf send", "visibility": 3},
|
||||||
|
{"path": "status", "type": "text", "visibility": 3},
|
||||||
|
{"path": "is_running", "type": "int", "readonly": false, "cmd": "mf is_running", "visibility": 3},
|
||||||
|
{"path": "statustext", "type": "text"},
|
||||||
|
{"path": "ramp", "type": "float", "readonly": false, "cmd": "mf ramp"},
|
||||||
|
{"path": "persistent_mode", "type": "enum", "enum": {"forever_off": -1, "off": 0, "on": 1}, "readonly": false, "cmd": "mf persistent_mode", "description": "hidden mode -1: completely off"},
|
||||||
|
{"path": "gen", "type": "none", "kids": 13},
|
||||||
|
{"path": "gen/persistent_delay", "type": "float", "readonly": false, "cmd": "mf gen/persistent_delay", "description": "timeout for going automatically into persistent mode"},
|
||||||
|
{"path": "gen/tolerance", "type": "float", "readonly": false, "cmd": "mf gen/tolerance"},
|
||||||
|
{"path": "gen/wait_switch_on", "type": "float", "readonly": false, "cmd": "mf gen/wait_switch_on"},
|
||||||
|
{"path": "gen/wait_switch_off", "type": "float", "readonly": false, "cmd": "mf gen/wait_switch_off"},
|
||||||
|
{"path": "gen/wait_stable_leads", "type": "float", "readonly": false, "cmd": "mf gen/wait_stable_leads"},
|
||||||
|
{"path": "gen/wait_stable_field", "type": "float", "readonly": false, "cmd": "mf gen/wait_stable_field"},
|
||||||
|
{"path": "gen/expectend", "type": "text"},
|
||||||
|
{"path": "gen/trained_pos", "type": "float", "readonly": false, "cmd": "mf gen/trained_pos"},
|
||||||
|
{"path": "gen/trained_neg", "type": "float", "readonly": false, "cmd": "mf gen/trained_neg"},
|
||||||
|
{"path": "gen/profile", "type": "text", "readonly": false, "cmd": "mf gen/profile", "description": "syntax: <field1>:<ramp1> <field2>:<ramp2> ... (<ramp2> is the ramp limit from <field1> to <field2>)"},
|
||||||
|
{"path": "gen/profile_training", "type": "text", "readonly": false, "cmd": "mf gen/profile_training", "description": "syntax: <field1>:<ramp1> <field2>:<ramp2> ... (<ramp2> is the ramp limit from <field1> to <field2>)"},
|
||||||
|
{"path": "gen/limit", "type": "float", "readonly": false, "cmd": "mf gen/limit"},
|
||||||
|
{"path": "gen/bipolar", "type": "bool", "readonly": false, "cmd": "mf gen/bipolar"},
|
||||||
|
{"path": "ips", "type": "float", "kids": 17},
|
||||||
|
{"path": "ips/ramp_slow", "type": "float", "readonly": false, "cmd": "mf ips/ramp_slow", "description": "ramp rate for coils Tesla/min."},
|
||||||
|
{"path": "ips/ramp_fast", "type": "float", "description": "ramp rate for leads Tesla/min."},
|
||||||
|
{"path": "ips/set_field", "type": "float", "readonly": false, "cmd": "mf ips/set_field"},
|
||||||
|
{"path": "ips/heater", "type": "bool", "readonly": false, "cmd": "mf ips/heater"},
|
||||||
|
{"path": "ips/ramp_state", "type": "enum", "enum": {"hold": 0, "to_zero": 1, "to_set": 2, "clamp": 3}, "readonly": false, "cmd": "mf ips/ramp_state"},
|
||||||
|
{"path": "ips/leads_set", "type": "float", "description": "calculated current in the leads, converted to Tesla"},
|
||||||
|
{"path": "ips/show_internals", "type": "bool", "readonly": false, "cmd": "mf ips/show_internals"},
|
||||||
|
{"path": "ips/leads_meas", "type": "float", "description": "measured current in the leads, converted to Tesla"},
|
||||||
|
{"path": "ips/slave1", "type": "float"},
|
||||||
|
{"path": "ips/slave2", "type": "float"},
|
||||||
|
{"path": "ips/slave3", "type": "float"},
|
||||||
|
{"path": "ips/volt", "type": "float"},
|
||||||
|
{"path": "ips/symode", "type": "text"},
|
||||||
|
{"path": "ips/engineering_password", "type": "text", "readonly": false, "cmd": "mf ips/engineering_password"},
|
||||||
|
{"path": "ips/atob", "type": "float", "readonly": false, "cmd": "mf ips/atob", "description": "Amp/Tesla"},
|
||||||
|
{"path": "ips/inductance", "type": "float", "readonly": false, "cmd": "mf ips/inductance", "description": "henries"},
|
||||||
|
{"path": "ips/switch_heater_current", "type": "float", "readonly": false, "cmd": "mf ips/switch_heater_current", "description": "switch heater current [mA]"}]},
|
||||||
|
|
||||||
|
"lev": {"base": "/lev", "params": [
|
||||||
|
{"path": "", "type": "float", "kids": 4},
|
||||||
|
{"path": "send", "type": "text", "readonly": false, "cmd": "lev send", "visibility": 3},
|
||||||
|
{"path": "status", "type": "text", "visibility": 3},
|
||||||
|
{"path": "mode", "type": "enum", "enum": {"slow": 0, "fast": 1}, "readonly": false, "cmd": "lev mode"},
|
||||||
|
{"path": "n2", "type": "float"}]},
|
||||||
|
|
||||||
|
"ln2fill": {"base": "/ln2fill", "params": [
|
||||||
|
{"path": "", "type": "enum", "enum": {"watching": 0, "filling": 1, "inactive": 2, "manualfill": 3}, "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": "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"},
|
||||||
|
{"path": "minfillminutes", "type": "float", "readonly": false, "cmd": "ln2fill minfillminutes"},
|
||||||
|
{"path": "maxfillminutes", "type": "float", "readonly": false, "cmd": "ln2fill maxfillminutes"},
|
||||||
|
{"path": "minholdhours", "type": "float", "readonly": false, "cmd": "ln2fill minholdhours"},
|
||||||
|
{"path": "maxholdhours", "type": "float", "readonly": false, "cmd": "ln2fill maxholdhours"},
|
||||||
|
{"path": "tolerance", "type": "float", "readonly": false, "cmd": "ln2fill tolerance"},
|
||||||
|
{"path": "badreadingminutes", "type": "float", "readonly": false, "cmd": "ln2fill badreadingminutes"},
|
||||||
|
{"path": "tubecoolingminutes", "type": "float", "readonly": false, "cmd": "ln2fill tubecoolingminutes"}]},
|
||||||
|
|
||||||
|
"hefill": {"base": "/hefill", "params": [
|
||||||
|
{"path": "", "type": "enum", "enum": {"watching": 0, "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": "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"}]},
|
||||||
|
|
||||||
|
"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 ..."}]}}
|
426
cfg/sea/ma7_piezo.config.json
Normal file
426
cfg/sea/ma7_piezo.config.json
Normal file
@ -0,0 +1,426 @@
|
|||||||
|
{"tt": {"base": "/tt", "params": [
|
||||||
|
{"path": "", "type": "float", "readonly": false, "cmd": "run tt", "description": "tt", "kids": 18},
|
||||||
|
{"path": "send", "type": "text", "readonly": false, "cmd": "tt send", "visibility": 3},
|
||||||
|
{"path": "status", "type": "text", "visibility": 3},
|
||||||
|
{"path": "is_running", "type": "int", "readonly": false, "cmd": "tt is_running", "visibility": 3},
|
||||||
|
{"path": "mainloop", "type": "text", "readonly": false, "cmd": "tt mainloop", "visibility": 3},
|
||||||
|
{"path": "target", "type": "float"},
|
||||||
|
{"path": "running", "type": "int"},
|
||||||
|
{"path": "tolerance", "type": "float", "readonly": false, "cmd": "tt tolerance"},
|
||||||
|
{"path": "maxwait", "type": "float", "readonly": false, "cmd": "tt maxwait"},
|
||||||
|
{"path": "settle", "type": "float", "readonly": false, "cmd": "tt settle"},
|
||||||
|
{"path": "log", "type": "text", "readonly": false, "cmd": "tt log", "visibility": 3, "kids": 4},
|
||||||
|
{"path": "log/mean", "type": "float", "visibility": 3},
|
||||||
|
{"path": "log/m2", "type": "float", "visibility": 3},
|
||||||
|
{"path": "log/stddev", "type": "float", "visibility": 3},
|
||||||
|
{"path": "log/n", "type": "float", "visibility": 3},
|
||||||
|
{"path": "dblctrl", "type": "bool", "readonly": false, "cmd": "tt dblctrl", "kids": 9},
|
||||||
|
{"path": "dblctrl/tshift", "type": "float", "readonly": false, "cmd": "tt dblctrl/tshift"},
|
||||||
|
{"path": "dblctrl/mode", "type": "enum", "enum": {"disabled": -1, "inactive": 0, "stable": 1, "up": 2, "down": 3}, "readonly": false, "cmd": "tt dblctrl/mode"},
|
||||||
|
{"path": "dblctrl/shift_up", "type": "float"},
|
||||||
|
{"path": "dblctrl/shift_lo", "type": "float"},
|
||||||
|
{"path": "dblctrl/t_min", "type": "float"},
|
||||||
|
{"path": "dblctrl/t_max", "type": "float"},
|
||||||
|
{"path": "dblctrl/int2", "type": "float", "readonly": false, "cmd": "tt dblctrl/int2"},
|
||||||
|
{"path": "dblctrl/prop_up", "type": "float", "readonly": false, "cmd": "tt dblctrl/prop_up"},
|
||||||
|
{"path": "dblctrl/prop_lo", "type": "float", "readonly": false, "cmd": "tt dblctrl/prop_lo"},
|
||||||
|
{"path": "tm", "type": "float", "kids": 4},
|
||||||
|
{"path": "tm/curve", "type": "text", "readonly": false, "cmd": "tt tm/curve", "kids": 1},
|
||||||
|
{"path": "tm/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt tm/curve/points", "visibility": 3},
|
||||||
|
{"path": "tm/alarm", "type": "float", "readonly": false, "cmd": "tt tm/alarm"},
|
||||||
|
{"path": "tm/stddev", "type": "float"},
|
||||||
|
{"path": "tm/raw", "type": "float"},
|
||||||
|
{"path": "ts", "type": "float", "kids": 4},
|
||||||
|
{"path": "ts/curve", "type": "text", "readonly": false, "cmd": "tt ts/curve", "kids": 1},
|
||||||
|
{"path": "ts/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt ts/curve/points", "visibility": 3},
|
||||||
|
{"path": "ts/alarm", "type": "float", "readonly": false, "cmd": "tt ts/alarm"},
|
||||||
|
{"path": "ts/stddev", "type": "float"},
|
||||||
|
{"path": "ts/raw", "type": "float"},
|
||||||
|
{"path": "ts_2", "type": "float", "visibility": 3, "kids": 4},
|
||||||
|
{"path": "ts_2/curve", "type": "text", "readonly": false, "cmd": "tt ts_2/curve", "visibility": 3, "kids": 1},
|
||||||
|
{"path": "ts_2/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt ts_2/curve/points", "visibility": 3},
|
||||||
|
{"path": "ts_2/alarm", "type": "float", "readonly": false, "cmd": "tt ts_2/alarm", "visibility": 3},
|
||||||
|
{"path": "ts_2/stddev", "type": "float", "visibility": 3},
|
||||||
|
{"path": "ts_2/raw", "type": "float", "visibility": 3},
|
||||||
|
{"path": "set", "type": "float", "readonly": false, "cmd": "tt set", "kids": 18},
|
||||||
|
{"path": "set/mode", "type": "enum", "enum": {"disabled": -1, "off": 0, "controlling": 1, "manual": 2}, "readonly": false, "cmd": "tt set/mode"},
|
||||||
|
{"path": "set/reg", "type": "float"},
|
||||||
|
{"path": "set/ramp", "type": "float", "readonly": false, "cmd": "tt set/ramp", "description": "maximum ramp in K/min (0: ramp off)"},
|
||||||
|
{"path": "set/wramp", "type": "float", "readonly": false, "cmd": "tt set/wramp"},
|
||||||
|
{"path": "set/smooth", "type": "float", "readonly": false, "cmd": "tt set/smooth", "description": "smooth time (minutes)"},
|
||||||
|
{"path": "set/channel", "type": "text", "readonly": false, "cmd": "tt set/channel"},
|
||||||
|
{"path": "set/limit", "type": "float", "readonly": false, "cmd": "tt set/limit"},
|
||||||
|
{"path": "set/resist", "type": "float", "readonly": false, "cmd": "tt set/resist"},
|
||||||
|
{"path": "set/maxheater", "type": "text", "readonly": false, "cmd": "tt set/maxheater", "description": "maximum heater limit, units should be given without space: W, mW, A, mA"},
|
||||||
|
{"path": "set/linearpower", "type": "float", "readonly": false, "cmd": "tt set/linearpower", "description": "when not 0, it is the maximum effective power, and the power is linear to the heater output"},
|
||||||
|
{"path": "set/maxpowerlim", "type": "float", "description": "the maximum power limit (before any booster or converter)"},
|
||||||
|
{"path": "set/maxpower", "type": "float", "readonly": false, "cmd": "tt set/maxpower", "description": "maximum power [W]"},
|
||||||
|
{"path": "set/maxcurrent", "type": "float", "description": "the maximum current before any booster or converter"},
|
||||||
|
{"path": "set/power", "type": "float"},
|
||||||
|
{"path": "set/prop", "type": "float", "readonly": false, "cmd": "tt set/prop", "description": "bigger means more gain"},
|
||||||
|
{"path": "set/integ", "type": "float", "readonly": false, "cmd": "tt set/integ", "description": "bigger means faster"},
|
||||||
|
{"path": "set/deriv", "type": "float", "readonly": false, "cmd": "tt set/deriv"},
|
||||||
|
{"path": "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/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)"},
|
||||||
|
{"path": "setsamp/channel", "type": "text", "readonly": false, "cmd": "tt setsamp/channel"},
|
||||||
|
{"path": "setsamp/limit", "type": "float", "readonly": false, "cmd": "tt setsamp/limit"},
|
||||||
|
{"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/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/power", "type": "float"},
|
||||||
|
{"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": "voltage/manualpower", "type": "float", "readonly": false, "cmd": "tt voltage/manualpower"},
|
||||||
|
{"path": "display", "type": "text", "readonly": false, "cmd": "tt display"},
|
||||||
|
{"path": "remote", "type": "bool"}]},
|
||||||
|
|
||||||
|
"cc": {"base": "/cc", "params": [
|
||||||
|
{"path": "", "type": "bool", "kids": 96},
|
||||||
|
{"path": "send", "type": "text", "readonly": false, "cmd": "cc send", "visibility": 3},
|
||||||
|
{"path": "status", "type": "text", "visibility": 3},
|
||||||
|
{"path": "autodevice", "type": "bool", "readonly": false, "cmd": "cc autodevice"},
|
||||||
|
{"path": "fav", "type": "bool", "readonly": false, "cmd": "cc fav"},
|
||||||
|
{"path": "f", "type": "float"},
|
||||||
|
{"path": "fs", "type": "enum", "enum": {"ok": 0, "no_sens": 1}, "readonly": false, "cmd": "cc fs"},
|
||||||
|
{"path": "mav", "type": "bool", "readonly": false, "cmd": "cc mav"},
|
||||||
|
{"path": "fm", "type": "enum", "enum": {"idle": 0, "opening": 1, "closing": 2, "opened": 3, "closed": 4, "no_motor": 5}},
|
||||||
|
{"path": "fa", "type": "enum", "enum": {"fixed": 0, "controlled": 1, "automatic": 2, "offline": 3}, "readonly": false, "cmd": "cc fa"},
|
||||||
|
{"path": "mp", "type": "float", "readonly": false, "cmd": "cc mp"},
|
||||||
|
{"path": "msp", "type": "float"},
|
||||||
|
{"path": "mmp", "type": "float"},
|
||||||
|
{"path": "mc", "type": "float", "readonly": false, "cmd": "cc mc"},
|
||||||
|
{"path": "mfc", "type": "float", "readonly": false, "cmd": "cc mfc"},
|
||||||
|
{"path": "moc", "type": "float", "readonly": false, "cmd": "cc moc"},
|
||||||
|
{"path": "mtc", "type": "float", "readonly": false, "cmd": "cc mtc"},
|
||||||
|
{"path": "mtl", "type": "float"},
|
||||||
|
{"path": "mft", "type": "float", "readonly": false, "cmd": "cc mft"},
|
||||||
|
{"path": "mt", "type": "float"},
|
||||||
|
{"path": "mo", "type": "float"},
|
||||||
|
{"path": "mcr", "type": "float"},
|
||||||
|
{"path": "mot", "type": "float"},
|
||||||
|
{"path": "mw", "type": "float", "readonly": false, "cmd": "cc mw", "description": "correction pulse after automatic open"},
|
||||||
|
{"path": "hav", "type": "bool", "readonly": false, "cmd": "cc hav"},
|
||||||
|
{"path": "h", "type": "float"},
|
||||||
|
{"path": "hr", "type": "float"},
|
||||||
|
{"path": "hc", "type": "float"},
|
||||||
|
{"path": "hu", "type": "float"},
|
||||||
|
{"path": "hh", "type": "float", "readonly": false, "cmd": "cc hh"},
|
||||||
|
{"path": "hl", "type": "float", "readonly": false, "cmd": "cc hl"},
|
||||||
|
{"path": "htf", "type": "float", "readonly": false, "cmd": "cc htf", "description": "meas. period in fast mode"},
|
||||||
|
{"path": "hts", "type": "float", "readonly": false, "cmd": "cc hts", "description": "meas. period in slow mode"},
|
||||||
|
{"path": "hd", "type": "float", "readonly": false, "cmd": "cc hd"},
|
||||||
|
{"path": "hwr", "type": "float", "readonly": false, "cmd": "cc hwr"},
|
||||||
|
{"path": "hem", "type": "float", "readonly": false, "cmd": "cc hem", "description": "sensor length in mm from top to empty pos."},
|
||||||
|
{"path": "hfu", "type": "float", "readonly": false, "cmd": "cc hfu", "description": "sensor length in mm from top to full pos."},
|
||||||
|
{"path": "hcd", "type": "enum", "enum": {"stop": 0, "fill": 1, "off": 2, "auto": 3, "manual": 7}, "readonly": false, "cmd": "cc hcd"},
|
||||||
|
{"path": "hv", "type": "enum", "enum": {"fill_valve_off": 0, "filling": 1, "no_fill_valve": 2, "timeout": 3, "timeout1": 4}},
|
||||||
|
{"path": "hsf", "type": "enum", "enum": {"sens_ok": 0, "sens_warm": 1, "no_sens": 2, "timeout": 3, "not_yet_read": 4, "disabled": 5}},
|
||||||
|
{"path": "ha", "type": "bool", "readonly": false, "cmd": "cc ha"},
|
||||||
|
{"path": "hm", "type": "bool"},
|
||||||
|
{"path": "hf", "type": "enum", "enum": {"slow": 0, "fast": 1}, "readonly": false, "cmd": "cc hf"},
|
||||||
|
{"path": "hbe", "type": "bool", "readonly": false, "cmd": "cc hbe"},
|
||||||
|
{"path": "hmf", "type": "float"},
|
||||||
|
{"path": "hms", "type": "float"},
|
||||||
|
{"path": "hit", "type": "float", "readonly": false, "cmd": "cc hit"},
|
||||||
|
{"path": "hft", "type": "int", "readonly": false, "cmd": "cc hft"},
|
||||||
|
{"path": "hea", "type": "enum", "enum": {"0": 0, "1": 1, "6": 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": "hfb", "type": "float"},
|
||||||
|
{"path": "nav", "type": "bool", "readonly": false, "cmd": "cc nav"},
|
||||||
|
{"path": "nu", "type": "float"},
|
||||||
|
{"path": "nl", "type": "float"},
|
||||||
|
{"path": "nth", "type": "float", "readonly": false, "cmd": "cc nth"},
|
||||||
|
{"path": "ntc", "type": "float", "readonly": false, "cmd": "cc ntc"},
|
||||||
|
{"path": "ntm", "type": "float", "readonly": false, "cmd": "cc ntm"},
|
||||||
|
{"path": "ns", "type": "enum", "enum": {"sens_ok": 0, "no_sens": 1, "short_circuit": 2, "upside_down": 3, "sens_warm": 4, "empty": 5}},
|
||||||
|
{"path": "na", "type": "bool", "readonly": false, "cmd": "cc na"},
|
||||||
|
{"path": "nv", "type": "enum", "enum": {"fill_valve_off": 0, "filling": 1, "no_fill_valve": 2, "timeout": 3, "timeout1": 4, "boost": 5}},
|
||||||
|
{"path": "nc", "type": "enum", "enum": {"stop": 0, "fill": 1, "off": 2, "auto": 3}, "readonly": false, "cmd": "cc nc"},
|
||||||
|
{"path": "nfb", "type": "float"},
|
||||||
|
{"path": "cda", "type": "float"},
|
||||||
|
{"path": "cdb", "type": "float"},
|
||||||
|
{"path": "cba", "type": "float"},
|
||||||
|
{"path": "cbb", "type": "float"},
|
||||||
|
{"path": "cvs", "type": "int"},
|
||||||
|
{"path": "csp", "type": "int"},
|
||||||
|
{"path": "cdv", "type": "text", "readonly": false, "cmd": "cc cdv"},
|
||||||
|
{"path": "cic", "type": "text", "readonly": false, "cmd": "cc cic"},
|
||||||
|
{"path": "cin", "type": "text"},
|
||||||
|
{"path": "cds", "type": "enum", "enum": {"local": 0, "remote": 1, "loading": 2, "by_code": 3, "by_touch": 4}, "readonly": false, "cmd": "cc cds"},
|
||||||
|
{"path": "timing", "type": "bool", "readonly": false, "cmd": "cc timing"},
|
||||||
|
{"path": "tc", "type": "float", "visibility": 3},
|
||||||
|
{"path": "tn", "type": "float", "visibility": 3},
|
||||||
|
{"path": "th", "type": "float", "visibility": 3},
|
||||||
|
{"path": "tf", "type": "float", "visibility": 3},
|
||||||
|
{"path": "tm", "type": "float", "visibility": 3},
|
||||||
|
{"path": "tv", "type": "float", "visibility": 3},
|
||||||
|
{"path": "tq", "type": "float", "visibility": 3},
|
||||||
|
{"path": "bdl", "type": "float", "readonly": false, "cmd": "cc bdl"}]},
|
||||||
|
|
||||||
|
"nv": {"base": "/nv", "params": [
|
||||||
|
{"path": "", "type": "enum", "enum": {"fixed": 0, "controlled": 1, "automatic": 2, "close": 3, "open": 4}, "readonly": false, "cmd": "nv", "kids": 11},
|
||||||
|
{"path": "send", "type": "text", "readonly": false, "cmd": "nv send", "visibility": 3},
|
||||||
|
{"path": "status", "type": "text", "visibility": 3},
|
||||||
|
{"path": "motstat", "type": "enum", "enum": {"idle": 0, "opening": 1, "closing": 2, "opened": 3, "closed": 4, "no_motor": 5}},
|
||||||
|
{"path": "flow", "type": "float"},
|
||||||
|
{"path": "set", "type": "float", "readonly": false, "cmd": "nv set"},
|
||||||
|
{"path": "flowmax", "type": "float", "readonly": false, "cmd": "nv flowmax"},
|
||||||
|
{"path": "flowp", "type": "float"},
|
||||||
|
{"path": "span", "type": "float"},
|
||||||
|
{"path": "ctrl", "type": "none", "kids": 13},
|
||||||
|
{"path": "ctrl/regtext", "type": "text"},
|
||||||
|
{"path": "ctrl/prop_o", "type": "float", "readonly": false, "cmd": "nv ctrl/prop_o", "description": "prop [sec/mbar] when opening. above 4 mbar a 10 times lower value is used"},
|
||||||
|
{"path": "ctrl/prop_c", "type": "float", "readonly": false, "cmd": "nv ctrl/prop_c", "description": "prop [sec/mbar] when closing. above 4 mbar a 10 times lower value is used"},
|
||||||
|
{"path": "ctrl/deriv_o", "type": "float", "readonly": false, "cmd": "nv ctrl/deriv_o", "description": "convergence target time [sec] when opening"},
|
||||||
|
{"path": "ctrl/deriv_c", "type": "float", "readonly": false, "cmd": "nv ctrl/deriv_c", "description": "convergence target time [sec] when closing"},
|
||||||
|
{"path": "ctrl/minpulse_o", "type": "float", "readonly": false, "cmd": "nv ctrl/minpulse_o", "description": "minimum close pulse [sec]"},
|
||||||
|
{"path": "ctrl/minpulse_c", "type": "float", "readonly": false, "cmd": "nv ctrl/minpulse_c", "description": "standard close pulse [sec]"},
|
||||||
|
{"path": "ctrl/hystpulse_o", "type": "float", "readonly": false, "cmd": "nv ctrl/hystpulse_o", "description": "motor pulse to overcome hysteresis when opening"},
|
||||||
|
{"path": "ctrl/hystpulse_c", "type": "float", "readonly": false, "cmd": "nv ctrl/hystpulse_c", "description": "motor pulse to overcome hysteresis when closing"},
|
||||||
|
{"path": "ctrl/tol", "type": "float", "readonly": false, "cmd": "nv ctrl/tol", "description": "valid below 3 mbar"},
|
||||||
|
{"path": "ctrl/tolhigh", "type": "float", "readonly": false, "cmd": "nv ctrl/tolhigh", "description": "valid above 4 mbar"},
|
||||||
|
{"path": "ctrl/openpulse", "type": "float", "readonly": false, "cmd": "nv ctrl/openpulse", "description": "time to open from completely closed to a significant opening"},
|
||||||
|
{"path": "ctrl/adjust_minpulse", "type": "bool", "readonly": false, "cmd": "nv ctrl/adjust_minpulse", "description": "adjust minpulse automatically"},
|
||||||
|
{"path": "autoflow", "type": "none", "kids": 24},
|
||||||
|
{"path": "autoflow/suspended", "type": "bool", "readonly": false, "cmd": "nv autoflow/suspended"},
|
||||||
|
{"path": "autoflow/prop", "type": "float", "readonly": false, "cmd": "nv autoflow/prop"},
|
||||||
|
{"path": "autoflow/flowstd", "type": "float", "readonly": false, "cmd": "nv autoflow/flowstd"},
|
||||||
|
{"path": "autoflow/flowlim", "type": "float", "readonly": false, "cmd": "nv autoflow/flowlim"},
|
||||||
|
{"path": "autoflow/smooth", "type": "float", "readonly": false, "cmd": "nv autoflow/smooth"},
|
||||||
|
{"path": "autoflow/difSize", "type": "float", "readonly": false, "cmd": "nv autoflow/difSize"},
|
||||||
|
{"path": "autoflow/difRange", "type": "float", "readonly": false, "cmd": "nv autoflow/difRange"},
|
||||||
|
{"path": "autoflow/flowSize", "type": "float", "readonly": false, "cmd": "nv autoflow/flowSize"},
|
||||||
|
{"path": "autoflow/convTime", "type": "float", "readonly": false, "cmd": "nv autoflow/convTime"},
|
||||||
|
{"path": "autoflow/Tmin", "type": "float", "readonly": false, "cmd": "nv autoflow/Tmin"},
|
||||||
|
{"path": "autoflow/script", "type": "text", "readonly": false, "cmd": "nv autoflow/script"},
|
||||||
|
{"path": "autoflow/getTemp", "type": "text", "readonly": false, "cmd": "nv autoflow/getTemp"},
|
||||||
|
{"path": "autoflow/getTset", "type": "text", "readonly": false, "cmd": "nv autoflow/getTset"},
|
||||||
|
{"path": "autoflow/getFlow", "type": "text", "readonly": false, "cmd": "nv autoflow/getFlow"},
|
||||||
|
{"path": "autoflow/difBuf", "type": "text"},
|
||||||
|
{"path": "autoflow/flowBuf", "type": "text"},
|
||||||
|
{"path": "autoflow/flowset", "type": "float"},
|
||||||
|
{"path": "autoflow/flowmin", "type": "float"},
|
||||||
|
{"path": "autoflow/flowmax", "type": "float"},
|
||||||
|
{"path": "autoflow/difmin", "type": "float"},
|
||||||
|
{"path": "autoflow/difmax", "type": "float"},
|
||||||
|
{"path": "autoflow/setmin", "type": "float"},
|
||||||
|
{"path": "autoflow/setmax", "type": "float"},
|
||||||
|
{"path": "autoflow/flowtarget", "type": "float"},
|
||||||
|
{"path": "calib", "type": "none", "kids": 2},
|
||||||
|
{"path": "calib/ln_per_min_per_mbar", "type": "float", "readonly": false, "cmd": "nv calib/ln_per_min_per_mbar"},
|
||||||
|
{"path": "calib/mbar_offset", "type": "float", "readonly": false, "cmd": "nv calib/mbar_offset"}]},
|
||||||
|
|
||||||
|
"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": "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"}]},
|
||||||
|
|
||||||
|
"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"},
|
||||||
|
{"path": "auto", "type": "bool", "readonly": false, "cmd": "hepump auto"},
|
||||||
|
{"path": "valve", "type": "enum", "enum": {"closed": 0, "closing": 1, "opening": 2, "opened": 3, "undefined": 4}, "readonly": false, "cmd": "hepump valve"},
|
||||||
|
{"path": "eco_t_lim", "type": "float", "readonly": false, "cmd": "hepump eco_t_lim", "description": "switch off eco mode when T_set < eco_t_lim and T < eco_t_lim * 2"},
|
||||||
|
{"path": "calib", "type": "float", "readonly": false, "cmd": "hepump calib", "visibility": 3},
|
||||||
|
{"path": "health", "type": "float"}]},
|
||||||
|
|
||||||
|
"hemot": {"base": "/hepump/hemot", "params": [
|
||||||
|
{"path": "", "type": "float", "readonly": false, "cmd": "run hemot", "kids": 30},
|
||||||
|
{"path": "send", "type": "text", "readonly": false, "cmd": "hemot send", "visibility": 3},
|
||||||
|
{"path": "status", "type": "text", "visibility": 3},
|
||||||
|
{"path": "is_running", "type": "int", "readonly": false, "cmd": "hemot is_running", "visibility": 3},
|
||||||
|
{"path": "pos", "type": "float"},
|
||||||
|
{"path": "encoder", "type": "float"},
|
||||||
|
{"path": "zero", "type": "float", "readonly": false, "cmd": "hemot zero"},
|
||||||
|
{"path": "lowerlimit", "type": "float", "readonly": false, "cmd": "hemot lowerlimit"},
|
||||||
|
{"path": "upperlimit", "type": "float", "readonly": false, "cmd": "hemot upperlimit"},
|
||||||
|
{"path": "disablelimits", "type": "bool", "readonly": false, "cmd": "hemot disablelimits"},
|
||||||
|
{"path": "verbose", "type": "bool", "readonly": false, "cmd": "hemot verbose"},
|
||||||
|
{"path": "target", "type": "float"},
|
||||||
|
{"path": "runstate", "type": "enum", "enum": {"idle": 0, "running": 1, "finished": 2, "error": 3}},
|
||||||
|
{"path": "precision", "type": "float", "readonly": false, "cmd": "hemot precision"},
|
||||||
|
{"path": "maxencdif", "type": "float", "readonly": false, "cmd": "hemot maxencdif"},
|
||||||
|
{"path": "id", "type": "float", "readonly": false, "cmd": "hemot id"},
|
||||||
|
{"path": "pump_number", "type": "float", "readonly": false, "cmd": "hemot pump_number"},
|
||||||
|
{"path": "init", "type": "float", "readonly": false, "cmd": "hemot init"},
|
||||||
|
{"path": "maxspeed", "type": "float", "readonly": false, "cmd": "hemot maxspeed"},
|
||||||
|
{"path": "acceleration", "type": "float", "readonly": false, "cmd": "hemot acceleration"},
|
||||||
|
{"path": "maxcurrent", "type": "float", "readonly": false, "cmd": "hemot maxcurrent"},
|
||||||
|
{"path": "standbycurrent", "type": "float", "readonly": false, "cmd": "hemot standbycurrent"},
|
||||||
|
{"path": "freewheeling", "type": "bool", "readonly": false, "cmd": "hemot freewheeling"},
|
||||||
|
{"path": "output0", "type": "bool", "readonly": false, "cmd": "hemot output0"},
|
||||||
|
{"path": "output1", "type": "bool", "readonly": false, "cmd": "hemot output1"},
|
||||||
|
{"path": "input3", "type": "bool"},
|
||||||
|
{"path": "pullup", "type": "float", "readonly": false, "cmd": "hemot pullup"},
|
||||||
|
{"path": "nopumpfeedback", "type": "bool", "readonly": false, "cmd": "hemot nopumpfeedback"},
|
||||||
|
{"path": "eeprom", "type": "enum", "enum": {"ok": 0, "dirty": 1, "save": 2, "load": 3}, "readonly": false, "cmd": "hemot eeprom"},
|
||||||
|
{"path": "customadr", "type": "text", "readonly": false, "cmd": "hemot customadr"},
|
||||||
|
{"path": "custompar", "type": "float", "readonly": false, "cmd": "hemot custompar"}]},
|
||||||
|
|
||||||
|
"nvflow": {"base": "/nvflow", "params": [
|
||||||
|
{"path": "", "type": "float", "kids": 7},
|
||||||
|
{"path": "send", "type": "text", "readonly": false, "cmd": "nvflow send", "visibility": 3},
|
||||||
|
{"path": "status", "type": "text", "visibility": 3},
|
||||||
|
{"path": "stddev", "type": "float"},
|
||||||
|
{"path": "nsamples", "type": "int", "readonly": false, "cmd": "nvflow nsamples"},
|
||||||
|
{"path": "offset", "type": "float", "readonly": false, "cmd": "nvflow offset"},
|
||||||
|
{"path": "scale", "type": "float", "readonly": false, "cmd": "nvflow scale"},
|
||||||
|
{"path": "save", "type": "bool", "readonly": false, "cmd": "nvflow save", "description": "unchecked: current calib is not saved. set checked: save calib"}]},
|
||||||
|
|
||||||
|
"ln2fill": {"base": "/ln2fill", "params": [
|
||||||
|
{"path": "", "type": "enum", "enum": {"watching": 0, "filling": 1, "inactive": 2, "manualfill": 3}, "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": "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"},
|
||||||
|
{"path": "minfillminutes", "type": "float", "readonly": false, "cmd": "ln2fill minfillminutes"},
|
||||||
|
{"path": "maxfillminutes", "type": "float", "readonly": false, "cmd": "ln2fill maxfillminutes"},
|
||||||
|
{"path": "minholdhours", "type": "float", "readonly": false, "cmd": "ln2fill minholdhours"},
|
||||||
|
{"path": "maxholdhours", "type": "float", "readonly": false, "cmd": "ln2fill maxholdhours"},
|
||||||
|
{"path": "tolerance", "type": "float", "readonly": false, "cmd": "ln2fill tolerance"},
|
||||||
|
{"path": "badreadingminutes", "type": "float", "readonly": false, "cmd": "ln2fill badreadingminutes"},
|
||||||
|
{"path": "tubecoolingminutes", "type": "float", "readonly": false, "cmd": "ln2fill tubecoolingminutes"}]},
|
||||||
|
|
||||||
|
"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": "nowait", "type": "int", "readonly": false, "cmd": "mf nowait"},
|
||||||
|
{"path": "maxlimit", "type": "float", "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": "trainedTo", "type": "float", "readonly": false, "cmd": "mf trainedTo"},
|
||||||
|
{"path": "trainMode", "type": "int"},
|
||||||
|
{"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": "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": "status", "type": "text", "readonly": false, "cmd": "mf status", "visibility": 3}]},
|
||||||
|
|
||||||
|
"lev": {"base": "/lev", "params": [
|
||||||
|
{"path": "", "type": "float", "kids": 4},
|
||||||
|
{"path": "send", "type": "text", "readonly": false, "cmd": "lev send", "visibility": 3},
|
||||||
|
{"path": "status", "type": "text", "visibility": 3},
|
||||||
|
{"path": "mode", "type": "enum", "enum": {"slow": 0, "fast (switches to slow automatically after filling)": 1}, "readonly": false, "cmd": "lev mode"},
|
||||||
|
{"path": "n2", "type": "float"}]},
|
||||||
|
|
||||||
|
"tcoil": {"base": "/tcoil", "params": [
|
||||||
|
{"path": "", "type": "float", "kids": 11},
|
||||||
|
{"path": "send", "type": "text", "readonly": false, "cmd": "tcoil send", "visibility": 3},
|
||||||
|
{"path": "status", "type": "text", "visibility": 3},
|
||||||
|
{"path": "excitation", "type": "float", "readonly": false, "cmd": "tcoil excitation", "visibility": 3},
|
||||||
|
{"path": "ta", "type": "float", "kids": 3},
|
||||||
|
{"path": "ta/enable", "type": "bool", "readonly": false, "cmd": "tcoil ta/enable"},
|
||||||
|
{"path": "ta/r", "type": "float"},
|
||||||
|
{"path": "ta/curve", "type": "text", "readonly": false, "cmd": "tcoil ta/curve", "kids": 3},
|
||||||
|
{"path": "ta/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil ta/curve/adjust"},
|
||||||
|
{"path": "ta/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil ta/curve/points"},
|
||||||
|
{"path": "ta/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil ta/curve/cpoints"},
|
||||||
|
{"path": "tb", "type": "float", "kids": 3},
|
||||||
|
{"path": "tb/enable", "type": "bool", "readonly": false, "cmd": "tcoil tb/enable"},
|
||||||
|
{"path": "tb/r", "type": "float"},
|
||||||
|
{"path": "tb/curve", "type": "text", "readonly": false, "cmd": "tcoil tb/curve", "kids": 3},
|
||||||
|
{"path": "tb/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil tb/curve/adjust"},
|
||||||
|
{"path": "tb/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil tb/curve/points"},
|
||||||
|
{"path": "tb/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil tb/curve/cpoints"},
|
||||||
|
{"path": "td", "type": "float", "visibility": 3, "kids": 3},
|
||||||
|
{"path": "td/enable", "type": "bool", "readonly": false, "cmd": "tcoil td/enable"},
|
||||||
|
{"path": "td/r", "type": "float"},
|
||||||
|
{"path": "td/curve", "type": "text", "readonly": false, "cmd": "tcoil td/curve", "kids": 3},
|
||||||
|
{"path": "td/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil td/curve/adjust"},
|
||||||
|
{"path": "td/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil td/curve/points"},
|
||||||
|
{"path": "td/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil td/curve/cpoints"},
|
||||||
|
{"path": "ref", "type": "float", "visibility": 3, "kids": 3},
|
||||||
|
{"path": "ref/enable", "type": "bool", "readonly": false, "cmd": "tcoil ref/enable"},
|
||||||
|
{"path": "ref/r", "type": "float"},
|
||||||
|
{"path": "ref/curve", "type": "text", "readonly": false, "cmd": "tcoil ref/curve", "kids": 3},
|
||||||
|
{"path": "ref/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil ref/curve/adjust"},
|
||||||
|
{"path": "ref/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil ref/curve/points"},
|
||||||
|
{"path": "ref/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil ref/curve/cpoints"},
|
||||||
|
{"path": "tc", "type": "float", "visibility": 3, "kids": 3},
|
||||||
|
{"path": "tc/enable", "type": "bool", "readonly": false, "cmd": "tcoil tc/enable"},
|
||||||
|
{"path": "tc/r", "type": "float"},
|
||||||
|
{"path": "tc/curve", "type": "text", "readonly": false, "cmd": "tcoil tc/curve", "kids": 3},
|
||||||
|
{"path": "tc/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil tc/curve/adjust"},
|
||||||
|
{"path": "tc/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil tc/curve/points"},
|
||||||
|
{"path": "tc/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil tc/curve/cpoints"},
|
||||||
|
{"path": "ext", "type": "float", "visibility": 3},
|
||||||
|
{"path": "com", "type": "float", "visibility": 3},
|
||||||
|
{"path": "gnd", "type": "float", "visibility": 3}]},
|
||||||
|
|
||||||
|
"table": {"base": "/table", "params": [
|
||||||
|
{"path": "", "type": "none", "kids": 17},
|
||||||
|
{"path": "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 ..."}]}}
|
421
cfg/sea/ma7_thermalc.config.json
Normal file
421
cfg/sea/ma7_thermalc.config.json
Normal file
@ -0,0 +1,421 @@
|
|||||||
|
{"tt": {"base": "/tt", "params": [
|
||||||
|
{"path": "", "type": "float", "readonly": false, "cmd": "run tt", "description": "tt", "kids": 17},
|
||||||
|
{"path": "send", "type": "text", "readonly": false, "cmd": "tt send", "visibility": 3},
|
||||||
|
{"path": "status", "type": "text", "visibility": 3},
|
||||||
|
{"path": "is_running", "type": "int", "readonly": false, "cmd": "tt is_running", "visibility": 3},
|
||||||
|
{"path": "mainloop", "type": "text", "readonly": false, "cmd": "tt mainloop", "visibility": 3},
|
||||||
|
{"path": "target", "type": "float"},
|
||||||
|
{"path": "running", "type": "int"},
|
||||||
|
{"path": "tolerance", "type": "float", "readonly": false, "cmd": "tt tolerance"},
|
||||||
|
{"path": "maxwait", "type": "float", "readonly": false, "cmd": "tt maxwait"},
|
||||||
|
{"path": "settle", "type": "float", "readonly": false, "cmd": "tt settle"},
|
||||||
|
{"path": "log", "type": "text", "readonly": false, "cmd": "tt log", "visibility": 3, "kids": 4},
|
||||||
|
{"path": "log/mean", "type": "float", "visibility": 3},
|
||||||
|
{"path": "log/m2", "type": "float", "visibility": 3},
|
||||||
|
{"path": "log/stddev", "type": "float", "visibility": 3},
|
||||||
|
{"path": "log/n", "type": "float", "visibility": 3},
|
||||||
|
{"path": "dblctrl", "type": "bool", "readonly": false, "cmd": "tt dblctrl", "kids": 9},
|
||||||
|
{"path": "dblctrl/tshift", "type": "float", "readonly": false, "cmd": "tt dblctrl/tshift"},
|
||||||
|
{"path": "dblctrl/mode", "type": "enum", "enum": {"disabled": -1, "inactive": 0, "stable": 1, "up": 2, "down": 3}, "readonly": false, "cmd": "tt dblctrl/mode"},
|
||||||
|
{"path": "dblctrl/shift_up", "type": "float"},
|
||||||
|
{"path": "dblctrl/shift_lo", "type": "float"},
|
||||||
|
{"path": "dblctrl/t_min", "type": "float"},
|
||||||
|
{"path": "dblctrl/t_max", "type": "float"},
|
||||||
|
{"path": "dblctrl/int2", "type": "float", "readonly": false, "cmd": "tt dblctrl/int2"},
|
||||||
|
{"path": "dblctrl/prop_up", "type": "float", "readonly": false, "cmd": "tt dblctrl/prop_up"},
|
||||||
|
{"path": "dblctrl/prop_lo", "type": "float", "readonly": false, "cmd": "tt dblctrl/prop_lo"},
|
||||||
|
{"path": "tm", "type": "float", "kids": 4},
|
||||||
|
{"path": "tm/curve", "type": "text", "readonly": false, "cmd": "tt tm/curve", "kids": 1},
|
||||||
|
{"path": "tm/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt tm/curve/points", "visibility": 3},
|
||||||
|
{"path": "tm/alarm", "type": "float", "readonly": false, "cmd": "tt tm/alarm"},
|
||||||
|
{"path": "tm/stddev", "type": "float"},
|
||||||
|
{"path": "tm/raw", "type": "float"},
|
||||||
|
{"path": "ts", "type": "float", "kids": 4},
|
||||||
|
{"path": "ts/curve", "type": "text", "readonly": false, "cmd": "tt ts/curve", "kids": 1},
|
||||||
|
{"path": "ts/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt ts/curve/points", "visibility": 3},
|
||||||
|
{"path": "ts/alarm", "type": "float", "readonly": false, "cmd": "tt ts/alarm"},
|
||||||
|
{"path": "ts/stddev", "type": "float"},
|
||||||
|
{"path": "ts/raw", "type": "float"},
|
||||||
|
{"path": "set", "type": "float", "readonly": false, "cmd": "tt set", "kids": 18},
|
||||||
|
{"path": "set/mode", "type": "enum", "enum": {"disabled": -1, "off": 0, "controlling": 1, "manual": 2}, "readonly": false, "cmd": "tt set/mode"},
|
||||||
|
{"path": "set/reg", "type": "float"},
|
||||||
|
{"path": "set/ramp", "type": "float", "readonly": false, "cmd": "tt set/ramp", "description": "maximum ramp in K/min (0: ramp off)"},
|
||||||
|
{"path": "set/wramp", "type": "float", "readonly": false, "cmd": "tt set/wramp"},
|
||||||
|
{"path": "set/smooth", "type": "float", "readonly": false, "cmd": "tt set/smooth", "description": "smooth time (minutes)"},
|
||||||
|
{"path": "set/channel", "type": "text", "readonly": false, "cmd": "tt set/channel"},
|
||||||
|
{"path": "set/limit", "type": "float", "readonly": false, "cmd": "tt set/limit"},
|
||||||
|
{"path": "set/resist", "type": "float", "readonly": false, "cmd": "tt set/resist"},
|
||||||
|
{"path": "set/maxheater", "type": "text", "readonly": false, "cmd": "tt set/maxheater", "description": "maximum heater limit, units should be given without space: W, mW, A, mA"},
|
||||||
|
{"path": "set/linearpower", "type": "float", "readonly": false, "cmd": "tt set/linearpower", "description": "when not 0, it is the maximum effective power, and the power is linear to the heater output"},
|
||||||
|
{"path": "set/maxpowerlim", "type": "float", "description": "the maximum power limit (before any booster or converter)"},
|
||||||
|
{"path": "set/maxpower", "type": "float", "readonly": false, "cmd": "tt set/maxpower", "description": "maximum power [W]"},
|
||||||
|
{"path": "set/maxcurrent", "type": "float", "description": "the maximum current before any booster or converter"},
|
||||||
|
{"path": "set/manualpower", "type": "float", "readonly": false, "cmd": "tt set/manualpower"},
|
||||||
|
{"path": "set/power", "type": "float"},
|
||||||
|
{"path": "set/prop", "type": "float", "readonly": false, "cmd": "tt set/prop", "description": "bigger means more gain"},
|
||||||
|
{"path": "set/integ", "type": "float", "readonly": false, "cmd": "tt set/integ", "description": "bigger means faster"},
|
||||||
|
{"path": "set/deriv", "type": "float", "readonly": false, "cmd": "tt set/deriv"},
|
||||||
|
{"path": "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/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)"},
|
||||||
|
{"path": "setsamp/channel", "type": "text", "readonly": false, "cmd": "tt setsamp/channel"},
|
||||||
|
{"path": "setsamp/limit", "type": "float", "readonly": false, "cmd": "tt setsamp/limit"},
|
||||||
|
{"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/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/manualpower", "type": "float", "readonly": false, "cmd": "tt setsamp/manualpower"},
|
||||||
|
{"path": "setsamp/power", "type": "float"},
|
||||||
|
{"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"}]},
|
||||||
|
|
||||||
|
"cc": {"base": "/cc", "params": [
|
||||||
|
{"path": "", "type": "bool", "kids": 96},
|
||||||
|
{"path": "send", "type": "text", "readonly": false, "cmd": "cc send", "visibility": 3},
|
||||||
|
{"path": "status", "type": "text", "visibility": 3},
|
||||||
|
{"path": "autodevice", "type": "bool", "readonly": false, "cmd": "cc autodevice"},
|
||||||
|
{"path": "fav", "type": "bool", "readonly": false, "cmd": "cc fav"},
|
||||||
|
{"path": "f", "type": "float"},
|
||||||
|
{"path": "fs", "type": "enum", "enum": {"ok": 0, "no_sens": 1}, "readonly": false, "cmd": "cc fs"},
|
||||||
|
{"path": "mav", "type": "bool", "readonly": false, "cmd": "cc mav"},
|
||||||
|
{"path": "fm", "type": "enum", "enum": {"idle": 0, "opening": 1, "closing": 2, "opened": 3, "closed": 4, "no_motor": 5}},
|
||||||
|
{"path": "fa", "type": "enum", "enum": {"fixed": 0, "controlled": 1, "automatic": 2, "offline": 3}, "readonly": false, "cmd": "cc fa"},
|
||||||
|
{"path": "mp", "type": "float", "readonly": false, "cmd": "cc mp"},
|
||||||
|
{"path": "msp", "type": "float"},
|
||||||
|
{"path": "mmp", "type": "float"},
|
||||||
|
{"path": "mc", "type": "float", "readonly": false, "cmd": "cc mc"},
|
||||||
|
{"path": "mfc", "type": "float", "readonly": false, "cmd": "cc mfc"},
|
||||||
|
{"path": "moc", "type": "float", "readonly": false, "cmd": "cc moc"},
|
||||||
|
{"path": "mtc", "type": "float", "readonly": false, "cmd": "cc mtc"},
|
||||||
|
{"path": "mtl", "type": "float"},
|
||||||
|
{"path": "mft", "type": "float", "readonly": false, "cmd": "cc mft"},
|
||||||
|
{"path": "mt", "type": "float"},
|
||||||
|
{"path": "mo", "type": "float"},
|
||||||
|
{"path": "mcr", "type": "float"},
|
||||||
|
{"path": "mot", "type": "float"},
|
||||||
|
{"path": "mw", "type": "float", "readonly": false, "cmd": "cc mw", "description": "correction pulse after automatic open"},
|
||||||
|
{"path": "hav", "type": "bool", "readonly": false, "cmd": "cc hav"},
|
||||||
|
{"path": "h", "type": "float"},
|
||||||
|
{"path": "hr", "type": "float"},
|
||||||
|
{"path": "hc", "type": "float"},
|
||||||
|
{"path": "hu", "type": "float"},
|
||||||
|
{"path": "hh", "type": "float", "readonly": false, "cmd": "cc hh"},
|
||||||
|
{"path": "hl", "type": "float", "readonly": false, "cmd": "cc hl"},
|
||||||
|
{"path": "htf", "type": "float", "readonly": false, "cmd": "cc htf", "description": "meas. period in fast mode"},
|
||||||
|
{"path": "hts", "type": "float", "readonly": false, "cmd": "cc hts", "description": "meas. period in slow mode"},
|
||||||
|
{"path": "hd", "type": "float", "readonly": false, "cmd": "cc hd"},
|
||||||
|
{"path": "hwr", "type": "float", "readonly": false, "cmd": "cc hwr"},
|
||||||
|
{"path": "hem", "type": "float", "readonly": false, "cmd": "cc hem", "description": "sensor length in mm from top to empty pos."},
|
||||||
|
{"path": "hfu", "type": "float", "readonly": false, "cmd": "cc hfu", "description": "sensor length in mm from top to full pos."},
|
||||||
|
{"path": "hcd", "type": "enum", "enum": {"stop": 0, "fill": 1, "off": 2, "auto": 3, "manual": 7}, "readonly": false, "cmd": "cc hcd"},
|
||||||
|
{"path": "hv", "type": "enum", "enum": {"fill_valve_off": 0, "filling": 1, "no_fill_valve": 2, "timeout": 3, "timeout1": 4}},
|
||||||
|
{"path": "hsf", "type": "enum", "enum": {"sens_ok": 0, "sens_warm": 1, "no_sens": 2, "timeout": 3, "not_yet_read": 4, "disabled": 5}},
|
||||||
|
{"path": "ha", "type": "bool", "readonly": false, "cmd": "cc ha"},
|
||||||
|
{"path": "hm", "type": "bool"},
|
||||||
|
{"path": "hf", "type": "enum", "enum": {"slow": 0, "fast": 1}, "readonly": false, "cmd": "cc hf"},
|
||||||
|
{"path": "hbe", "type": "bool", "readonly": false, "cmd": "cc hbe"},
|
||||||
|
{"path": "hmf", "type": "float"},
|
||||||
|
{"path": "hms", "type": "float"},
|
||||||
|
{"path": "hit", "type": "float", "readonly": false, "cmd": "cc hit"},
|
||||||
|
{"path": "hft", "type": "int", "readonly": false, "cmd": "cc hft"},
|
||||||
|
{"path": "hea", "type": "enum", "enum": {"0": 0, "1": 1, "6": 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": "hfb", "type": "float"},
|
||||||
|
{"path": "nav", "type": "bool", "readonly": false, "cmd": "cc nav"},
|
||||||
|
{"path": "nu", "type": "float"},
|
||||||
|
{"path": "nl", "type": "float"},
|
||||||
|
{"path": "nth", "type": "float", "readonly": false, "cmd": "cc nth"},
|
||||||
|
{"path": "ntc", "type": "float", "readonly": false, "cmd": "cc ntc"},
|
||||||
|
{"path": "ntm", "type": "float", "readonly": false, "cmd": "cc ntm"},
|
||||||
|
{"path": "ns", "type": "enum", "enum": {"sens_ok": 0, "no_sens": 1, "short_circuit": 2, "upside_down": 3, "sens_warm": 4, "empty": 5}},
|
||||||
|
{"path": "na", "type": "bool", "readonly": false, "cmd": "cc na"},
|
||||||
|
{"path": "nv", "type": "enum", "enum": {"fill_valve_off": 0, "filling": 1, "no_fill_valve": 2, "timeout": 3, "timeout1": 4, "boost": 5}},
|
||||||
|
{"path": "nc", "type": "enum", "enum": {"stop": 0, "fill": 1, "off": 2, "auto": 3}, "readonly": false, "cmd": "cc nc"},
|
||||||
|
{"path": "nfb", "type": "float"},
|
||||||
|
{"path": "cda", "type": "float"},
|
||||||
|
{"path": "cdb", "type": "float"},
|
||||||
|
{"path": "cba", "type": "float"},
|
||||||
|
{"path": "cbb", "type": "float"},
|
||||||
|
{"path": "cvs", "type": "int"},
|
||||||
|
{"path": "csp", "type": "int"},
|
||||||
|
{"path": "cdv", "type": "text", "readonly": false, "cmd": "cc cdv"},
|
||||||
|
{"path": "cic", "type": "text", "readonly": false, "cmd": "cc cic"},
|
||||||
|
{"path": "cin", "type": "text"},
|
||||||
|
{"path": "cds", "type": "enum", "enum": {"local": 0, "remote": 1, "loading": 2, "by_code": 3, "by_touch": 4}, "readonly": false, "cmd": "cc cds"},
|
||||||
|
{"path": "timing", "type": "bool", "readonly": false, "cmd": "cc timing"},
|
||||||
|
{"path": "tc", "type": "float", "visibility": 3},
|
||||||
|
{"path": "tn", "type": "float", "visibility": 3},
|
||||||
|
{"path": "th", "type": "float", "visibility": 3},
|
||||||
|
{"path": "tf", "type": "float", "visibility": 3},
|
||||||
|
{"path": "tm", "type": "float", "visibility": 3},
|
||||||
|
{"path": "tv", "type": "float", "visibility": 3},
|
||||||
|
{"path": "tq", "type": "float", "visibility": 3},
|
||||||
|
{"path": "bdl", "type": "float", "readonly": false, "cmd": "cc bdl"}]},
|
||||||
|
|
||||||
|
"nv": {"base": "/nv", "params": [
|
||||||
|
{"path": "", "type": "enum", "enum": {"fixed": 0, "controlled": 1, "automatic": 2, "close": 3, "open": 4}, "readonly": false, "cmd": "nv", "kids": 11},
|
||||||
|
{"path": "send", "type": "text", "readonly": false, "cmd": "nv send", "visibility": 3},
|
||||||
|
{"path": "status", "type": "text", "visibility": 3},
|
||||||
|
{"path": "motstat", "type": "enum", "enum": {"idle": 0, "opening": 1, "closing": 2, "opened": 3, "closed": 4, "no_motor": 5}},
|
||||||
|
{"path": "flow", "type": "float"},
|
||||||
|
{"path": "set", "type": "float", "readonly": false, "cmd": "nv set"},
|
||||||
|
{"path": "flowmax", "type": "float", "readonly": false, "cmd": "nv flowmax"},
|
||||||
|
{"path": "flowp", "type": "float"},
|
||||||
|
{"path": "span", "type": "float"},
|
||||||
|
{"path": "ctrl", "type": "none", "kids": 13},
|
||||||
|
{"path": "ctrl/regtext", "type": "text"},
|
||||||
|
{"path": "ctrl/prop_o", "type": "float", "readonly": false, "cmd": "nv ctrl/prop_o", "description": "prop [sec/mbar] when opening. above 4 mbar a 10 times lower value is used"},
|
||||||
|
{"path": "ctrl/prop_c", "type": "float", "readonly": false, "cmd": "nv ctrl/prop_c", "description": "prop [sec/mbar] when closing. above 4 mbar a 10 times lower value is used"},
|
||||||
|
{"path": "ctrl/deriv_o", "type": "float", "readonly": false, "cmd": "nv ctrl/deriv_o", "description": "convergence target time [sec] when opening"},
|
||||||
|
{"path": "ctrl/deriv_c", "type": "float", "readonly": false, "cmd": "nv ctrl/deriv_c", "description": "convergence target time [sec] when closing"},
|
||||||
|
{"path": "ctrl/minpulse_o", "type": "float", "readonly": false, "cmd": "nv ctrl/minpulse_o", "description": "minimum close pulse [sec]"},
|
||||||
|
{"path": "ctrl/minpulse_c", "type": "float", "readonly": false, "cmd": "nv ctrl/minpulse_c", "description": "standard close pulse [sec]"},
|
||||||
|
{"path": "ctrl/hystpulse_o", "type": "float", "readonly": false, "cmd": "nv ctrl/hystpulse_o", "description": "motor pulse to overcome hysteresis when opening"},
|
||||||
|
{"path": "ctrl/hystpulse_c", "type": "float", "readonly": false, "cmd": "nv ctrl/hystpulse_c", "description": "motor pulse to overcome hysteresis when closing"},
|
||||||
|
{"path": "ctrl/tol", "type": "float", "readonly": false, "cmd": "nv ctrl/tol", "description": "valid below 3 mbar"},
|
||||||
|
{"path": "ctrl/tolhigh", "type": "float", "readonly": false, "cmd": "nv ctrl/tolhigh", "description": "valid above 4 mbar"},
|
||||||
|
{"path": "ctrl/openpulse", "type": "float", "readonly": false, "cmd": "nv ctrl/openpulse", "description": "time to open from completely closed to a significant opening"},
|
||||||
|
{"path": "ctrl/adjust_minpulse", "type": "bool", "readonly": false, "cmd": "nv ctrl/adjust_minpulse", "description": "adjust minpulse automatically"},
|
||||||
|
{"path": "autoflow", "type": "none", "kids": 24},
|
||||||
|
{"path": "autoflow/suspended", "type": "bool", "readonly": false, "cmd": "nv autoflow/suspended"},
|
||||||
|
{"path": "autoflow/prop", "type": "float", "readonly": false, "cmd": "nv autoflow/prop"},
|
||||||
|
{"path": "autoflow/flowstd", "type": "float", "readonly": false, "cmd": "nv autoflow/flowstd"},
|
||||||
|
{"path": "autoflow/flowlim", "type": "float", "readonly": false, "cmd": "nv autoflow/flowlim"},
|
||||||
|
{"path": "autoflow/smooth", "type": "float", "readonly": false, "cmd": "nv autoflow/smooth"},
|
||||||
|
{"path": "autoflow/difSize", "type": "float", "readonly": false, "cmd": "nv autoflow/difSize"},
|
||||||
|
{"path": "autoflow/difRange", "type": "float", "readonly": false, "cmd": "nv autoflow/difRange"},
|
||||||
|
{"path": "autoflow/flowSize", "type": "float", "readonly": false, "cmd": "nv autoflow/flowSize"},
|
||||||
|
{"path": "autoflow/convTime", "type": "float", "readonly": false, "cmd": "nv autoflow/convTime"},
|
||||||
|
{"path": "autoflow/Tmin", "type": "float", "readonly": false, "cmd": "nv autoflow/Tmin"},
|
||||||
|
{"path": "autoflow/script", "type": "text", "readonly": false, "cmd": "nv autoflow/script"},
|
||||||
|
{"path": "autoflow/getTemp", "type": "text", "readonly": false, "cmd": "nv autoflow/getTemp"},
|
||||||
|
{"path": "autoflow/getTset", "type": "text", "readonly": false, "cmd": "nv autoflow/getTset"},
|
||||||
|
{"path": "autoflow/getFlow", "type": "text", "readonly": false, "cmd": "nv autoflow/getFlow"},
|
||||||
|
{"path": "autoflow/difBuf", "type": "text"},
|
||||||
|
{"path": "autoflow/flowBuf", "type": "text"},
|
||||||
|
{"path": "autoflow/flowset", "type": "float"},
|
||||||
|
{"path": "autoflow/flowmin", "type": "float"},
|
||||||
|
{"path": "autoflow/flowmax", "type": "float"},
|
||||||
|
{"path": "autoflow/difmin", "type": "float"},
|
||||||
|
{"path": "autoflow/difmax", "type": "float"},
|
||||||
|
{"path": "autoflow/setmin", "type": "float"},
|
||||||
|
{"path": "autoflow/setmax", "type": "float"},
|
||||||
|
{"path": "autoflow/flowtarget", "type": "float"},
|
||||||
|
{"path": "calib", "type": "none", "kids": 2},
|
||||||
|
{"path": "calib/ln_per_min_per_mbar", "type": "float", "readonly": false, "cmd": "nv calib/ln_per_min_per_mbar"},
|
||||||
|
{"path": "calib/mbar_offset", "type": "float", "readonly": false, "cmd": "nv calib/mbar_offset"}]},
|
||||||
|
|
||||||
|
"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": "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"}]},
|
||||||
|
|
||||||
|
"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"},
|
||||||
|
{"path": "auto", "type": "bool", "readonly": false, "cmd": "hepump auto"},
|
||||||
|
{"path": "valve", "type": "enum", "enum": {"closed": 0, "closing": 1, "opening": 2, "opened": 3, "undefined": 4}, "readonly": false, "cmd": "hepump valve"},
|
||||||
|
{"path": "eco_t_lim", "type": "float", "readonly": false, "cmd": "hepump eco_t_lim", "description": "switch off eco mode when T_set < eco_t_lim and T < eco_t_lim * 2"},
|
||||||
|
{"path": "calib", "type": "float", "readonly": false, "cmd": "hepump calib", "visibility": 3},
|
||||||
|
{"path": "health", "type": "float"}]},
|
||||||
|
|
||||||
|
"hemot": {"base": "/hepump/hemot", "params": [
|
||||||
|
{"path": "", "type": "float", "readonly": false, "cmd": "run hemot", "kids": 30},
|
||||||
|
{"path": "send", "type": "text", "readonly": false, "cmd": "hemot send", "visibility": 3},
|
||||||
|
{"path": "status", "type": "text", "visibility": 3},
|
||||||
|
{"path": "is_running", "type": "int", "readonly": false, "cmd": "hemot is_running", "visibility": 3},
|
||||||
|
{"path": "pos", "type": "float"},
|
||||||
|
{"path": "encoder", "type": "float"},
|
||||||
|
{"path": "zero", "type": "float", "readonly": false, "cmd": "hemot zero"},
|
||||||
|
{"path": "lowerlimit", "type": "float", "readonly": false, "cmd": "hemot lowerlimit"},
|
||||||
|
{"path": "upperlimit", "type": "float", "readonly": false, "cmd": "hemot upperlimit"},
|
||||||
|
{"path": "disablelimits", "type": "bool", "readonly": false, "cmd": "hemot disablelimits"},
|
||||||
|
{"path": "verbose", "type": "bool", "readonly": false, "cmd": "hemot verbose"},
|
||||||
|
{"path": "target", "type": "float"},
|
||||||
|
{"path": "runstate", "type": "enum", "enum": {"idle": 0, "running": 1, "finished": 2, "error": 3}},
|
||||||
|
{"path": "precision", "type": "float", "readonly": false, "cmd": "hemot precision"},
|
||||||
|
{"path": "maxencdif", "type": "float", "readonly": false, "cmd": "hemot maxencdif"},
|
||||||
|
{"path": "id", "type": "float", "readonly": false, "cmd": "hemot id"},
|
||||||
|
{"path": "pump_number", "type": "float", "readonly": false, "cmd": "hemot pump_number"},
|
||||||
|
{"path": "init", "type": "float", "readonly": false, "cmd": "hemot init"},
|
||||||
|
{"path": "maxspeed", "type": "float", "readonly": false, "cmd": "hemot maxspeed"},
|
||||||
|
{"path": "acceleration", "type": "float", "readonly": false, "cmd": "hemot acceleration"},
|
||||||
|
{"path": "maxcurrent", "type": "float", "readonly": false, "cmd": "hemot maxcurrent"},
|
||||||
|
{"path": "standbycurrent", "type": "float", "readonly": false, "cmd": "hemot standbycurrent"},
|
||||||
|
{"path": "freewheeling", "type": "bool", "readonly": false, "cmd": "hemot freewheeling"},
|
||||||
|
{"path": "output0", "type": "bool", "readonly": false, "cmd": "hemot output0"},
|
||||||
|
{"path": "output1", "type": "bool", "readonly": false, "cmd": "hemot output1"},
|
||||||
|
{"path": "input3", "type": "bool"},
|
||||||
|
{"path": "pullup", "type": "float", "readonly": false, "cmd": "hemot pullup"},
|
||||||
|
{"path": "nopumpfeedback", "type": "bool", "readonly": false, "cmd": "hemot nopumpfeedback"},
|
||||||
|
{"path": "eeprom", "type": "enum", "enum": {"ok": 0, "dirty": 1, "save": 2, "load": 3}, "readonly": false, "cmd": "hemot eeprom"},
|
||||||
|
{"path": "customadr", "type": "text", "readonly": false, "cmd": "hemot customadr"},
|
||||||
|
{"path": "custompar", "type": "float", "readonly": false, "cmd": "hemot custompar"}]},
|
||||||
|
|
||||||
|
"nvflow": {"base": "/nvflow", "params": [
|
||||||
|
{"path": "", "type": "float", "kids": 7},
|
||||||
|
{"path": "send", "type": "text", "readonly": false, "cmd": "nvflow send", "visibility": 3},
|
||||||
|
{"path": "status", "type": "text", "visibility": 3},
|
||||||
|
{"path": "stddev", "type": "float"},
|
||||||
|
{"path": "nsamples", "type": "int", "readonly": false, "cmd": "nvflow nsamples"},
|
||||||
|
{"path": "offset", "type": "float", "readonly": false, "cmd": "nvflow offset"},
|
||||||
|
{"path": "scale", "type": "float", "readonly": false, "cmd": "nvflow scale"},
|
||||||
|
{"path": "save", "type": "bool", "readonly": false, "cmd": "nvflow save", "description": "unchecked: current calib is not saved. set checked: save calib"}]},
|
||||||
|
|
||||||
|
"ln2fill": {"base": "/ln2fill", "params": [
|
||||||
|
{"path": "", "type": "enum", "enum": {"watching": 0, "filling": 1, "inactive": 2, "manualfill": 3}, "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": "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"},
|
||||||
|
{"path": "minfillminutes", "type": "float", "readonly": false, "cmd": "ln2fill minfillminutes"},
|
||||||
|
{"path": "maxfillminutes", "type": "float", "readonly": false, "cmd": "ln2fill maxfillminutes"},
|
||||||
|
{"path": "minholdhours", "type": "float", "readonly": false, "cmd": "ln2fill minholdhours"},
|
||||||
|
{"path": "maxholdhours", "type": "float", "readonly": false, "cmd": "ln2fill maxholdhours"},
|
||||||
|
{"path": "tolerance", "type": "float", "readonly": false, "cmd": "ln2fill tolerance"},
|
||||||
|
{"path": "badreadingminutes", "type": "float", "readonly": false, "cmd": "ln2fill badreadingminutes"},
|
||||||
|
{"path": "tubecoolingminutes", "type": "float", "readonly": false, "cmd": "ln2fill tubecoolingminutes"}]},
|
||||||
|
|
||||||
|
"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": "nowait", "type": "int", "readonly": false, "cmd": "mf nowait"},
|
||||||
|
{"path": "maxlimit", "type": "float", "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": "trainedTo", "type": "float", "readonly": false, "cmd": "mf trainedTo"},
|
||||||
|
{"path": "trainMode", "type": "int"},
|
||||||
|
{"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": "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": "status", "type": "text", "readonly": false, "cmd": "mf status", "visibility": 3}]},
|
||||||
|
|
||||||
|
"lev": {"base": "/lev", "params": [
|
||||||
|
{"path": "", "type": "float", "kids": 4},
|
||||||
|
{"path": "send", "type": "text", "readonly": false, "cmd": "lev send", "visibility": 3},
|
||||||
|
{"path": "status", "type": "text", "visibility": 3},
|
||||||
|
{"path": "mode", "type": "enum", "enum": {"slow": 0, "fast (switches to slow automatically after filling)": 1}, "readonly": false, "cmd": "lev mode"},
|
||||||
|
{"path": "n2", "type": "float"}]},
|
||||||
|
|
||||||
|
"tcoil": {"base": "/tcoil", "params": [
|
||||||
|
{"path": "", "type": "float", "kids": 11},
|
||||||
|
{"path": "send", "type": "text", "readonly": false, "cmd": "tcoil send", "visibility": 3},
|
||||||
|
{"path": "status", "type": "text", "visibility": 3},
|
||||||
|
{"path": "excitation", "type": "float", "readonly": false, "cmd": "tcoil excitation", "visibility": 3},
|
||||||
|
{"path": "ta", "type": "float", "visibility": 3, "kids": 3},
|
||||||
|
{"path": "ta/enable", "type": "bool", "readonly": false, "cmd": "tcoil ta/enable"},
|
||||||
|
{"path": "ta/r", "type": "float"},
|
||||||
|
{"path": "ta/curve", "type": "text", "readonly": false, "cmd": "tcoil ta/curve", "kids": 3},
|
||||||
|
{"path": "ta/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil ta/curve/adjust"},
|
||||||
|
{"path": "ta/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil ta/curve/points"},
|
||||||
|
{"path": "ta/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil ta/curve/cpoints"},
|
||||||
|
{"path": "tb", "type": "float", "visibility": 3, "kids": 3},
|
||||||
|
{"path": "tb/enable", "type": "bool", "readonly": false, "cmd": "tcoil tb/enable"},
|
||||||
|
{"path": "tb/r", "type": "float"},
|
||||||
|
{"path": "tb/curve", "type": "text", "readonly": false, "cmd": "tcoil tb/curve", "kids": 3},
|
||||||
|
{"path": "tb/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil tb/curve/adjust"},
|
||||||
|
{"path": "tb/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil tb/curve/points"},
|
||||||
|
{"path": "tb/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil tb/curve/cpoints"},
|
||||||
|
{"path": "td", "type": "float", "visibility": 3, "kids": 3},
|
||||||
|
{"path": "td/enable", "type": "bool", "readonly": false, "cmd": "tcoil td/enable"},
|
||||||
|
{"path": "td/r", "type": "float"},
|
||||||
|
{"path": "td/curve", "type": "text", "readonly": false, "cmd": "tcoil td/curve", "kids": 3},
|
||||||
|
{"path": "td/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil td/curve/adjust"},
|
||||||
|
{"path": "td/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil td/curve/points"},
|
||||||
|
{"path": "td/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil td/curve/cpoints"},
|
||||||
|
{"path": "ref", "type": "float", "visibility": 3, "kids": 3},
|
||||||
|
{"path": "ref/enable", "type": "bool", "readonly": false, "cmd": "tcoil ref/enable"},
|
||||||
|
{"path": "ref/r", "type": "float"},
|
||||||
|
{"path": "ref/curve", "type": "text", "readonly": false, "cmd": "tcoil ref/curve", "kids": 3},
|
||||||
|
{"path": "ref/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil ref/curve/adjust"},
|
||||||
|
{"path": "ref/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil ref/curve/points"},
|
||||||
|
{"path": "ref/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil ref/curve/cpoints"},
|
||||||
|
{"path": "tc", "type": "float", "visibility": 3, "kids": 3},
|
||||||
|
{"path": "tc/enable", "type": "bool", "readonly": false, "cmd": "tcoil tc/enable"},
|
||||||
|
{"path": "tc/r", "type": "float"},
|
||||||
|
{"path": "tc/curve", "type": "text", "readonly": false, "cmd": "tcoil tc/curve", "kids": 3},
|
||||||
|
{"path": "tc/curve/adjust", "type": "text", "readonly": false, "cmd": "tcoil tc/curve/adjust"},
|
||||||
|
{"path": "tc/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tcoil tc/curve/points"},
|
||||||
|
{"path": "tc/curve/cpoints", "type": "floatvarar", "readonly": false, "cmd": "tcoil tc/curve/cpoints"},
|
||||||
|
{"path": "ext", "type": "float", "visibility": 3},
|
||||||
|
{"path": "com", "type": "float", "visibility": 3},
|
||||||
|
{"path": "gnd", "type": "float", "visibility": 3}]},
|
||||||
|
|
||||||
|
"table": {"base": "/table", "params": [
|
||||||
|
{"path": "", "type": "none", "kids": 17},
|
||||||
|
{"path": "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 ..."}]}}
|
76
cfg/sea/thermalcond.stick.json
Normal file
76
cfg/sea/thermalcond.stick.json
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
{"res": {"base": "/res", "params": [
|
||||||
|
{"path": "", "type": "int", "kids": 10},
|
||||||
|
{"path": "send", "type": "text", "readonly": false, "cmd": "res send", "visibility": 3},
|
||||||
|
{"path": "status", "type": "text", "visibility": 3},
|
||||||
|
{"path": "autoscan", "type": "bool", "readonly": false, "cmd": "res autoscan", "kids": 4},
|
||||||
|
{"path": "autoscan/synchronized", "type": "bool", "readonly": false, "cmd": "res autoscan/synchronized"},
|
||||||
|
{"path": "autoscan/interval", "type": "text", "readonly": false, "cmd": "res autoscan/interval"},
|
||||||
|
{"path": "autoscan/pause", "type": "text", "readonly": false, "cmd": "res autoscan/pause"},
|
||||||
|
{"path": "autoscan/dwell", "type": "text", "readonly": false, "cmd": "res autoscan/dwell"},
|
||||||
|
{"path": "s1", "type": "float", "kids": 14},
|
||||||
|
{"path": "s1/active", "type": "enum", "enum": {"inactive": 0, "active": 1}, "readonly": false, "cmd": "res s1/active"},
|
||||||
|
{"path": "s1/autorange", "type": "bool", "readonly": false, "cmd": "res s1/autorange", "description": "autorange (common for all channels)"},
|
||||||
|
{"path": "s1/range", "type": "text", "readonly": false, "cmd": "res s1/range", "description": "resistance range in Ohm"},
|
||||||
|
{"path": "s1/range_num", "type": "int"},
|
||||||
|
{"path": "s1/excitation", "type": "text", "readonly": false, "cmd": "res s1/excitation", "description": "excitation with unit, i.e. 2uV or 3pA"},
|
||||||
|
{"path": "s1/excitation_num", "type": "int"},
|
||||||
|
{"path": "s1/excitation_mode", "type": "enum", "enum": {"voltage": 0, "current": 1, "off": 2}},
|
||||||
|
{"path": "s1/pause", "type": "int", "readonly": false, "cmd": "res s1/pause", "description": "pause time [sec] after channel change"},
|
||||||
|
{"path": "s1/filter", "type": "int", "readonly": false, "cmd": "res s1/filter", "description": "filter average time [sec]"},
|
||||||
|
{"path": "s1/dwell", "type": "int", "readonly": false, "cmd": "res s1/dwell", "description": "dwell time [sec]. Total time per channel: pause + filter + dwell"},
|
||||||
|
{"path": "s1/status", "type": "text"},
|
||||||
|
{"path": "s1/curve", "type": "text", "readonly": false, "cmd": "res s1/curve", "kids": 1},
|
||||||
|
{"path": "s1/curve/points", "type": "floatvarar", "readonly": false, "cmd": "res s1/curve/points", "visibility": 3},
|
||||||
|
{"path": "s1/alarm", "type": "float", "readonly": false, "cmd": "res s1/alarm"},
|
||||||
|
{"path": "s1/raw", "type": "float"},
|
||||||
|
{"path": "s2", "type": "float", "kids": 14},
|
||||||
|
{"path": "s2/active", "type": "enum", "enum": {"inactive": 0, "active": 1}, "readonly": false, "cmd": "res s2/active"},
|
||||||
|
{"path": "s2/autorange", "type": "bool", "readonly": false, "cmd": "res s2/autorange", "description": "autorange (common for all channels)"},
|
||||||
|
{"path": "s2/range", "type": "text", "readonly": false, "cmd": "res s2/range", "description": "resistance range in Ohm"},
|
||||||
|
{"path": "s2/range_num", "type": "int"},
|
||||||
|
{"path": "s2/excitation", "type": "text", "readonly": false, "cmd": "res s2/excitation", "description": "excitation with unit, i.e. 2uV or 3pA"},
|
||||||
|
{"path": "s2/excitation_num", "type": "int"},
|
||||||
|
{"path": "s2/excitation_mode", "type": "enum", "enum": {"voltage": 0, "current": 1, "off": 2}},
|
||||||
|
{"path": "s2/pause", "type": "int", "readonly": false, "cmd": "res s2/pause", "description": "pause time [sec] after channel change"},
|
||||||
|
{"path": "s2/filter", "type": "int", "readonly": false, "cmd": "res s2/filter", "description": "filter average time [sec]"},
|
||||||
|
{"path": "s2/dwell", "type": "int", "readonly": false, "cmd": "res s2/dwell", "description": "dwell time [sec]. Total time per channel: pause + filter + dwell"},
|
||||||
|
{"path": "s2/status", "type": "text"},
|
||||||
|
{"path": "s2/curve", "type": "text", "readonly": false, "cmd": "res s2/curve", "kids": 1},
|
||||||
|
{"path": "s2/curve/points", "type": "floatvarar", "readonly": false, "cmd": "res s2/curve/points", "visibility": 3},
|
||||||
|
{"path": "s2/alarm", "type": "float", "readonly": false, "cmd": "res s2/alarm"},
|
||||||
|
{"path": "s2/raw", "type": "float"},
|
||||||
|
{"path": "s3", "type": "float", "kids": 14},
|
||||||
|
{"path": "s3/active", "type": "enum", "enum": {"inactive": 0, "active": 1}, "readonly": false, "cmd": "res s3/active"},
|
||||||
|
{"path": "s3/autorange", "type": "bool", "readonly": false, "cmd": "res s3/autorange", "description": "autorange (common for all channels)"},
|
||||||
|
{"path": "s3/range", "type": "text", "readonly": false, "cmd": "res s3/range", "description": "resistance range in Ohm"},
|
||||||
|
{"path": "s3/range_num", "type": "int"},
|
||||||
|
{"path": "s3/excitation", "type": "text", "readonly": false, "cmd": "res s3/excitation", "description": "excitation with unit, i.e. 2uV or 3pA"},
|
||||||
|
{"path": "s3/excitation_num", "type": "int"},
|
||||||
|
{"path": "s3/excitation_mode", "type": "enum", "enum": {"voltage": 0, "current": 1, "off": 2}},
|
||||||
|
{"path": "s3/pause", "type": "int", "readonly": false, "cmd": "res s3/pause", "description": "pause time [sec] after channel change"},
|
||||||
|
{"path": "s3/filter", "type": "int", "readonly": false, "cmd": "res s3/filter", "description": "filter average time [sec]"},
|
||||||
|
{"path": "s3/dwell", "type": "int", "readonly": false, "cmd": "res s3/dwell", "description": "dwell time [sec]. Total time per channel: pause + filter + dwell"},
|
||||||
|
{"path": "s3/status", "type": "text"},
|
||||||
|
{"path": "s3/curve", "type": "text", "readonly": false, "cmd": "res s3/curve", "kids": 1},
|
||||||
|
{"path": "s3/curve/points", "type": "floatvarar", "readonly": false, "cmd": "res s3/curve/points", "visibility": 3},
|
||||||
|
{"path": "s3/alarm", "type": "float", "readonly": false, "cmd": "res s3/alarm"},
|
||||||
|
{"path": "s3/raw", "type": "float"},
|
||||||
|
{"path": "s4", "type": "float", "kids": 14},
|
||||||
|
{"path": "s4/active", "type": "enum", "enum": {"inactive": 0, "active": 1}, "readonly": false, "cmd": "res s4/active"},
|
||||||
|
{"path": "s4/autorange", "type": "bool", "readonly": false, "cmd": "res s4/autorange", "description": "autorange (common for all channels)"},
|
||||||
|
{"path": "s4/range", "type": "text", "readonly": false, "cmd": "res s4/range", "description": "resistance range in Ohm"},
|
||||||
|
{"path": "s4/range_num", "type": "int"},
|
||||||
|
{"path": "s4/excitation", "type": "text", "readonly": false, "cmd": "res s4/excitation", "description": "excitation with unit, i.e. 2uV or 3pA"},
|
||||||
|
{"path": "s4/excitation_num", "type": "int"},
|
||||||
|
{"path": "s4/excitation_mode", "type": "enum", "enum": {"voltage": 0, "current": 1, "off": 2}},
|
||||||
|
{"path": "s4/pause", "type": "int", "readonly": false, "cmd": "res s4/pause", "description": "pause time [sec] after channel change"},
|
||||||
|
{"path": "s4/filter", "type": "int", "readonly": false, "cmd": "res s4/filter", "description": "filter average time [sec]"},
|
||||||
|
{"path": "s4/dwell", "type": "int", "readonly": false, "cmd": "res s4/dwell", "description": "dwell time [sec]. Total time per channel: pause + filter + dwell"},
|
||||||
|
{"path": "s4/status", "type": "text"},
|
||||||
|
{"path": "s4/curve", "type": "text", "readonly": false, "cmd": "res s4/curve", "kids": 1},
|
||||||
|
{"path": "s4/curve/points", "type": "floatvarar", "readonly": false, "cmd": "res s4/curve/points", "visibility": 3},
|
||||||
|
{"path": "s4/alarm", "type": "float", "readonly": false, "cmd": "res s4/alarm"},
|
||||||
|
{"path": "s4/raw", "type": "float"},
|
||||||
|
{"path": "analog2", "type": "float", "readonly": false, "cmd": "res analog2"},
|
||||||
|
{"path": "remote", "type": "bool"},
|
||||||
|
{"path": "display", "type": "text", "readonly": false, "cmd": "res display"}]}}
|
49
cfg/sim_mlz_entangle_simulation_cfg.py
Normal file
49
cfg/sim_mlz_entangle_simulation_cfg.py
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
Node('simulation',
|
||||||
|
'Auto-generated configuration by frappy.',
|
||||||
|
'tcp://10767',
|
||||||
|
)
|
||||||
|
Mod('analoginput',
|
||||||
|
'frappy_mlz.entangle.AnalogInput',
|
||||||
|
'from test/sim/analoginput',
|
||||||
|
tangodevice = 'tango://localhost:10000/test/sim/analoginput',
|
||||||
|
)
|
||||||
|
Mod('sensor',
|
||||||
|
'frappy_mlz.entangle.Sensor',
|
||||||
|
'from test/sim/sensor',
|
||||||
|
tangodevice = 'tango://localhost:10000/test/sim/sensor',
|
||||||
|
)
|
||||||
|
Mod('analogoutput',
|
||||||
|
'frappy_mlz.entangle.AnalogOutput',
|
||||||
|
'from test/sim/analogoutput',
|
||||||
|
tangodevice = 'tango://localhost:10000/test/sim/analogoutput',
|
||||||
|
)
|
||||||
|
Mod('actuator',
|
||||||
|
'frappy_mlz.entangle.Actuator',
|
||||||
|
'from test/sim/actuator',
|
||||||
|
tangodevice = 'tango://localhost:10000/test/sim/actuator',
|
||||||
|
)
|
||||||
|
Mod('motor',
|
||||||
|
'frappy_mlz.entangle.Motor',
|
||||||
|
'from test/sim/motor',
|
||||||
|
tangodevice = 'tango://localhost:10000/test/sim/motor',
|
||||||
|
)
|
||||||
|
Mod('powersupply',
|
||||||
|
'frappy_mlz.entangle.PowerSupply',
|
||||||
|
'from test/sim/powersupply',
|
||||||
|
tangodevice = 'tango://localhost:10000/test/sim/powersupply',
|
||||||
|
)
|
||||||
|
Mod('digitalinput',
|
||||||
|
'frappy_mlz.entangle.DigitalInput',
|
||||||
|
'from test/sim/digitalinput',
|
||||||
|
tangodevice = 'tango://localhost:10000/test/sim/digitalinput',
|
||||||
|
)
|
||||||
|
Mod('digitaloutput',
|
||||||
|
'frappy_mlz.entangle.DigitalOutput',
|
||||||
|
'from test/sim/digitaloutput',
|
||||||
|
tangodevice = 'tango://localhost:10000/test/sim/digitaloutput',
|
||||||
|
)
|
||||||
|
Mod('stringio',
|
||||||
|
'frappy_mlz.entangle.StringIO',
|
||||||
|
'from test/sim/stringio',
|
||||||
|
tangodevice = 'tango://localhost:10000/test/sim/stringio',
|
||||||
|
)
|
@ -10,6 +10,14 @@ Mod('sea_stick',
|
|||||||
)
|
)
|
||||||
|
|
||||||
Mod('ts',
|
Mod('ts',
|
||||||
|
'frappy_psi.sea.SeaReadable', '',
|
||||||
|
io='sea_stick',
|
||||||
|
sea_object='tt',
|
||||||
|
json_file='ma6.config.json',
|
||||||
|
rel_paths=['ts_2'],
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('ts_reg',
|
||||||
'frappy_psi.sea.SeaReadable', '',
|
'frappy_psi.sea.SeaReadable', '',
|
||||||
io='sea_stick',
|
io='sea_stick',
|
||||||
sea_object='tt',
|
sea_object='tt',
|
||||||
@ -22,3 +30,11 @@ Mod('hcp',
|
|||||||
io='sea_stick',
|
io='sea_stick',
|
||||||
sea_object='hcp',
|
sea_object='hcp',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Mod('v',
|
||||||
|
'frappy_psi.parmod.Driv',
|
||||||
|
'drivable voltage',
|
||||||
|
read='hcp.value',
|
||||||
|
write='hcp.set',
|
||||||
|
unit='V',
|
||||||
|
)
|
||||||
|
@ -22,8 +22,8 @@ Mod('ts',
|
|||||||
'frappy_psi.parmod.Converging',
|
'frappy_psi.parmod.Converging',
|
||||||
'virtual stick T',
|
'virtual stick T',
|
||||||
unit='K',
|
unit='K',
|
||||||
read='tsam.value',
|
value_param='tsam.value',
|
||||||
write='tsam.setsamp',
|
target_param='tsam.setsamp',
|
||||||
meaning=['temperature', 20],
|
meaning=['temperature', 20],
|
||||||
settling_time=20,
|
settling_time=20,
|
||||||
tolerance=1,
|
tolerance=1,
|
||||||
|
11
cfg/stick/ma6_sampleheat_cfg.py
Normal file
11
cfg/stick/ma6_sampleheat_cfg.py
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
Node('ma6_sampleheat.psi.ch', '', interface='tcp://5000')
|
||||||
|
|
||||||
|
Mod('sea_stick',
|
||||||
|
'frappy_psi.sea.SeaClient',
|
||||||
|
'stick sea connection for ma6_sampleheat.stick',
|
||||||
|
config='ma6_sampleheat.stick',
|
||||||
|
service='stick',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# ts is configured on ma7_sampleheat_cfg.py
|
18
cfg/stick/ma7_piezostick_cfg.py
Normal file
18
cfg/stick/ma7_piezostick_cfg.py
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
Node('ma7.stick.sea.psi.ch',
|
||||||
|
'MA7 standard sample stick',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('sea_stick',
|
||||||
|
'frappy_psi.sea.SeaClient',
|
||||||
|
'SEA stick connection',
|
||||||
|
config='ma7_piezo.stick',
|
||||||
|
service='stick',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('ts',
|
||||||
|
'frappy_psi.sea.SeaReadable', '',
|
||||||
|
io='sea_stick',
|
||||||
|
sea_object='tt',
|
||||||
|
json_file='ma7_piezo.config.json',
|
||||||
|
rel_paths=['ts'],
|
||||||
|
)
|
72
cfg/stick/ma7combi_cfg.py
Normal file
72
cfg/stick/ma7combi_cfg.py
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
Node('ma7.stick.sea.psi.ch',
|
||||||
|
'MA7 standard sample stick',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('sea_stick',
|
||||||
|
'frappy_psi.sea.SeaClient',
|
||||||
|
'SEA stick connection',
|
||||||
|
config='ma7.stick',
|
||||||
|
service='stick',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('ts_sea',
|
||||||
|
'frappy_psi.sea.SeaReadable',
|
||||||
|
'sample stick temperature',
|
||||||
|
io='sea_stick',
|
||||||
|
json_file='ma7.config.json',
|
||||||
|
sea_object='tt',
|
||||||
|
rel_paths=['ts', 'setsamp'],
|
||||||
|
# export=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('tm_sea',
|
||||||
|
'frappy_psi.sea.SeaReadable',
|
||||||
|
'sample stick temperature',
|
||||||
|
io='sea_stick',
|
||||||
|
json_file='ma7.config.json',
|
||||||
|
sea_object='tt',
|
||||||
|
rel_paths=['tm', 'set'],
|
||||||
|
# export=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('ts_writ',
|
||||||
|
'frappy_psi.parmod.ParWrit',
|
||||||
|
'writable version of ts',
|
||||||
|
unit='K',
|
||||||
|
value_param='ts_sea.value',
|
||||||
|
target_param='ts_sea.setsamp',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('tm_writ',
|
||||||
|
'frappy_psi.parmod.ParWrit',
|
||||||
|
'writable version of tm',
|
||||||
|
unit='K',
|
||||||
|
value_param='tm_sea.value',
|
||||||
|
target_param='tm_sea.set',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('sam_htr',
|
||||||
|
'frappy_psi.parmod.Par',
|
||||||
|
'htr module version',
|
||||||
|
unit='W',
|
||||||
|
value_param='ts_sea.power',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('vti_htr',
|
||||||
|
'frappy_psi.parmod.Par',
|
||||||
|
'htr module version',
|
||||||
|
unit='W',
|
||||||
|
value_param='tm_sea.power',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('ts',
|
||||||
|
'frappy_psi.parmod.CombinedSampleVti',
|
||||||
|
'drivable stick T using setsamp',
|
||||||
|
sample='ts_writ',
|
||||||
|
sample_htr='sam_htr',
|
||||||
|
vti='tm_writ',
|
||||||
|
vti_htr='vti_htr',
|
||||||
|
meaning=['temperature', 20],
|
||||||
|
settling_time=20,
|
||||||
|
tolerance=1,
|
||||||
|
)
|
@ -9,7 +9,7 @@ Mod('sea_stick',
|
|||||||
service='stick',
|
service='stick',
|
||||||
)
|
)
|
||||||
|
|
||||||
Mod('tsam',
|
Mod('ts_sea',
|
||||||
'frappy_psi.sea.SeaReadable',
|
'frappy_psi.sea.SeaReadable',
|
||||||
'sample stick temperature',
|
'sample stick temperature',
|
||||||
io='sea_stick',
|
io='sea_stick',
|
||||||
@ -22,8 +22,8 @@ Mod('ts',
|
|||||||
'frappy_psi.parmod.Converging',
|
'frappy_psi.parmod.Converging',
|
||||||
'drivable stick T using setsamp',
|
'drivable stick T using setsamp',
|
||||||
unit='K',
|
unit='K',
|
||||||
read='tsam.value',
|
value_param='ts_sea.value',
|
||||||
write='tsam.setsamp',
|
target_param='ts_sea.setsamp',
|
||||||
meaning=['temperature', 20],
|
meaning=['temperature', 20],
|
||||||
settling_time=20,
|
settling_time=20,
|
||||||
tolerance=1,
|
tolerance=1,
|
||||||
|
@ -9,10 +9,23 @@ Mod('sea_stick',
|
|||||||
service='stick',
|
service='stick',
|
||||||
)
|
)
|
||||||
|
|
||||||
Mod('ts',
|
Mod('tsam',
|
||||||
'frappy_psi.sea.SeaReadable', '',
|
'frappy_psi.sea.SeaReadable',
|
||||||
|
'sample stick temperature',
|
||||||
io='sea_stick',
|
io='sea_stick',
|
||||||
sea_object='tt',
|
|
||||||
json_file='ma7.config.json',
|
json_file='ma7.config.json',
|
||||||
rel_paths=['ts'],
|
sea_object='tt',
|
||||||
|
rel_paths=['ts', 'setsamp']
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Mod('ts',
|
||||||
|
'frappy_psi.parmod.Converging',
|
||||||
|
'drivable stick T using setsamp',
|
||||||
|
unit='K',
|
||||||
|
value_param='tsam.value',
|
||||||
|
target_param='tsam.setsamp',
|
||||||
|
meaning=['temperature', 20],
|
||||||
|
settling_time=20,
|
||||||
|
tolerance=1,
|
||||||
|
)
|
||||||
|
|
||||||
|
@ -1,23 +0,0 @@
|
|||||||
[NODE]
|
|
||||||
description = PGAS5 gas pressure cell stick
|
|
||||||
id = pgas5.stick.sea.psi.ch
|
|
||||||
|
|
||||||
[sea_stick]
|
|
||||||
class = secop_psi.sea.SeaClient
|
|
||||||
description = stick SEA connection to ill5.stick
|
|
||||||
config = ill5.stick
|
|
||||||
service = stick
|
|
||||||
|
|
||||||
[ts]
|
|
||||||
class = secop_psi.sea.SeaReadable
|
|
||||||
io = sea_stick
|
|
||||||
sea_object = tt
|
|
||||||
json_file = ill5.config.json
|
|
||||||
rel_paths = ts
|
|
||||||
|
|
||||||
[T_bottom]
|
|
||||||
class = secop_psi.sea.SeaReadable
|
|
||||||
io = sea_stick
|
|
||||||
sea_object = tt
|
|
||||||
json_file = ill5.config.json
|
|
||||||
rel_paths = tb
|
|
28
cfg/stick/pgas5_cfg.py
Normal file
28
cfg/stick/pgas5_cfg.py
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
Node('pgas5.stick.sea.psi.ch',
|
||||||
|
'PGAS5 gas pressure cell stick',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('sea_stick',
|
||||||
|
'frappy_psi.sea.SeaClient',
|
||||||
|
'stick SEA connection to ill5.stick',
|
||||||
|
config = 'pgas5.stick',
|
||||||
|
service = 'stick',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('ts',
|
||||||
|
'frappy_psi.sea.SeaReadable',
|
||||||
|
'sample temperature',
|
||||||
|
io = 'sea_stick',
|
||||||
|
sea_object = 'tt',
|
||||||
|
json_file = 'ill5pgas5.config.json',
|
||||||
|
rel_paths = ['ts'],
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('T_bottom',
|
||||||
|
'frappy_psi.sea.SeaReadable',
|
||||||
|
'bottom T',
|
||||||
|
io = 'sea_stick',
|
||||||
|
sea_object = 'tt',
|
||||||
|
json_file = 'ill5pgas5.config.json',
|
||||||
|
rel_paths = ['ts_2'],
|
||||||
|
)
|
67
cfg/stick/thermalcond_cfg.py
Normal file
67
cfg/stick/thermalcond_cfg.py
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
Node('thermalcond.stick.sea.psi.ch',
|
||||||
|
'''50 mm thermal conductivity stick, version with standard lsc370 driver''',
|
||||||
|
)
|
||||||
|
Mod('sea_stick',
|
||||||
|
'frappy_psi.sea.SeaClient',
|
||||||
|
'stick sea connection for thermalcond.stick',
|
||||||
|
config = 'thermalcond.stick',
|
||||||
|
service = 'stick',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#Mod('tsam',
|
||||||
|
# 'frappy_psi.sea.SeaReadable',
|
||||||
|
# 'sample stick temperature',
|
||||||
|
# io='sea_stick',
|
||||||
|
# json_file='ma7.config.json',
|
||||||
|
# sea_object='tt',
|
||||||
|
# rel_paths=['ts', 'setsamp']
|
||||||
|
#)
|
||||||
|
|
||||||
|
#Mod('ts',
|
||||||
|
# 'frappy_psi.parmod.Converging',
|
||||||
|
# 'drivable stick T using setsamp',
|
||||||
|
# unit='K',
|
||||||
|
# value_param='tsam.value',
|
||||||
|
# target_param='tsam.setsamp',
|
||||||
|
# meaning=['temperature', 20],
|
||||||
|
# settling_time=20,
|
||||||
|
# tolerance=1,
|
||||||
|
#)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#Mod('res',
|
||||||
|
# 'frappy_psi.sea.SeaReadable', '',
|
||||||
|
# io = 'sea_stick',
|
||||||
|
# sea_object = 'res',
|
||||||
|
#)
|
||||||
|
|
||||||
|
Mod('R1',
|
||||||
|
'frappy_psi.sea.SeaReadable', '',
|
||||||
|
io='sea_stick',
|
||||||
|
sea_object='res',
|
||||||
|
rel_paths=['s1'],
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('R2',
|
||||||
|
'frappy_psi.sea.SeaReadable', '',
|
||||||
|
io='sea_stick',
|
||||||
|
sea_object='res',
|
||||||
|
rel_paths=['s2'],
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('R3',
|
||||||
|
'frappy_psi.sea.SeaReadable', '',
|
||||||
|
io='sea_stick',
|
||||||
|
sea_object='res',
|
||||||
|
rel_paths=['s3'],
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('R4',
|
||||||
|
'frappy_psi.sea.SeaReadable', '',
|
||||||
|
io='sea_stick',
|
||||||
|
sea_object='res',
|
||||||
|
rel_paths=['s4'],
|
||||||
|
)
|
56
debian/changelog
vendored
56
debian/changelog
vendored
@ -1,3 +1,59 @@
|
|||||||
|
frappy-core (0.19.0) jammy; urgency=medium
|
||||||
|
|
||||||
|
[ Markus Zolliker ]
|
||||||
|
* simulation: extra_params might be a list
|
||||||
|
* add FloatEnumParam
|
||||||
|
* move StructParam to frappy/extparams.py
|
||||||
|
* bugfix in automatic creation if attached io
|
||||||
|
* fixes for proxy modules
|
||||||
|
* simplify callbacks
|
||||||
|
* fix docstring in frappy.error.OutOfRangeError
|
||||||
|
|
||||||
|
[ Alexander Zaft ]
|
||||||
|
* core: introduce common handler class
|
||||||
|
* core: cover errors in handler setup()
|
||||||
|
|
||||||
|
[ Markus Zolliker ]
|
||||||
|
* fix command doc string handling and change default stop doc string
|
||||||
|
* follow up fix for change 33168
|
||||||
|
* follow up fix: handler export=True correctly
|
||||||
|
|
||||||
|
[ Alexander Zaft ]
|
||||||
|
* core: add websocket interface
|
||||||
|
|
||||||
|
[ Georg Brandl ]
|
||||||
|
* dispatcher: consistent handling of missing timestamps
|
||||||
|
|
||||||
|
[ Alexander Zaft ]
|
||||||
|
* gui: catch invalid inputs
|
||||||
|
* gui: sort qt imports
|
||||||
|
|
||||||
|
[ Markus Zolliker ]
|
||||||
|
* frappy.client: cleanup properly after a reply timeout
|
||||||
|
|
||||||
|
[ Alexander Zaft ]
|
||||||
|
* gui: more specialized input widgets
|
||||||
|
* test: add uri attach test
|
||||||
|
|
||||||
|
[ Markus Zolliker ]
|
||||||
|
* frappy.client: catch errors on callbacks
|
||||||
|
* frappy.client: improve error handling more
|
||||||
|
* frappy.client.interactive: improve logging and error handling
|
||||||
|
* frappy.client.SecopClient: add the option to use no logging at all
|
||||||
|
* SecopClient.__del__ must not call callbacks
|
||||||
|
* frappy.client: avoid shutdown callback sent twice
|
||||||
|
|
||||||
|
[ Georg Brandl ]
|
||||||
|
* add config for the Entangle simulation server
|
||||||
|
|
||||||
|
[ Jens Krüger ]
|
||||||
|
* Fix abslimits reading from entangle device
|
||||||
|
|
||||||
|
[ Georg Brandl ]
|
||||||
|
* fix LimitsType to be actually used and validated
|
||||||
|
|
||||||
|
-- Markus Zolliker <jenkins@frm2.tum.de> Thu, 16 May 2024 11:31:25 +0200
|
||||||
|
|
||||||
frappy-core (0.18.1) focal; urgency=medium
|
frappy-core (0.18.1) focal; urgency=medium
|
||||||
|
|
||||||
* mlz: Zapf fix unit handling and small errors
|
* mlz: Zapf fix unit handling and small errors
|
||||||
|
@ -29,10 +29,10 @@ import time
|
|||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from threading import Event, RLock, current_thread
|
from threading import Event, RLock, current_thread
|
||||||
|
|
||||||
import frappy.errors
|
|
||||||
import frappy.params
|
import frappy.params
|
||||||
|
from frappy.errors import make_secop_error, SECoPError, WrongTypeError
|
||||||
from frappy.datatypes import get_datatype
|
from frappy.datatypes import get_datatype
|
||||||
from frappy.lib import mkthread, formatExtendedStack
|
from frappy.lib import mkthread
|
||||||
from frappy.lib.asynconn import AsynConn, ConnectionClosed
|
from frappy.lib.asynconn import AsynConn, ConnectionClosed
|
||||||
from frappy.protocol.interface import decode_msg, encode_msg_frame
|
from frappy.protocol.interface import decode_msg, encode_msg_frame
|
||||||
from frappy.protocol.messages import COMMANDREQUEST, \
|
from frappy.protocol.messages import COMMANDREQUEST, \
|
||||||
@ -43,7 +43,7 @@ from frappy.protocol.messages import COMMANDREQUEST, \
|
|||||||
# replies to be handled for cache
|
# replies to be handled for cache
|
||||||
UPDATE_MESSAGES = {EVENTREPLY, READREPLY, WRITEREPLY, ERRORPREFIX + READREQUEST, ERRORPREFIX + EVENTREPLY}
|
UPDATE_MESSAGES = {EVENTREPLY, READREPLY, WRITEREPLY, ERRORPREFIX + READREQUEST, ERRORPREFIX + EVENTREPLY}
|
||||||
|
|
||||||
VERSIONFMT= re.compile(r'^[^,]*?ISSE[^,]*,SECoP,')
|
VERSIONFMT = re.compile(r'^[^,]*?ISSE[^,]*,SECoP,')
|
||||||
|
|
||||||
|
|
||||||
class UnregisterCallback(Exception):
|
class UnregisterCallback(Exception):
|
||||||
@ -68,6 +68,10 @@ class Logger:
|
|||||||
error = exception = warning = critical = info
|
error = exception = warning = critical = info
|
||||||
|
|
||||||
|
|
||||||
|
class NullLogger(Logger):
|
||||||
|
error = exception = warning = critical = info = Logger.noop
|
||||||
|
|
||||||
|
|
||||||
class CallbackObject:
|
class CallbackObject:
|
||||||
"""abstract definition for a target object for callbacks
|
"""abstract definition for a target object for callbacks
|
||||||
|
|
||||||
@ -83,6 +87,12 @@ class CallbackObject:
|
|||||||
def unhandledMessage(self, action, ident, data):
|
def unhandledMessage(self, action, ident, data):
|
||||||
"""called on an unhandled message"""
|
"""called on an unhandled message"""
|
||||||
|
|
||||||
|
def handleError(self, exc):
|
||||||
|
"""called on errors handling messages
|
||||||
|
|
||||||
|
:param exc: the exception raised (= sys.exception())
|
||||||
|
"""
|
||||||
|
|
||||||
def nodeStateChange(self, online, state):
|
def nodeStateChange(self, online, state):
|
||||||
"""called when the state of the connection changes
|
"""called when the state of the connection changes
|
||||||
|
|
||||||
@ -105,19 +115,13 @@ class CacheItem(tuple):
|
|||||||
inheriting from tuple: compatible with old previous version of cache
|
inheriting from tuple: compatible with old previous version of cache
|
||||||
"""
|
"""
|
||||||
def __new__(cls, value, timestamp=None, readerror=None, datatype=None):
|
def __new__(cls, value, timestamp=None, readerror=None, datatype=None):
|
||||||
if readerror:
|
|
||||||
assert isinstance(readerror, Exception)
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
value = datatype.import_value(value)
|
|
||||||
except (KeyError, ValueError, AttributeError):
|
|
||||||
readerror = ValueError(f'can not import {value!r} as {datatype!r}')
|
|
||||||
value = None
|
|
||||||
obj = tuple.__new__(cls, (value, timestamp, readerror))
|
obj = tuple.__new__(cls, (value, timestamp, readerror))
|
||||||
|
if datatype:
|
||||||
try:
|
try:
|
||||||
|
# override default method
|
||||||
obj.format_value = datatype.format_value
|
obj.format_value = datatype.format_value
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
obj.format_value = lambda value, unit=None: str(value)
|
pass
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -144,6 +148,11 @@ class CacheItem(tuple):
|
|||||||
return repr(self[2])
|
return repr(self[2])
|
||||||
return self.format_value(self[0])
|
return self.format_value(self[0])
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def format_value(value, unit=None):
|
||||||
|
"""typically overridden with datatype.format_value"""
|
||||||
|
return str(value)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
args = (self.value,)
|
args = (self.value,)
|
||||||
if self.timestamp:
|
if self.timestamp:
|
||||||
@ -153,11 +162,22 @@ class CacheItem(tuple):
|
|||||||
return f'CacheItem{repr(args)}'
|
return f'CacheItem{repr(args)}'
|
||||||
|
|
||||||
|
|
||||||
|
class Cache(dict):
|
||||||
|
class Undefined(Exception):
|
||||||
|
def __repr__(self):
|
||||||
|
return '<undefined>'
|
||||||
|
|
||||||
|
undefined = CacheItem(None, None, Undefined())
|
||||||
|
|
||||||
|
def __missing__(self, key):
|
||||||
|
return self.undefined
|
||||||
|
|
||||||
|
|
||||||
class ProxyClient:
|
class ProxyClient:
|
||||||
"""common functionality for proxy clients"""
|
"""common functionality for proxy clients"""
|
||||||
|
|
||||||
CALLBACK_NAMES = {'updateEvent', 'updateItem', 'descriptiveDataChange',
|
CALLBACK_NAMES = {'updateEvent', 'updateItem', 'descriptiveDataChange',
|
||||||
'nodeStateChange', 'unhandledMessage'}
|
'nodeStateChange', 'unhandledMessage', 'handleError'}
|
||||||
online = False # connected or reconnecting since a short time
|
online = False # connected or reconnecting since a short time
|
||||||
state = 'disconnected' # further possible values: 'connecting', 'reconnecting', 'connected'
|
state = 'disconnected' # further possible values: 'connecting', 'reconnecting', 'connected'
|
||||||
log = None
|
log = None
|
||||||
@ -165,7 +185,7 @@ class ProxyClient:
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.callbacks = {cbname: defaultdict(list) for cbname in self.CALLBACK_NAMES}
|
self.callbacks = {cbname: defaultdict(list) for cbname in self.CALLBACK_NAMES}
|
||||||
# caches (module, parameter) = value, timestamp, readerror (internal names!)
|
# caches (module, parameter) = value, timestamp, readerror (internal names!)
|
||||||
self.cache = {}
|
self.cache = Cache() # dict returning Cache.undefined for missing keys
|
||||||
|
|
||||||
def register_callback(self, key, *args, **kwds):
|
def register_callback(self, key, *args, **kwds):
|
||||||
"""register callback functions
|
"""register callback functions
|
||||||
@ -244,16 +264,18 @@ class ProxyClient:
|
|||||||
except UnregisterCallback:
|
except UnregisterCallback:
|
||||||
cblist.remove(cbfunc)
|
cblist.remove(cbfunc)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
# the programmer should catch all errors in callbacks
|
if cbname != 'handleError':
|
||||||
# if not, the log will be flooded with errors
|
try:
|
||||||
if self.log:
|
e.args = [f'error in callback {cbname}{args}: {e}']
|
||||||
self.log.exception('error %r calling %s%r', e, cbfunc.__name__, args)
|
self.callback(None, 'handleError', e)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
return bool(cblist)
|
return bool(cblist)
|
||||||
|
|
||||||
def updateValue(self, module, param, value, timestamp, readerror):
|
def updateValue(self, module, param, value, timestamp, readerror):
|
||||||
self.callback(None, 'updateEvent', module, param, value, timestamp, readerror)
|
self.callback(None, 'updateEvent', module, param, value, timestamp, readerror)
|
||||||
self.callback(module, 'updateEvent', module, param, value, timestamp, readerror)
|
self.callback(module, 'updateEvent', module, param, value, timestamp, readerror)
|
||||||
self.callback((module, param), 'updateEvent', module, param,value, timestamp, readerror)
|
self.callback((module, param), 'updateEvent', module, param, value, timestamp, readerror)
|
||||||
|
|
||||||
|
|
||||||
class SecopClient(ProxyClient):
|
class SecopClient(ProxyClient):
|
||||||
@ -268,8 +290,17 @@ class SecopClient(ProxyClient):
|
|||||||
descriptive_data = {}
|
descriptive_data = {}
|
||||||
modules = {}
|
modules = {}
|
||||||
_last_error = None
|
_last_error = None
|
||||||
|
_update_error_count = 0
|
||||||
|
_max_error_count = 10
|
||||||
|
|
||||||
def __init__(self, uri, log=Logger):
|
def __init__(self, uri, log=Logger):
|
||||||
|
"""initialize SecopClient
|
||||||
|
|
||||||
|
:param uri: the uri to connect to
|
||||||
|
:param log: a logger.
|
||||||
|
when not given, the print command is used for messages with at least info level.
|
||||||
|
when None, nothing is logged at all
|
||||||
|
"""
|
||||||
super().__init__()
|
super().__init__()
|
||||||
# maps expected replies to [request, Event, is_error, result] until a response came
|
# maps expected replies to [request, Event, is_error, result] until a response came
|
||||||
# there can only be one entry per thread calling 'request'
|
# there can only be one entry per thread calling 'request'
|
||||||
@ -277,15 +308,21 @@ class SecopClient(ProxyClient):
|
|||||||
self.io = None
|
self.io = None
|
||||||
self.txq = queue.Queue(30) # queue for tx requests
|
self.txq = queue.Queue(30) # queue for tx requests
|
||||||
self.pending = queue.Queue(30) # requests with colliding action + ident
|
self.pending = queue.Queue(30) # requests with colliding action + ident
|
||||||
self.log = log
|
self.log = log or NullLogger
|
||||||
self.uri = uri
|
self.uri = uri
|
||||||
self.nodename = uri
|
self.nodename = uri
|
||||||
self._lock = RLock()
|
self._lock = RLock()
|
||||||
self._shutdown = Event()
|
self._shutdown = Event()
|
||||||
|
self.cleanup = []
|
||||||
|
self.register_callback(None, self.handleError)
|
||||||
|
|
||||||
def __del__(self):
|
def __del__(self):
|
||||||
|
# make sure threads are stopping. this is needed in case
|
||||||
|
# a frappy client object is lost without calling .disconnect()
|
||||||
try:
|
try:
|
||||||
self.disconnect()
|
# avoid callbacks when deleting. may cause deadlocks in NICOS
|
||||||
|
self.callbacks.clear()
|
||||||
|
self.disconnect(True)
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@ -297,6 +334,11 @@ class SecopClient(ProxyClient):
|
|||||||
with self._lock:
|
with self._lock:
|
||||||
if self.io:
|
if self.io:
|
||||||
return
|
return
|
||||||
|
self._shutdown.clear()
|
||||||
|
self.txq = queue.Queue(30)
|
||||||
|
self.pending = queue.Queue(30)
|
||||||
|
self.active_requests.clear()
|
||||||
|
self.cleanup.clear()
|
||||||
if self.online:
|
if self.online:
|
||||||
self._set_state(True, 'reconnecting')
|
self._set_state(True, 'reconnecting')
|
||||||
else:
|
else:
|
||||||
@ -328,9 +370,9 @@ class SecopClient(ProxyClient):
|
|||||||
# pylint: disable=unsubscriptable-object
|
# pylint: disable=unsubscriptable-object
|
||||||
self._init_descriptive_data(self.request(DESCRIPTIONREQUEST)[2])
|
self._init_descriptive_data(self.request(DESCRIPTIONREQUEST)[2])
|
||||||
self.nodename = self.properties.get('equipment_id', self.uri)
|
self.nodename = self.properties.get('equipment_id', self.uri)
|
||||||
|
self._set_state(True, 'connected')
|
||||||
if self.activate:
|
if self.activate:
|
||||||
self.request(ENABLEEVENTSREQUEST)
|
self.request(ENABLEEVENTSREQUEST)
|
||||||
self._set_state(True, 'connected')
|
|
||||||
break
|
break
|
||||||
except Exception:
|
except Exception:
|
||||||
# print(formatExtendedTraceback())
|
# print(formatExtendedTraceback())
|
||||||
@ -366,8 +408,15 @@ class SecopClient(ProxyClient):
|
|||||||
|
|
||||||
def __rxthread(self):
|
def __rxthread(self):
|
||||||
noactivity = 0
|
noactivity = 0
|
||||||
|
shutdown = False
|
||||||
try:
|
try:
|
||||||
while self._running:
|
while self._running:
|
||||||
|
while self.cleanup:
|
||||||
|
entry = self.cleanup.pop()
|
||||||
|
for key, prev in self.active_requests.items():
|
||||||
|
if prev is entry:
|
||||||
|
self.active_requests.pop(key)
|
||||||
|
break
|
||||||
# may raise ConnectionClosed
|
# may raise ConnectionClosed
|
||||||
reply = self.io.readline()
|
reply = self.io.readline()
|
||||||
if reply is None:
|
if reply is None:
|
||||||
@ -378,6 +427,7 @@ class SecopClient(ProxyClient):
|
|||||||
continue
|
continue
|
||||||
self.log.debug('RX: %r', reply)
|
self.log.debug('RX: %r', reply)
|
||||||
noactivity = 0
|
noactivity = 0
|
||||||
|
try:
|
||||||
action, ident, data = decode_msg(reply)
|
action, ident, data = decode_msg(reply)
|
||||||
if ident == '.':
|
if ident == '.':
|
||||||
ident = None
|
ident = None
|
||||||
@ -393,7 +443,7 @@ class SecopClient(ProxyClient):
|
|||||||
now = time.time()
|
now = time.time()
|
||||||
if action.startswith(ERRORPREFIX):
|
if action.startswith(ERRORPREFIX):
|
||||||
timestamp = data[2].get('t', now)
|
timestamp = data[2].get('t', now)
|
||||||
readerror = frappy.errors.make_secop_error(*data[0:2])
|
readerror = make_secop_error(*data[0:2])
|
||||||
value = None
|
value = None
|
||||||
else:
|
else:
|
||||||
timestamp = data[1].get('t', now)
|
timestamp = data[1].get('t', now)
|
||||||
@ -401,12 +451,13 @@ class SecopClient(ProxyClient):
|
|||||||
readerror = None
|
readerror = None
|
||||||
module, param = module_param
|
module, param = module_param
|
||||||
timestamp = min(now, timestamp) # no timestamps in the future!
|
timestamp = min(now, timestamp) # no timestamps in the future!
|
||||||
try:
|
|
||||||
self.updateValue(module, param, value, timestamp, readerror)
|
self.updateValue(module, param, value, timestamp, readerror)
|
||||||
except KeyError:
|
|
||||||
pass # ignore updates of unknown parameters
|
|
||||||
if action in (EVENTREPLY, ERRORPREFIX + EVENTREPLY):
|
if action in (EVENTREPLY, ERRORPREFIX + EVENTREPLY):
|
||||||
continue
|
continue
|
||||||
|
except Exception as e:
|
||||||
|
e.args = (f'error handling SECoP message {reply!r}: {e}',)
|
||||||
|
self.callback(None, 'handleError', e)
|
||||||
|
continue
|
||||||
try:
|
try:
|
||||||
key = action, ident
|
key = action, ident
|
||||||
entry = self.active_requests.pop(key)
|
entry = self.active_requests.pop(key)
|
||||||
@ -433,9 +484,11 @@ class SecopClient(ProxyClient):
|
|||||||
except ConnectionClosed:
|
except ConnectionClosed:
|
||||||
pass
|
pass
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.log.error('rxthread ended with %r', e)
|
shutdown = True
|
||||||
|
self.callback(None, 'handleError', e)
|
||||||
|
finally:
|
||||||
self._rxthread = None
|
self._rxthread = None
|
||||||
self.disconnect(False)
|
self.disconnect(shutdown)
|
||||||
if self._shutdown.is_set():
|
if self._shutdown.is_set():
|
||||||
return
|
return
|
||||||
if self.activate:
|
if self.activate:
|
||||||
@ -571,6 +624,13 @@ class SecopClient(ProxyClient):
|
|||||||
if not self.callback(None, 'unhandledMessage', action, ident, data):
|
if not self.callback(None, 'unhandledMessage', action, ident, data):
|
||||||
self.log.warning('unhandled message: %s %s %r', action, ident, data)
|
self.log.warning('unhandled message: %s %s %r', action, ident, data)
|
||||||
|
|
||||||
|
def handleError(self, exc):
|
||||||
|
if self._update_error_count < self._max_error_count:
|
||||||
|
self.log.exception('%s', exc)
|
||||||
|
self._update_error_count += 1
|
||||||
|
if self._update_error_count == self._max_error_count:
|
||||||
|
self.log.error('disabled reporting of further update errors')
|
||||||
|
|
||||||
def _set_state(self, online, state=None):
|
def _set_state(self, online, state=None):
|
||||||
# remark: reconnecting is treated as online
|
# remark: reconnecting is treated as online
|
||||||
self.online = online
|
self.online = online
|
||||||
@ -591,12 +651,16 @@ class SecopClient(ProxyClient):
|
|||||||
def get_reply(self, entry):
|
def get_reply(self, entry):
|
||||||
"""wait for reply and return it"""
|
"""wait for reply and return it"""
|
||||||
if not entry[1].wait(10): # event
|
if not entry[1].wait(10): # event
|
||||||
|
self.cleanup.append(entry)
|
||||||
raise TimeoutError('no response within 10s')
|
raise TimeoutError('no response within 10s')
|
||||||
if not entry[2]: # reply
|
if not entry[2]: # reply
|
||||||
|
if self._shutdown.is_set():
|
||||||
|
raise ConnectionError('connection shut down')
|
||||||
|
# no cleanup needed as self.active_requests will be cleared on connect
|
||||||
raise ConnectionError('connection closed before reply')
|
raise ConnectionError('connection closed before reply')
|
||||||
action, _, data = entry[2] # pylint: disable=unpacking-non-sequence
|
action, _, data = entry[2] # pylint: disable=unpacking-non-sequence
|
||||||
if action.startswith(ERRORPREFIX):
|
if action.startswith(ERRORPREFIX):
|
||||||
raise frappy.errors.make_secop_error(*data[0:2])
|
raise make_secop_error(*data[0:2])
|
||||||
return entry[2] # reply
|
return entry[2] # reply
|
||||||
|
|
||||||
def request(self, action, ident=None, data=None):
|
def request(self, action, ident=None, data=None):
|
||||||
@ -611,9 +675,14 @@ class SecopClient(ProxyClient):
|
|||||||
"""forced read over connection"""
|
"""forced read over connection"""
|
||||||
try:
|
try:
|
||||||
self.request(READREQUEST, self.identifier[module, parameter])
|
self.request(READREQUEST, self.identifier[module, parameter])
|
||||||
except frappy.errors.SECoPError:
|
except SECoPError as e:
|
||||||
# error reply message is already stored as readerror in cache
|
result = self.cache[module, parameter]
|
||||||
pass
|
if e == result.readerror:
|
||||||
|
# the update was already done in the rx thread
|
||||||
|
return result
|
||||||
|
# e was not originating from a secop error message e.g. a connection problem
|
||||||
|
# -> we have to do the error update
|
||||||
|
self.updateValue(module, parameter, None, time.time(), e)
|
||||||
return self.cache.get((module, parameter), None)
|
return self.cache.get((module, parameter), None)
|
||||||
|
|
||||||
def getParameter(self, module, parameter, trycache=False):
|
def getParameter(self, module, parameter, trycache=False):
|
||||||
@ -639,7 +708,7 @@ class SecopClient(ProxyClient):
|
|||||||
argument = datatype.export_value(argument)
|
argument = datatype.export_value(argument)
|
||||||
else:
|
else:
|
||||||
if argument is not None:
|
if argument is not None:
|
||||||
raise frappy.errors.WrongTypeError('command has no argument')
|
raise WrongTypeError('command has no argument')
|
||||||
# pylint: disable=unsubscriptable-object
|
# pylint: disable=unsubscriptable-object
|
||||||
data, qualifiers = self.request(COMMANDREQUEST, self.identifier[module, command], argument)[2]
|
data, qualifiers = self.request(COMMANDREQUEST, self.identifier[module, command], argument)[2]
|
||||||
datatype = self.modules[module]['commands'][command]['datatype'].result
|
datatype = self.modules[module]['commands'][command]['datatype'].result
|
||||||
@ -648,8 +717,12 @@ class SecopClient(ProxyClient):
|
|||||||
return data, qualifiers
|
return data, qualifiers
|
||||||
|
|
||||||
def updateValue(self, module, param, value, timestamp, readerror):
|
def updateValue(self, module, param, value, timestamp, readerror):
|
||||||
entry = CacheItem(value, timestamp, readerror,
|
datatype = self.modules[module]['parameters'][param]['datatype']
|
||||||
self.modules[module]['parameters'][param]['datatype'])
|
if readerror:
|
||||||
|
assert isinstance(readerror, Exception)
|
||||||
|
else:
|
||||||
|
value = datatype.import_value(value)
|
||||||
|
entry = CacheItem(value, timestamp, readerror, datatype)
|
||||||
self.cache[(module, param)] = entry
|
self.cache[(module, param)] = entry
|
||||||
self.callback(None, 'updateItem', module, param, entry)
|
self.callback(None, 'updateItem', module, param, entry)
|
||||||
self.callback(module, 'updateItem', module, param, entry)
|
self.callback(module, 'updateItem', module, param, entry)
|
||||||
|
@ -28,8 +28,9 @@ import signal
|
|||||||
import os
|
import os
|
||||||
import traceback
|
import traceback
|
||||||
import threading
|
import threading
|
||||||
|
import logging
|
||||||
from os.path import expanduser
|
from os.path import expanduser
|
||||||
from frappy.client import SecopClient
|
from frappy.client import SecopClient, UnregisterCallback
|
||||||
from frappy.errors import SECoPError
|
from frappy.errors import SECoPError
|
||||||
from frappy.datatypes import get_datatype, StatusType
|
from frappy.datatypes import get_datatype, StatusType
|
||||||
try:
|
try:
|
||||||
@ -55,40 +56,45 @@ watch(io, T=True) # watch io and all parameters of T
|
|||||||
{tail}"""
|
{tail}"""
|
||||||
|
|
||||||
|
|
||||||
LOG_LEVELS = {'debug', 'comlog', 'info', 'warning', 'error', 'off'}
|
LOG_LEVELS = {
|
||||||
|
'debug': logging.DEBUG,
|
||||||
|
'comlog': logging.DEBUG+1,
|
||||||
|
'info': logging.INFO,
|
||||||
|
'warning': logging.WARN,
|
||||||
|
'error': logging.ERROR,
|
||||||
|
'off': logging.ERROR+1}
|
||||||
CLR = '\r\x1b[K' # code to move to the left and clear current line
|
CLR = '\r\x1b[K' # code to move to the left and clear current line
|
||||||
|
|
||||||
|
|
||||||
class Logger:
|
class Handler(logging.StreamHandler):
|
||||||
show_time = False
|
def emit(self, record):
|
||||||
sigwinch = False
|
super().emit(record)
|
||||||
|
if clientenv.sigwinch:
|
||||||
def __init__(self, loglevel='info'):
|
|
||||||
func = self.noop
|
|
||||||
for lev in 'debug', 'info', 'warning', 'error', 'exception':
|
|
||||||
if lev == loglevel:
|
|
||||||
func = self.emit
|
|
||||||
setattr(self, lev, func)
|
|
||||||
self._minute = 0
|
|
||||||
|
|
||||||
def emit(self, fmt, *args, **kwds):
|
|
||||||
if self.show_time:
|
|
||||||
now = time.time()
|
|
||||||
tm = time.localtime(now)
|
|
||||||
if tm.tm_min != self._minute:
|
|
||||||
self._minute = tm.tm_min
|
|
||||||
print(CLR + time.strftime('--- %H:%M:%S ---', tm))
|
|
||||||
sec = f'{now % 60.0:6.3f}'.replace(' ', '0')
|
|
||||||
print(CLR + sec, str(fmt) % args)
|
|
||||||
else:
|
|
||||||
print(CLR + (str(fmt) % args))
|
|
||||||
if self.sigwinch:
|
|
||||||
# SIGWINCH: 'window size has changed' -> triggers a refresh of the input line
|
# SIGWINCH: 'window size has changed' -> triggers a refresh of the input line
|
||||||
os.kill(os.getpid(), signal.SIGWINCH)
|
os.kill(os.getpid(), signal.SIGWINCH)
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def noop(fmt, *args, **kwds):
|
class Logger(logging.Logger):
|
||||||
pass
|
show_time = False
|
||||||
|
_minute = None
|
||||||
|
|
||||||
|
def __init__(self, name, loglevel='info'):
|
||||||
|
super().__init__(name, LOG_LEVELS.get(loglevel, logging.INFO))
|
||||||
|
handler = Handler()
|
||||||
|
handler.formatter = logging.Formatter('%(asctime)s%(message)s')
|
||||||
|
handler.formatter.formatTime = self.format_time
|
||||||
|
self.addHandler(handler)
|
||||||
|
|
||||||
|
def format_time(self, record, datefmt=None):
|
||||||
|
if self.show_time:
|
||||||
|
now = record.created
|
||||||
|
tm = time.localtime(now)
|
||||||
|
sec = f'{now % 60.0:6.3f}'.replace(' ', '0')
|
||||||
|
if tm.tm_min == self._minute:
|
||||||
|
return f'{CLR}{sec} '
|
||||||
|
self._minute = tm.tm_min
|
||||||
|
return f"{CLR}{time.strftime('--- %H:%M:%S ---', tm)}\n{sec} "
|
||||||
|
return ''
|
||||||
|
|
||||||
|
|
||||||
class PrettyFloat(float):
|
class PrettyFloat(float):
|
||||||
@ -238,6 +244,9 @@ class Module:
|
|||||||
return self.value
|
return self.value
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
|
return f'<module {self._name}>'
|
||||||
|
|
||||||
|
def showAll(self):
|
||||||
wid = max((len(k) for k in self._parameters), default=0)
|
wid = max((len(k) for k in self._parameters), default=0)
|
||||||
return '%s\n%s%s' % (
|
return '%s\n%s%s' % (
|
||||||
self._title,
|
self._title,
|
||||||
@ -271,10 +280,7 @@ class Param:
|
|||||||
return value
|
return value
|
||||||
|
|
||||||
def formatted(self, obj):
|
def formatted(self, obj):
|
||||||
value, _, error = obj._secnode.cache[obj._name, self.name]
|
return obj._secnode.cache[obj._name, self.name].formatted()
|
||||||
if error:
|
|
||||||
return repr(error)
|
|
||||||
return self.format(value)
|
|
||||||
|
|
||||||
def __set__(self, obj, value):
|
def __set__(self, obj, value):
|
||||||
try:
|
try:
|
||||||
@ -286,9 +292,6 @@ class Param:
|
|||||||
clientenv.raise_with_short_traceback(e)
|
clientenv.raise_with_short_traceback(e)
|
||||||
obj._secnode.log.error(repr(e))
|
obj._secnode.log.error(repr(e))
|
||||||
|
|
||||||
def format(self, value):
|
|
||||||
return self.datatype.format_value(value)
|
|
||||||
|
|
||||||
|
|
||||||
class Command:
|
class Command:
|
||||||
def __init__(self, name, modname, secnode):
|
def __init__(self, name, modname, secnode):
|
||||||
@ -337,9 +340,28 @@ def watch(*args, **kwds):
|
|||||||
mobj._set_watching(arg)
|
mobj._set_watching(arg)
|
||||||
print('---')
|
print('---')
|
||||||
try:
|
try:
|
||||||
|
nodes = set()
|
||||||
for mobj in modules:
|
for mobj in modules:
|
||||||
|
nodes.add(mobj._secnode)
|
||||||
mobj._start_watching()
|
mobj._start_watching()
|
||||||
time.sleep(3600)
|
|
||||||
|
close_event = threading.Event()
|
||||||
|
|
||||||
|
def close_node(online, state):
|
||||||
|
if online and state != 'shutdown':
|
||||||
|
return
|
||||||
|
close_event.set()
|
||||||
|
return UnregisterCallback
|
||||||
|
|
||||||
|
def handle_error(*_):
|
||||||
|
close_event.set()
|
||||||
|
return UnregisterCallback
|
||||||
|
|
||||||
|
for node in nodes:
|
||||||
|
node.register_callback(None, nodeStateChange=close_node, handleError=handle_error)
|
||||||
|
|
||||||
|
close_event.wait()
|
||||||
|
|
||||||
except KeyboardInterrupt as e:
|
except KeyboardInterrupt as e:
|
||||||
clientenv.raise_with_short_traceback(e)
|
clientenv.raise_with_short_traceback(e)
|
||||||
finally:
|
finally:
|
||||||
@ -359,7 +381,7 @@ class Client(SecopClient):
|
|||||||
clientenv.init(sys.modules['__main__'].__dict__)
|
clientenv.init(sys.modules['__main__'].__dict__)
|
||||||
# remove previous client:
|
# remove previous client:
|
||||||
prev = self.secnodes.pop(uri, None)
|
prev = self.secnodes.pop(uri, None)
|
||||||
log = Logger(loglevel)
|
log = Logger(name, loglevel)
|
||||||
removed_modules = []
|
removed_modules = []
|
||||||
if prev:
|
if prev:
|
||||||
log.info('remove previous client to %s', uri)
|
log.info('remove previous client to %s', uri)
|
||||||
@ -433,8 +455,10 @@ def run(filepath):
|
|||||||
class ClientEnvironment:
|
class ClientEnvironment:
|
||||||
namespace = None
|
namespace = None
|
||||||
last_frames = 0
|
last_frames = 0
|
||||||
|
sigwinch = False
|
||||||
|
|
||||||
def init(self, namespace=None):
|
def init(self, namespace=None):
|
||||||
|
self.nodes = []
|
||||||
self.namespace = namespace or {}
|
self.namespace = namespace or {}
|
||||||
self.namespace.update(run=run, watch=watch, Client=Client)
|
self.namespace.update(run=run, watch=watch, Client=Client)
|
||||||
|
|
||||||
@ -444,7 +468,7 @@ class ClientEnvironment:
|
|||||||
raise exc
|
raise exc
|
||||||
|
|
||||||
def short_traceback(self):
|
def short_traceback(self):
|
||||||
"""cleanup tracback from irrelevant lines"""
|
"""cleanup traceback from irrelevant lines"""
|
||||||
lines = traceback.format_exception(*sys.exc_info())
|
lines = traceback.format_exception(*sys.exc_info())
|
||||||
# line 0: Traceback header
|
# line 0: Traceback header
|
||||||
# skip line 1+2 (contains unspecific console line and exec code)
|
# skip line 1+2 (contains unspecific console line and exec code)
|
||||||
@ -482,11 +506,15 @@ class Console(code.InteractiveConsole):
|
|||||||
readline.write_history_file(history)
|
readline.write_history_file(history)
|
||||||
|
|
||||||
def raw_input(self, prompt=""):
|
def raw_input(self, prompt=""):
|
||||||
Logger.sigwinch = bool(readline) # activate refresh signal
|
clientenv.sigwinch = bool(readline) # activate refresh signal
|
||||||
line = input(prompt)
|
line = input(prompt)
|
||||||
Logger.sigwinch = False
|
clientenv.sigwinch = False
|
||||||
if line.startswith('/'):
|
if line.startswith('/'):
|
||||||
line = f"run('{line[1:].strip()}')"
|
line = f"run('{line[1:].strip()}')"
|
||||||
|
module = clientenv.namespace.get(line.strip())
|
||||||
|
if isinstance(module, Module):
|
||||||
|
print(module.showAll())
|
||||||
|
line = ''
|
||||||
return line
|
return line
|
||||||
|
|
||||||
def showtraceback(self):
|
def showtraceback(self):
|
||||||
@ -499,7 +527,8 @@ def init(*nodes):
|
|||||||
for idx, node in enumerate(nodes):
|
for idx, node in enumerate(nodes):
|
||||||
client_name = '_c%d' % idx
|
client_name = '_c%d' % idx
|
||||||
try:
|
try:
|
||||||
clientenv.namespace[client_name] = Client(node, name=client_name)
|
node = clientenv.namespace[client_name] = Client(node, name=client_name)
|
||||||
|
clientenv.nodes.append(node)
|
||||||
success = True
|
success = True
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(repr(e))
|
print(repr(e))
|
||||||
|
@ -1244,16 +1244,20 @@ UInt64 = IntRange(0, (1 << 64) - 1)
|
|||||||
|
|
||||||
# Goodie: Convenience Datatypes for Programming
|
# Goodie: Convenience Datatypes for Programming
|
||||||
class LimitsType(TupleOf):
|
class LimitsType(TupleOf):
|
||||||
def __init__(self, members):
|
def __init__(self, member):
|
||||||
super().__init__(members, members)
|
super().__init__(member, member)
|
||||||
|
|
||||||
def __call__(self, value):
|
def validate(self, value, previous=None):
|
||||||
"""accepts an ordered tuple of numeric member types"""
|
"""accepts an ordered tuple of numeric member types"""
|
||||||
limits = TupleOf.validate(self, value)
|
limits = TupleOf.validate(self, value, previous)
|
||||||
if limits[1] < limits[0]:
|
if limits[1] < limits[0]:
|
||||||
raise RangeError(f'Maximum Value {limits[1]} must be greater than minimum value {limits[0]}!')
|
raise RangeError(f'maximum value {limits[1]} must be greater than '
|
||||||
|
f'minimum value {limits[0]}')
|
||||||
return limits
|
return limits
|
||||||
|
|
||||||
|
def copy(self):
|
||||||
|
return LimitsType(TupleOf.copy(self).members[0])
|
||||||
|
|
||||||
|
|
||||||
class StatusType(TupleOf):
|
class StatusType(TupleOf):
|
||||||
"""convenience type for status
|
"""convenience type for status
|
||||||
|
@ -218,7 +218,7 @@ class IsErrorError(SECoPError):
|
|||||||
|
|
||||||
class DisabledError(SECoPError):
|
class DisabledError(SECoPError):
|
||||||
"""The requested action can not be performed while the module is disabled"""
|
"""The requested action can not be performed while the module is disabled"""
|
||||||
name = 'disabled'
|
name = 'Disabled'
|
||||||
|
|
||||||
|
|
||||||
class ImpossibleError(SECoPError):
|
class ImpossibleError(SECoPError):
|
||||||
@ -232,7 +232,7 @@ class ReadFailedError(SECoPError):
|
|||||||
|
|
||||||
|
|
||||||
class OutOfRangeError(SECoPError):
|
class OutOfRangeError(SECoPError):
|
||||||
"""The requested parameter can not be read just now"""
|
"""The value read from the hardware is out of sensor or calibration range"""
|
||||||
name = 'OutOfRange'
|
name = 'OutOfRange'
|
||||||
|
|
||||||
|
|
||||||
|
304
frappy/extparams.py
Normal file
304
frappy/extparams.py
Normal file
@ -0,0 +1,304 @@
|
|||||||
|
# *****************************************************************************
|
||||||
|
#
|
||||||
|
# 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>
|
||||||
|
#
|
||||||
|
# *****************************************************************************
|
||||||
|
"""extended parameters
|
||||||
|
|
||||||
|
special parameter classes with some automatic functionality
|
||||||
|
"""
|
||||||
|
|
||||||
|
import re
|
||||||
|
from frappy.core import Parameter, Property
|
||||||
|
from frappy.datatypes import BoolType, DataType, DataTypeType, EnumType, \
|
||||||
|
FloatRange, StringType, StructOf, ValueType
|
||||||
|
from frappy.errors import ProgrammingError
|
||||||
|
|
||||||
|
|
||||||
|
class StructParam(Parameter):
|
||||||
|
"""convenience class to create a struct Parameter together with individual params
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
class Controller(Drivable):
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
|
ctrlpars = StructParam('ctrlpars struct', [
|
||||||
|
('pid_p', 'p', Parameter('control parameter p', FloatRange())),
|
||||||
|
('pid_i', 'i', Parameter('control parameter i', FloatRange())),
|
||||||
|
('pid_d', 'd', Parameter('control parameter d', FloatRange())),
|
||||||
|
], readonly=False)
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
|
then implement either read_ctrlpars and write_ctrlpars or
|
||||||
|
read_pid_p, read_pid_i, read_pid_d, write_pid_p, write_pid_i and write_pid_d
|
||||||
|
|
||||||
|
the methods not implemented will be created automatically
|
||||||
|
"""
|
||||||
|
|
||||||
|
# use properties, as simple attributes are not considered on copy()
|
||||||
|
paramdict = Property('dict <parametername> of Parameter(...)', ValueType())
|
||||||
|
hasStructRW = Property('has a read_<struct param> or write_<struct param> method',
|
||||||
|
BoolType(), default=False)
|
||||||
|
|
||||||
|
insideRW = 0 # counter for avoiding multiple superfluous updates
|
||||||
|
|
||||||
|
def __init__(self, description=None, paramdict=None, prefix_or_map='', *, datatype=None, readonly=False, **kwds):
|
||||||
|
"""create a struct parameter together with individual parameters
|
||||||
|
|
||||||
|
in addition to normal Parameter arguments:
|
||||||
|
|
||||||
|
:param paramdict: dict <member name> of Parameter(...)
|
||||||
|
:param prefix_or_map: either a prefix for the parameter name to add to the member name
|
||||||
|
or a dict <member name> or <parameter name>
|
||||||
|
"""
|
||||||
|
if isinstance(paramdict, DataType):
|
||||||
|
raise ProgrammingError('second argument must be a dict of Param')
|
||||||
|
if datatype is None and paramdict is not None: # omit the following on Parameter.copy()
|
||||||
|
if isinstance(prefix_or_map, str):
|
||||||
|
prefix_or_map = {m: prefix_or_map + m for m in paramdict}
|
||||||
|
for membername, param in paramdict.items():
|
||||||
|
param.name = prefix_or_map[membername]
|
||||||
|
datatype = StructOf(**{m: p.datatype for m, p in paramdict.items()})
|
||||||
|
kwds['influences'] = [p.name for p in paramdict.values()]
|
||||||
|
self.updateEnable = {}
|
||||||
|
if paramdict:
|
||||||
|
kwds['paramdict'] = paramdict
|
||||||
|
super().__init__(description, datatype, readonly=readonly, **kwds)
|
||||||
|
|
||||||
|
def __set_name__(self, owner, name):
|
||||||
|
# names of access methods of structed param (e.g. ctrlpars)
|
||||||
|
struct_read_name = f'read_{name}' # e.g. 'read_ctrlpars'
|
||||||
|
struct_write_name = f'write_{name}' # e.h. 'write_ctrlpars'
|
||||||
|
self.hasStructRW = hasattr(owner, struct_read_name) or hasattr(owner, struct_write_name)
|
||||||
|
|
||||||
|
for membername, param in self.paramdict.items():
|
||||||
|
pname = param.name
|
||||||
|
changes = {
|
||||||
|
'readonly': self.readonly,
|
||||||
|
'influences': set(param.influences) | {name},
|
||||||
|
}
|
||||||
|
param.ownProperties.update(changes)
|
||||||
|
param.init(changes)
|
||||||
|
setattr(owner, pname, param)
|
||||||
|
param.__set_name__(owner, param.name)
|
||||||
|
|
||||||
|
if self.hasStructRW:
|
||||||
|
rname = f'read_{pname}'
|
||||||
|
|
||||||
|
if not hasattr(owner, rname):
|
||||||
|
def rfunc(self, membername=membername, struct_read_name=struct_read_name):
|
||||||
|
return getattr(self, struct_read_name)()[membername]
|
||||||
|
|
||||||
|
rfunc.poll = False # read_<struct param> is polled only
|
||||||
|
setattr(owner, rname, rfunc)
|
||||||
|
|
||||||
|
if not self.readonly:
|
||||||
|
wname = f'write_{pname}'
|
||||||
|
if not hasattr(owner, wname):
|
||||||
|
def wfunc(self, value, membername=membername,
|
||||||
|
name=name, rname=rname, struct_write_name=struct_write_name):
|
||||||
|
valuedict = dict(getattr(self, name))
|
||||||
|
valuedict[membername] = value
|
||||||
|
getattr(self, struct_write_name)(valuedict)
|
||||||
|
return getattr(self, rname)()
|
||||||
|
|
||||||
|
setattr(owner, wname, wfunc)
|
||||||
|
|
||||||
|
if not self.hasStructRW:
|
||||||
|
if not hasattr(owner, struct_read_name):
|
||||||
|
def struct_read_func(self, name=name, flist=tuple(
|
||||||
|
(m, f'read_{p.name}') for m, p in self.paramdict.items())):
|
||||||
|
pobj = self.parameters[name]
|
||||||
|
# disable updates generated from the callbacks of individual params
|
||||||
|
pobj.insideRW += 1 # guarded by self.accessLock
|
||||||
|
try:
|
||||||
|
return {m: getattr(self, f)() for m, f in flist}
|
||||||
|
finally:
|
||||||
|
pobj.insideRW -= 1
|
||||||
|
|
||||||
|
setattr(owner, struct_read_name, struct_read_func)
|
||||||
|
|
||||||
|
if not (self.readonly or hasattr(owner, struct_write_name)):
|
||||||
|
|
||||||
|
def struct_write_func(self, value, name=name, funclist=tuple(
|
||||||
|
(m, f'write_{p.name}') for m, p in self.paramdict.items())):
|
||||||
|
pobj = self.parameters[name]
|
||||||
|
pobj.insideRW += 1 # guarded by self.accessLock
|
||||||
|
try:
|
||||||
|
return {m: getattr(self, f)(value[m]) for m, f in funclist}
|
||||||
|
finally:
|
||||||
|
pobj.insideRW -= 1
|
||||||
|
|
||||||
|
setattr(owner, struct_write_name, struct_write_func)
|
||||||
|
|
||||||
|
super().__set_name__(owner, name)
|
||||||
|
|
||||||
|
def finish(self, modobj=None):
|
||||||
|
"""register callbacks for consistency"""
|
||||||
|
super().finish(modobj)
|
||||||
|
if modobj:
|
||||||
|
|
||||||
|
if self.hasStructRW:
|
||||||
|
def cb(value, modobj=modobj, structparam=self):
|
||||||
|
for membername, param in structparam.paramdict.items():
|
||||||
|
setattr(modobj, param.name, value[membername])
|
||||||
|
|
||||||
|
modobj.addCallback(self.name, cb)
|
||||||
|
else:
|
||||||
|
for membername, param in self.paramdict.items():
|
||||||
|
def cb(value, modobj=modobj, structparam=self, membername=membername):
|
||||||
|
if not structparam.insideRW:
|
||||||
|
prev = dict(getattr(modobj, structparam.name))
|
||||||
|
prev[membername] = value
|
||||||
|
setattr(modobj, structparam.name, prev)
|
||||||
|
|
||||||
|
modobj.addCallback(param.name, cb)
|
||||||
|
|
||||||
|
|
||||||
|
class FloatEnumParam(Parameter):
|
||||||
|
"""combine enum and float parameter
|
||||||
|
|
||||||
|
Example Usage:
|
||||||
|
|
||||||
|
vrange = FloatEnumParam('sensor range', ['500uV', '20mV', '1V'], 'V')
|
||||||
|
|
||||||
|
The following will be created automatically:
|
||||||
|
|
||||||
|
- the parameter vrange will get a datatype FloatRange(5e-4, 1, unit='V')
|
||||||
|
- an additional parameter `vrange_idx` will be created with an enum type
|
||||||
|
{'500uV': 0, '20mV': 1, '1V': 2}
|
||||||
|
- the method `write_vrange` will be created automatically
|
||||||
|
|
||||||
|
However, the methods `write_vrange_idx` and `read_vrange_idx`, if needed,
|
||||||
|
have to implemented by the programmer.
|
||||||
|
|
||||||
|
Writing to the float parameter involves 'rounding' to the closest allowed value.
|
||||||
|
|
||||||
|
Customization:
|
||||||
|
|
||||||
|
The individual labels might be customized by defining them as a tuple
|
||||||
|
(<index>, <label>, <float value>) where either the index or the float value
|
||||||
|
may be omitted.
|
||||||
|
|
||||||
|
When the index is omitted, the element will be the previous index + 1 or
|
||||||
|
0 when it is the first element.
|
||||||
|
|
||||||
|
Omitted values will be determined from the label, assuming that they use
|
||||||
|
one of the predefined unit prefixes together with the given unit.
|
||||||
|
|
||||||
|
The name of the index parameter is by default '<name>_idx' but might be
|
||||||
|
changed with the idx_name argument.
|
||||||
|
"""
|
||||||
|
# use properties, as simple attributes are not considered on copy()
|
||||||
|
idx_name = Property('name of attached index parameter', StringType(), default='')
|
||||||
|
valuedict = Property('dict <index> of <value>', ValueType(dict))
|
||||||
|
enumtype = Property('dict <label> of <index', DataTypeType())
|
||||||
|
|
||||||
|
# TODO: factor out unit handling, at the latest when needed elsewhere
|
||||||
|
PREFIXES = {'q': -30, 'r': -27, 'y': -24, 'z': -21, 'a': -18, 'f': -15,
|
||||||
|
'p': -12, 'n': -9, 'u': -6, 'µ': -6, 'm': -3,
|
||||||
|
'': 0, 'k': 3, 'M': 6, 'G': 9, 'T': 12,
|
||||||
|
'P': 15, 'E': 18, 'Z': 21, 'Y': 24, 'R': 25, 'Q': 30}
|
||||||
|
|
||||||
|
def __init__(self, description=None, labels=None, unit='',
|
||||||
|
*, datatype=None, readonly=False, **kwds):
|
||||||
|
if labels is None:
|
||||||
|
# called on Parameter.copy()
|
||||||
|
super().__init__(description, datatype, readonly=readonly, **kwds)
|
||||||
|
return
|
||||||
|
if isinstance(labels, DataType):
|
||||||
|
raise ProgrammingError('second argument must be a list of labels, not a datatype')
|
||||||
|
nextidx = 0
|
||||||
|
try:
|
||||||
|
edict = {}
|
||||||
|
vdict = {}
|
||||||
|
for elem in labels:
|
||||||
|
if isinstance(elem, str):
|
||||||
|
idx, label = [nextidx, elem]
|
||||||
|
else:
|
||||||
|
if isinstance(elem[0], str):
|
||||||
|
elem = [nextidx] + list(elem)
|
||||||
|
idx, label, *tail = elem
|
||||||
|
if tail:
|
||||||
|
vdict[idx], = tail
|
||||||
|
edict[label] = idx
|
||||||
|
nextidx = idx + 1
|
||||||
|
except (ValueError, TypeError) as e:
|
||||||
|
raise ProgrammingError('labels must be a list of labels or tuples '
|
||||||
|
'([index], label, [value])') from e
|
||||||
|
pat = re.compile(rf'([+-]?\d*\.?\d*) *({"|".join(self.PREFIXES)}){unit}$')
|
||||||
|
try:
|
||||||
|
# determine missing values from labels
|
||||||
|
for label, idx in edict.items():
|
||||||
|
if idx not in vdict:
|
||||||
|
value, prefix = pat.match(label).groups()
|
||||||
|
vdict[idx] = float(f'{value}e{self.PREFIXES[prefix]}')
|
||||||
|
except (AttributeError, ValueError) as e:
|
||||||
|
raise ProgrammingError(f"{label!r} has not the form '<float><prefix>{unit}'") from e
|
||||||
|
try:
|
||||||
|
enumtype = EnumType(**edict)
|
||||||
|
except TypeError as e:
|
||||||
|
raise ProgrammingError(str(e)) from e
|
||||||
|
datatype = FloatRange(min(vdict.values()), max(vdict.values()), unit=unit)
|
||||||
|
super().__init__(description, datatype, enumtype=enumtype, valuedict=vdict,
|
||||||
|
readonly=readonly, **kwds)
|
||||||
|
|
||||||
|
def __set_name__(self, owner, name):
|
||||||
|
super().__set_name__(owner, name)
|
||||||
|
if not self.idx_name:
|
||||||
|
self.idx_name = name + '_idx'
|
||||||
|
iname = self.idx_name
|
||||||
|
idx_param = Parameter(f'index of {name}', self.enumtype,
|
||||||
|
readonly=self.readonly, influences={name})
|
||||||
|
idx_param.init({})
|
||||||
|
setattr(owner, iname, idx_param)
|
||||||
|
idx_param.__set_name__(owner, iname)
|
||||||
|
|
||||||
|
self.setProperty('influences', {iname})
|
||||||
|
|
||||||
|
if not hasattr(owner, f'write_{name}'):
|
||||||
|
|
||||||
|
# customization (like rounding up or down) might be
|
||||||
|
# achieved by adding write_<name>. if not, the default
|
||||||
|
# is rounding to the closest value
|
||||||
|
|
||||||
|
def wfunc(mobj, value, vdict=self.valuedict, fname=name, wfunc_iname=f'write_{iname}'):
|
||||||
|
getattr(mobj, wfunc_iname)(
|
||||||
|
min(vdict, key=lambda i: abs(vdict[i] - value)))
|
||||||
|
return getattr(mobj, fname)
|
||||||
|
|
||||||
|
setattr(owner, f'write_{name}', wfunc)
|
||||||
|
|
||||||
|
def __get__(self, instance, owner):
|
||||||
|
"""getter for value"""
|
||||||
|
if instance is None:
|
||||||
|
return self
|
||||||
|
return self.valuedict[instance.parameters[self.idx_name].value]
|
||||||
|
|
||||||
|
def trigger_setter(self, modobj, _):
|
||||||
|
# trigger update of float parameter on change of enum parameter
|
||||||
|
modobj.announceUpdate(self.name, getattr(modobj, self.name))
|
||||||
|
|
||||||
|
def finish(self, modobj=None):
|
||||||
|
"""register callbacks for consistency"""
|
||||||
|
super().finish(modobj)
|
||||||
|
if modobj:
|
||||||
|
modobj.addCallback(self.idx_name, self.trigger_setter, modobj)
|
@ -1,6 +1,10 @@
|
|||||||
from frappy.gui.qt import QCheckBox, QComboBox, QLineEdit, pyqtSignal
|
import sys
|
||||||
|
|
||||||
from frappy.datatypes import BoolType, EnumType
|
from frappy.gui.qt import QCheckBox, QComboBox, QDoubleSpinBox, QLineEdit, \
|
||||||
|
QSpinBox, pyqtSignal
|
||||||
|
|
||||||
|
from frappy.datatypes import BoolType, EnumType, FloatRange, IntRange, \
|
||||||
|
StringType, TextType
|
||||||
|
|
||||||
# ArrayOf, BLOBType, FloatRange, IntRange, StringType, StructOf, TextType, TupleOf
|
# ArrayOf, BLOBType, FloatRange, IntRange, StringType, StructOf, TextType, TupleOf
|
||||||
|
|
||||||
@ -9,11 +13,24 @@ def get_input_widget(datatype, parent=None):
|
|||||||
return {
|
return {
|
||||||
EnumType: EnumInput,
|
EnumType: EnumInput,
|
||||||
BoolType: BoolInput,
|
BoolType: BoolInput,
|
||||||
|
IntRange: IntInput,
|
||||||
|
StringType: StringInput,
|
||||||
|
TextType: StringInput,
|
||||||
}.get(datatype.__class__, GenericInput)(datatype, parent)
|
}.get(datatype.__class__, GenericInput)(datatype, parent)
|
||||||
|
|
||||||
|
|
||||||
class GenericInput(QLineEdit):
|
class InputBase:
|
||||||
submitted = pyqtSignal()
|
submitted = pyqtSignal()
|
||||||
|
input_feedback = pyqtSignal(str)
|
||||||
|
|
||||||
|
def get_input(self):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def submit(self):
|
||||||
|
self.submitted.emit()
|
||||||
|
|
||||||
|
|
||||||
|
class GenericInput(InputBase, QLineEdit):
|
||||||
def __init__(self, datatype, parent=None):
|
def __init__(self, datatype, parent=None):
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
self.datatype = datatype
|
self.datatype = datatype
|
||||||
@ -23,12 +40,28 @@ class GenericInput(QLineEdit):
|
|||||||
def get_input(self):
|
def get_input(self):
|
||||||
return self.datatype.from_string(self.text())
|
return self.datatype.from_string(self.text())
|
||||||
|
|
||||||
def submit(self):
|
|
||||||
self.submitted.emit()
|
class StringInput(GenericInput):
|
||||||
|
def __init__(self, datatype, parent=None):
|
||||||
|
super().__init__(datatype, parent)
|
||||||
|
|
||||||
|
|
||||||
class EnumInput(QComboBox):
|
class IntInput(InputBase, QSpinBox):
|
||||||
submitted = pyqtSignal()
|
def __init__(self, datatype, parent=None):
|
||||||
|
super().__init__(parent)
|
||||||
|
self.datatype = datatype
|
||||||
|
# we dont use setMaximum and setMinimum because it is quite restrictive
|
||||||
|
# when typing, so set it as high as possible
|
||||||
|
self.setMaximum(2147483647)
|
||||||
|
self.setMinimum(-2147483648)
|
||||||
|
|
||||||
|
self.lineEdit().returnPressed.connect(self.submit)
|
||||||
|
|
||||||
|
def get_input(self):
|
||||||
|
return self.datatype(self.value())
|
||||||
|
|
||||||
|
|
||||||
|
class EnumInput(InputBase, QComboBox):
|
||||||
def __init__(self, datatype, parent=None):
|
def __init__(self, datatype, parent=None):
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
self.setPlaceholderText('choose value')
|
self.setPlaceholderText('choose value')
|
||||||
@ -45,18 +78,11 @@ class EnumInput(QComboBox):
|
|||||||
def get_input(self):
|
def get_input(self):
|
||||||
return self._map[self.currentIndex()].value
|
return self._map[self.currentIndex()].value
|
||||||
|
|
||||||
def submit(self):
|
|
||||||
self.submitted.emit()
|
|
||||||
|
|
||||||
|
class BoolInput(InputBase, QCheckBox):
|
||||||
class BoolInput(QCheckBox):
|
|
||||||
submitted = pyqtSignal()
|
|
||||||
def __init__(self, datatype, parent=None):
|
def __init__(self, datatype, parent=None):
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
self.datatype = datatype
|
self.datatype = datatype
|
||||||
|
|
||||||
def get_input(self):
|
def get_input(self):
|
||||||
return self.isChecked()
|
return self.isChecked()
|
||||||
|
|
||||||
def submit(self):
|
|
||||||
self.submitted.emit()
|
|
||||||
|
@ -24,9 +24,9 @@ from frappy.gui.qt import QColor, QDialog, QHBoxLayout, QIcon, QLabel, \
|
|||||||
QLineEdit, QMessageBox, QPropertyAnimation, QPushButton, Qt, QToolButton, \
|
QLineEdit, QMessageBox, QPropertyAnimation, QPushButton, Qt, QToolButton, \
|
||||||
QWidget, pyqtProperty, pyqtSignal
|
QWidget, pyqtProperty, pyqtSignal
|
||||||
|
|
||||||
|
from frappy.gui.inputwidgets import get_input_widget
|
||||||
from frappy.gui.util import Colors, loadUi
|
from frappy.gui.util import Colors, loadUi
|
||||||
from frappy.gui.valuewidgets import get_widget
|
from frappy.gui.valuewidgets import get_widget
|
||||||
from frappy.gui.inputwidgets import get_input_widget
|
|
||||||
|
|
||||||
|
|
||||||
class CommandDialog(QDialog):
|
class CommandDialog(QDialog):
|
||||||
@ -54,7 +54,11 @@ class CommandDialog(QDialog):
|
|||||||
self.resize(self.sizeHint())
|
self.resize(self.sizeHint())
|
||||||
|
|
||||||
def get_value(self):
|
def get_value(self):
|
||||||
return True, self.widgets[0].get_value()
|
try:
|
||||||
|
return self.widgets[0].get_value()
|
||||||
|
except Exception as e:
|
||||||
|
QMessageBox.warning(self.parent(), 'Operation failed', str(e))
|
||||||
|
return None
|
||||||
|
|
||||||
def exec(self):
|
def exec(self):
|
||||||
if super().exec():
|
if super().exec():
|
||||||
@ -95,8 +99,9 @@ class CommandButton(QPushButton):
|
|||||||
if self._argintype:
|
if self._argintype:
|
||||||
dlg = CommandDialog(self._cmdname, self._argintype)
|
dlg = CommandDialog(self._cmdname, self._argintype)
|
||||||
args = dlg.exec()
|
args = dlg.exec()
|
||||||
if args: # not 'Cancel' clicked
|
if args is not None:
|
||||||
self._cb(self._cmdname, args[1])
|
# no errors when converting value and 'Cancel' wasn't clicked
|
||||||
|
self._cb(self._cmdname, args)
|
||||||
else:
|
else:
|
||||||
# no need for arguments
|
# no need for arguments
|
||||||
self._cb(self._cmdname, None)
|
self._cb(self._cmdname, None)
|
||||||
@ -442,8 +447,8 @@ class ModuleWidget(QWidget):
|
|||||||
self.paramDetails.emit(self._name, param)
|
self.paramDetails.emit(self._name, param)
|
||||||
|
|
||||||
def _button_pressed(self, param):
|
def _button_pressed(self, param):
|
||||||
target = self._paramInputs[param].get_input()
|
|
||||||
try:
|
try:
|
||||||
|
target = self._paramInputs[param].get_input()
|
||||||
self._node.setParameter(self._name, param, target)
|
self._node.setParameter(self._name, param, target)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
QMessageBox.warning(self.parent(), 'Operation failed', str(e))
|
QMessageBox.warning(self.parent(), 'Operation failed', str(e))
|
||||||
|
@ -42,10 +42,10 @@ try:
|
|||||||
QDialogButtonBox, QDoubleSpinBox, QFileDialog, QFrame, QGridLayout, \
|
QDialogButtonBox, QDoubleSpinBox, QFileDialog, QFrame, QGridLayout, \
|
||||||
QGroupBox, QHBoxLayout, QInputDialog, QLabel, QLineEdit, QMainWindow, \
|
QGroupBox, QHBoxLayout, QInputDialog, QLabel, QLineEdit, QMainWindow, \
|
||||||
QMenu, QMessageBox, QPlainTextEdit, QPushButton, QRadioButton, \
|
QMenu, QMessageBox, QPlainTextEdit, QPushButton, QRadioButton, \
|
||||||
QScrollArea, QSizePolicy, QSpacerItem, QSpinBox, QStyle, \
|
QScrollArea, QSizePolicy, QSlider, QSpacerItem, QSpinBox, QStyle, \
|
||||||
QStyleOptionTab, QStylePainter, QTabBar, QTabWidget, QTextEdit, \
|
QStyleOptionTab, QStylePainter, QTabBar, QTabWidget, QTextEdit, \
|
||||||
QToolButton, QTreeView, QTreeWidget, QTreeWidgetItem, QVBoxLayout, \
|
QToolButton, QTreeView, QTreeWidget, QTreeWidgetItem, QVBoxLayout, \
|
||||||
QWidget,QSlider
|
QWidget
|
||||||
|
|
||||||
import frappy.gui.resources_qt6
|
import frappy.gui.resources_qt6
|
||||||
|
|
||||||
@ -62,9 +62,9 @@ except ImportError as e:
|
|||||||
QDialog, QDialogButtonBox, QDoubleSpinBox, QFileDialog, QFrame, \
|
QDialog, QDialogButtonBox, QDoubleSpinBox, QFileDialog, QFrame, \
|
||||||
QGridLayout, QGroupBox, QHBoxLayout, QInputDialog, QLabel, QLineEdit, \
|
QGridLayout, QGroupBox, QHBoxLayout, QInputDialog, QLabel, QLineEdit, \
|
||||||
QMainWindow, QMenu, QMessageBox, QPlainTextEdit, QPushButton, \
|
QMainWindow, QMenu, QMessageBox, QPlainTextEdit, QPushButton, \
|
||||||
QRadioButton, QScrollArea, QShortcut, QSizePolicy, QSpacerItem, \
|
QRadioButton, QScrollArea, QShortcut, QSizePolicy, QSlider, \
|
||||||
QSpinBox, QStyle, QStyleOptionTab, QStylePainter, QTabBar, \
|
QSpacerItem, QSpinBox, QStyle, QStyleOptionTab, QStylePainter, \
|
||||||
QTabWidget, QTextEdit, QToolButton, QTreeView, QTreeWidget, \
|
QTabBar, QTabWidget, QTextEdit, QToolButton, QTreeView, QTreeWidget, \
|
||||||
QTreeWidgetItem, QVBoxLayout, QWidget, QSlider
|
QTreeWidgetItem, QVBoxLayout, QWidget
|
||||||
|
|
||||||
import frappy.gui.resources_qt5
|
import frappy.gui.resources_qt5
|
||||||
|
@ -61,7 +61,6 @@ class HasIO(Module):
|
|||||||
ioname = opts.get('io') or f'{name}_io'
|
ioname = opts.get('io') or f'{name}_io'
|
||||||
io = self.ioClass(ioname, srv.log.getChild(ioname), opts, srv) # pylint: disable=not-callable
|
io = self.ioClass(ioname, srv.log.getChild(ioname), opts, srv) # pylint: disable=not-callable
|
||||||
io.callingModule = []
|
io.callingModule = []
|
||||||
srv.modules[ioname] = io
|
|
||||||
srv.secnode.add_module(io, ioname)
|
srv.secnode.add_module(io, ioname)
|
||||||
self.ioDict[self.uri] = ioname
|
self.ioDict[self.uri] = ioname
|
||||||
self.io = ioname
|
self.io = ioname
|
||||||
|
@ -141,6 +141,7 @@ class SequencerMixin:
|
|||||||
return self.Status.IDLE, ''
|
return self.Status.IDLE, ''
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
|
"""stop sequence"""
|
||||||
if self.seq_is_alive():
|
if self.seq_is_alive():
|
||||||
self._seq_stopflag = True
|
self._seq_stopflag = True
|
||||||
|
|
||||||
|
@ -83,15 +83,14 @@ class HasAccessibles(HasProperties):
|
|||||||
override_values.pop(key, None)
|
override_values.pop(key, None)
|
||||||
elif key in accessibles:
|
elif key in accessibles:
|
||||||
override_values[key] = value
|
override_values[key] = value
|
||||||
|
# remark: merged_properties contain already the properties of accessibles of cls
|
||||||
for aname, aobj in list(accessibles.items()):
|
for aname, aobj in list(accessibles.items()):
|
||||||
if aname in override_values:
|
if aname in override_values:
|
||||||
aobj = aobj.copy()
|
|
||||||
value = override_values[aname]
|
value = override_values[aname]
|
||||||
if value is None:
|
if value is None:
|
||||||
accessibles.pop(aname)
|
accessibles.pop(aname)
|
||||||
continue
|
continue
|
||||||
aobj.merge(merged_properties[aname])
|
aobj = aobj.create_from_value(merged_properties[aname], value)
|
||||||
aobj.override(value)
|
|
||||||
# replace the bare value by the created accessible
|
# replace the bare value by the created accessible
|
||||||
setattr(cls, aname, aobj)
|
setattr(cls, aname, aobj)
|
||||||
else:
|
else:
|
||||||
@ -334,8 +333,7 @@ class Module(HasAccessibles):
|
|||||||
self.secNode = srv.secnode
|
self.secNode = srv.secnode
|
||||||
self.log = logger
|
self.log = logger
|
||||||
self.name = name
|
self.name = name
|
||||||
self.valueCallbacks = {}
|
self.paramCallbacks = {}
|
||||||
self.errorCallbacks = {}
|
|
||||||
self.earlyInitDone = False
|
self.earlyInitDone = False
|
||||||
self.initModuleDone = False
|
self.initModuleDone = False
|
||||||
self.startModuleDone = False
|
self.startModuleDone = False
|
||||||
@ -469,8 +467,7 @@ class Module(HasAccessibles):
|
|||||||
apply default when no value is given (in cfg or as Parameter argument)
|
apply default when no value is given (in cfg or as Parameter argument)
|
||||||
or complain, when cfg is needed
|
or complain, when cfg is needed
|
||||||
"""
|
"""
|
||||||
self.valueCallbacks[pname] = []
|
self.paramCallbacks[pname] = []
|
||||||
self.errorCallbacks[pname] = []
|
|
||||||
if isinstance(pobj, Limit):
|
if isinstance(pobj, Limit):
|
||||||
basepname = pname.rpartition('_')[0]
|
basepname = pname.rpartition('_')[0]
|
||||||
baseparam = self.parameters.get(basepname)
|
baseparam = self.parameters.get(basepname)
|
||||||
@ -535,68 +532,46 @@ class Module(HasAccessibles):
|
|||||||
err.report_error = False
|
err.report_error = False
|
||||||
return # no updates for repeated errors
|
return # no updates for repeated errors
|
||||||
err = secop_error(err)
|
err = secop_error(err)
|
||||||
elif not changed and timestamp < (pobj.timestamp or 0) + pobj.omit_unchanged_within:
|
value_err = value, err
|
||||||
|
else:
|
||||||
|
if not changed and timestamp < (pobj.timestamp or 0) + pobj.omit_unchanged_within:
|
||||||
# no change within short time -> omit
|
# no change within short time -> omit
|
||||||
return
|
return
|
||||||
|
value_err = (value,)
|
||||||
pobj.timestamp = timestamp or time.time()
|
pobj.timestamp = timestamp or time.time()
|
||||||
if err:
|
pobj.readerror = err
|
||||||
callbacks = self.errorCallbacks
|
for cbfunc, cbargs in self.paramCallbacks[pname]:
|
||||||
pobj.readerror = arg = err
|
try:
|
||||||
else:
|
cbfunc(*cbargs, *value_err)
|
||||||
callbacks = self.valueCallbacks
|
except Exception:
|
||||||
arg = value
|
pass
|
||||||
pobj.readerror = None
|
|
||||||
if pobj.export:
|
if pobj.export:
|
||||||
self.updateCallback(self, pobj)
|
self.updateCallback(self, pobj)
|
||||||
cblist = callbacks[pname]
|
|
||||||
for cb in cblist:
|
def addCallback(self, pname, callback_function, *args):
|
||||||
try:
|
self.paramCallbacks[pname].append((callback_function, args))
|
||||||
cb(arg)
|
|
||||||
except Exception:
|
|
||||||
# print(formatExtendedTraceback())
|
|
||||||
pass
|
|
||||||
|
|
||||||
def registerCallbacks(self, modobj, autoupdate=()):
|
def registerCallbacks(self, modobj, autoupdate=()):
|
||||||
"""register callbacks to another module <modobj>
|
"""register callbacks to another module <modobj>
|
||||||
|
|
||||||
- whenever a self.<param> changes:
|
whenever a self.<param> changes or changes its error state:
|
||||||
<modobj>.update_<param> is called with the new value as argument.
|
<modobj>.update_param(<value> [, <exc>]) is called,
|
||||||
If this method raises an exception, <modobj>.<param> gets into an error state.
|
where <value> is the new value and <exc> is given only in case of error.
|
||||||
If the method does not exist and <param> is in autoupdate,
|
if the method does not exist, and <param> is in autoupdate
|
||||||
<modobj>.<param> is updated to self.<param>
|
<modobj>.announceUpdate(<pname>, <value>, <exc>) is called
|
||||||
- whenever <self>.<param> gets into an error state:
|
with <exc> being None in case of no error.
|
||||||
<modobj>.error_update_<param> is called with the exception as argument.
|
|
||||||
If this method raises an error, <modobj>.<param> gets into an error state.
|
|
||||||
If this method does not exist, and <param> is in autoupdate,
|
|
||||||
<modobj>.<param> gets into the same error state as self.<param>
|
|
||||||
"""
|
|
||||||
for pname in self.parameters:
|
|
||||||
errfunc = getattr(modobj, 'error_update_' + pname, None)
|
|
||||||
if errfunc:
|
|
||||||
def errcb(err, p=pname, efunc=errfunc):
|
|
||||||
try:
|
|
||||||
efunc(err)
|
|
||||||
except Exception as e:
|
|
||||||
modobj.announceUpdate(p, err=e)
|
|
||||||
self.errorCallbacks[pname].append(errcb)
|
|
||||||
else:
|
|
||||||
def errcb(err, p=pname):
|
|
||||||
modobj.announceUpdate(p, err=err)
|
|
||||||
if pname in autoupdate:
|
|
||||||
self.errorCallbacks[pname].append(errcb)
|
|
||||||
|
|
||||||
updfunc = getattr(modobj, 'update_' + pname, None)
|
Remark: when <modobj>.update_<param> does not accept the <exc> argument,
|
||||||
if updfunc:
|
nothing happens (the callback is catched by try / except).
|
||||||
def cb(value, ufunc=updfunc, efunc=errcb):
|
Any exceptions raised by the callback function are silently ignored.
|
||||||
try:
|
"""
|
||||||
ufunc(value)
|
autoupdate = set(autoupdate)
|
||||||
except Exception as e:
|
for pname in self.parameters:
|
||||||
efunc(e)
|
cbfunc = getattr(modobj, 'update_' + pname, None)
|
||||||
self.valueCallbacks[pname].append(cb)
|
if cbfunc:
|
||||||
|
self.addCallback(pname, cbfunc)
|
||||||
elif pname in autoupdate:
|
elif pname in autoupdate:
|
||||||
def cb(value, p=pname):
|
self.addCallback(pname, modobj.announceUpdate, pname)
|
||||||
modobj.announceUpdate(p, value)
|
|
||||||
self.valueCallbacks[pname].append(cb)
|
|
||||||
|
|
||||||
def isBusy(self, status=None):
|
def isBusy(self, status=None):
|
||||||
"""helper function for treating substates of BUSY correctly"""
|
"""helper function for treating substates of BUSY correctly"""
|
||||||
@ -614,6 +589,10 @@ class Module(HasAccessibles):
|
|||||||
# enablePoll == False: we still need the poll thread for writing values from writeDict
|
# enablePoll == False: we still need the poll thread for writing values from writeDict
|
||||||
if hasattr(self, 'io'):
|
if hasattr(self, 'io'):
|
||||||
self.io.polledModules.append(self)
|
self.io.polledModules.append(self)
|
||||||
|
if not self.io.triggerPoll:
|
||||||
|
# when self.io.enablePoll is False, triggerPoll is not
|
||||||
|
# created for self.io in the else clause below
|
||||||
|
self.io.triggerPoll = threading.Event()
|
||||||
else:
|
else:
|
||||||
self.triggerPoll = threading.Event()
|
self.triggerPoll = threading.Event()
|
||||||
self.polledModules.append(self)
|
self.polledModules.append(self)
|
||||||
@ -713,8 +692,8 @@ class Module(HasAccessibles):
|
|||||||
for mobj in polled_modules:
|
for mobj in polled_modules:
|
||||||
pinfo = mobj.pollInfo = PollInfo(mobj.pollinterval, self.triggerPoll)
|
pinfo = mobj.pollInfo = PollInfo(mobj.pollinterval, self.triggerPoll)
|
||||||
# trigger a poll interval change when self.pollinterval changes.
|
# trigger a poll interval change when self.pollinterval changes.
|
||||||
if 'pollinterval' in mobj.valueCallbacks:
|
if 'pollinterval' in mobj.paramCallbacks:
|
||||||
mobj.valueCallbacks['pollinterval'].append(pinfo.update_interval)
|
mobj.addCallback('pollinterval', pinfo.update_interval)
|
||||||
|
|
||||||
for pname, pobj in mobj.parameters.items():
|
for pname, pobj in mobj.parameters.items():
|
||||||
rfunc = getattr(mobj, 'read_' + pname)
|
rfunc = getattr(mobj, 'read_' + pname)
|
||||||
|
@ -36,6 +36,7 @@ from .modulebase import Module
|
|||||||
|
|
||||||
class Readable(Module):
|
class Readable(Module):
|
||||||
"""basic readable module"""
|
"""basic readable module"""
|
||||||
|
# pylint: disable=invalid-name
|
||||||
Status = Enum('Status',
|
Status = Enum('Status',
|
||||||
IDLE=StatusType.IDLE,
|
IDLE=StatusType.IDLE,
|
||||||
WARN=StatusType.WARN,
|
WARN=StatusType.WARN,
|
||||||
@ -92,7 +93,7 @@ class Drivable(Writable):
|
|||||||
|
|
||||||
@Command(None, result=None)
|
@Command(None, result=None)
|
||||||
def stop(self):
|
def stop(self):
|
||||||
"""cease driving, go to IDLE state"""
|
"""not implemented - this is a no-op"""
|
||||||
|
|
||||||
|
|
||||||
class Communicator(HasComlog, Module):
|
class Communicator(HasComlog, Module):
|
||||||
|
@ -57,13 +57,17 @@ class Accessible(HasProperties):
|
|||||||
def as_dict(self):
|
def as_dict(self):
|
||||||
return self.propertyValues
|
return self.propertyValues
|
||||||
|
|
||||||
def override(self, value):
|
def create_from_value(self, properties, value):
|
||||||
"""override with a bare value"""
|
"""return a clone with given value and inherited properties"""
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def clone(self, properties, **kwds):
|
||||||
|
"""return a clone of ourselfs with inherited properties"""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def copy(self):
|
def copy(self):
|
||||||
"""return a (deep) copy of ourselfs"""
|
"""return a (deep) copy of ourselfs"""
|
||||||
raise NotImplementedError
|
return self.clone(self.propertyValues)
|
||||||
|
|
||||||
def updateProperties(self, merged_properties):
|
def updateProperties(self, merged_properties):
|
||||||
"""update merged_properties with our own properties"""
|
"""update merged_properties with our own properties"""
|
||||||
@ -94,6 +98,16 @@ class Accessible(HasProperties):
|
|||||||
props.append(f'{k}={v!r}')
|
props.append(f'{k}={v!r}')
|
||||||
return f"{self.__class__.__name__}({', '.join(props)})"
|
return f"{self.__class__.__name__}({', '.join(props)})"
|
||||||
|
|
||||||
|
def fixExport(self):
|
||||||
|
if self.export is True:
|
||||||
|
predefined_cls = PREDEFINED_ACCESSIBLES.get(self.name)
|
||||||
|
if predefined_cls is None:
|
||||||
|
self.export = '_' + self.name
|
||||||
|
elif isinstance(self, predefined_cls):
|
||||||
|
self.export = self.name
|
||||||
|
else:
|
||||||
|
raise ProgrammingError(f'can not use {self.name!r} as name of a {type(self).__name__}')
|
||||||
|
|
||||||
|
|
||||||
class Parameter(Accessible):
|
class Parameter(Accessible):
|
||||||
"""defines a parameter
|
"""defines a parameter
|
||||||
@ -221,26 +235,17 @@ class Parameter(Accessible):
|
|||||||
self.name = name
|
self.name = name
|
||||||
if isinstance(self.datatype, EnumType):
|
if isinstance(self.datatype, EnumType):
|
||||||
self.datatype.set_name(name)
|
self.datatype.set_name(name)
|
||||||
|
self.fixExport()
|
||||||
|
|
||||||
if self.export is True:
|
def clone(self, properties, **kwds):
|
||||||
predefined_cls = PREDEFINED_ACCESSIBLES.get(self.name, None)
|
"""return a clone of ourselfs with inherited properties"""
|
||||||
if predefined_cls is Parameter:
|
res = type(self)(**kwds)
|
||||||
self.export = self.name
|
|
||||||
elif predefined_cls is None:
|
|
||||||
self.export = '_' + self.name
|
|
||||||
else:
|
|
||||||
raise ProgrammingError(f'can not use {self.name!r} as name of a Parameter')
|
|
||||||
if 'export' in self.ownProperties:
|
|
||||||
# avoid export=True overrides export=<name>
|
|
||||||
self.ownProperties['export'] = self.export
|
|
||||||
|
|
||||||
def copy(self):
|
|
||||||
"""return a (deep) copy of ourselfs"""
|
|
||||||
res = type(self)()
|
|
||||||
res.name = self.name
|
res.name = self.name
|
||||||
res.init(self.propertyValues)
|
res.init(properties)
|
||||||
|
res.init(res.ownProperties)
|
||||||
if 'datatype' in self.propertyValues:
|
if 'datatype' in self.propertyValues:
|
||||||
res.datatype = res.datatype.copy()
|
res.datatype = res.datatype.copy()
|
||||||
|
res.finish()
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def updateProperties(self, merged_properties):
|
def updateProperties(self, merged_properties):
|
||||||
@ -253,9 +258,9 @@ class Parameter(Accessible):
|
|||||||
merged_properties.pop(key)
|
merged_properties.pop(key)
|
||||||
merged_properties.update(self.ownProperties)
|
merged_properties.update(self.ownProperties)
|
||||||
|
|
||||||
def override(self, value):
|
def create_from_value(self, properties, value):
|
||||||
"""override default"""
|
"""return a clone with given value and inherited properties"""
|
||||||
self.value = self.datatype(value)
|
return self.clone(properties, value=self.datatype(value))
|
||||||
|
|
||||||
def merge(self, merged_properties):
|
def merge(self, merged_properties):
|
||||||
"""merge with inherited properties
|
"""merge with inherited properties
|
||||||
@ -274,7 +279,7 @@ class Parameter(Accessible):
|
|||||||
|
|
||||||
:param modobj: final call, called from Module.__init__
|
:param modobj: final call, called from Module.__init__
|
||||||
"""
|
"""
|
||||||
|
self.fixExport()
|
||||||
if self.constant is not None:
|
if self.constant is not None:
|
||||||
constant = self.datatype(self.constant)
|
constant = self.datatype(self.constant)
|
||||||
# The value of the `constant` property should be the
|
# The value of the `constant` property should be the
|
||||||
@ -390,7 +395,7 @@ class Command(Accessible):
|
|||||||
else:
|
else:
|
||||||
# goodie: allow @Command instead of @Command()
|
# goodie: allow @Command instead of @Command()
|
||||||
self.func = argument # this is the wrapped method!
|
self.func = argument # this is the wrapped method!
|
||||||
if argument.__doc__:
|
if argument.__doc__ is not None:
|
||||||
self.description = inspect.cleandoc(argument.__doc__)
|
self.description = inspect.cleandoc(argument.__doc__)
|
||||||
self.name = self.func.__name__ # this is probably not needed
|
self.name = self.func.__name__ # this is probably not needed
|
||||||
self._inherit = inherit # save for __set_name__
|
self._inherit = inherit # save for __set_name__
|
||||||
@ -401,18 +406,8 @@ class Command(Accessible):
|
|||||||
if self.func is None:
|
if self.func is None:
|
||||||
raise ProgrammingError(f'Command {owner.__name__}.{name} must be used as a method decorator')
|
raise ProgrammingError(f'Command {owner.__name__}.{name} must be used as a method decorator')
|
||||||
|
|
||||||
|
self.fixExport()
|
||||||
self.datatype = CommandType(self.argument, self.result)
|
self.datatype = CommandType(self.argument, self.result)
|
||||||
if self.export is True:
|
|
||||||
predefined_cls = PREDEFINED_ACCESSIBLES.get(name, None)
|
|
||||||
if predefined_cls is Command:
|
|
||||||
self.export = name
|
|
||||||
elif predefined_cls is None:
|
|
||||||
self.export = '_' + name
|
|
||||||
else:
|
|
||||||
raise ProgrammingError(f'can not use {name!r} as name of a Command') from None
|
|
||||||
if 'export' in self.ownProperties:
|
|
||||||
# avoid export=True overrides export=<name>
|
|
||||||
self.ownProperties['export'] = self.export
|
|
||||||
if not self._inherit:
|
if not self._inherit:
|
||||||
for key, pobj in self.properties.items():
|
for key, pobj in self.properties.items():
|
||||||
if key not in self.propertyValues:
|
if key not in self.propertyValues:
|
||||||
@ -439,38 +434,38 @@ class Command(Accessible):
|
|||||||
f' members!: {params} != {members}')
|
f' members!: {params} != {members}')
|
||||||
self.argument.optional = [p for p,v in sig.parameters.items()
|
self.argument.optional = [p for p,v in sig.parameters.items()
|
||||||
if v.default is not inspect.Parameter.empty]
|
if v.default is not inspect.Parameter.empty]
|
||||||
if 'description' not in self.propertyValues and func.__doc__:
|
if 'description' not in self.ownProperties and func.__doc__ is not None:
|
||||||
self.description = inspect.cleandoc(func.__doc__)
|
self.description = inspect.cleandoc(func.__doc__)
|
||||||
self.ownProperties['description'] = self.description
|
self.ownProperties['description'] = self.description
|
||||||
self.func = func
|
self.func = func
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def copy(self):
|
def clone(self, properties, **kwds):
|
||||||
"""return a (deep) copy of ourselfs"""
|
"""return a clone of ourselfs with inherited properties"""
|
||||||
res = type(self)()
|
res = type(self)(**kwds)
|
||||||
res.name = self.name
|
res.name = self.name
|
||||||
|
self.fixExport()
|
||||||
res.func = self.func
|
res.func = self.func
|
||||||
res.init(self.propertyValues)
|
res.init(properties)
|
||||||
|
res.init(res.ownProperties)
|
||||||
if res.argument:
|
if res.argument:
|
||||||
res.argument = res.argument.copy()
|
res.argument = res.argument.copy()
|
||||||
if res.result:
|
if res.result:
|
||||||
res.result = res.result.copy()
|
res.result = res.result.copy()
|
||||||
self.finish()
|
res.finish()
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def updateProperties(self, merged_properties):
|
def updateProperties(self, merged_properties):
|
||||||
"""update merged_properties with our own properties"""
|
"""update merged_properties with our own properties"""
|
||||||
merged_properties.update(self.ownProperties)
|
merged_properties.update(self.ownProperties)
|
||||||
|
|
||||||
def override(self, value):
|
def create_from_value(self, properties, value):
|
||||||
"""override method
|
"""return a clone with given value and inherited properties
|
||||||
|
|
||||||
this is needed when the @Command is missing on a method overriding a command"""
|
this is needed when the @Command is missing on a method overriding a command"""
|
||||||
if not callable(value):
|
if not callable(value):
|
||||||
raise ProgrammingError(f'{self.name} = {value!r} is overriding a Command')
|
raise ProgrammingError(f'{self.name} = {value!r} is overriding a Command')
|
||||||
self.func = value
|
return self.clone(properties)(value)
|
||||||
if value.__doc__:
|
|
||||||
self.description = inspect.cleandoc(value.__doc__)
|
|
||||||
|
|
||||||
def merge(self, merged_properties):
|
def merge(self, merged_properties):
|
||||||
"""merge with inherited properties
|
"""merge with inherited properties
|
||||||
|
@ -84,9 +84,7 @@ class PersistentMixin(Module):
|
|||||||
flag = getattr(pobj, 'persistent', False)
|
flag = getattr(pobj, 'persistent', False)
|
||||||
if flag:
|
if flag:
|
||||||
if flag == 'auto':
|
if flag == 'auto':
|
||||||
def cb(value, m=self):
|
self.addCallback(pname, self.saveParameters)
|
||||||
m.saveParameters()
|
|
||||||
self.valueCallbacks[pname].append(cb)
|
|
||||||
self.initData[pname] = pobj.value
|
self.initData[pname] = pobj.value
|
||||||
if not pobj.given:
|
if not pobj.given:
|
||||||
if pname in loaded:
|
if pname in loaded:
|
||||||
@ -129,16 +127,18 @@ class PersistentMixin(Module):
|
|||||||
self.writeInitParams()
|
self.writeInitParams()
|
||||||
return loaded
|
return loaded
|
||||||
|
|
||||||
def saveParameters(self):
|
def saveParameters(self, _=None):
|
||||||
"""save persistent parameters
|
"""save persistent parameters
|
||||||
|
|
||||||
- to be called regularly explicitly by the module
|
- to be called regularly explicitly by the module
|
||||||
- the caller has to make sure that this is not called after
|
- the caller has to make sure that this is not called after
|
||||||
a power down of the connected hardware before loadParameters
|
a power down of the connected hardware before loadParameters
|
||||||
|
|
||||||
|
dummy argument to avoid closure for callback
|
||||||
"""
|
"""
|
||||||
if self.writeDict:
|
if self.writeDict:
|
||||||
# do not save before all values are written to the hw, as potentially
|
# do not save before all values are written to the hw, as potentially
|
||||||
# factory default values were read in the mean time
|
# factory default values were read in the meantime
|
||||||
return
|
return
|
||||||
self.__save_params()
|
self.__save_params()
|
||||||
|
|
||||||
|
@ -47,9 +47,11 @@ def make_update(modulename, pobj):
|
|||||||
if pobj.readerror:
|
if pobj.readerror:
|
||||||
return (ERRORPREFIX + EVENTREPLY, f'{modulename}:{pobj.export}',
|
return (ERRORPREFIX + EVENTREPLY, f'{modulename}:{pobj.export}',
|
||||||
# error-report !
|
# error-report !
|
||||||
[pobj.readerror.name, str(pobj.readerror), {'t': pobj.timestamp}])
|
[pobj.readerror.name, str(pobj.readerror),
|
||||||
|
{'t': pobj.timestamp} if pobj.timestamp else {}])
|
||||||
return (EVENTREPLY, f'{modulename}:{pobj.export}',
|
return (EVENTREPLY, f'{modulename}:{pobj.export}',
|
||||||
[pobj.export_value(), {'t': pobj.timestamp}])
|
[pobj.export_value(),
|
||||||
|
{'t': pobj.timestamp} if pobj.timestamp else {}])
|
||||||
|
|
||||||
|
|
||||||
class Dispatcher:
|
class Dispatcher:
|
||||||
|
237
frappy/protocol/interface/handler.py
Normal file
237
frappy/protocol/interface/handler.py
Normal file
@ -0,0 +1,237 @@
|
|||||||
|
# *****************************************************************************
|
||||||
|
# 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:
|
||||||
|
# Enrico Faulhaber <enrico.faulhaber@frm2.tum.de>
|
||||||
|
# Markus Zolliker <markus.zolliker@psi.ch>
|
||||||
|
#
|
||||||
|
# *****************************************************************************
|
||||||
|
"""The common parts of the SECNodes outside interfaces"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import threading
|
||||||
|
|
||||||
|
from frappy.errors import SECoPError
|
||||||
|
from frappy.lib import formatException, formatExtendedStack, \
|
||||||
|
formatExtendedTraceback
|
||||||
|
from frappy.protocol.messages import ERRORPREFIX, HELPREPLY, HELPREQUEST, \
|
||||||
|
HelpMessage
|
||||||
|
|
||||||
|
|
||||||
|
class DecodeError(Exception):
|
||||||
|
def __init__(self, message, raw_msg):
|
||||||
|
super().__init__(message)
|
||||||
|
self._raw_msg = raw_msg
|
||||||
|
|
||||||
|
@property
|
||||||
|
def raw_msg(self):
|
||||||
|
return self._raw_msg
|
||||||
|
|
||||||
|
|
||||||
|
class ConnectionClose(Exception):
|
||||||
|
"""Indicates that receive quit due to an error."""
|
||||||
|
|
||||||
|
|
||||||
|
class RequestHandler:
|
||||||
|
"""Base class for the request handlers.
|
||||||
|
|
||||||
|
This is an extended copy of the BaseRequestHandler from socketserver.
|
||||||
|
|
||||||
|
To make a new interface, implement these methods:
|
||||||
|
ingest, next_message, decode_message, receive, send_reply and format
|
||||||
|
and extend (override) setup() and finish() if needed.
|
||||||
|
|
||||||
|
For an example, have a look at TCPRequestHandler.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Methods from BaseRequestHandler
|
||||||
|
def __init__(self, request, client_address, server):
|
||||||
|
self.request = request
|
||||||
|
self.client_address = client_address
|
||||||
|
self.server = server
|
||||||
|
self.log = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.setup()
|
||||||
|
self.handle()
|
||||||
|
except Exception:
|
||||||
|
if self.log:
|
||||||
|
self.log.error(formatException())
|
||||||
|
else:
|
||||||
|
server.log.error(formatException())
|
||||||
|
finally:
|
||||||
|
self.finish()
|
||||||
|
|
||||||
|
def setup(self):
|
||||||
|
self.log = self.server.log
|
||||||
|
self.log.info("new connection %s", self.format())
|
||||||
|
# notify dispatcher of us
|
||||||
|
self.server.dispatcher.add_connection(self)
|
||||||
|
self.send_lock = threading.Lock()
|
||||||
|
self.running = True
|
||||||
|
# overwrite this with an appropriate buffer if needed
|
||||||
|
self.data = None
|
||||||
|
|
||||||
|
def handle(self):
|
||||||
|
"""handle a new connection"""
|
||||||
|
# copy state info
|
||||||
|
serverobj = self.server
|
||||||
|
# copy relevant settings from Interface
|
||||||
|
detailed_errors = serverobj.detailed_errors
|
||||||
|
|
||||||
|
# start serving
|
||||||
|
while self.running:
|
||||||
|
try:
|
||||||
|
newdata = self.receive()
|
||||||
|
if newdata is None:
|
||||||
|
# no new data during read, continue
|
||||||
|
continue
|
||||||
|
self.ingest(newdata)
|
||||||
|
except ConnectionClose:
|
||||||
|
# either normal close or error in receive
|
||||||
|
return
|
||||||
|
# put data into (de-) framer,
|
||||||
|
# de-frame data with next_message() and decode it
|
||||||
|
# call dispatcher.handle_request(self, message)
|
||||||
|
# dispatcher will queue the reply before returning
|
||||||
|
while self.running:
|
||||||
|
try:
|
||||||
|
msg = self.next_message()
|
||||||
|
if msg is None:
|
||||||
|
break # no more messages to process
|
||||||
|
except DecodeError as err:
|
||||||
|
# we have to decode 'origin' here
|
||||||
|
# use latin-1, as utf-8 or ascii may lead to encoding errors
|
||||||
|
msg = err.raw_msg.decode('latin-1').split(' ', 3) + [
|
||||||
|
None
|
||||||
|
] # make sure len(msg) > 1
|
||||||
|
result = (
|
||||||
|
ERRORPREFIX + msg[0],
|
||||||
|
msg[1],
|
||||||
|
[
|
||||||
|
'InternalError', str(err),
|
||||||
|
{
|
||||||
|
'exception': formatException(),
|
||||||
|
'traceback': formatExtendedStack()
|
||||||
|
}
|
||||||
|
]
|
||||||
|
)
|
||||||
|
print('--------------------')
|
||||||
|
print(formatException())
|
||||||
|
print('--------------------')
|
||||||
|
print(formatExtendedTraceback(sys.exc_info()))
|
||||||
|
print('====================')
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
if msg[0] == HELPREQUEST:
|
||||||
|
self.handle_help()
|
||||||
|
result = (HELPREPLY, None, None)
|
||||||
|
else:
|
||||||
|
result = serverobj.dispatcher.handle_request(self,
|
||||||
|
msg)
|
||||||
|
except SECoPError as err:
|
||||||
|
result = (
|
||||||
|
ERRORPREFIX + msg[0],
|
||||||
|
msg[1],
|
||||||
|
[
|
||||||
|
err.name,
|
||||||
|
str(err),
|
||||||
|
{
|
||||||
|
'exception': formatException(),
|
||||||
|
'traceback': formatExtendedStack()
|
||||||
|
}
|
||||||
|
]
|
||||||
|
)
|
||||||
|
except Exception as err:
|
||||||
|
# create Error Obj instead
|
||||||
|
result = (
|
||||||
|
ERRORPREFIX + msg[0],
|
||||||
|
msg[1],
|
||||||
|
[
|
||||||
|
'InternalError',
|
||||||
|
repr(err),
|
||||||
|
{
|
||||||
|
'exception': formatException(),
|
||||||
|
'traceback': formatExtendedStack()
|
||||||
|
}
|
||||||
|
]
|
||||||
|
)
|
||||||
|
print('--------------------')
|
||||||
|
print(formatException())
|
||||||
|
print('--------------------')
|
||||||
|
print(formatExtendedTraceback(sys.exc_info()))
|
||||||
|
print('====================')
|
||||||
|
|
||||||
|
if not result:
|
||||||
|
self.log.error('empty result upon msg %s', repr(msg))
|
||||||
|
if result[0].startswith(ERRORPREFIX) and not detailed_errors:
|
||||||
|
# strip extra information
|
||||||
|
result[2][2].clear()
|
||||||
|
self.send_reply(result)
|
||||||
|
|
||||||
|
def handle_help(self):
|
||||||
|
for idx, line in enumerate(HelpMessage.splitlines()):
|
||||||
|
# not sending HELPREPLY here, as there should be only one reply for
|
||||||
|
# every request
|
||||||
|
self.send_reply(('_', f'{idx + 1}', line))
|
||||||
|
|
||||||
|
def finish(self):
|
||||||
|
"""called when handle() terminates, i.e. the socket closed"""
|
||||||
|
self.log.info('closing connection %s', self.format())
|
||||||
|
# notify dispatcher
|
||||||
|
self.server.dispatcher.remove_connection(self)
|
||||||
|
|
||||||
|
# Methods for implementing in derived classes:
|
||||||
|
def ingest(self, newdata):
|
||||||
|
"""Put the new data into the buffer."""
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def next_message(self):
|
||||||
|
"""Get the next decoded message from the buffer.
|
||||||
|
|
||||||
|
Has to return a triple of (MESSAGE, specifier, data) or None, in case
|
||||||
|
there are no further messages in the receive queue.
|
||||||
|
|
||||||
|
If there is an Error during decoding, this method has to raise a
|
||||||
|
DecodeError.
|
||||||
|
"""
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def receive(self):
|
||||||
|
"""Receive data from the link.
|
||||||
|
|
||||||
|
Should return the received data or None if there was nothing new. Has
|
||||||
|
to raise a ConnectionClose on shutdown of the connection or on errors
|
||||||
|
that are not recoverable.
|
||||||
|
"""
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def send_reply(self, data):
|
||||||
|
"""send reply
|
||||||
|
|
||||||
|
stops recv loop on error
|
||||||
|
"""
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def format(self):
|
||||||
|
"""
|
||||||
|
Format available connection data into something recognizable for
|
||||||
|
logging.
|
||||||
|
|
||||||
|
For example, the remote IP address or a connection identifier.
|
||||||
|
"""
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
# TODO: server baseclass?
|
@ -18,122 +18,77 @@
|
|||||||
# Markus Zolliker <markus.zolliker@psi.ch>
|
# Markus Zolliker <markus.zolliker@psi.ch>
|
||||||
#
|
#
|
||||||
# *****************************************************************************
|
# *****************************************************************************
|
||||||
"""provides tcp interface to the SECoP Server"""
|
"""TCP interface to the SECoP Server"""
|
||||||
|
|
||||||
import errno
|
import errno
|
||||||
import os
|
import os
|
||||||
import socket
|
import socket
|
||||||
import socketserver
|
import socketserver
|
||||||
import sys
|
|
||||||
import threading
|
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from frappy.datatypes import BoolType, StringType
|
from frappy.datatypes import BoolType, StringType
|
||||||
from frappy.errors import SECoPError
|
from frappy.lib import SECoP_DEFAULT_PORT
|
||||||
from frappy.lib import formatException, formatExtendedStack, \
|
|
||||||
formatExtendedTraceback, SECoP_DEFAULT_PORT
|
|
||||||
from frappy.properties import Property
|
from frappy.properties import Property
|
||||||
from frappy.protocol.interface import decode_msg, encode_msg_frame, get_msg
|
from frappy.protocol.interface import decode_msg, encode_msg_frame, get_msg
|
||||||
from frappy.protocol.messages import ERRORPREFIX, HELPREPLY, HELPREQUEST, \
|
from frappy.protocol.interface.handler import ConnectionClose, \
|
||||||
HelpMessage
|
RequestHandler, DecodeError
|
||||||
|
from frappy.protocol.messages import HELPREQUEST
|
||||||
|
|
||||||
|
|
||||||
MESSAGE_READ_SIZE = 1024
|
MESSAGE_READ_SIZE = 1024
|
||||||
HELP = HELPREQUEST.encode()
|
|
||||||
|
|
||||||
|
|
||||||
class TCPRequestHandler(socketserver.BaseRequestHandler):
|
def format_address(addr):
|
||||||
|
if len(addr) == 2:
|
||||||
|
return '%s:%d' % addr
|
||||||
|
address, port = addr[0:2]
|
||||||
|
if address.startswith('::ffff'):
|
||||||
|
return '%s:%d' % (address[7:], port)
|
||||||
|
return '[%s]:%d' % (address, port)
|
||||||
|
|
||||||
|
|
||||||
|
class TCPRequestHandler(RequestHandler):
|
||||||
def setup(self):
|
def setup(self):
|
||||||
self.log = self.server.log
|
super().setup()
|
||||||
self.running = True
|
self.request.settimeout(1)
|
||||||
self.send_lock = threading.Lock()
|
self.data = b''
|
||||||
|
|
||||||
def handle(self):
|
def finish(self):
|
||||||
"""handle a new tcp-connection"""
|
"""called when handle() terminates, i.e. the socket closed"""
|
||||||
# copy state info
|
super().finish()
|
||||||
mysocket = self.request
|
# close socket
|
||||||
clientaddr = self.client_address
|
|
||||||
serverobj = self.server
|
|
||||||
|
|
||||||
self.log.info("handling new connection from %s", format_address(clientaddr))
|
|
||||||
data = b''
|
|
||||||
|
|
||||||
# notify dispatcher of us
|
|
||||||
serverobj.dispatcher.add_connection(self)
|
|
||||||
|
|
||||||
# copy relevant settings from Interface
|
|
||||||
detailed_errors = serverobj.detailed_errors
|
|
||||||
|
|
||||||
mysocket.settimeout(1)
|
|
||||||
# start serving
|
|
||||||
while self.running:
|
|
||||||
try:
|
try:
|
||||||
newdata = mysocket.recv(MESSAGE_READ_SIZE)
|
self.request.shutdown(socket.SHUT_RDWR)
|
||||||
if not newdata:
|
except Exception:
|
||||||
# no timeout error, but no new data -> connection closed
|
pass
|
||||||
return
|
finally:
|
||||||
data = data + newdata
|
self.request.close()
|
||||||
|
|
||||||
|
def ingest(self, newdata):
|
||||||
|
self.data += newdata
|
||||||
|
|
||||||
|
def next_message(self):
|
||||||
|
try:
|
||||||
|
message, self.data = get_msg(self.data)
|
||||||
|
if message is None:
|
||||||
|
return None
|
||||||
|
if message.strip() == b'':
|
||||||
|
return (HELPREQUEST, None, None)
|
||||||
|
return decode_msg(message)
|
||||||
|
except Exception as e:
|
||||||
|
raise DecodeError('exception in receive', raw_msg=message) from e
|
||||||
|
|
||||||
|
def receive(self):
|
||||||
|
try:
|
||||||
|
data = self.request.recv(MESSAGE_READ_SIZE)
|
||||||
|
if not data:
|
||||||
|
raise ConnectionClose('socket was closed')
|
||||||
|
return data
|
||||||
except socket.timeout:
|
except socket.timeout:
|
||||||
continue
|
return None
|
||||||
except socket.error as e:
|
except socket.error as e:
|
||||||
self.log.exception(e)
|
self.log.exception(e)
|
||||||
return
|
raise ConnectionClose() from e
|
||||||
if not data:
|
|
||||||
continue
|
|
||||||
# put data into (de-) framer,
|
|
||||||
# put frames into (de-) coder and if a message appear,
|
|
||||||
# call dispatcher.handle_request(self, message)
|
|
||||||
# dispatcher will queue the reply before returning
|
|
||||||
while self.running:
|
|
||||||
origin, data = get_msg(data)
|
|
||||||
if origin is None:
|
|
||||||
break # no more messages to process
|
|
||||||
origin = origin.strip()
|
|
||||||
if origin in (HELP, b''): # empty string -> send help message
|
|
||||||
for idx, line in enumerate(HelpMessage.splitlines()):
|
|
||||||
# not sending HELPREPLY here, as there should be only one reply for every request
|
|
||||||
self.send_reply(('_', f'{idx + 1}', line))
|
|
||||||
# ident matches request
|
|
||||||
self.send_reply((HELPREPLY, None, None))
|
|
||||||
continue
|
|
||||||
try:
|
|
||||||
msg = decode_msg(origin)
|
|
||||||
except Exception as err:
|
|
||||||
# we have to decode 'origin' here
|
|
||||||
# use latin-1, as utf-8 or ascii may lead to encoding errors
|
|
||||||
msg = origin.decode('latin-1').split(' ', 3) + [None] # make sure len(msg) > 1
|
|
||||||
result = (ERRORPREFIX + msg[0], msg[1], ['InternalError', str(err),
|
|
||||||
{'exception': formatException(),
|
|
||||||
'traceback': formatExtendedStack()}])
|
|
||||||
print('--------------------')
|
|
||||||
print(formatException())
|
|
||||||
print('--------------------')
|
|
||||||
print(formatExtendedTraceback(sys.exc_info()))
|
|
||||||
print('====================')
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
result = serverobj.dispatcher.handle_request(self, msg)
|
|
||||||
except SECoPError as err:
|
|
||||||
result = (ERRORPREFIX + msg[0], msg[1], [err.name, str(err),
|
|
||||||
{'exception': formatException(),
|
|
||||||
'traceback': formatExtendedStack()}])
|
|
||||||
except Exception as err:
|
|
||||||
# create Error Obj instead
|
|
||||||
result = (ERRORPREFIX + msg[0], msg[1], ['InternalError', repr(err),
|
|
||||||
{'exception': formatException(),
|
|
||||||
'traceback': formatExtendedStack()}])
|
|
||||||
print('--------------------')
|
|
||||||
print(formatException())
|
|
||||||
print('--------------------')
|
|
||||||
print(formatExtendedTraceback(sys.exc_info()))
|
|
||||||
print('====================')
|
|
||||||
|
|
||||||
if not result:
|
|
||||||
self.log.error('empty result upon msg %s', repr(msg))
|
|
||||||
if result[0].startswith(ERRORPREFIX) and not detailed_errors:
|
|
||||||
# strip extra information
|
|
||||||
result[2][2].clear()
|
|
||||||
self.send_reply(result)
|
|
||||||
|
|
||||||
def send_reply(self, data):
|
def send_reply(self, data):
|
||||||
"""send reply
|
"""send reply
|
||||||
@ -156,18 +111,9 @@ class TCPRequestHandler(socketserver.BaseRequestHandler):
|
|||||||
self.log.error('ERROR in send_reply %r', e)
|
self.log.error('ERROR in send_reply %r', e)
|
||||||
self.running = False
|
self.running = False
|
||||||
|
|
||||||
def finish(self):
|
def format(self):
|
||||||
"""called when handle() terminates, i.e. the socket closed"""
|
return f'from {format_address(self.client_address)}'
|
||||||
self.log.info('closing connection from %s', format_address(self.client_address))
|
|
||||||
# notify dispatcher
|
|
||||||
self.server.dispatcher.remove_connection(self)
|
|
||||||
# close socket
|
|
||||||
try:
|
|
||||||
self.request.shutdown(socket.SHUT_RDWR)
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
finally:
|
|
||||||
self.request.close()
|
|
||||||
|
|
||||||
class DualStackTCPServer(socketserver.ThreadingTCPServer):
|
class DualStackTCPServer(socketserver.ThreadingTCPServer):
|
||||||
"""Subclassed to provide IPv6 capabilities as socketserver only uses IPv4"""
|
"""Subclassed to provide IPv6 capabilities as socketserver only uses IPv4"""
|
||||||
@ -230,12 +176,3 @@ class TCPServer(DualStackTCPServer):
|
|||||||
if ntry:
|
if ntry:
|
||||||
self.log.warning('tried again %d times after "Address already in use"', ntry)
|
self.log.warning('tried again %d times after "Address already in use"', ntry)
|
||||||
self.log.info("TCPServer initiated")
|
self.log.info("TCPServer initiated")
|
||||||
|
|
||||||
|
|
||||||
def format_address(addr):
|
|
||||||
if len(addr) == 2:
|
|
||||||
return '%s:%d' % addr
|
|
||||||
address, port = addr[0:2]
|
|
||||||
if address.startswith('::ffff'):
|
|
||||||
return '%s:%d' % (address[7:], port)
|
|
||||||
return '[%s]:%d' % (address, port)
|
|
||||||
|
160
frappy/protocol/interface/ws.py
Normal file
160
frappy/protocol/interface/ws.py
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
# *****************************************************************************
|
||||||
|
# This program is free software; you can redistribute it and/or modify it under
|
||||||
|
# the terms of the GNU General Public License as published by the Free Software
|
||||||
|
# Foundation; either version 2 of the License, or (at your option) any later
|
||||||
|
# version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||||
|
# details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License along with
|
||||||
|
# this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
#
|
||||||
|
# Module authors:
|
||||||
|
# Alexander Zaft <a.zaft@fz-juelich.de>
|
||||||
|
#
|
||||||
|
# *****************************************************************************
|
||||||
|
|
||||||
|
import json
|
||||||
|
from functools import partial
|
||||||
|
|
||||||
|
from websockets.exceptions import ConnectionClosedOK, ConnectionClosedError
|
||||||
|
from websockets.sync.server import CloseCode, serve
|
||||||
|
|
||||||
|
from frappy.protocol.interface.handler import ConnectionClose, \
|
||||||
|
RequestHandler, DecodeError
|
||||||
|
from frappy.protocol.messages import HELPREQUEST
|
||||||
|
|
||||||
|
|
||||||
|
def encode_msg_frame_str(action, specifier=None, data=None):
|
||||||
|
""" encode a msg_triple into an msg_frame, ready to be sent
|
||||||
|
|
||||||
|
action (and optional specifier) are str strings,
|
||||||
|
data may be an json-yfied python object"""
|
||||||
|
msg = (action, specifier or '', '' if data is None else json.dumps(data))
|
||||||
|
return ' '.join(msg).strip()
|
||||||
|
|
||||||
|
|
||||||
|
class WSRequestHandler(RequestHandler):
|
||||||
|
"""Handles a Websocket connection."""
|
||||||
|
|
||||||
|
def __init__(self, conn, server):
|
||||||
|
self.conn = conn
|
||||||
|
client_address = conn.remote_address
|
||||||
|
request = conn.socket
|
||||||
|
super().__init__(request, client_address, server)
|
||||||
|
|
||||||
|
def setup(self):
|
||||||
|
super().setup()
|
||||||
|
self.server.connections.add(self)
|
||||||
|
|
||||||
|
def finish(self):
|
||||||
|
"""called when handle() terminates, i.e. the socket closed"""
|
||||||
|
super().finish()
|
||||||
|
self.server.connections.discard(self)
|
||||||
|
# this will be called for a second time if the server is shutting down,
|
||||||
|
# but in that case it will be a no-op
|
||||||
|
self.conn.close()
|
||||||
|
|
||||||
|
def ingest(self, newdata):
|
||||||
|
# recv on the websocket connection returns one message, we don't save
|
||||||
|
# anything in data
|
||||||
|
self.data = newdata
|
||||||
|
|
||||||
|
def next_message(self):
|
||||||
|
"""split the string into a message triple."""
|
||||||
|
if self.data is None:
|
||||||
|
return None
|
||||||
|
try:
|
||||||
|
message = self.data.strip()
|
||||||
|
if message == '':
|
||||||
|
return HELPREQUEST, None, None
|
||||||
|
res = message.split(' ', 2) + ['', '']
|
||||||
|
action, specifier, data = res[0:3]
|
||||||
|
self.data = None
|
||||||
|
return (
|
||||||
|
action,
|
||||||
|
specifier or None,
|
||||||
|
None if data == '' else json.loads(data)
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
raise DecodeError('exception when reading in message',
|
||||||
|
raw_msg=bytes(message, 'utf-8')) from e
|
||||||
|
|
||||||
|
def receive(self):
|
||||||
|
"""receives one message from the websocket."""
|
||||||
|
try:
|
||||||
|
return self.conn.recv()
|
||||||
|
except TimeoutError:
|
||||||
|
return None
|
||||||
|
except ConnectionClosedOK:
|
||||||
|
raise ConnectionClose from None
|
||||||
|
except ConnectionClosedError as e:
|
||||||
|
self.log.error('No close frame received from %s', self.format())
|
||||||
|
raise ConnectionClose from e
|
||||||
|
except OSError as e:
|
||||||
|
self.log.exception(e)
|
||||||
|
raise ConnectionClose from e
|
||||||
|
|
||||||
|
def send_reply(self, data):
|
||||||
|
"""send reply
|
||||||
|
|
||||||
|
stops recv loop on error (including timeout when output buffer full for
|
||||||
|
more than 1 sec)
|
||||||
|
"""
|
||||||
|
if not data:
|
||||||
|
self.log.error('should not reply empty data!')
|
||||||
|
return
|
||||||
|
outdata = encode_msg_frame_str(*data)
|
||||||
|
with self.send_lock:
|
||||||
|
if self.running:
|
||||||
|
try:
|
||||||
|
self.conn.send(outdata)
|
||||||
|
except (BrokenPipeError, IOError) as e:
|
||||||
|
self.log.debug('send_reply got an %r, connection closed?',
|
||||||
|
e)
|
||||||
|
self.running = False
|
||||||
|
except Exception as e:
|
||||||
|
self.log.error('ERROR in send_reply %r', e)
|
||||||
|
self.running = False
|
||||||
|
|
||||||
|
def format(self):
|
||||||
|
return f'{self.conn.id} from {self.client_address}'
|
||||||
|
|
||||||
|
class WSServer:
|
||||||
|
"""Server for providing a websocket interface.
|
||||||
|
|
||||||
|
Implementation note:
|
||||||
|
The websockets library doesn't provide an option to subclass its server, so
|
||||||
|
we take the returned value as an attribute and provide the neccessary
|
||||||
|
function calls.
|
||||||
|
"""
|
||||||
|
def __init__(self, name, logger, options, srv):
|
||||||
|
self.connections = set() # keep track for shutting down
|
||||||
|
self.dispatcher = srv.dispatcher
|
||||||
|
self.name = name
|
||||||
|
self.log = logger
|
||||||
|
self.port = int(options.pop('uri').split('://', 1)[-1])
|
||||||
|
self.detailed_errors = options.pop('detailed_errors', False)
|
||||||
|
|
||||||
|
handle = partial(WSRequestHandler, server=self)
|
||||||
|
# websockets only gives the serve method without an option to subclass
|
||||||
|
self.ws_server = serve(handle, '', self.port, logger=logger)
|
||||||
|
self.log.info("Websocket server %s binding to port %d", name, self.port)
|
||||||
|
|
||||||
|
def serve_forever(self):
|
||||||
|
self.ws_server.serve_forever()
|
||||||
|
|
||||||
|
def shutdown(self):
|
||||||
|
for c in list(self.connections):
|
||||||
|
c.conn.close(code=CloseCode.GOING_AWAY, reason='shutting down')
|
||||||
|
self.ws_server.shutdown()
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __exit__(self, *args):
|
||||||
|
return self.shutdown()
|
@ -71,7 +71,7 @@ class ProxyModule(HasIO, Module):
|
|||||||
pname, pobj = params.popitem()
|
pname, pobj = params.popitem()
|
||||||
props = remoteparams.get(pname, None)
|
props = remoteparams.get(pname, None)
|
||||||
if props is None:
|
if props is None:
|
||||||
if pobj.export:
|
if pobj.export and pname != 'status':
|
||||||
self.log.warning('remote parameter %s:%s does not exist', self.module, pname)
|
self.log.warning('remote parameter %s:%s does not exist', self.module, pname)
|
||||||
continue
|
continue
|
||||||
dt = props['datatype']
|
dt = props['datatype']
|
||||||
@ -108,17 +108,19 @@ class ProxyModule(HasIO, Module):
|
|||||||
# for now, the error message must be enough
|
# for now, the error message must be enough
|
||||||
|
|
||||||
def nodeStateChange(self, online, state):
|
def nodeStateChange(self, online, state):
|
||||||
|
disconnected = Readable.Status.ERROR, 'disconnected'
|
||||||
if online:
|
if online:
|
||||||
if not self._consistency_check_done:
|
if not self._consistency_check_done:
|
||||||
self._check_descriptive_data()
|
self._check_descriptive_data()
|
||||||
self._consistency_check_done = True
|
self._consistency_check_done = True
|
||||||
|
if self.status == disconnected:
|
||||||
|
self.status = Readable.Status.IDLE, 'connected'
|
||||||
else:
|
else:
|
||||||
newstatus = Readable.Status.ERROR, 'disconnected'
|
|
||||||
readerror = CommunicationFailedError('disconnected')
|
readerror = CommunicationFailedError('disconnected')
|
||||||
if self.status != newstatus:
|
if self.status != disconnected:
|
||||||
for pname in set(self.parameters) - set(('module', 'status')):
|
for pname in set(self.parameters) - set(('module', 'status')):
|
||||||
self.announceUpdate(pname, None, readerror)
|
self.announceUpdate(pname, None, readerror)
|
||||||
self.announceUpdate('status', newstatus)
|
self.status = disconnected
|
||||||
|
|
||||||
def checkProperties(self):
|
def checkProperties(self):
|
||||||
pass # skip
|
pass # skip
|
||||||
@ -193,7 +195,7 @@ def proxy_class(remote_class, name=None):
|
|||||||
attrs[aname] = pobj
|
attrs[aname] = pobj
|
||||||
|
|
||||||
def rfunc(self, pname=aname):
|
def rfunc(self, pname=aname):
|
||||||
value, _, readerror = self._secnode.getParameter(self.name, pname, True)
|
value, _, readerror = self._secnode.getParameter(self.module, pname, True)
|
||||||
if readerror:
|
if readerror:
|
||||||
raise readerror
|
raise readerror
|
||||||
return value
|
return value
|
||||||
@ -203,7 +205,7 @@ def proxy_class(remote_class, name=None):
|
|||||||
if not pobj.readonly:
|
if not pobj.readonly:
|
||||||
|
|
||||||
def wfunc(self, value, pname=aname):
|
def wfunc(self, value, pname=aname):
|
||||||
value, _, readerror = self._secnode.setParameter(self.name, pname, value)
|
value, _, readerror = self._secnode.setParameter(self.module, pname, value)
|
||||||
if readerror:
|
if readerror:
|
||||||
raise readerror
|
raise readerror
|
||||||
return value
|
return value
|
||||||
@ -214,7 +216,7 @@ def proxy_class(remote_class, name=None):
|
|||||||
cobj = aobj.copy()
|
cobj = aobj.copy()
|
||||||
|
|
||||||
def cfunc(self, arg=None, cname=aname):
|
def cfunc(self, arg=None, cname=aname):
|
||||||
return self._secnode.execCommand(self.name, cname, arg)[0]
|
return self._secnode.execCommand(self.module, cname, arg)[0]
|
||||||
|
|
||||||
attrs[aname] = cobj(cfunc)
|
attrs[aname] = cobj(cfunc)
|
||||||
|
|
||||||
|
@ -53,6 +53,7 @@ except ImportError:
|
|||||||
class Server:
|
class Server:
|
||||||
INTERFACES = {
|
INTERFACES = {
|
||||||
'tcp': 'protocol.interface.tcp.TCPServer',
|
'tcp': 'protocol.interface.tcp.TCPServer',
|
||||||
|
'ws': 'protocol.interface.ws.WSServer',
|
||||||
}
|
}
|
||||||
_restart = True
|
_restart = True
|
||||||
|
|
||||||
|
@ -239,6 +239,7 @@ class HasStates:
|
|||||||
|
|
||||||
@Command
|
@Command
|
||||||
def stop(self):
|
def stop(self):
|
||||||
|
"""stop state machine"""
|
||||||
self.stop_machine()
|
self.stop_machine()
|
||||||
|
|
||||||
def final_status(self, code=IDLE, text=''):
|
def final_status(self, code=IDLE, text=''):
|
||||||
|
@ -1,164 +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>
|
|
||||||
#
|
|
||||||
# *****************************************************************************
|
|
||||||
"""convenience class to create a struct Parameter together with indivdual params
|
|
||||||
|
|
||||||
Usage:
|
|
||||||
|
|
||||||
class Controller(Drivable):
|
|
||||||
|
|
||||||
...
|
|
||||||
|
|
||||||
ctrlpars = StructParam('ctrlpars struct', [
|
|
||||||
('pid_p', 'p', Parameter('control parameter p', FloatRange())),
|
|
||||||
('pid_i', 'i', Parameter('control parameter i', FloatRange())),
|
|
||||||
('pid_d', 'd', Parameter('control parameter d', FloatRange())),
|
|
||||||
], readonly=False)
|
|
||||||
|
|
||||||
...
|
|
||||||
|
|
||||||
then implement either read_ctrlpars and write_ctrlpars or
|
|
||||||
read_pid_p, read_pid_i, read_pid_d, write_pid_p, write_pid_i and write_pid_d
|
|
||||||
|
|
||||||
the methods not implemented will be created automatically
|
|
||||||
"""
|
|
||||||
|
|
||||||
from frappy.core import Parameter, Property
|
|
||||||
from frappy.datatypes import BoolType, DataType, StructOf, ValueType
|
|
||||||
from frappy.errors import ProgrammingError
|
|
||||||
|
|
||||||
|
|
||||||
class StructParam(Parameter):
|
|
||||||
"""create a struct parameter together with individual parameters
|
|
||||||
|
|
||||||
in addition to normal Parameter arguments:
|
|
||||||
|
|
||||||
:param paramdict: dict <member name> of Parameter(...)
|
|
||||||
:param prefix_or_map: either a prefix for the parameter name to add to the member name
|
|
||||||
or a dict <member name> or <paramerter name>
|
|
||||||
"""
|
|
||||||
# use properties, as simple attributes are not considered on copy()
|
|
||||||
paramdict = Property('dict <parametername> of Parameter(...)', ValueType())
|
|
||||||
hasStructRW = Property('has a read_<struct param> or write_<struct param> method',
|
|
||||||
BoolType(), default=False)
|
|
||||||
|
|
||||||
insideRW = 0 # counter for avoiding multiple superfluous updates
|
|
||||||
|
|
||||||
def __init__(self, description=None, paramdict=None, prefix_or_map='', *, datatype=None, readonly=False, **kwds):
|
|
||||||
if isinstance(paramdict, DataType):
|
|
||||||
raise ProgrammingError('second argument must be a dict of Param')
|
|
||||||
if datatype is None and paramdict is not None: # omit the following on Parameter.copy()
|
|
||||||
if isinstance(prefix_or_map, str):
|
|
||||||
prefix_or_map = {m: prefix_or_map + m for m in paramdict}
|
|
||||||
for membername, param in paramdict.items():
|
|
||||||
param.name = prefix_or_map[membername]
|
|
||||||
datatype = StructOf(**{m: p.datatype for m, p in paramdict.items()})
|
|
||||||
kwds['influences'] = [p.name for p in paramdict.values()]
|
|
||||||
self.updateEnable = {}
|
|
||||||
super().__init__(description, datatype, paramdict=paramdict, readonly=readonly, **kwds)
|
|
||||||
|
|
||||||
def __set_name__(self, owner, name):
|
|
||||||
# names of access methods of structed param (e.g. ctrlpars)
|
|
||||||
struct_read_name = f'read_{name}' # e.g. 'read_ctrlpars'
|
|
||||||
struct_write_name = f'write_{name}' # e.h. 'write_ctrlpars'
|
|
||||||
self.hasStructRW = hasattr(owner, struct_read_name) or hasattr(owner, struct_write_name)
|
|
||||||
|
|
||||||
for membername, param in self.paramdict.items():
|
|
||||||
pname = param.name
|
|
||||||
changes = {
|
|
||||||
'readonly': self.readonly,
|
|
||||||
'influences': set(param.influences) | {name},
|
|
||||||
}
|
|
||||||
param.ownProperties.update(changes)
|
|
||||||
param.init(changes)
|
|
||||||
setattr(owner, pname, param)
|
|
||||||
param.__set_name__(owner, param.name)
|
|
||||||
|
|
||||||
if self.hasStructRW:
|
|
||||||
rname = f'read_{pname}'
|
|
||||||
|
|
||||||
if not hasattr(owner, rname):
|
|
||||||
def rfunc(self, membername=membername, struct_read_name=struct_read_name):
|
|
||||||
return getattr(self, struct_read_name)()[membername]
|
|
||||||
|
|
||||||
rfunc.poll = False # read_<struct param> is polled only
|
|
||||||
setattr(owner, rname, rfunc)
|
|
||||||
|
|
||||||
if not self.readonly:
|
|
||||||
wname = f'write_{pname}'
|
|
||||||
if not hasattr(owner, wname):
|
|
||||||
def wfunc(self, value, membername=membername,
|
|
||||||
name=name, rname=rname, struct_write_name=struct_write_name):
|
|
||||||
valuedict = dict(getattr(self, name))
|
|
||||||
valuedict[membername] = value
|
|
||||||
getattr(self, struct_write_name)(valuedict)
|
|
||||||
return getattr(self, rname)()
|
|
||||||
|
|
||||||
setattr(owner, wname, wfunc)
|
|
||||||
|
|
||||||
if not self.hasStructRW:
|
|
||||||
if not hasattr(owner, struct_read_name):
|
|
||||||
def struct_read_func(self, name=name, flist=tuple(
|
|
||||||
(m, f'read_{p.name}') for m, p in self.paramdict.items())):
|
|
||||||
pobj = self.parameters[name]
|
|
||||||
# disable updates generated from the callbacks of individual params
|
|
||||||
pobj.insideRW += 1 # guarded by self.accessLock
|
|
||||||
try:
|
|
||||||
return {m: getattr(self, f)() for m, f in flist}
|
|
||||||
finally:
|
|
||||||
pobj.insideRW -= 1
|
|
||||||
|
|
||||||
setattr(owner, struct_read_name, struct_read_func)
|
|
||||||
|
|
||||||
if not (self.readonly or hasattr(owner, struct_write_name)):
|
|
||||||
|
|
||||||
def struct_write_func(self, value, name=name, funclist=tuple(
|
|
||||||
(m, f'write_{p.name}') for m, p in self.paramdict.items())):
|
|
||||||
pobj = self.parameters[name]
|
|
||||||
pobj.insideRW += 1 # guarded by self.accessLock
|
|
||||||
try:
|
|
||||||
return {m: getattr(self, f)(value[m]) for m, f in funclist}
|
|
||||||
finally:
|
|
||||||
pobj.insideRW -= 1
|
|
||||||
|
|
||||||
setattr(owner, struct_write_name, struct_write_func)
|
|
||||||
|
|
||||||
super().__set_name__(owner, name)
|
|
||||||
|
|
||||||
def finish(self, modobj=None):
|
|
||||||
"""register callbacks for consistency"""
|
|
||||||
super().finish(modobj)
|
|
||||||
if modobj:
|
|
||||||
|
|
||||||
if self.hasStructRW:
|
|
||||||
def cb(value, modobj=modobj, structparam=self):
|
|
||||||
for membername, param in structparam.paramdict.items():
|
|
||||||
setattr(modobj, param.name, value[membername])
|
|
||||||
|
|
||||||
modobj.valueCallbacks[self.name].append(cb)
|
|
||||||
else:
|
|
||||||
for membername, param in self.paramdict.items():
|
|
||||||
def cb(value, modobj=modobj, structparam=self, membername=membername):
|
|
||||||
if not structparam.insideRW:
|
|
||||||
prev = dict(getattr(modobj, structparam.name))
|
|
||||||
prev[membername] = value
|
|
||||||
setattr(modobj, structparam.name, prev)
|
|
||||||
|
|
||||||
modobj.valueCallbacks[param.name].append(cb)
|
|
@ -120,6 +120,7 @@ class MagneticField(Drivable):
|
|||||||
)
|
)
|
||||||
heatswitch = Attached(Switch, description='name of heat switch device')
|
heatswitch = Attached(Switch, description='name of heat switch device')
|
||||||
|
|
||||||
|
# pylint: disable=invalid-name
|
||||||
Status = Enum(Drivable.Status, PERSIST=PERSIST, PREPARE=301, RAMPING=302, FINISH=303)
|
Status = Enum(Drivable.Status, PERSIST=PERSIST, PREPARE=301, RAMPING=302, FINISH=303)
|
||||||
|
|
||||||
status = Parameter(datatype=TupleOf(EnumType(Status), StringType()))
|
status = Parameter(datatype=TupleOf(EnumType(Status), StringType()))
|
||||||
@ -193,6 +194,7 @@ class MagneticField(Drivable):
|
|||||||
self.log.error(self, 'main thread exited unexpectedly!')
|
self.log.error(self, 'main thread exited unexpectedly!')
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
|
"""stop at current value"""
|
||||||
self.write_target(self.read_value())
|
self.write_target(self.read_value())
|
||||||
|
|
||||||
|
|
||||||
|
@ -30,6 +30,7 @@ MLZ TANGO interface for the respective device classes.
|
|||||||
# pylint: disable=too-many-lines, consider-using-f-string
|
# pylint: disable=too-many-lines, consider-using-f-string
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
import sys
|
||||||
import threading
|
import threading
|
||||||
from time import sleep, time as currenttime
|
from time import sleep, time as currenttime
|
||||||
|
|
||||||
@ -38,7 +39,7 @@ import PyTango
|
|||||||
from frappy.datatypes import ArrayOf, EnumType, FloatRange, IntRange, \
|
from frappy.datatypes import ArrayOf, EnumType, FloatRange, IntRange, \
|
||||||
LimitsType, StatusType, StringType, TupleOf, ValueType
|
LimitsType, StatusType, StringType, TupleOf, ValueType
|
||||||
from frappy.errors import CommunicationFailedError, ConfigError, \
|
from frappy.errors import CommunicationFailedError, ConfigError, \
|
||||||
HardwareError, ProgrammingError, WrongTypeError
|
HardwareError, ProgrammingError, WrongTypeError, RangeError
|
||||||
from frappy.lib import lazy_property
|
from frappy.lib import lazy_property
|
||||||
from frappy.modules import Command, Drivable, Module, Parameter, Property, \
|
from frappy.modules import Command, Drivable, Module, Parameter, Property, \
|
||||||
Readable, Writable
|
Readable, Writable
|
||||||
@ -440,6 +441,7 @@ class AnalogOutput(PyTangoDevice, Drivable):
|
|||||||
)
|
)
|
||||||
abslimits = Parameter('Absolute limits of device value',
|
abslimits = Parameter('Absolute limits of device value',
|
||||||
datatype=LimitsType(FloatRange(unit='$')),
|
datatype=LimitsType(FloatRange(unit='$')),
|
||||||
|
export=False,
|
||||||
)
|
)
|
||||||
precision = Parameter('Precision of the device value (allowed deviation '
|
precision = Parameter('Precision of the device value (allowed deviation '
|
||||||
'of stable values from target)',
|
'of stable values from target)',
|
||||||
@ -466,30 +468,52 @@ class AnalogOutput(PyTangoDevice, Drivable):
|
|||||||
# replacement of '$' by main unit must be done later
|
# replacement of '$' by main unit must be done later
|
||||||
self.__main_unit = mainunit
|
self.__main_unit = mainunit
|
||||||
|
|
||||||
def _init_abslimits(self):
|
def _init_limits(self):
|
||||||
"""Get abslimits from tango if not configured. Otherwise, check if both
|
"""Get abslimits from tango if not configured. Otherwise, check if both
|
||||||
ranges are compatible."""
|
ranges are compatible."""
|
||||||
|
|
||||||
|
def intersect_limits(first, second, first_kind, second_kind):
|
||||||
|
lower = max(first[0], second[0])
|
||||||
|
upper = min(first[1], second[1])
|
||||||
|
if lower >= upper:
|
||||||
|
raise WrongTypeError(f"{first_kind} limits '{first}' are not "
|
||||||
|
f"compatible with {second_kind} limits "
|
||||||
|
f"'{second}'!")
|
||||||
|
return lower, upper
|
||||||
|
|
||||||
|
tangoabslim = (-sys.float_info.max, sys.float_info.max)
|
||||||
try:
|
try:
|
||||||
tangoabslim = (
|
read_tangoabslim = (float(self._getProperty('absmin')),
|
||||||
float(self._getProperty('absmin')),
|
float(self._getProperty('absmax')))
|
||||||
float(self._getProperty('absmax'))
|
# Entangle convention for "unrestricted"
|
||||||
)
|
if read_tangoabslim != (0, 0):
|
||||||
|
tangoabslim = read_tangoabslim
|
||||||
|
except Exception as e:
|
||||||
|
self.log.error('could not read Tango abslimits: %s' % e)
|
||||||
|
|
||||||
if self.parameters['abslimits'].readerror:
|
if self.parameters['abslimits'].readerror:
|
||||||
# no abslimits configured in frappy. read from entangle
|
# no abslimits configured in frappy
|
||||||
self.parameters['abslimits'].readerror = None
|
self.parameters['abslimits'].readerror = None
|
||||||
self.abslimits = tangoabslim
|
self.abslimits = tangoabslim
|
||||||
except Exception as e:
|
|
||||||
self.log.error(e)
|
|
||||||
# check if compatible
|
|
||||||
try:
|
|
||||||
dt = FloatRange(*tangoabslim)
|
|
||||||
dt.validate(self.parameters['abslimits'].datatype.min)
|
|
||||||
dt.validate(self.parameters['abslimits'].datatype.max)
|
|
||||||
except WrongTypeError as e:
|
|
||||||
raise WrongTypeError(f'Absolute limits configured in frappy \''
|
|
||||||
f'{self.abslimits}\' extend beyond the limits '
|
|
||||||
f'defined in entangle \'{tangoabslim}\'!') from e
|
|
||||||
|
|
||||||
|
# check both abslimits against each other
|
||||||
|
self.abslimits = intersect_limits(self.abslimits, tangoabslim,
|
||||||
|
'frappy absolute',
|
||||||
|
'entangle absolute')
|
||||||
|
|
||||||
|
# set abslimits as hard target limits
|
||||||
|
self.parameters['target'].datatype.set_properties(
|
||||||
|
min=self.abslimits[0], max=self.abslimits[1])
|
||||||
|
|
||||||
|
# restrict current user limits by abslimits
|
||||||
|
self.userlimits = intersect_limits(self.userlimits, self.abslimits,
|
||||||
|
'user', 'absolute')
|
||||||
|
|
||||||
|
# restrict settable user limits by abslimits
|
||||||
|
self.parameters['userlimits'].datatype.members[0].set_properties(
|
||||||
|
min=self.abslimits[0], max=self.abslimits[1])
|
||||||
|
self.parameters['userlimits'].datatype.members[1].set_properties(
|
||||||
|
min=self.abslimits[0], max=self.abslimits[1])
|
||||||
|
|
||||||
def initModule(self):
|
def initModule(self):
|
||||||
super().initModule()
|
super().initModule()
|
||||||
@ -509,9 +533,8 @@ class AnalogOutput(PyTangoDevice, Drivable):
|
|||||||
self.__main_unit = attrInfo.unit
|
self.__main_unit = attrInfo.unit
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.log.error(e)
|
self.log.error(e)
|
||||||
if self.__main_unit:
|
|
||||||
super().applyMainUnit(self.__main_unit)
|
super().applyMainUnit(self.__main_unit)
|
||||||
self._init_abslimits()
|
self._init_limits()
|
||||||
|
|
||||||
def doPoll(self):
|
def doPoll(self):
|
||||||
super().doPoll()
|
super().doPoll()
|
||||||
@ -597,22 +620,20 @@ class AnalogOutput(PyTangoDevice, Drivable):
|
|||||||
|
|
||||||
del __getusermin, __setusermin, __getusermax, __setusermax
|
del __getusermin, __setusermin, __getusermax, __setusermax
|
||||||
|
|
||||||
def _checkLimits(self, limits):
|
def write_userlimits(self, value):
|
||||||
umin, umax = limits
|
umin, umax = value
|
||||||
amin, amax = self.abslimits
|
amin, amax = self.abslimits
|
||||||
if umin > umax:
|
|
||||||
raise ValueError(
|
|
||||||
self, f'user minimum ({umin}) above the user maximum ({umax})')
|
|
||||||
if umin < amin - abs(amin * 1e-12):
|
if umin < amin - abs(amin * 1e-12):
|
||||||
umin = amin
|
umin = amin
|
||||||
if umax > amax + abs(amax * 1e-12):
|
if umax > amax + abs(amax * 1e-12):
|
||||||
umax = amax
|
umax = amax
|
||||||
return (umin, umax)
|
return umin, umax
|
||||||
|
|
||||||
def write_userlimits(self, value):
|
|
||||||
return self._checkLimits(value)
|
|
||||||
|
|
||||||
def write_target(self, value=FloatRange()):
|
def write_target(self, value=FloatRange()):
|
||||||
|
umin, umax = self.userlimits
|
||||||
|
if not umin <= value <= umax:
|
||||||
|
raise RangeError(
|
||||||
|
f'target value {value} must be between {umin} and {umax}')
|
||||||
if self.status[0] == self.Status.BUSY:
|
if self.status[0] == self.Status.BUSY:
|
||||||
# changing target value during movement is not allowed by the
|
# changing target value during movement is not allowed by the
|
||||||
# Tango base class state machine. If we are moving, stop first.
|
# Tango base class state machine. If we are moving, stop first.
|
||||||
@ -641,6 +662,7 @@ class AnalogOutput(PyTangoDevice, Drivable):
|
|||||||
sleep(0.3)
|
sleep(0.3)
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
|
"""cease driving, go to IDLE state"""
|
||||||
self._dev.Stop()
|
self._dev.Stop()
|
||||||
|
|
||||||
|
|
||||||
|
152
frappy_psi/ACM1219.py
Normal file
152
frappy_psi/ACM1219.py
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
# *****************************************************************************
|
||||||
|
# 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:
|
||||||
|
# Paul M. Neves <pmneves@mit.edu>
|
||||||
|
# *****************************************************************************
|
||||||
|
|
||||||
|
import time
|
||||||
|
from frappy.core import Readable, Parameter, FloatRange, HasIO, StringIO, Property, IntRange,\
|
||||||
|
IDLE, BUSY, WARN, ERROR, Drivable, BoolType, Attached, StructOf
|
||||||
|
|
||||||
|
|
||||||
|
class ACM1219IO(StringIO):
|
||||||
|
"""communication with ACM1219"""
|
||||||
|
end_of_line = ('\r\n', '\r') # ('\n', '\r') ('\r\n', '\r')
|
||||||
|
encoding = 'latin-1' # initial reply might not be ascii for a strange reason
|
||||||
|
identification = [('*IDN?', r'.*,ACM1219,.*')]
|
||||||
|
|
||||||
|
def checkHWIdent(self):
|
||||||
|
for _ in range(3):
|
||||||
|
time.sleep(0.5)
|
||||||
|
try:
|
||||||
|
self.communicate('*IDN?')
|
||||||
|
break
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
super().checkHWIdent()
|
||||||
|
|
||||||
|
|
||||||
|
class Channel(Readable):
|
||||||
|
value = Parameter('one channel', unit='pF')
|
||||||
|
|
||||||
|
|
||||||
|
class OneChannel(HasIO, Readable):
|
||||||
|
"""read both capacitance channels in multiplex mode"""
|
||||||
|
|
||||||
|
# define the communication class for automatic creation of the IO module
|
||||||
|
ioClass = ACM1219IO
|
||||||
|
|
||||||
|
# modifying a property of inherited parameters (unit is propagated to the FloatRange datatype)
|
||||||
|
value = Parameter('Capacitance of one channel',
|
||||||
|
FloatRange(0, 21.096, unit='pF'), readonly=True)
|
||||||
|
channel_enabled = Parameter('channel on or off', BoolType(), readonly=False)
|
||||||
|
channel = Property('the voltage channel', datatype=IntRange(1,2))
|
||||||
|
_ch_enabled = False
|
||||||
|
|
||||||
|
def read_value(self):
|
||||||
|
reply = self.communicate('readCVT').split(',')
|
||||||
|
value = float(reply[self.channel-1])
|
||||||
|
return value
|
||||||
|
|
||||||
|
def read_status(self):
|
||||||
|
# code = self.communicate(f'readStatus') # returns tons of data
|
||||||
|
return IDLE, ''
|
||||||
|
|
||||||
|
def read_channel_enabled(self):
|
||||||
|
return self._ch_enabled
|
||||||
|
|
||||||
|
def write_channel_enabled(self, channel_enabled):
|
||||||
|
if channel_enabled:
|
||||||
|
self.communicate(f'setCIN {self.channel},0,00.0,00.0,0,00.0,00.0')
|
||||||
|
# self.communicate(f'setCIN 2,0,00.0,00.0,0,00.0,00.0')
|
||||||
|
self._ch_enabled = True
|
||||||
|
else:
|
||||||
|
self.communicate(f'setCIN 0,0,00.0,00.0,0,00.0,00.0')
|
||||||
|
self._ch_enabled = False
|
||||||
|
|
||||||
|
return self.read_channel_enabled()
|
||||||
|
|
||||||
|
|
||||||
|
class BothChannels(HasIO, Readable):
|
||||||
|
"""read both capacitance channels in multiplex mode"""
|
||||||
|
|
||||||
|
# define the communication class for automatic creation of the IO module
|
||||||
|
ioClass = ACM1219IO
|
||||||
|
chan1 = Attached(Readable)
|
||||||
|
chan2 = Attached(Readable)
|
||||||
|
|
||||||
|
# modifying a property of inherited parameters (unit is propagated to the FloatRange datatype)
|
||||||
|
value = Parameter('Capacitance 1 and 2, and VT',
|
||||||
|
StructOf(C1=FloatRange(0, 21.096, unit='pF'), C2=FloatRange(0, 21.096, unit='pF'), VT=FloatRange(-1, 1000, unit='')),
|
||||||
|
readonly=True)
|
||||||
|
channels_enabled = Parameter('channels on or off', BoolType(), readonly=False)
|
||||||
|
_ch_enabled = False
|
||||||
|
_channel = 1
|
||||||
|
|
||||||
|
def Xread_value(self):
|
||||||
|
# using the inherited HasIO.communicate method to send a command and get the reply
|
||||||
|
natempt = 0
|
||||||
|
maxAttempts = 5
|
||||||
|
|
||||||
|
while natempt < maxAttempts:
|
||||||
|
try:
|
||||||
|
reply = self.communicate(f'readMUC')
|
||||||
|
# print(reply)
|
||||||
|
reply = reply.split(',')
|
||||||
|
C1 = float(reply[0])
|
||||||
|
C2 = float(reply[1])
|
||||||
|
VT = float(reply[2])
|
||||||
|
self.chan1.value = C1
|
||||||
|
self.chan2.value = C2
|
||||||
|
return {'C1': C1, 'C2': C2, 'VT': VT}
|
||||||
|
except:
|
||||||
|
''
|
||||||
|
natempt+=1
|
||||||
|
if natempt >= maxAttempts:
|
||||||
|
print('Max attempt reached for reading arduino.')
|
||||||
|
return self.value
|
||||||
|
|
||||||
|
def read_value(self):
|
||||||
|
reply = self.communicate('readCVT').split(',')
|
||||||
|
value = float(reply[self._channel-1])
|
||||||
|
previous = dict(self.value)
|
||||||
|
previous[f'C{self._channel}'] = value
|
||||||
|
getattr(self, f'chan{self._channel}').value = value
|
||||||
|
self._channel = 3 - self._channel
|
||||||
|
self.communicate(f'setCIN {self._channel},0,00.0,00.0,0,00.0,00.0')
|
||||||
|
return previous
|
||||||
|
|
||||||
|
def read_status(self):
|
||||||
|
# code = self.communicate(f'readStatus') # returns tons of data
|
||||||
|
return IDLE, ''
|
||||||
|
|
||||||
|
def read_channels_enabled(self):
|
||||||
|
return self._ch_enabled
|
||||||
|
|
||||||
|
def write_channels_enabled(self, channels_enabled):
|
||||||
|
if channels_enabled:
|
||||||
|
self.communicate(f'setCIN {self._channel},0,00.0,00.0,0,00.0,00.0')
|
||||||
|
# self.communicate(f'setCIN 2,0,00.0,00.0,0,00.0,00.0')
|
||||||
|
self._ch_enabled = True
|
||||||
|
self.communicate(f'setEXC 2,2,3,1')
|
||||||
|
else:
|
||||||
|
self.communicate(f'setCIN 0,0,00.0,00.0,0,00.0,00.0')
|
||||||
|
self._ch_enabled = False
|
||||||
|
self.communicate(f'setEXC 0,0,3,1')
|
||||||
|
|
||||||
|
return self.read_channels_enabled()
|
||||||
|
|
||||||
|
|
130
frappy_psi/RP100.py
Normal file
130
frappy_psi/RP100.py
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
# *****************************************************************************
|
||||||
|
# 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:
|
||||||
|
# Paul M. Neves <pmneves@mit.edu>
|
||||||
|
# *****************************************************************************
|
||||||
|
|
||||||
|
from frappy.core import Readable, Parameter, FloatRange, HasIO, StringIO, Property, IntRange,\
|
||||||
|
IDLE, BUSY, WARN, ERROR, Drivable, BoolType, Attached
|
||||||
|
from ast import literal_eval
|
||||||
|
|
||||||
|
|
||||||
|
class RP100IO(StringIO):
|
||||||
|
"""communication with RP100"""
|
||||||
|
end_of_line = '\n'
|
||||||
|
#wait_before = 0.05
|
||||||
|
identification = [('*IDN?', r'Razorbill,.*')]
|
||||||
|
|
||||||
|
|
||||||
|
class VoltageChannel(HasIO, Drivable):
|
||||||
|
"""a voltage output with loop"""
|
||||||
|
|
||||||
|
temp = Attached()
|
||||||
|
|
||||||
|
# define the communication class for automatic creation of the IO module
|
||||||
|
ioClass = RP100IO
|
||||||
|
|
||||||
|
# internal property to configure the channel
|
||||||
|
channel = Property('the voltage channel', datatype=IntRange(1,2))
|
||||||
|
|
||||||
|
# modifying a property of inherited parameters (unit is propagated to the FloatRange datatype)
|
||||||
|
value = Parameter('output voltage', FloatRange(-210, 210, unit='V'),
|
||||||
|
readonly=True)
|
||||||
|
target = Parameter('target voltage', FloatRange(-210, 210, unit='V'),
|
||||||
|
readonly=False)
|
||||||
|
meas_voltage = Parameter('measured output voltage', FloatRange(-250, 250, unit='V'),
|
||||||
|
readonly=True)
|
||||||
|
meas_current = Parameter('measured output current', FloatRange(-0.007, 0.007, unit='A'),
|
||||||
|
readonly=True)
|
||||||
|
max_target = Parameter('max. target', FloatRange(0, 210, unit='V'), readonly=False)
|
||||||
|
min_target = Parameter('max. target', FloatRange(-210, 0, unit='V'), readonly=False)
|
||||||
|
slew_rate = Parameter('voltage slew rate', FloatRange(0.1e-3, 100e3, unit='V/s'), readonly=False)
|
||||||
|
output_state = Parameter('output on or off', BoolType(), readonly=False)
|
||||||
|
|
||||||
|
def doPoll(self):
|
||||||
|
super().doPoll()
|
||||||
|
|
||||||
|
# calculate temperature dependent voltage limits
|
||||||
|
temp = self.temp.target
|
||||||
|
if temp > 250:
|
||||||
|
self.max_target = 120
|
||||||
|
self.min_target = -20
|
||||||
|
elif temp >= 100:
|
||||||
|
self.max_target = 120
|
||||||
|
self.min_target = -50 + (temp-100)/5
|
||||||
|
elif temp >= 10:
|
||||||
|
self.max_target = 200 - 8*(temp-10)/9
|
||||||
|
self.min_target = -200 + 5*(temp-10)/3
|
||||||
|
elif temp < 10:
|
||||||
|
self.max_target = 200
|
||||||
|
self.min_target = -200
|
||||||
|
|
||||||
|
# if the current voltage exceeds these limits, reduce voltage to max/min
|
||||||
|
if self.target > self.max_target:
|
||||||
|
self.write_target(self.max_target)
|
||||||
|
if self.target < self.min_target:
|
||||||
|
self.write_target(self.min_target)
|
||||||
|
|
||||||
|
def read_value(self):
|
||||||
|
# using the inherited HasIO.communicate method to send a command and get the reply
|
||||||
|
reply = self.communicate(f'SOUR{self.channel}:VOLT:NOW?')
|
||||||
|
return float(reply)
|
||||||
|
|
||||||
|
def read_status(self):
|
||||||
|
while 1:
|
||||||
|
code, text = literal_eval(self.communicate(f'SYST:ERR?'))
|
||||||
|
if code == 0:
|
||||||
|
break
|
||||||
|
self.log.warning('got error %d %s', code, text)
|
||||||
|
return IDLE, ''
|
||||||
|
|
||||||
|
def read_target(self):
|
||||||
|
# read back the target value
|
||||||
|
target = float(self.communicate(f'SOUR{self.channel}:VOLT?'))
|
||||||
|
return target
|
||||||
|
|
||||||
|
def write_target(self, target):
|
||||||
|
# write here the target to the hardware
|
||||||
|
if target > self.max_target:
|
||||||
|
target = self.max_target
|
||||||
|
self.log.warning('Attempted to set voltage above maximum allowed voltage. Setting to max allowed instead.')
|
||||||
|
if target < self.min_target:
|
||||||
|
target = self.min_target
|
||||||
|
self.log.warning('Attempted to set voltage below minimum allowed voltage. Setting to min allowed instead.')
|
||||||
|
self.communicate(f'SOUR{self.channel}:VOLT {target};*OPC?')
|
||||||
|
return self.read_target() # return the read back value
|
||||||
|
|
||||||
|
def read_slew_rate(self):
|
||||||
|
return float(self.communicate(f'SOUR{self.channel}:VOLT:SLEW?'))
|
||||||
|
|
||||||
|
def write_slew_rate(self, slew_rate):
|
||||||
|
self.communicate(f'SOUR{self.channel}:VOLT:SLEW {slew_rate};*OPC?')
|
||||||
|
return self.read_slew_rate()
|
||||||
|
|
||||||
|
def read_output_state(self):
|
||||||
|
return int(self.communicate(f'OUTP{self.channel}?'))
|
||||||
|
|
||||||
|
def write_output_state(self, output_state):
|
||||||
|
self.communicate(f'OUTP{self.channel} {int(output_state)};*OPC?')
|
||||||
|
if not output_state:
|
||||||
|
self.write_target(0)
|
||||||
|
return self.read_output_state()
|
||||||
|
|
||||||
|
def read_meas_voltage(self):
|
||||||
|
return float(self.communicate(f'MEAS{self.channel}:VOLT?'))
|
||||||
|
|
||||||
|
def read_meas_current(self):
|
||||||
|
return float(self.communicate(f'MEAS{self.channel}:CURR?'))
|
@ -17,248 +17,690 @@
|
|||||||
# Markus Zolliker <markus.zolliker@psi.ch>
|
# Markus Zolliker <markus.zolliker@psi.ch>
|
||||||
# *****************************************************************************
|
# *****************************************************************************
|
||||||
|
|
||||||
import sys
|
|
||||||
import time
|
import time
|
||||||
from frappy.core import Drivable, Parameter, Command, Property, ERROR, WARN, BUSY, IDLE, Done, nopoll
|
import threading
|
||||||
from frappy.features import HasTargetLimits, HasSimpleOffset
|
from frappy.core import Drivable, Parameter, Command, Property, Module, HasIO, \
|
||||||
|
ERROR, WARN, BUSY, IDLE, nopoll, Limit
|
||||||
from frappy.datatypes import IntRange, FloatRange, StringType, BoolType
|
from frappy.datatypes import IntRange, FloatRange, StringType, BoolType
|
||||||
from frappy.errors import ConfigError, BadValueError
|
from frappy.errors import BadValueError, HardwareError, ConfigError
|
||||||
sys.path.append('/home/l_samenv/Documents/anc350/Linux64/userlib/lib')
|
|
||||||
from PyANC350v4 import Positioner
|
from PyANC350v4 import Positioner
|
||||||
|
|
||||||
|
|
||||||
DIRECTION_NAME = {1: 'forward', -1: 'backward'}
|
class IO(Module):
|
||||||
|
"""'communication' module for attocube controller
|
||||||
|
|
||||||
|
why an extra class:
|
||||||
class FreezeStatus:
|
- HasIO assures that a single common communicator is used
|
||||||
"""freeze status for some time
|
- access must be thread safe
|
||||||
|
|
||||||
hardware quite often does not treat status correctly: on a target change it
|
|
||||||
may take some time to return the 'busy' status correctly.
|
|
||||||
|
|
||||||
in classes with this mixin, within :meth:`write_target` call
|
|
||||||
|
|
||||||
self.freeze_status(0.5, BUSY, 'changed target')
|
|
||||||
|
|
||||||
a wrapper around read_status will take care that the status will be the given value,
|
|
||||||
for at least the given delay. This does NOT cover the case when self.status is set
|
|
||||||
directly from an other method.
|
|
||||||
"""
|
"""
|
||||||
__freeze_status_until = 0
|
uri = Property('dummy uri, only one controller may exists',
|
||||||
|
StringType())
|
||||||
|
export = False
|
||||||
|
_hw = None
|
||||||
|
_lock = None
|
||||||
|
used_axes = set()
|
||||||
|
|
||||||
def __init_subclass__(cls):
|
def initModule(self):
|
||||||
def wrapped(self, inner=cls.read_status):
|
if self._hw is None:
|
||||||
if time.time() < self.__freeze_status_until:
|
IO._lock = threading.Lock()
|
||||||
return Done
|
IO._hw = Positioner()
|
||||||
return inner(self)
|
super().initModule()
|
||||||
|
|
||||||
cls.read_status = wrapped
|
def shutdownModule(self):
|
||||||
super().__init_subclass__()
|
if IO._hw:
|
||||||
|
IO._hw.disconnect()
|
||||||
|
IO._hw = None
|
||||||
|
|
||||||
def freeze_status(self, delay, code=BUSY, text='changed target'):
|
def configureAQuadBIn(self, axisNo, enable, resolution):
|
||||||
"""freezze status to the given value for the given delay"""
|
"""Enables and configures the A-Quad-B (quadrature) input for the target position.
|
||||||
self.__freeze_status_until = time.time() + delay
|
Parameters
|
||||||
self.status = code, text
|
axisNo Axis number (0 ... 2)
|
||||||
|
enable Enable (1) or disable (0) A-Quad-B input
|
||||||
|
resolution A-Quad-B step width in m. Internal resolution is 1 nm.
|
||||||
|
Returns
|
||||||
|
None
|
||||||
|
"""
|
||||||
|
with self._lock:
|
||||||
|
return self._hw.configureAQuadBIn(axisNo, enable, resolution)
|
||||||
|
|
||||||
|
def configureAQuadBOut(self, axisNo, enable, resolution, clock):
|
||||||
|
"""Enables and configures the A-Quad-B output of the current position.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
axisNo Axis number (0 ... 2)
|
||||||
|
enable Enable (1) or disable (0) A-Quad-B output
|
||||||
|
resolution A-Quad-B step width in m; internal resolution is 1 nm
|
||||||
|
clock Clock of the A-Quad-B output [s]. Allowed range is 40ns ... 1.3ms; internal resulution is 20ns.
|
||||||
|
Returns
|
||||||
|
None
|
||||||
|
"""
|
||||||
|
with self._lock:
|
||||||
|
return self._hw.configureAQuadBOut(axisNo, enable, resolution, clock)
|
||||||
|
|
||||||
|
def configureExtTrigger(self, axisNo, mode):
|
||||||
|
"""Enables the input trigger for steps.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
axisNo Axis number (0 ... 2)
|
||||||
|
mode Disable (0), Quadratur (1), Trigger(2) for external triggering
|
||||||
|
Returns
|
||||||
|
None
|
||||||
|
"""
|
||||||
|
with self._lock:
|
||||||
|
return self._hw.configureExtTrigger(axisNo, mode)
|
||||||
|
|
||||||
|
def configureNslTriggerAxis(self, axisNo):
|
||||||
|
"""Selects Axis for NSL Trigger.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
axisNo Axis number (0 ... 2)
|
||||||
|
Returns
|
||||||
|
None
|
||||||
|
"""
|
||||||
|
with self._lock:
|
||||||
|
return self._hw.configureNslTriggerAxis(axisNo)
|
||||||
|
|
||||||
|
def configureRngTrigger(self, axisNo, lower, upper):
|
||||||
|
"""Configure lower position for range Trigger.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
axisNo Axis number (0 ... 2)
|
||||||
|
lower Lower position for range trigger (nm)
|
||||||
|
upper Upper position for range trigger (nm)
|
||||||
|
Returns
|
||||||
|
None
|
||||||
|
"""
|
||||||
|
with self._lock:
|
||||||
|
return self._hw.configureRngTrigger(axisNo, lower, upper)
|
||||||
|
|
||||||
|
def configureRngTriggerEps(self, axisNo, epsilon):
|
||||||
|
"""Configure hysteresis for range Trigger.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
axisNo Axis number (0 ... 2)
|
||||||
|
epsilon hysteresis in nm / mdeg
|
||||||
|
Returns
|
||||||
|
None
|
||||||
|
"""
|
||||||
|
with self._lock:
|
||||||
|
return self._hw.configureRngTriggerEps(axisNo, epsilon)
|
||||||
|
|
||||||
|
def configureRngTriggerPol(self, axisNo, polarity):
|
||||||
|
"""Configure lower position for range Trigger.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
axisNo Axis number (0 ... 2)
|
||||||
|
polarity Polarity of trigger signal when position is between lower and upper Low(0) and High(1)
|
||||||
|
Returns
|
||||||
|
None
|
||||||
|
"""
|
||||||
|
with self._lock:
|
||||||
|
return self._hw.configureRngTriggerPol(axisNo, polarity)
|
||||||
|
|
||||||
|
def getActuatorName(self, axisNo):
|
||||||
|
"""Get the name of the currently selected actuator
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
axisNo Axis number (0 ... 2)
|
||||||
|
Returns
|
||||||
|
name Name of the actuator
|
||||||
|
"""
|
||||||
|
with self._lock:
|
||||||
|
return self._hw.getActuatorName(axisNo)
|
||||||
|
|
||||||
|
def getActuatorType(self, axisNo):
|
||||||
|
"""Get the type of the currently selected actuator
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
axisNo Axis number (0 ... 2)
|
||||||
|
Returns
|
||||||
|
type_ Type of the actuator {0: linear, 1: goniometer, 2: rotator}
|
||||||
|
"""
|
||||||
|
with self._lock:
|
||||||
|
return self._hw.getActuatorType(axisNo)
|
||||||
|
|
||||||
|
def getAmplitude(self, axisNo):
|
||||||
|
"""Reads back the amplitude parameter of an axis.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
axisNo Axis number (0 ... 2)
|
||||||
|
Returns
|
||||||
|
amplitude Amplitude V
|
||||||
|
"""
|
||||||
|
with self._lock:
|
||||||
|
return self._hw.getAmplitude(axisNo)
|
||||||
|
|
||||||
|
def getAxisStatus(self, axisNo):
|
||||||
|
"""Reads status information about an axis of the device.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
axisNo Axis number (0 ... 2)
|
||||||
|
Returns
|
||||||
|
connected Output: If the axis is connected to a sensor.
|
||||||
|
enabled Output: If the axis voltage output is enabled.
|
||||||
|
moving Output: If the axis is moving.
|
||||||
|
target Output: If the target is reached in automatic positioning
|
||||||
|
eotFwd Output: If end of travel detected in forward direction.
|
||||||
|
eotBwd Output: If end of travel detected in backward direction.
|
||||||
|
error Output: If the axis' sensor is in error state.
|
||||||
|
"""
|
||||||
|
with self._lock:
|
||||||
|
return self._hw.getAxisStatus(axisNo)
|
||||||
|
|
||||||
|
def getFrequency(self, axisNo):
|
||||||
|
"""Reads back the frequency parameter of an axis.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
axisNo Axis number (0 ... 2)
|
||||||
|
Returns
|
||||||
|
frequency Output: Frequency in Hz
|
||||||
|
"""
|
||||||
|
with self._lock:
|
||||||
|
return self._hw.getFrequency(axisNo)
|
||||||
|
|
||||||
|
def getPosition(self, axisNo):
|
||||||
|
"""Retrieves the current actuator position. For linear type actuators the position unit is m; for goniometers and rotators it is degree.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
axisNo Axis number (0 ... 2)
|
||||||
|
Returns
|
||||||
|
position Output: Current position [m] or [°]
|
||||||
|
"""
|
||||||
|
with self._lock:
|
||||||
|
return self._hw.getPosition(axisNo)
|
||||||
|
|
||||||
|
def measureCapacitance(self, axisNo):
|
||||||
|
"""Performs a measurement of the capacitance of the piezo motor and returns the result. If no motor is connected, the result will be 0. The function doesn't return before the measurement is complete; this will take a few seconds of time.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
axisNo Axis number (0 ... 2)
|
||||||
|
Returns
|
||||||
|
cap Output: Capacitance [F]
|
||||||
|
"""
|
||||||
|
with self._lock:
|
||||||
|
return self._hw.measureCapacitance(axisNo)
|
||||||
|
|
||||||
|
def selectActuator(self, axisNo, actuator):
|
||||||
|
"""Selects the actuator to be used for the axis from actuator presets.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
axisNo Axis number (0 ... 2)
|
||||||
|
actuator Actuator selection (0 ... 255)
|
||||||
|
0: ANPg101res
|
||||||
|
1: ANGt101res
|
||||||
|
2: ANPx51res
|
||||||
|
3: ANPx101res
|
||||||
|
4: ANPx121res
|
||||||
|
5: ANPx122res
|
||||||
|
6: ANPz51res
|
||||||
|
7: ANPz101res
|
||||||
|
8: ANR50res
|
||||||
|
9: ANR51res
|
||||||
|
10: ANR101res
|
||||||
|
11: Test
|
||||||
|
Returns
|
||||||
|
None
|
||||||
|
"""
|
||||||
|
with self._lock:
|
||||||
|
return self._hw.selectActuator(axisNo, actuator)
|
||||||
|
|
||||||
|
def setAmplitude(self, axisNo, amplitude):
|
||||||
|
"""Sets the amplitude parameter for an axis
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
axisNo Axis number (0 ... 2)
|
||||||
|
amplitude Amplitude in V, internal resolution is 1 mV
|
||||||
|
Returns
|
||||||
|
None
|
||||||
|
"""
|
||||||
|
with self._lock:
|
||||||
|
return self._hw.setAmplitude(axisNo, amplitude)
|
||||||
|
|
||||||
|
def setAxisOutput(self, axisNo, enable, autoDisable):
|
||||||
|
"""Enables or disables the voltage output of an axis.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
axisNo Axis number (0 ... 2)
|
||||||
|
enable Enables (1) or disables (0) the voltage output.
|
||||||
|
autoDisable If the voltage output is to be deactivated automatically when end of travel is detected.
|
||||||
|
Returns
|
||||||
|
None
|
||||||
|
"""
|
||||||
|
with self._lock:
|
||||||
|
return self._hw.setAxisOutput(axisNo, enable, autoDisable)
|
||||||
|
|
||||||
|
def setDcVoltage(self, axisNo, voltage):
|
||||||
|
"""Sets the DC level on the voltage output when no sawtooth based motion is active.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
axisNo Axis number (0 ... 2)
|
||||||
|
voltage DC output voltage [V], internal resolution is 1 mV
|
||||||
|
Returns
|
||||||
|
None
|
||||||
|
"""
|
||||||
|
with self._lock:
|
||||||
|
return self._hw.setDcVoltage(axisNo, voltage)
|
||||||
|
|
||||||
|
def setFrequency(self, axisNo, frequency):
|
||||||
|
"""Sets the frequency parameter for an axis
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
axisNo Axis number (0 ... 2)
|
||||||
|
frequency Frequency in Hz, internal resolution is 1 Hz
|
||||||
|
Returns
|
||||||
|
None
|
||||||
|
"""
|
||||||
|
with self._lock:
|
||||||
|
return self._hw.setFrequency(axisNo, frequency)
|
||||||
|
|
||||||
|
def setTargetPosition(self, axisNo, target):
|
||||||
|
"""Sets the target position for automatic motion, see ANC_startAutoMove. For linear type actuators the position unit is m, for goniometers and rotators it is degree.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
axisNo Axis number (0 ... 2)
|
||||||
|
target Target position [m] or [°]. Internal resulution is 1 nm or 1 µ°.
|
||||||
|
Returns
|
||||||
|
None
|
||||||
|
"""
|
||||||
|
with self._lock:
|
||||||
|
return self._hw.setTargetPosition(axisNo, target)
|
||||||
|
|
||||||
|
def setTargetRange(self, axisNo, targetRg):
|
||||||
|
"""Defines the range around the target position where the target is considered to be reached.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
axisNo Axis number (0 ... 2)
|
||||||
|
targetRg Target range [m] or [°]. Internal resulution is 1 nm or 1 µ°.
|
||||||
|
Returns
|
||||||
|
None
|
||||||
|
"""
|
||||||
|
with self._lock:
|
||||||
|
return self._hw.setTargetRange(axisNo, targetRg)
|
||||||
|
|
||||||
|
def startAutoMove(self, axisNo, enable, relative):
|
||||||
|
"""Switches automatic moving (i.e. following the target position) on or off
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
axisNo Axis number (0 ... 2)
|
||||||
|
enable Enables (1) or disables (0) automatic motion
|
||||||
|
relative If the target position is to be interpreted absolute (0) or relative to the current position (1)
|
||||||
|
Returns
|
||||||
|
None
|
||||||
|
"""
|
||||||
|
with self._lock:
|
||||||
|
return self._hw.startAutoMove(axisNo, enable, relative)
|
||||||
|
|
||||||
|
def startContinuousMove(self, axisNo, start, backward):
|
||||||
|
"""Starts or stops continous motion in forward direction. Other kinds of motions are stopped.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
axisNo Axis number (0 ... 2)
|
||||||
|
start Starts (1) or stops (0) the motion
|
||||||
|
backward If the move direction is forward (0) or backward (1)
|
||||||
|
Returns
|
||||||
|
None
|
||||||
|
"""
|
||||||
|
with self._lock:
|
||||||
|
return self._hw.startContinuousMove(axisNo, start, backward)
|
||||||
|
|
||||||
|
def startSingleStep(self, axisNo, backward):
|
||||||
|
"""Triggers a single step in desired direction.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
axisNo Axis number (0 ... 2)
|
||||||
|
backward If the step direction is forward (0) or backward (1)
|
||||||
|
Returns
|
||||||
|
None
|
||||||
|
"""
|
||||||
|
with self._lock:
|
||||||
|
return self._hw.startSingleStep(axisNo, backward)
|
||||||
|
|
||||||
|
|
||||||
class Axis(HasTargetLimits, FreezeStatus, Drivable):
|
class Stopped(RuntimeError):
|
||||||
|
"""thread was stopped"""
|
||||||
|
|
||||||
|
|
||||||
|
class Axis(HasIO, Drivable):
|
||||||
axis = Property('axis number', IntRange(0, 2), 0)
|
axis = Property('axis number', IntRange(0, 2), 0)
|
||||||
value = Parameter('axis position', FloatRange(unit='deg'))
|
value = Parameter('axis position', FloatRange(unit='deg'))
|
||||||
frequency = Parameter('frequency', FloatRange(1, unit='Hz'), readonly=False)
|
frequency = Parameter('frequency', FloatRange(1, unit='Hz'), readonly=False)
|
||||||
amplitude = Parameter('amplitude', FloatRange(0, unit='V'), readonly=False)
|
amplitude = Parameter('amplitude', FloatRange(0, unit='V'), readonly=False)
|
||||||
gear = Parameter('gear factor', FloatRange(), readonly=False, default=1, initwrite=True)
|
gear = Parameter('gear factor', FloatRange(), readonly=False, value=1)
|
||||||
tolerance = Parameter('positioning tolerance', FloatRange(0, unit='$'), readonly=False, default=0.01)
|
tolerance = Parameter('positioning tolerance', FloatRange(0, unit='$'),
|
||||||
output = Parameter('enable output', BoolType(), readonly=False)
|
readonly=False, default=0.01)
|
||||||
|
sensor_connected = Parameter('a sensor is connected', BoolType())
|
||||||
info = Parameter('axis info', StringType())
|
info = Parameter('axis info', StringType())
|
||||||
statusbits = Parameter('status bits', StringType())
|
statusbits = Parameter('status bits', StringType())
|
||||||
|
step_mode = Parameter('step mode (soft closed loop)', BoolType(),
|
||||||
|
default=False, readonly=False, group='step_mode')
|
||||||
|
timeout = Parameter('timeout after no progress detected', FloatRange(0),
|
||||||
|
default=1, readonly=False, group='step_mode')
|
||||||
|
steps_fwd = Parameter('forward steps / main unit', FloatRange(0), unit='$/s',
|
||||||
|
default=0, readonly=False, group='step_mode')
|
||||||
|
steps_bwd = Parameter('backward steps / main unit', FloatRange(0, unit='$/s'),
|
||||||
|
default=0, readonly=False, group='step_mode')
|
||||||
|
delay = Parameter('delay between tries within loop', FloatRange(0, unit='s'),
|
||||||
|
readonly=False, default=0.05, group='step_mode')
|
||||||
|
maxstep = Parameter('max. step duration', FloatRange(0, unit='s'),
|
||||||
|
default=0.25, readonly=False, group='step_mode')
|
||||||
|
prop = Parameter('factor for control loop', FloatRange(0, 1),
|
||||||
|
readonly=False, default=0.8, group='step_mode')
|
||||||
|
uri = 'ANC'
|
||||||
|
ioClass = IO
|
||||||
|
target_min = Limit()
|
||||||
|
target_max = Limit()
|
||||||
|
|
||||||
_hw = Positioner()
|
fast_interval = 0.25
|
||||||
|
|
||||||
|
_hw = None
|
||||||
_scale = 1 # scale for custom units
|
_scale = 1 # scale for custom units
|
||||||
_move_steps = 0 # number of steps to move (used by move command)
|
|
||||||
SCALES = {'deg': 1, 'm': 1, 'mm': 1000, 'um': 1000000, 'µm': 1000000}
|
SCALES = {'deg': 1, 'm': 1, 'mm': 1000, 'um': 1000000, 'µm': 1000000}
|
||||||
_direction = 1 # move direction
|
_thread = None
|
||||||
_idle_status = IDLE, ''
|
_moving_since = 0
|
||||||
_error_state = '' # empty string: no error
|
_status = IDLE, ''
|
||||||
_history = None
|
_calib_range = None
|
||||||
_check_sensor = False
|
_try_cnt = 0
|
||||||
_try_count = 0
|
_at_target = False
|
||||||
|
|
||||||
def __init__(self, name, logger, opts, srv):
|
def initModule(self):
|
||||||
unit = opts.pop('unit', 'deg')
|
super().initModule()
|
||||||
opts['value.unit'] = unit
|
self._stopped = threading.Event()
|
||||||
|
if self.axis in IO.used_axes:
|
||||||
|
raise ConfigError(f'a module with axisNo={self.axis} already exists')
|
||||||
|
IO.used_axes.add(self.axis)
|
||||||
|
|
||||||
|
def initialReads(self):
|
||||||
|
self.read_info()
|
||||||
|
super().initialReads()
|
||||||
|
|
||||||
|
def shutdownModule(self):
|
||||||
|
IO.used_axes.discard(self.axis)
|
||||||
|
|
||||||
|
def read_value(self):
|
||||||
|
if self._thread:
|
||||||
|
return self.value
|
||||||
try:
|
try:
|
||||||
self._scale = self.SCALES[unit] * opts.get('gear', 1)
|
return self._read_pos()
|
||||||
except KeyError as e:
|
except Stopped:
|
||||||
raise ConfigError('unsupported unit: %s' % unit)
|
return self.value
|
||||||
super().__init__(name, logger, opts, srv)
|
|
||||||
|
|
||||||
def write_gear(self, value):
|
def write_gear(self, value):
|
||||||
self._scale = self.SCALES[self.parameters['value'].datatype.unit] * self.gear
|
self._scale = self.SCALES[self.parameters['value'].datatype.unit] * self.gear
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def startModule(self, start_events):
|
|
||||||
super().startModule(start_events)
|
|
||||||
start_events.queue(self.read_info)
|
|
||||||
|
|
||||||
def check_value(self, value):
|
|
||||||
"""check if value allows moving in current direction"""
|
|
||||||
if self._direction > 0:
|
|
||||||
if value > self.target_limits[1]:
|
|
||||||
raise BadValueError('above upper limit')
|
|
||||||
elif value < self.target_limits[0]:
|
|
||||||
raise BadValueError('below lower limit')
|
|
||||||
|
|
||||||
def read_value(self):
|
|
||||||
pos = self._hw.getPosition(self.axis) * self._scale
|
|
||||||
if self.isBusy():
|
|
||||||
try:
|
|
||||||
self.check_value(pos)
|
|
||||||
except BadValueError as e:
|
|
||||||
self._stop()
|
|
||||||
self._idle_status = ERROR, str(e)
|
|
||||||
return pos
|
|
||||||
|
|
||||||
def read_frequency(self):
|
def read_frequency(self):
|
||||||
return self._hw.getFrequency(self.axis)
|
return self.io.getFrequency(self.axis)
|
||||||
|
|
||||||
def write_frequency(self, value):
|
def write_frequency(self, value):
|
||||||
self._hw.setFrequency(self.axis, value)
|
self.io.setFrequency(self.axis, value)
|
||||||
return self._hw.getFrequency(self.axis)
|
return self.io.getFrequency(self.axis)
|
||||||
|
|
||||||
def read_amplitude(self):
|
def read_amplitude(self):
|
||||||
return self._hw.getAmplitude(self.axis)
|
return self.io.getAmplitude(self.axis)
|
||||||
|
|
||||||
def write_amplitude(self, value):
|
def write_amplitude(self, value):
|
||||||
self._hw.setAmplitude(self.axis, value)
|
self.io.setAmplitude(self.axis, value)
|
||||||
return self._hw.getAmplitude(self.axis)
|
return self.io.getAmplitude(self.axis)
|
||||||
|
|
||||||
def write_tolerance(self, value):
|
def read_statusbits(self):
|
||||||
self._hw.setTargetRange(self.axis, value / self._scale)
|
self._get_status()
|
||||||
return value
|
return self.statusbits
|
||||||
|
|
||||||
def write_output(self, value):
|
def _get_status(self):
|
||||||
self._hw.setAxisOutput(self.axis, enable=value, autoDisable=0)
|
"""get axis status
|
||||||
return value
|
|
||||||
|
- update self.sensor_connected and self.statusbits
|
||||||
|
- return <moving flag>, <error flag>, <reason>
|
||||||
|
|
||||||
|
<moving flag> is True whn moving
|
||||||
|
<in_error> is True when in error
|
||||||
|
<reason> is an error text, when in error, 'at target' or '' otherwise
|
||||||
|
"""
|
||||||
|
statusbits = self.io.getAxisStatus(self.axis)
|
||||||
|
self.sensor_connected, self._output, moving, at_target, fwd_stuck, bwd_stuck, error = statusbits
|
||||||
|
self.statusbits = ''.join(k for k, v in zip('OTFBE', (self._output,) + statusbits[3:]) if v)
|
||||||
|
if error:
|
||||||
|
return ERROR, 'other error'
|
||||||
|
if bwd_stuck:
|
||||||
|
return ERROR, 'end of travel backward'
|
||||||
|
if fwd_stuck:
|
||||||
|
return ERROR, 'end of travel forward'
|
||||||
|
target_reached = at_target > self._at_target
|
||||||
|
self._at_target = at_target
|
||||||
|
if self._moving_since:
|
||||||
|
if target_reached:
|
||||||
|
return IDLE, 'at target'
|
||||||
|
if time.time() < self._moving_since + 0.25:
|
||||||
|
return BUSY, 'started'
|
||||||
|
if at_target:
|
||||||
|
return IDLE, 'at target'
|
||||||
|
if moving and self._output:
|
||||||
|
return BUSY, 'moving'
|
||||||
|
return WARN, 'stopped by unknown reason'
|
||||||
|
if self._moving_since is False:
|
||||||
|
return IDLE, 'stopped'
|
||||||
|
if not self.step_mode and at_target:
|
||||||
|
return IDLE, 'at target'
|
||||||
|
return IDLE, ''
|
||||||
|
|
||||||
def read_status(self):
|
def read_status(self):
|
||||||
statusbits = self._hw.getAxisStatus(self.axis)
|
status = self._get_status()
|
||||||
sensor, self.output, moving, attarget, eot_fwd, eot_bwd, sensor_error = statusbits
|
if self.step_mode:
|
||||||
self.statusbits = ''.join((k for k, v in zip('SOMTFBE', statusbits) if v))
|
return self._status
|
||||||
if self._move_steps:
|
if self._moving_since:
|
||||||
if not (eot_fwd or eot_bwd):
|
if status[0] != BUSY:
|
||||||
return BUSY, 'moving by steps'
|
self._moving_since = 0
|
||||||
if not sensor:
|
self.setFastPoll(False)
|
||||||
self._error_state = 'no sensor connected'
|
return status
|
||||||
elif sensor_error:
|
|
||||||
self._error_state = 'sensor error'
|
def _wait(self, delay):
|
||||||
elif eot_fwd:
|
if self._stopped.wait(delay):
|
||||||
self._error_state = 'end of travel forward'
|
raise Stopped()
|
||||||
elif eot_bwd:
|
|
||||||
self._error_state = 'end of travel backward'
|
def _read_pos(self):
|
||||||
|
if not self.sensor_connected:
|
||||||
|
return 0
|
||||||
|
poslist = []
|
||||||
|
for i in range(9):
|
||||||
|
if i:
|
||||||
|
self._wait(0.001)
|
||||||
|
poslist.append(self.io.getPosition(self.axis) * self._scale)
|
||||||
|
self._poslist = sorted(poslist)
|
||||||
|
return self._poslist[len(poslist) // 2] # median
|
||||||
|
|
||||||
|
def _run_drive(self, target):
|
||||||
|
self.value = self._read_pos()
|
||||||
|
self.status = self._status = BUSY, 'drive by steps'
|
||||||
|
deadline = time.time() + self.timeout
|
||||||
|
max_steps = self.maxstep * self.frequency
|
||||||
|
while True:
|
||||||
|
for _ in range(2):
|
||||||
|
dif = target - self.value
|
||||||
|
steps_per_unit = self.steps_bwd if dif < 0 else self.steps_fwd
|
||||||
|
tol = max(self.tolerance, 0.6 / steps_per_unit) # avoid a tolerance less than 60% of a step
|
||||||
|
if abs(dif) > tol * 3:
|
||||||
|
break
|
||||||
|
# extra wait time when already close
|
||||||
|
self._wait(2 * self.delay)
|
||||||
|
self.read_value()
|
||||||
|
status = None
|
||||||
|
if abs(dif) < tol:
|
||||||
|
status = IDLE, 'in tolerance'
|
||||||
|
elif self._poslist[2] <= target <= self._poslist[-3]: # target within noise
|
||||||
|
status = IDLE, 'within noise'
|
||||||
|
elif dif > 0:
|
||||||
|
steps = min(max_steps, min(dif, (dif + tol) * self.prop) * steps_per_unit)
|
||||||
else:
|
else:
|
||||||
if self._error_state and not DIRECTION_NAME[self._direction] in self._error_state:
|
steps = max(-max_steps, max(dif, (dif - tol) * self.prop) * steps_per_unit)
|
||||||
self._error_state = ''
|
if status or steps == 0:
|
||||||
status_text = 'moving' if self._try_count == 0 else 'moving (retry %d)' % self._try_count
|
self._status = status
|
||||||
if moving and self._history is not None: # history None: moving by steps
|
break
|
||||||
self._history.append(self.value)
|
if round(steps) == 0: # this should not happen
|
||||||
if len(self._history) < 5:
|
self._status = WARN, 'steps=0'
|
||||||
return BUSY, status_text
|
break
|
||||||
beg = self._history.pop(0)
|
self._move_steps(steps)
|
||||||
if abs(beg - self.target) < self.tolerance:
|
if self._step_size > self.prop * 0.25 / steps_per_unit:
|
||||||
# reset normal tolerance
|
# some progress happened
|
||||||
self._stop()
|
deadline = time.time() + self.timeout
|
||||||
self._idle_status = IDLE, 'in tolerance'
|
elif time.time() > deadline:
|
||||||
return self._idle_status
|
self._status = WARN, 'timeout - no progress'
|
||||||
# self._hw.setTargetRange(self.axis, self.tolerance / self._scale)
|
break
|
||||||
if (self.value - beg) * self._direction > 0:
|
self.read_status()
|
||||||
return BUSY, status_text
|
|
||||||
self._try_count += 1
|
|
||||||
if self._try_count < 10:
|
|
||||||
self.log.warn('no progress retry %d', self._try_count)
|
|
||||||
return BUSY, status_text
|
|
||||||
self._idle_status = WARN, 'no progress'
|
|
||||||
if self._error_state:
|
|
||||||
self._try_count += 1
|
|
||||||
if self._try_count < 10 and self._history is not None:
|
|
||||||
self.log.warn('end of travel retry %d', self._try_count)
|
|
||||||
self.write_target(self.target)
|
|
||||||
return Done
|
|
||||||
self._idle_status = WARN, self._error_state
|
|
||||||
if self.status[0] != IDLE:
|
|
||||||
self._stop()
|
|
||||||
return self._idle_status
|
|
||||||
|
|
||||||
def write_target(self, value):
|
def _thread_wrapper(self, func, *args):
|
||||||
if value == self.read_value():
|
|
||||||
return value
|
|
||||||
self.check_limits(value)
|
|
||||||
self._try_count = 0
|
|
||||||
self._direction = 1 if value > self.value else -1
|
|
||||||
# if self._error_state and DIRECTION_NAME[-self._direction] not in self._error_state:
|
|
||||||
# raise BadValueError('can not move (%s)' % self._error_state)
|
|
||||||
self._move_steps = 0
|
|
||||||
self.write_output(1)
|
|
||||||
# try first with 50 % of tolerance
|
|
||||||
self._hw.setTargetRange(self.axis, self.tolerance * 0.5 / self._scale)
|
|
||||||
for itry in range(5):
|
|
||||||
try:
|
try:
|
||||||
self._hw.setTargetPosition(self.axis, value / self._scale)
|
func(*args)
|
||||||
self._hw.startAutoMove(self.axis, enable=1, relative=0)
|
except Stopped as e:
|
||||||
|
self._status = IDLE, str(e)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if itry == 4:
|
self._status = ERROR, f'{type(e).__name__} - {e}'
|
||||||
raise
|
finally:
|
||||||
self.log.warn('%r', e)
|
self.io.setAxisOutput(self.axis, enable=0, autoDisable=0)
|
||||||
self._history = [self.value]
|
self.setFastPoll(False)
|
||||||
self._idle_status = IDLE, ''
|
self._stopped.clear()
|
||||||
self.freeze_status(1, BUSY, 'changed target')
|
self._thread = None
|
||||||
self.setFastPoll(True, 1)
|
|
||||||
return value
|
|
||||||
|
|
||||||
def doPoll(self):
|
def _stop_thread(self):
|
||||||
if self._move_steps == 0:
|
if self._thread:
|
||||||
super().doPoll()
|
self._stopped.set()
|
||||||
|
self._thread.join()
|
||||||
|
|
||||||
|
def _start_thread(self, *args):
|
||||||
|
self._stop_thread()
|
||||||
|
thread = threading.Thread(target=self._thread_wrapper, args=args)
|
||||||
|
self._thread = thread
|
||||||
|
thread.start()
|
||||||
|
|
||||||
|
def write_target(self, target):
|
||||||
|
if not self.sensor_connected:
|
||||||
|
raise HardwareError('no sensor connected')
|
||||||
|
self._stop_thread()
|
||||||
|
self.io.setTargetRange(self.axis, self.tolerance / self._scale)
|
||||||
|
if self.step_mode:
|
||||||
|
self.status = BUSY, 'changed target'
|
||||||
|
self._start_thread(self._run_drive, target)
|
||||||
|
else:
|
||||||
|
self._try_cnt = 0
|
||||||
|
self.setFastPoll(True, self.fast_interval)
|
||||||
|
self.io.setTargetPosition(self.axis, target / self._scale)
|
||||||
|
self.io.setAxisOutput(self.axis, enable=1, autoDisable=0)
|
||||||
|
self.io.startAutoMove(self.axis, enable=1, relative=0)
|
||||||
|
self._moving_since = time.time()
|
||||||
|
self.status = self._get_status()
|
||||||
|
return target
|
||||||
|
|
||||||
|
@Command()
|
||||||
|
def stop(self):
|
||||||
|
if self.step_mode:
|
||||||
|
self._stop_thread()
|
||||||
|
self._status = IDLE, 'stopped'
|
||||||
|
elif self._moving_since:
|
||||||
|
self._moving_since = False # indicate stop
|
||||||
|
self.read_status()
|
||||||
|
|
||||||
|
@Command(IntRange())
|
||||||
|
def move(self, steps):
|
||||||
|
"""relative move by number of steps"""
|
||||||
|
self._stop_thread()
|
||||||
|
self.read_value()
|
||||||
|
if steps > 0:
|
||||||
|
if self.value > self.target_max:
|
||||||
|
raise BadValueError('above upper limit')
|
||||||
|
elif self.value < self.target_min:
|
||||||
|
raise BadValueError('below lower limit')
|
||||||
|
self.status = self._status = BUSY, 'moving relative'
|
||||||
|
self._start_thread(self._run_move, steps)
|
||||||
|
|
||||||
|
def _run_move(self, steps):
|
||||||
|
self.setFastPoll(True, self.fast_interval)
|
||||||
|
self._move_steps(steps)
|
||||||
|
self.status = self._status = IDLE, ''
|
||||||
|
|
||||||
|
def _move_steps(self, steps):
|
||||||
|
steps = round(steps)
|
||||||
|
if not steps:
|
||||||
return
|
return
|
||||||
self._hw.startSingleStep(self.axis, self._direction < 0)
|
previous = self._read_pos()
|
||||||
self._move_steps -= self._direction
|
self.io.setAxisOutput(self.axis, enable=1, autoDisable=0)
|
||||||
if self._move_steps % int(self.frequency) == 0: # poll value and status every second
|
# wait for output is really on
|
||||||
super().doPoll()
|
for i in range(100):
|
||||||
|
self._wait(0.001)
|
||||||
|
self._get_status()
|
||||||
|
if self._output:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
raise ValueError('can not switch on output')
|
||||||
|
for cnt in range(abs(steps)):
|
||||||
|
self.io.setAxisOutput(self.axis, enable=1, autoDisable=0)
|
||||||
|
if not self._thread:
|
||||||
|
raise Stopped('stopped')
|
||||||
|
self.io.startSingleStep(self.axis, steps < 0)
|
||||||
|
self._wait(1 / self.frequency)
|
||||||
|
self._get_status()
|
||||||
|
if cnt and not self._output:
|
||||||
|
steps = cnt
|
||||||
|
break
|
||||||
|
self._wait(self.delay)
|
||||||
|
self.value = self._read_pos()
|
||||||
|
self._step_size = (self.value - previous) / steps
|
||||||
|
|
||||||
|
@Command(IntRange(0))
|
||||||
|
def calib_steps(self, delta):
|
||||||
|
"""calibrate steps_fwd and steps_bwd using <delta> steps forwards and backwards"""
|
||||||
|
if not self.sensor_connected:
|
||||||
|
raise HardwareError('no sensor connected')
|
||||||
|
self._stop_thread()
|
||||||
|
self._status = BUSY, 'calibrate step size'
|
||||||
|
self.read_status()
|
||||||
|
self._start_thread(self._run_calib, delta)
|
||||||
|
|
||||||
|
def _run_calib(self, steps):
|
||||||
|
self.value = self._read_pos()
|
||||||
|
if self._calib_range is None or abs(self.target - self.value) > self._calib_range:
|
||||||
|
self.target = self.value
|
||||||
|
maxfwd = 0
|
||||||
|
maxbwd = 0
|
||||||
|
cntfwd = 0
|
||||||
|
cntbwd = 0
|
||||||
|
self._calib_range = 0
|
||||||
|
for i in range(10):
|
||||||
|
if self.value <= self.target:
|
||||||
|
self._status = BUSY, 'move forwards'
|
||||||
|
self.read_status()
|
||||||
|
self._move_steps(steps)
|
||||||
|
while True:
|
||||||
|
self._move_steps(steps)
|
||||||
|
if self._step_size and self._output:
|
||||||
|
maxfwd = max(maxfwd, self._step_size)
|
||||||
|
cntfwd += 1
|
||||||
|
if self.value > self.target:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
self._status = BUSY, 'move backwards'
|
||||||
|
self.read_status()
|
||||||
|
self._move_steps(-steps)
|
||||||
|
while True:
|
||||||
|
self._move_steps(-steps)
|
||||||
|
if self._step_size:
|
||||||
|
maxbwd = max(maxbwd, self._step_size)
|
||||||
|
cntbwd += 1
|
||||||
|
if self.value < self.target:
|
||||||
|
break
|
||||||
|
# keep track how far we had to go for calibration
|
||||||
|
self._calib_range = max(self._calib_range, abs(self.value - self.target))
|
||||||
|
if cntfwd >= 3 and cntbwd >= 3:
|
||||||
|
self.steps_fwd = 1 / maxfwd
|
||||||
|
self.steps_bwd = 1 / maxbwd
|
||||||
|
self._status = IDLE, 'calib step size done'
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
self._status = WARN, 'calib step size failed'
|
||||||
|
self.read_status()
|
||||||
|
|
||||||
@nopoll
|
@nopoll
|
||||||
def read_info(self):
|
def read_info(self):
|
||||||
"""read info from controller"""
|
"""read info from controller"""
|
||||||
cap = self._hw.measureCapacitance(self.axis) * 1e9
|
axistype = ['linear', 'gonio', 'rotator'][self.io.getActuatorType(self.axis)]
|
||||||
axistype = ['linear', 'gonio', 'rotator'][self._hw.getActuatorType(self.axis)]
|
name = self.io.getActuatorName(self.axis)
|
||||||
return '%s %s %.3gnF' % (self._hw.getActuatorName(self.axis), axistype, cap)
|
cap = self.io.measureCapacitance(self.axis) * 1e9
|
||||||
|
return f'{name} {axistype} {cap:.3g}nF'
|
||||||
def _stop(self):
|
|
||||||
self._move_steps = 0
|
|
||||||
self._history = None
|
|
||||||
for _ in range(5):
|
|
||||||
try:
|
|
||||||
self._hw.startAutoMove(self.axis, enable=0, relative=0)
|
|
||||||
break
|
|
||||||
except Exception as e:
|
|
||||||
if itry == 4:
|
|
||||||
raise
|
|
||||||
self.log.warn('%r', e)
|
|
||||||
self._hw.setTargetRange(self.axis, self.tolerance / self._scale)
|
|
||||||
self.setFastPoll(False)
|
|
||||||
|
|
||||||
@Command()
|
|
||||||
def stop(self):
|
|
||||||
self._idle_status = IDLE, 'stopped' if self.isBusy() else ''
|
|
||||||
self._stop()
|
|
||||||
self.status = self._idle_status
|
|
||||||
|
|
||||||
@Command(IntRange())
|
|
||||||
def move(self, value):
|
|
||||||
"""relative move by number of steps"""
|
|
||||||
self._direction = 1 if value > 0 else -1
|
|
||||||
self.check_value(self.value)
|
|
||||||
self._history = None
|
|
||||||
if DIRECTION_NAME[self._direction] in self._error_state:
|
|
||||||
raise BadValueError('can not move (%s)' % self._error_state)
|
|
||||||
self._move_steps = value
|
|
||||||
self._idle_status = IDLE, ''
|
|
||||||
self.read_status()
|
|
||||||
self.setFastPoll(True, 1/self.frequency)
|
|
||||||
|
116
frappy_psi/cetoni_pump.py
Normal file
116
frappy_psi/cetoni_pump.py
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
libpath = '/home/l_samenv/frappy/cetoniSDK/CETONI_SDK_Raspi_64bit_v20220627/python/src/'
|
||||||
|
import sys
|
||||||
|
if libpath not in sys.path:
|
||||||
|
sys.path.append(libpath)
|
||||||
|
|
||||||
|
from frappy.core import Drivable, Readable, StringIO, HasIO, FloatRange, IntRange, StringType, BoolType, EnumType, \
|
||||||
|
Parameter, Property, PersistentParam, Command, IDLE, BUSY, ERROR, Attached
|
||||||
|
from qmixsdk import qmixbus
|
||||||
|
from qmixsdk import qmixpump
|
||||||
|
from qmixsdk import qmixvalve
|
||||||
|
from qmixsdk.qmixbus import UnitPrefix, TimeUnit
|
||||||
|
|
||||||
|
class LabCannBus(Readable):
|
||||||
|
deviceconfig = Property('config files', StringType(),default="/home/l_samenv/frappy/cetoniSDK/CETONI_SDK_Raspi_64bit_v20220627/config/dual_pumps")
|
||||||
|
|
||||||
|
def earlyInit(self):
|
||||||
|
super().earlyInit()
|
||||||
|
self.bus = qmixbus.Bus()
|
||||||
|
self.bus.open(self.deviceconfig, "")
|
||||||
|
|
||||||
|
def initModule(self):
|
||||||
|
super().initModule()
|
||||||
|
self.bus.start()
|
||||||
|
|
||||||
|
def shutdownModule(self):
|
||||||
|
"""Not so gracefully close the connection"""
|
||||||
|
self.bus.stop()
|
||||||
|
self.bus.close()
|
||||||
|
|
||||||
|
class SyringePump(Drivable):
|
||||||
|
io = Attached()
|
||||||
|
pump_name = Property('name of pump', StringType(),default="Nemesys_S_1_Pump")
|
||||||
|
valve_name = Property('name of valve', StringType(),default="Nemesys_S_1_Valve")
|
||||||
|
|
||||||
|
inner_diameter_set = Property('inner diameter', FloatRange(), default=1)
|
||||||
|
piston_stroke_set = Property('piston stroke', FloatRange(), default=60)
|
||||||
|
|
||||||
|
value = PersistentParam('volume', FloatRange(unit='mL'))
|
||||||
|
status = PersistentParam()
|
||||||
|
|
||||||
|
max_flow_rate = Parameter('max flow rate', FloatRange(0,100000, unit='mL/min',), readonly=True)
|
||||||
|
max_volume = Parameter('max volume', FloatRange(0,100000, unit='mL',), readonly=True)
|
||||||
|
target_flow_rate = Parameter('target flow rate', FloatRange(unit='mL/min'), readonly=False)
|
||||||
|
real_flow_rate = Parameter('actual flow rate', FloatRange(unit='mL/min'), readonly=True)
|
||||||
|
target = Parameter('target volume', FloatRange(unit='mL'), readonly=False)
|
||||||
|
|
||||||
|
no_of_valve_pos = Property('number of valve positions', IntRange(0,10), default=1)
|
||||||
|
valve_pos = Parameter('valve position', EnumType('valve', CLOSED=0, APP=1, RES=2, OPEN=3), readonly=False)
|
||||||
|
|
||||||
|
def initModule(self):
|
||||||
|
super().initModule()
|
||||||
|
|
||||||
|
self.pump = qmixpump.Pump()
|
||||||
|
self.pump.lookup_by_name(self.pump_name)
|
||||||
|
|
||||||
|
self.valve = qmixvalve.Valve()
|
||||||
|
self.valve.lookup_by_name(self.valve_name)
|
||||||
|
|
||||||
|
|
||||||
|
def initialReads(self):
|
||||||
|
if self.pump.is_in_fault_state():
|
||||||
|
self.pump.clear_fault()
|
||||||
|
|
||||||
|
if not self.pump.is_enabled():
|
||||||
|
self.pump.enable(True)
|
||||||
|
|
||||||
|
self.pump.set_syringe_param(self.inner_diameter_set, self.piston_stroke_set)
|
||||||
|
|
||||||
|
self.pump.set_volume_unit(qmixpump.UnitPrefix.milli, qmixpump.VolumeUnit.litres)
|
||||||
|
|
||||||
|
self.pump.set_flow_unit(qmixpump.UnitPrefix.milli, qmixpump.VolumeUnit.litres, qmixpump.TimeUnit.per_minute)
|
||||||
|
|
||||||
|
self.max_flow_rate = self.pump.get_flow_rate_max()
|
||||||
|
self.max_volume = self.pump.get_volume_max()
|
||||||
|
self.no_of_valve_pos = self.valve.number_of_valve_positions()
|
||||||
|
self.valve_pos = self.valve.actual_valve_position()
|
||||||
|
|
||||||
|
self.target_flow_rate = self.max_flow_rate * 0.5
|
||||||
|
self.target = self.pump.get_fill_level()
|
||||||
|
|
||||||
|
def read_value(self):
|
||||||
|
return self.pump.get_fill_level()
|
||||||
|
|
||||||
|
def write_target(self, target):
|
||||||
|
self.pump.set_fill_level(target, self.target_flow_rate)
|
||||||
|
self.status = BUSY, 'Target changed'
|
||||||
|
return target
|
||||||
|
|
||||||
|
def write_target_flow_rate(self, rate):
|
||||||
|
self.pump.target_flow_rate = rate
|
||||||
|
return rate
|
||||||
|
|
||||||
|
def read_real_flow_rate(self):
|
||||||
|
return self.pump.get_flow_is()
|
||||||
|
|
||||||
|
def read_valve_pos(self):
|
||||||
|
return self.valve.actual_valve_position()
|
||||||
|
|
||||||
|
def write_valve_pos(self, target_pos):
|
||||||
|
self.valve.switch_valve_to_position(target_pos)
|
||||||
|
return target_pos
|
||||||
|
|
||||||
|
def read_status(self):
|
||||||
|
fault_state = self.pump.is_in_fault_state()
|
||||||
|
pumping = self.pump.is_pumping()
|
||||||
|
if fault_state == True:
|
||||||
|
return ERROR, 'Pump in fault state'
|
||||||
|
elif pumping == True:
|
||||||
|
return BUSY, 'Pumping'
|
||||||
|
else:
|
||||||
|
return IDLE, ''
|
||||||
|
|
||||||
|
@Command
|
||||||
|
def stop(self):
|
||||||
|
self.pump.stop_pumping()
|
||||||
|
self.target = self.pump.get_fill_level()
|
@ -1,3 +1,4 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
# *****************************************************************************
|
# *****************************************************************************
|
||||||
# This program is free software; you can redistribute it and/or modify it under
|
# 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
|
# the terms of the GNU General Public License as published by the Free Software
|
||||||
@ -18,79 +19,78 @@
|
|||||||
# *****************************************************************************
|
# *****************************************************************************
|
||||||
"""vector field"""
|
"""vector field"""
|
||||||
|
|
||||||
from frappy.core import Drivable, Done, BUSY, IDLE, WARN, ERROR
|
import math
|
||||||
from frappy.errors import BadValueError
|
from frappy.core import Drivable, Done, BUSY, IDLE, ERROR, Parameter, TupleOf, ArrayOf, FloatRange
|
||||||
|
from frappy.errors import RangeError
|
||||||
from frappy_psi.vector import Vector
|
from frappy_psi.vector import Vector
|
||||||
|
from frappy.states import HasStates, Retry, status_code
|
||||||
|
|
||||||
|
|
||||||
DECREASE = 1
|
DECREASE = 1
|
||||||
INCREASE = 2
|
INCREASE = 2
|
||||||
|
|
||||||
|
|
||||||
class VectorField(Vector, Drivable):
|
class VectorField(HasStates, Vector, Drivable):
|
||||||
_state = None
|
sphere_radius = Parameter('max. sphere', datatype=FloatRange(0, 0.7, unit='T'), readonly=True, default=0.6)
|
||||||
|
cylinders = Parameter('allowed cylinders (list of radius and height)',
|
||||||
|
datatype=ArrayOf(TupleOf(FloatRange(0, 0.6, unit='T'), FloatRange(0, 5.2, unit='T')), 1, 9),
|
||||||
|
readonly=True, default=((0.23, 5.2), (0.45, 0.8)))
|
||||||
|
|
||||||
def doPoll(self):
|
def initModule(self):
|
||||||
"""periodically called method"""
|
super().initModule()
|
||||||
try:
|
# override check_limits of the components with a check for restrictions on the vector
|
||||||
if self._starting:
|
for idx, component in enumerate(self.components):
|
||||||
# first decrease components
|
|
||||||
driving = False
|
def outer_check(target, vector=self, i=idx, inner_check=component.check_target):
|
||||||
for target, component in zip(self.target, self.components):
|
inner_check(target)
|
||||||
if target * component.value < 0:
|
value = [c.value - math.copysign(c.tolerance, c.value)
|
||||||
# change sign: drive to zero first
|
for c in vector.components]
|
||||||
target = 0
|
value[i] = target
|
||||||
if abs(target) < abs(component.target):
|
vector._check_limits(value)
|
||||||
if target != component.target:
|
|
||||||
component.write_target(target)
|
component.check_target = outer_check
|
||||||
if component.isDriving():
|
|
||||||
driving = True
|
|
||||||
if driving:
|
|
||||||
return
|
|
||||||
# now we can go to the final targets
|
|
||||||
for target, component in zip(self.target, self.components):
|
|
||||||
component.write_target(target)
|
|
||||||
self._starting = False
|
|
||||||
else:
|
|
||||||
for component in self.components:
|
|
||||||
if component.isDriving():
|
|
||||||
return
|
|
||||||
self.setFastPoll(False)
|
|
||||||
finally:
|
|
||||||
super().doPoll()
|
|
||||||
|
|
||||||
def merge_status(self):
|
def merge_status(self):
|
||||||
names = [c.name for c in self.components if c.status[0] >= ERROR]
|
|
||||||
if names:
|
|
||||||
return ERROR, 'error in %s' % ', '.join(names)
|
|
||||||
names = [c.name for c in self.components if c.isDriving()]
|
|
||||||
if self._state:
|
|
||||||
# self.log.info('merge %r', [c.status for c in self.components])
|
|
||||||
if names:
|
|
||||||
direction = 'down ' if self._state == DECREASE else ''
|
|
||||||
return BUSY, 'ramping %s%s' % (direction, ', '.join(names))
|
|
||||||
if self.status[0] == BUSY:
|
|
||||||
return self.status
|
return self.status
|
||||||
return BUSY, 'driving'
|
|
||||||
if names:
|
|
||||||
return WARN, 'moving %s directly' % ', '.join(names)
|
|
||||||
names = [c.name for c in self.components if c.status[0] >= WARN]
|
|
||||||
if names:
|
|
||||||
return WARN, 'warnings in %s' % ', '.join(names)
|
|
||||||
return IDLE, ''
|
|
||||||
|
|
||||||
def write_target(self, value):
|
def _check_limits(self, value):
|
||||||
|
"""check if value is within one of the safe shapes"""
|
||||||
|
if sum((v ** 2 for v in value)) <= self.sphere_radius ** 2:
|
||||||
|
return
|
||||||
|
for r, h in self.cylinders:
|
||||||
|
if sum(v ** 2 for v in value[0:2]) <= r ** 2 and abs(value[2]) <= h:
|
||||||
|
return
|
||||||
|
raise RangeError('vector %s does not fit in any limiting shape' % repr(value))
|
||||||
|
|
||||||
|
def write_target(self, target):
|
||||||
"""initiate target change"""
|
"""initiate target change"""
|
||||||
# first make sure target is valid
|
# check limits first
|
||||||
for target, component in zip(self.target, self.components):
|
for component_target, component in zip(target, self.components):
|
||||||
# check against limits if individual components
|
# check against limits of individual components
|
||||||
component.check_limits(target)
|
component.check_target(component_target, vector=None) # no outer check here!
|
||||||
if sum(v * v for v in value) > 1:
|
self._check_limits(target)
|
||||||
raise BadValueError('norm of vector too high')
|
for component_target, component in zip(target, self.components):
|
||||||
self.log.info('decrease')
|
if component_target * component.value < 0:
|
||||||
self.setFastPoll(True)
|
# change sign: drive to zero first
|
||||||
self.target = value
|
component_target = 0
|
||||||
self._state = DECREASE
|
if abs(component_target) > abs(component.value):
|
||||||
self.doPoll()
|
continue # do not drive yet
|
||||||
self.log.info('done write_target %r', value)
|
component.write_target(component_target)
|
||||||
return Done
|
self.start_machine(self.ramp_down, target=target)
|
||||||
|
return target
|
||||||
|
|
||||||
|
@status_code(BUSY)
|
||||||
|
def ramp_down(self, state):
|
||||||
|
for target, component in zip(state.target, self.components):
|
||||||
|
if component.isDriving():
|
||||||
|
return Retry()
|
||||||
|
for target, component in zip(state.target, self.components):
|
||||||
|
component.write_target(target)
|
||||||
|
return self.final_ramp
|
||||||
|
|
||||||
|
@status_code(BUSY)
|
||||||
|
def final_ramp(self, state):
|
||||||
|
for component in self.components:
|
||||||
|
if component.isDriving():
|
||||||
|
return Retry()
|
||||||
|
return self.final_status()
|
||||||
|
@ -120,7 +120,7 @@ class SimpleMagfield(HasStates, Drivable):
|
|||||||
return last
|
return last
|
||||||
|
|
||||||
def write_target(self, target):
|
def write_target(self, target):
|
||||||
self.start_machine(self.start_field_change, target=target)
|
self.start_machine(self.start_field_change, target=target, try_cnt=0)
|
||||||
return target
|
return target
|
||||||
|
|
||||||
def init_progress(self, sm, value):
|
def init_progress(self, sm, value):
|
||||||
|
@ -63,6 +63,7 @@ fast_slow = Mapped(ON=0, OFF=1) # maps OIs slow=ON/fast=OFF to sample_rate.slow
|
|||||||
|
|
||||||
class IO(StringIO):
|
class IO(StringIO):
|
||||||
identification = [('*IDN?', r'IDN:OXFORD INSTRUMENTS:*')]
|
identification = [('*IDN?', r'IDN:OXFORD INSTRUMENTS:*')]
|
||||||
|
timeout = 5
|
||||||
|
|
||||||
|
|
||||||
class MercuryChannel(HasIO):
|
class MercuryChannel(HasIO):
|
||||||
|
@ -198,6 +198,10 @@ class MotorValve(PersistentMixin, Drivable):
|
|||||||
|
|
||||||
@Command
|
@Command
|
||||||
def stop(self):
|
def stop(self):
|
||||||
|
"""stop at current position
|
||||||
|
|
||||||
|
state will probably be undefined
|
||||||
|
"""
|
||||||
self._state.stop()
|
self._state.stop()
|
||||||
self.motor.stop()
|
self.motor.stop()
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
"""modules to access parameters"""
|
"""modules to access parameters"""
|
||||||
|
|
||||||
from frappy.core import Drivable, EnumType, IDLE, Attached, StringType, Property, \
|
from frappy.core import Drivable, EnumType, IDLE, Attached, StringType, Property, \
|
||||||
Parameter, FloatRange, BoolType, Readable, ERROR
|
Parameter, BoolType, FloatRange, Readable, ERROR, nopoll
|
||||||
from frappy.errors import ConfigError
|
from frappy.errors import ConfigError
|
||||||
from frappy_psi.convergence import HasConvergence
|
from frappy_psi.convergence import HasConvergence
|
||||||
from frappy_psi.mixins import HasRamp
|
from frappy_psi.mixins import HasRamp
|
||||||
@ -59,8 +59,12 @@ class Driv(Drivable):
|
|||||||
read = Attached(description='<module>.<parameter> for read')
|
read = Attached(description='<module>.<parameter> for read')
|
||||||
write = Attached(description='<module>.<parameter> for read')
|
write = Attached(description='<module>.<parameter> for read')
|
||||||
unit = Property('main unit', StringType())
|
unit = Property('main unit', StringType())
|
||||||
|
__error = None
|
||||||
|
__status = None
|
||||||
|
|
||||||
def setProperty(self, key, value):
|
def setProperty(self, key, value):
|
||||||
|
# split properties read/write (including .<param>)
|
||||||
|
# into read/write (modules) and read_param/write_param (parameters)
|
||||||
if key in ('read', 'write'):
|
if key in ('read', 'write'):
|
||||||
value, param = value.split('.')
|
value, param = value.split('.')
|
||||||
setattr(self, f'{key}_param', param)
|
setattr(self, f'{key}_param', param)
|
||||||
@ -72,22 +76,44 @@ class Driv(Drivable):
|
|||||||
raise ConfigError('illegal recursive read/write module')
|
raise ConfigError('illegal recursive read/write module')
|
||||||
super().checkProperties()
|
super().checkProperties()
|
||||||
|
|
||||||
#def registerUpdates(self):
|
def registerUpdates(self):
|
||||||
# self.read.valueCallbacks[self.read_param].append(self.update_value)
|
if self.read_param == 'value':
|
||||||
# self.write.valueCallbacks[self.write_param].append(self.update_target)
|
try:
|
||||||
#
|
self.read.addCallback('status', self.updateReadStatus)
|
||||||
#def startModule(self, start_events):
|
except KeyError:
|
||||||
# start_events.queue(self.registerUpdates)
|
pass # may be not needed: value is present but not status
|
||||||
# super().startModule(start_events)
|
self.read.addCallback(self.read_param, self.updateValue)
|
||||||
|
self.write.addCallback(self.write_param, self.announceUpdate, 'target')
|
||||||
|
|
||||||
|
def updateReadStatus(self, status, err=None):
|
||||||
|
self.__status = status
|
||||||
|
self.read_status()
|
||||||
|
|
||||||
|
def updateValue(self, value, err=None):
|
||||||
|
self.announceUpdate('value', value, err)
|
||||||
|
if err != self.__error:
|
||||||
|
self.__error = err
|
||||||
|
self.read_status()
|
||||||
|
|
||||||
|
def startModule(self, start_events):
|
||||||
|
start_events.queue(self.registerUpdates)
|
||||||
|
super().startModule(start_events)
|
||||||
|
|
||||||
|
@nopoll
|
||||||
def read_value(self):
|
def read_value(self):
|
||||||
return getattr(self.read, f'{self.read_param}')
|
return getattr(self.read, f'read_{self.read_param}')()
|
||||||
|
|
||||||
|
@nopoll
|
||||||
def read_target(self):
|
def read_target(self):
|
||||||
return getattr(self.write, f'{self.write_param}')
|
return getattr(self.write, f'read_{self.write_param}')()
|
||||||
|
|
||||||
|
@nopoll
|
||||||
def read_status(self):
|
def read_status(self):
|
||||||
return IDLE, ''
|
if self.__status:
|
||||||
|
if self.__status[0] < ERROR and self.__error:
|
||||||
|
return ERROR, repr(self.__error)
|
||||||
|
return self.__status
|
||||||
|
return (ERROR, repr(self.__error)) if self.__error else (IDLE, '')
|
||||||
|
|
||||||
def write_target(self, target):
|
def write_target(self, target):
|
||||||
return getattr(self.write, f'write_{self.write_param}')(target)
|
return getattr(self.write, f'write_{self.write_param}')(target)
|
||||||
@ -101,19 +127,6 @@ class Converging(HasConvergence, Driv):
|
|||||||
self.parameters['tolerance'].setProperty('unit', self.unit)
|
self.parameters['tolerance'].setProperty('unit', self.unit)
|
||||||
super().checkProperties()
|
super().checkProperties()
|
||||||
|
|
||||||
#def update_value(self, value):
|
|
||||||
# print('UV', value)
|
|
||||||
# self.value = value
|
|
||||||
|
|
||||||
#def error_update_value(self, err):
|
|
||||||
# raise err
|
|
||||||
|
|
||||||
#def update_target(self, value):
|
|
||||||
# self.target = value
|
|
||||||
|
|
||||||
#def error_update_target(self, err):
|
|
||||||
# raise err
|
|
||||||
|
|
||||||
def write_target(self, target):
|
def write_target(self, target):
|
||||||
self.convergence_start()
|
self.convergence_start()
|
||||||
return super().write_target(target)
|
return super().write_target(target)
|
||||||
@ -130,7 +143,7 @@ def set_enabled(modobj, value):
|
|||||||
|
|
||||||
|
|
||||||
def get_value(obj, default):
|
def get_value(obj, default):
|
||||||
"""get the value of given module. if not valid, return the limit (min_high or max_low)"""
|
"""get the value of given module. if not valid, return the default"""
|
||||||
if not getattr(obj, 'enabled', True):
|
if not getattr(obj, 'enabled', True):
|
||||||
return default
|
return default
|
||||||
# consider also that a value 0 is invalid
|
# consider also that a value 0 is invalid
|
||||||
|
@ -483,6 +483,10 @@ class Temp(PpmsDrivable):
|
|||||||
self._expected_target_time = time.time() + abs(target - self.value) * 60.0 / max(0.1, ramp)
|
self._expected_target_time = time.time() + abs(target - self.value) * 60.0 / max(0.1, ramp)
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
|
"""set setpoint to current value
|
||||||
|
|
||||||
|
but restrict to values between last target and current target
|
||||||
|
"""
|
||||||
if not self.isDriving():
|
if not self.isDriving():
|
||||||
return
|
return
|
||||||
if self.status[0] != StatusType.STABILIZING:
|
if self.status[0] != StatusType.STABILIZING:
|
||||||
@ -612,6 +616,7 @@ class Field(PpmsDrivable):
|
|||||||
# do not execute FIELD command, as this would trigger a ramp up of leads current
|
# do not execute FIELD command, as this would trigger a ramp up of leads current
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
|
"""stop at current driven Field"""
|
||||||
if not self.isDriving():
|
if not self.isDriving():
|
||||||
return
|
return
|
||||||
newtarget = clamp(self._last_target, self.value, self.target)
|
newtarget = clamp(self._last_target, self.value, self.target)
|
||||||
@ -714,6 +719,7 @@ class Position(PpmsDrivable):
|
|||||||
return value # do not execute MOVE command, as this would trigger an unnecessary move
|
return value # do not execute MOVE command, as this would trigger an unnecessary move
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
|
"""stop motor"""
|
||||||
if not self.isDriving():
|
if not self.isDriving():
|
||||||
return
|
return
|
||||||
newtarget = clamp(self._last_target, self.value, self.target)
|
newtarget = clamp(self._last_target, self.value, self.target)
|
||||||
|
@ -141,5 +141,6 @@ class TemperatureLoopTC1(SensorTC1, Drivable):
|
|||||||
return value
|
return value
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
|
"""stop at current value (does nothing if ramp is not used)"""
|
||||||
if self.control and self.ramp_used:
|
if self.control and self.ramp_used:
|
||||||
self.write_target(self.value)
|
self.write_target(self.value)
|
||||||
|
142
frappy_psi/razorbill.py
Normal file
142
frappy_psi/razorbill.py
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
# *****************************************************************************
|
||||||
|
# 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:
|
||||||
|
# Paul M. Neves <pmneves@mit.edu>
|
||||||
|
# *****************************************************************************
|
||||||
|
|
||||||
|
from frappy.core import Readable, Parameter, FloatRange, HasIO, StringIO, Property, IntRange,\
|
||||||
|
IDLE, BUSY, WARN, ERROR, Drivable, BoolType, Attached, Writable, StructOf
|
||||||
|
|
||||||
|
|
||||||
|
class Temp(Writable):
|
||||||
|
"""stores sample temperature"""
|
||||||
|
|
||||||
|
target = Parameter('target voltage', FloatRange(0, 325, unit='K'),
|
||||||
|
readonly=False)
|
||||||
|
|
||||||
|
def write_target(self, target):
|
||||||
|
self.value = target
|
||||||
|
|
||||||
|
def read_value(self):
|
||||||
|
return self.value
|
||||||
|
|
||||||
|
|
||||||
|
class Displacement(Readable):
|
||||||
|
|
||||||
|
# attached classes for capacitance and temperature
|
||||||
|
cap = Attached()
|
||||||
|
temp = Attached()
|
||||||
|
|
||||||
|
# modifying a property of inherited parameters (unit is propagated to the FloatRange datatype)
|
||||||
|
value = Parameter('displacement', FloatRange(None, None, unit='um'), readonly=True)
|
||||||
|
alpha290K = Parameter('capacitor constant at 290 K', FloatRange(None, None, unit='um pF'), readonly=False)
|
||||||
|
d0 = Parameter('offset displacement', FloatRange(None, None, unit='um'), readonly=False)
|
||||||
|
Cp = Parameter('parallel capacitance', FloatRange(None, None, unit='pF'), readonly=False)
|
||||||
|
d0_curve = Parameter('calibration curve for offset displacement',
|
||||||
|
StructOf(a=FloatRange(None, None, unit='um'),
|
||||||
|
b=FloatRange(None, None, unit='um/K'),
|
||||||
|
c=FloatRange(None, None, unit='um/K^2'),
|
||||||
|
d=FloatRange(None, None, unit='um/K^3'),
|
||||||
|
e=FloatRange(None, None, unit='um/K^4'),),
|
||||||
|
readonly=False)
|
||||||
|
|
||||||
|
def read_value(self):
|
||||||
|
# get temperature and capacitance
|
||||||
|
temp = self.temp.target
|
||||||
|
cap = self.cap.value
|
||||||
|
|
||||||
|
# calculate displacement from temperature and capacitance
|
||||||
|
d0_T = self.d0_curve['a'] + self.d0_curve['b']*temp + self.d0_curve['c']*temp**2 + self.d0_curve['d']*temp**3 + self.d0_curve['e']*temp**4
|
||||||
|
|
||||||
|
disp = self.alpha290K / (cap - self.Cp) - self.d0 - d0_T
|
||||||
|
|
||||||
|
return disp
|
||||||
|
|
||||||
|
|
||||||
|
class Force(Readable):
|
||||||
|
|
||||||
|
# attached classes for capacitance and temperature
|
||||||
|
cap = Attached()
|
||||||
|
temp = Attached()
|
||||||
|
|
||||||
|
# modifying a property of inherited parameters (unit is propagated to the FloatRange datatype)
|
||||||
|
value = Parameter('force', FloatRange(None, None, unit='N'), readonly=True)
|
||||||
|
alpha290K = Parameter('capacitor constant at 290 K', FloatRange(None, None, unit='N pF'), readonly=False)
|
||||||
|
f0 = Parameter('offset force', FloatRange(None, None, unit='N'), readonly=False)
|
||||||
|
Cp = Parameter('parallel capacitance', FloatRange(None, None, unit='pF'), readonly=False)
|
||||||
|
f0_curve = Parameter('calibration curve for offset force',
|
||||||
|
StructOf(a=FloatRange(None, None, unit='N'),
|
||||||
|
b=FloatRange(None, None, unit='N/K'),
|
||||||
|
c=FloatRange(None, None, unit='N/K^2'),
|
||||||
|
d=FloatRange(None, None, unit='N/K^3'),
|
||||||
|
e=FloatRange(None, None, unit='N/K^4'),),
|
||||||
|
readonly=False)
|
||||||
|
|
||||||
|
def read_value(self):
|
||||||
|
# get temperature and capacitance
|
||||||
|
temp = self.temp.target
|
||||||
|
cap = self.cap.value
|
||||||
|
|
||||||
|
# calculate force from temperature and capacitance
|
||||||
|
alpha = self.alpha290K * (0.91 + 5e-5*temp + 9e-7*temp**2)
|
||||||
|
f0_T = self.f0_curve['a'] + self.f0_curve['b']*temp + self.f0_curve['c']*temp**2 + self.f0_curve['d']*temp**3 + self.f0_curve['e']*temp**4
|
||||||
|
|
||||||
|
force = alpha / (cap - self.Cp) - self.f0 - f0_T
|
||||||
|
|
||||||
|
return force
|
||||||
|
|
||||||
|
|
||||||
|
class Stress(Readable):
|
||||||
|
|
||||||
|
# attached classes for displacement
|
||||||
|
force = Attached()
|
||||||
|
|
||||||
|
# modifying a property of inherited parameters (unit is propagated to the FloatRange datatype)
|
||||||
|
value = Parameter('stress', FloatRange(None, None, unit='GPa'), readonly=True)
|
||||||
|
area = Parameter('cross sectional area of sample in mm^2', FloatRange(None, None, unit='mm^2'), readonly=False)
|
||||||
|
|
||||||
|
def read_value(self):
|
||||||
|
return self.force.value / self.area / 1000
|
||||||
|
|
||||||
|
|
||||||
|
class Strain(Readable):
|
||||||
|
|
||||||
|
# attached classes for displacement
|
||||||
|
displacement = Attached()
|
||||||
|
|
||||||
|
# modifying a property of inherited parameters (unit is propagated to the FloatRange datatype)
|
||||||
|
value = Parameter('strain', FloatRange(None, None, unit='m/m'), readonly=True)
|
||||||
|
L = Parameter('length of sample in mm', FloatRange(None, None, unit='mm'), readonly=False)
|
||||||
|
|
||||||
|
def read_value(self):
|
||||||
|
return self.displacement.value / (1000*self.L)
|
||||||
|
|
||||||
|
|
||||||
|
class YoungsModulus(Readable):
|
||||||
|
|
||||||
|
# attached classes for displacement
|
||||||
|
stress = Attached()
|
||||||
|
strain = Attached()
|
||||||
|
|
||||||
|
# modifying a property of inherited parameters (unit is propagated to the FloatRange datatype)
|
||||||
|
value = Parameter('Young\'s modulus', FloatRange(None, None, unit='GPa'), readonly=True)
|
||||||
|
|
||||||
|
def read_value(self):
|
||||||
|
if self.strain.value:
|
||||||
|
return self.stress.value / self.strain.value
|
||||||
|
else:
|
||||||
|
return 0
|
||||||
|
|
@ -39,12 +39,13 @@ from os.path import expanduser, join, exists
|
|||||||
from frappy.client import ProxyClient
|
from frappy.client import ProxyClient
|
||||||
from frappy.datatypes import ArrayOf, BoolType, \
|
from frappy.datatypes import ArrayOf, BoolType, \
|
||||||
EnumType, FloatRange, IntRange, StringType
|
EnumType, FloatRange, IntRange, StringType
|
||||||
from frappy.errors import ConfigError, HardwareError, secop_error, CommunicationFailedError
|
from frappy.core import IDLE, BUSY, ERROR
|
||||||
|
from frappy.errors import ConfigError, HardwareError, CommunicationFailedError
|
||||||
from frappy.lib import generalConfig, mkthread
|
from frappy.lib import generalConfig, mkthread
|
||||||
from frappy.lib.asynconn import AsynConn, ConnectionClosed
|
from frappy.lib.asynconn import AsynConn, ConnectionClosed
|
||||||
from frappy.modules import Attached, Command, Done, Drivable, \
|
from frappy.modulebase import Done
|
||||||
|
from frappy.modules import Attached, Command, Drivable, \
|
||||||
Module, Parameter, Property, Readable, Writable
|
Module, Parameter, Property, Readable, Writable
|
||||||
from frappy.protocol.dispatcher import make_update
|
|
||||||
|
|
||||||
|
|
||||||
CFG_HEADER = """Node('%(config)s.sea.psi.ch',
|
CFG_HEADER = """Node('%(config)s.sea.psi.ch',
|
||||||
@ -99,7 +100,7 @@ class SeaClient(ProxyClient, Module):
|
|||||||
"""connection to SEA"""
|
"""connection to SEA"""
|
||||||
|
|
||||||
uri = Parameter('hostname:portnumber', datatype=StringType(), default='localhost:5000')
|
uri = Parameter('hostname:portnumber', datatype=StringType(), default='localhost:5000')
|
||||||
timeout = Parameter('timeout', datatype=FloatRange(0), default=10)
|
timeout = Parameter('timeout for connecting and requests', datatype=FloatRange(0), default=10)
|
||||||
config = Property("""needed SEA configuration, space separated
|
config = Property("""needed SEA configuration, space separated
|
||||||
|
|
||||||
Example: "ori4.config ori4.stick"
|
Example: "ori4.config ori4.stick"
|
||||||
@ -107,7 +108,6 @@ class SeaClient(ProxyClient, Module):
|
|||||||
service = Property("main/stick/addons", StringType(), default='')
|
service = Property("main/stick/addons", StringType(), default='')
|
||||||
visibility = 'expert'
|
visibility = 'expert'
|
||||||
default_json_file = {}
|
default_json_file = {}
|
||||||
_connect_thread = None
|
|
||||||
_instance = None
|
_instance = None
|
||||||
_last_connect = 0
|
_last_connect = 0
|
||||||
|
|
||||||
@ -124,6 +124,8 @@ class SeaClient(ProxyClient, Module):
|
|||||||
self.shutdown = False
|
self.shutdown = False
|
||||||
self.path2param = {}
|
self.path2param = {}
|
||||||
self._write_lock = threading.Lock()
|
self._write_lock = threading.Lock()
|
||||||
|
self._connect_thread = None
|
||||||
|
self._connected = threading.Event()
|
||||||
config = opts.get('config')
|
config = opts.get('config')
|
||||||
if isinstance(config, dict):
|
if isinstance(config, dict):
|
||||||
config = config['value']
|
config = config['value']
|
||||||
@ -135,14 +137,12 @@ class SeaClient(ProxyClient, Module):
|
|||||||
Module.__init__(self, name, log, opts, srv)
|
Module.__init__(self, name, log, opts, srv)
|
||||||
|
|
||||||
def doPoll(self):
|
def doPoll(self):
|
||||||
if not self.asynio and time.time() > self._last_connect + 10:
|
if not self._connected.is_set() and time.time() > self._last_connect + self.timeout:
|
||||||
with self._write_lock:
|
|
||||||
# make sure no more connect thread is running
|
|
||||||
if self._connect_thread and self._connect_thread.isAlive():
|
|
||||||
return
|
|
||||||
if not self._last_connect:
|
if not self._last_connect:
|
||||||
self.log.info('reconnect to SEA %s', self.service)
|
self.log.info('reconnect to SEA %s', self.service)
|
||||||
self._connect_thread = mkthread(self._connect, None)
|
if self._connect_thread is None:
|
||||||
|
self._connect_thread = mkthread(self._connect)
|
||||||
|
self._connected.wait(self.timeout)
|
||||||
|
|
||||||
def register_obj(self, module, obj):
|
def register_obj(self, module, obj):
|
||||||
self.objects.add(obj)
|
self.objects.add(obj)
|
||||||
@ -150,15 +150,13 @@ class SeaClient(ProxyClient, Module):
|
|||||||
self.path2param.setdefault(k, []).extend(v)
|
self.path2param.setdefault(k, []).extend(v)
|
||||||
self.register_callback(module.name, module.updateEvent)
|
self.register_callback(module.name, module.updateEvent)
|
||||||
|
|
||||||
def _connect(self, started_callback):
|
def _connect(self):
|
||||||
self.asynio = None
|
try:
|
||||||
if self.syncio:
|
if self.syncio:
|
||||||
# trigger syncio reconnect in self.request()
|
|
||||||
try:
|
try:
|
||||||
self.syncio.disconnect()
|
self.syncio.disconnect()
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
self.syncio = None
|
|
||||||
self._last_connect = time.time()
|
self._last_connect = time.time()
|
||||||
if self._instance:
|
if self._instance:
|
||||||
try:
|
try:
|
||||||
@ -179,38 +177,40 @@ class SeaClient(ProxyClient, Module):
|
|||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
raise CommunicationFailedError('reply %r should be "Login OK"' % reply)
|
raise CommunicationFailedError('reply %r should be "Login OK"' % reply)
|
||||||
result = self.request('frappy_config %s %s' % (self.service, self.config))
|
self.syncio = AsynConn(self.uri)
|
||||||
|
assert self.syncio.readline() == b'OK'
|
||||||
|
self.syncio.writeline(b'seauser seaser')
|
||||||
|
assert self.syncio.readline() == b'Login OK'
|
||||||
|
|
||||||
|
result = self.raw_request('frappy_config %s %s' % (self.service, self.config))
|
||||||
if result.startswith('ERROR:'):
|
if result.startswith('ERROR:'):
|
||||||
raise CommunicationFailedError(f'reply from frappy_config: {result}')
|
raise CommunicationFailedError(f'reply from frappy_config: {result}')
|
||||||
# frappy_async_client switches to the json protocol (better for updates)
|
# frappy_async_client switches to the json protocol (better for updates)
|
||||||
self.asynio.writeline(b'frappy_async_client')
|
self.asynio.writeline(b'frappy_async_client')
|
||||||
self.asynio.writeline(('get_all_param ' + ' '.join(self.objects)).encode())
|
self.asynio.writeline(('get_all_param ' + ' '.join(self.objects)).encode())
|
||||||
|
self.log.info('connected to %s', self.uri)
|
||||||
|
self._connected.set()
|
||||||
|
mkthread(self._rxthread)
|
||||||
|
finally:
|
||||||
self._connect_thread = None
|
self._connect_thread = None
|
||||||
mkthread(self._rxthread, started_callback)
|
|
||||||
|
|
||||||
def request(self, command, quiet=False):
|
def request(self, command, quiet=False):
|
||||||
"""send a request and wait for reply"""
|
|
||||||
with self._write_lock:
|
with self._write_lock:
|
||||||
if not self.syncio or not self.syncio.connection:
|
if not self._connected.is_set():
|
||||||
if not self.asynio or not self.asynio.connection:
|
if self._connect_thread is None:
|
||||||
try:
|
|
||||||
self._connect_thread.join()
|
|
||||||
except AttributeError:
|
|
||||||
pass
|
|
||||||
# let doPoll do the reconnect
|
# let doPoll do the reconnect
|
||||||
self.pollInfo.trigger(True)
|
self.pollInfo.trigger(True)
|
||||||
raise ConnectionClosed('disconnected - reconnect later')
|
raise ConnectionClosed('disconnected - reconnect is tried later')
|
||||||
self.syncio = AsynConn(self.uri)
|
return self.raw_request(command, quiet)
|
||||||
assert self.syncio.readline() == b'OK'
|
|
||||||
self.syncio.writeline(b'seauser seaser')
|
def raw_request(self, command, quiet=False):
|
||||||
assert self.syncio.readline() == b'Login OK'
|
"""send a request and wait for reply"""
|
||||||
self.log.info('connected to %s', self.uri)
|
|
||||||
try:
|
try:
|
||||||
self.syncio.flush_recv()
|
self.syncio.flush_recv()
|
||||||
ft = 'fulltransAct' if quiet else 'fulltransact'
|
ft = 'fulltransAct' if quiet else 'fulltransact'
|
||||||
self.syncio.writeline(('%s %s' % (ft, command)).encode())
|
self.syncio.writeline(('%s %s' % (ft, command)).encode())
|
||||||
result = None
|
result = None
|
||||||
deadline = time.time() + 10
|
deadline = time.time() + self.timeout
|
||||||
while time.time() < deadline:
|
while time.time() < deadline:
|
||||||
reply = self.syncio.readline()
|
reply = self.syncio.readline()
|
||||||
if reply is None:
|
if reply is None:
|
||||||
@ -233,16 +233,22 @@ class SeaClient(ProxyClient, Module):
|
|||||||
result = [reply.split('=', 1)[-1]]
|
result = [reply.split('=', 1)[-1]]
|
||||||
else:
|
else:
|
||||||
result.append(reply)
|
result.append(reply)
|
||||||
|
raise TimeoutError('no response within 10s')
|
||||||
except ConnectionClosed:
|
except ConnectionClosed:
|
||||||
|
self.close_connections()
|
||||||
|
raise
|
||||||
|
|
||||||
|
def close_connections(self):
|
||||||
|
connections = self.syncio, self.asynio
|
||||||
|
self.syncio = self.asynio = None
|
||||||
|
for conn in connections:
|
||||||
try:
|
try:
|
||||||
self.syncio.disconnect()
|
conn.disconnect()
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
self.syncio = None
|
self._connected.clear()
|
||||||
raise
|
|
||||||
raise TimeoutError('no response within 10s')
|
|
||||||
|
|
||||||
def _rxthread(self, started_callback):
|
def _rxthread(self):
|
||||||
recheck = None
|
recheck = None
|
||||||
while not self.shutdown:
|
while not self.shutdown:
|
||||||
if recheck and time.time() > recheck:
|
if recheck and time.time() > recheck:
|
||||||
@ -258,11 +264,7 @@ class SeaClient(ProxyClient, Module):
|
|||||||
if reply is None:
|
if reply is None:
|
||||||
continue
|
continue
|
||||||
except ConnectionClosed:
|
except ConnectionClosed:
|
||||||
try:
|
self.close_connections()
|
||||||
self.asynio.disconnect()
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
self.asynio = None
|
|
||||||
break
|
break
|
||||||
try:
|
try:
|
||||||
msg = json.loads(reply)
|
msg = json.loads(reply)
|
||||||
@ -289,9 +291,6 @@ class SeaClient(ProxyClient, Module):
|
|||||||
data = msg['data']
|
data = msg['data']
|
||||||
if flag == 'finish' and obj == 'get_all_param':
|
if flag == 'finish' and obj == 'get_all_param':
|
||||||
# first updates have finished
|
# first updates have finished
|
||||||
if started_callback:
|
|
||||||
started_callback()
|
|
||||||
started_callback = None
|
|
||||||
continue
|
continue
|
||||||
if flag != 'hdbevent':
|
if flag != 'hdbevent':
|
||||||
if obj not in ('frappy_async_client', 'get_all_param'):
|
if obj not in ('frappy_async_client', 'get_all_param'):
|
||||||
@ -352,7 +351,7 @@ class SeaClient(ProxyClient, Module):
|
|||||||
class SeaConfigCreator(SeaClient):
|
class SeaConfigCreator(SeaClient):
|
||||||
def startModule(self, start_events):
|
def startModule(self, start_events):
|
||||||
"""save objects (and sub-objects) description and exit"""
|
"""save objects (and sub-objects) description and exit"""
|
||||||
self._connect(None)
|
self._connect()
|
||||||
reply = self.request('describe_all')
|
reply = self.request('describe_all')
|
||||||
reply = ''.join('' if line.startswith('WARNING') else line for line in reply.split('\n'))
|
reply = ''.join('' if line.startswith('WARNING') else line for line in reply.split('\n'))
|
||||||
description, reply = json.loads(reply)
|
description, reply = json.loads(reply)
|
||||||
@ -644,22 +643,7 @@ class SeaModule(Module):
|
|||||||
if upd:
|
if upd:
|
||||||
upd(value, timestamp, readerror)
|
upd(value, timestamp, readerror)
|
||||||
return
|
return
|
||||||
try:
|
self.announceUpdate(parameter, value, readerror, timestamp)
|
||||||
pobj = self.parameters[parameter]
|
|
||||||
except KeyError:
|
|
||||||
self.log.error('do not know %s:%s', self.name, parameter)
|
|
||||||
raise
|
|
||||||
pobj.timestamp = timestamp
|
|
||||||
# should be done here: deal with clock differences
|
|
||||||
if not readerror:
|
|
||||||
try:
|
|
||||||
pobj.value = value # store the value even in case of a validation error
|
|
||||||
pobj.value = pobj.datatype(value)
|
|
||||||
except Exception as e:
|
|
||||||
readerror = secop_error(e)
|
|
||||||
pobj.readerror = readerror
|
|
||||||
if pobj.export:
|
|
||||||
self.secNode.srv.dispatcher.broadcast_event(make_update(self.name, pobj))
|
|
||||||
|
|
||||||
def initModule(self):
|
def initModule(self):
|
||||||
self.io.register_obj(self, self.sea_object)
|
self.io.register_obj(self, self.sea_object)
|
||||||
@ -670,20 +654,35 @@ class SeaModule(Module):
|
|||||||
|
|
||||||
|
|
||||||
class SeaReadable(SeaModule, Readable):
|
class SeaReadable(SeaModule, Readable):
|
||||||
|
_readerror = None
|
||||||
|
_status = IDLE, ''
|
||||||
|
|
||||||
|
def update_value(self, value, timestamp, readerror):
|
||||||
|
# make sure status is always ERROR when reading value fails
|
||||||
|
self._readerror = readerror
|
||||||
|
if readerror:
|
||||||
|
self.read_status() # forced ERROR status
|
||||||
|
self.announceUpdate('value', value, readerror, timestamp)
|
||||||
|
else: # order is important
|
||||||
|
self.value = value # includes announceUpdate
|
||||||
|
self.read_status() # send event for ordinary self._status
|
||||||
|
|
||||||
def update_status(self, value, timestamp, readerror):
|
def update_status(self, value, timestamp, readerror):
|
||||||
if readerror:
|
if readerror:
|
||||||
value = repr(readerror)
|
value = f'{readerror.name} - {readerror}'
|
||||||
if value == '':
|
if value == '':
|
||||||
self.status = (self.Status.IDLE, '')
|
self._status = IDLE, ''
|
||||||
else:
|
else:
|
||||||
self.status = (self.Status.ERROR, value)
|
self._status = ERROR, value
|
||||||
|
self.read_status()
|
||||||
|
|
||||||
def read_status(self):
|
def read_status(self):
|
||||||
return self.status
|
if self._readerror:
|
||||||
|
return ERROR, f'{self._readerror.name} - {self._readerror}'
|
||||||
|
return self._status
|
||||||
|
|
||||||
|
|
||||||
class SeaWritable(SeaModule, Writable):
|
class SeaWritable(SeaReadable, Writable):
|
||||||
def read_value(self):
|
def read_value(self):
|
||||||
return self.target
|
return self.target
|
||||||
|
|
||||||
@ -693,20 +692,13 @@ class SeaWritable(SeaModule, Writable):
|
|||||||
self.value = value
|
self.value = value
|
||||||
|
|
||||||
|
|
||||||
class SeaDrivable(SeaModule, Drivable):
|
class SeaDrivable(SeaReadable, Drivable):
|
||||||
_sea_status = ''
|
|
||||||
_is_running = 0
|
_is_running = 0
|
||||||
|
|
||||||
def earlyInit(self):
|
def earlyInit(self):
|
||||||
super().earlyInit()
|
super().earlyInit()
|
||||||
self._run_event = threading.Event()
|
self._run_event = threading.Event()
|
||||||
|
|
||||||
def read_status(self):
|
|
||||||
return self.status
|
|
||||||
|
|
||||||
# def read_target(self):
|
|
||||||
# return self.target
|
|
||||||
|
|
||||||
def write_target(self, value):
|
def write_target(self, value):
|
||||||
self._run_event.clear()
|
self._run_event.clear()
|
||||||
self.io.query(f'run {self.sea_object} {value}')
|
self.io.query(f'run {self.sea_object} {value}')
|
||||||
@ -714,25 +706,20 @@ class SeaDrivable(SeaModule, Drivable):
|
|||||||
self.log.warn('target changed but is_running stays 0')
|
self.log.warn('target changed but is_running stays 0')
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def update_status(self, value, timestamp, readerror):
|
|
||||||
if not readerror:
|
|
||||||
self._sea_status = value
|
|
||||||
self.updateStatus()
|
|
||||||
|
|
||||||
def update_is_running(self, value, timestamp, readerror):
|
def update_is_running(self, value, timestamp, readerror):
|
||||||
if not readerror:
|
if not readerror:
|
||||||
self._is_running = value
|
self._is_running = value
|
||||||
self.updateStatus()
|
self.read_status()
|
||||||
if value:
|
if value:
|
||||||
self._run_event.set()
|
self._run_event.set()
|
||||||
|
|
||||||
def updateStatus(self):
|
def read_status(self):
|
||||||
if self._sea_status:
|
status = super().read_status()
|
||||||
self.status = (self.Status.ERROR, self._sea_status)
|
if self._is_running:
|
||||||
elif self._is_running:
|
if status[0] >= ERROR:
|
||||||
self.status = (self.Status.BUSY, 'driving')
|
return ERROR, 'BUSY + ' + status[1]
|
||||||
else:
|
return BUSY, 'driving'
|
||||||
self.status = (self.Status.IDLE, '')
|
return status
|
||||||
|
|
||||||
def updateTarget(self, module, parameter, value, timestamp, readerror):
|
def updateTarget(self, module, parameter, value, timestamp, readerror):
|
||||||
if value is not None:
|
if value is not None:
|
||||||
|
@ -27,7 +27,7 @@ import numpy as np
|
|||||||
from scipy.interpolate import splev, splrep # pylint: disable=import-error
|
from scipy.interpolate import splev, splrep # pylint: disable=import-error
|
||||||
|
|
||||||
from frappy.core import Attached, BoolType, Parameter, Readable, StringType, \
|
from frappy.core import Attached, BoolType, Parameter, Readable, StringType, \
|
||||||
FloatRange
|
FloatRange, nopoll
|
||||||
|
|
||||||
|
|
||||||
def linear(x):
|
def linear(x):
|
||||||
@ -195,35 +195,40 @@ class Sensor(Readable):
|
|||||||
if self.description == '_':
|
if self.description == '_':
|
||||||
self.description = f'{self.rawsensor!r} calibrated with curve {self.calib!r}'
|
self.description = f'{self.rawsensor!r} calibrated with curve {self.calib!r}'
|
||||||
|
|
||||||
def doPoll(self):
|
|
||||||
self.read_status()
|
|
||||||
|
|
||||||
def write_calib(self, value):
|
def write_calib(self, value):
|
||||||
self._calib = CalCurve(value)
|
self._calib = CalCurve(value)
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def update_value(self, value):
|
def _get_value(self, rawvalue):
|
||||||
if self.abs:
|
if self.abs:
|
||||||
value = abs(float(value))
|
rawvalue = abs(float(rawvalue))
|
||||||
self.value = self._calib(value)
|
return self._calib(rawvalue)
|
||||||
self._value_error = None
|
|
||||||
|
|
||||||
def error_update_value(self, err):
|
def _get_status(self, rawstatus):
|
||||||
|
return rawstatus if self._value_error is None else (self.Status.ERROR, self._value_error)
|
||||||
|
|
||||||
|
def update_value(self, rawvalue, err=None):
|
||||||
|
if err:
|
||||||
if self.abs and str(err) == 'R_UNDER': # hack: ignore R_UNDER from ls370
|
if self.abs and str(err) == 'R_UNDER': # hack: ignore R_UNDER from ls370
|
||||||
self._value_error = None
|
self._value_error = None
|
||||||
return None
|
return
|
||||||
self._value_error = repr(err)
|
err = repr(err)
|
||||||
raise err
|
|
||||||
|
|
||||||
def update_status(self, value):
|
|
||||||
if self._value_error is None:
|
|
||||||
self.status = value
|
|
||||||
else:
|
else:
|
||||||
self.status = self.Status.ERROR, self._value_error
|
try:
|
||||||
|
self.value = self._get_value(rawvalue)
|
||||||
|
except Exception as e:
|
||||||
|
err = repr(e)
|
||||||
|
if err != self._value_error:
|
||||||
|
self._value_error = err
|
||||||
|
self.status = self._get_status(self.rawsensor.status)
|
||||||
|
|
||||||
|
def update_status(self, rawstatus):
|
||||||
|
self.status = self._get_status(rawstatus)
|
||||||
|
|
||||||
|
@nopoll
|
||||||
def read_value(self):
|
def read_value(self):
|
||||||
return self._calib(self.rawsensor.read_value())
|
return self._get_value(self.rawsensor.read_value())
|
||||||
|
|
||||||
|
@nopoll
|
||||||
def read_status(self):
|
def read_status(self):
|
||||||
self.update_status(self.rawsensor.status)
|
return self._get_status(self.rawsensor.read_status())
|
||||||
return self.status
|
|
||||||
|
@ -411,6 +411,7 @@ class Uniax(PersistentMixin, Drivable):
|
|||||||
|
|
||||||
@Command()
|
@Command()
|
||||||
def stop(self):
|
def stop(self):
|
||||||
|
"""stop motor and control"""
|
||||||
if self.motor.isBusy():
|
if self.motor.isBusy():
|
||||||
self.log.info('stop motor')
|
self.log.info('stop motor')
|
||||||
self.motor_stop()
|
self.motor_stop()
|
||||||
|
@ -5,6 +5,8 @@ mlzlog >=0.2.0
|
|||||||
# daemonizing
|
# daemonizing
|
||||||
psutil
|
psutil
|
||||||
python-daemon >=2.0
|
python-daemon >=2.0
|
||||||
|
# websocket interface:
|
||||||
|
websockets>=11.0
|
||||||
# for zmq interface
|
# for zmq interface
|
||||||
#pyzmq>=13.1.0
|
#pyzmq>=13.1.0
|
||||||
#for ppms on windows
|
#for ppms on windows
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
#
|
#
|
||||||
# *****************************************************************************
|
# *****************************************************************************
|
||||||
|
|
||||||
|
from frappy.io import HasIO
|
||||||
from frappy.modules import Module, Attached
|
from frappy.modules import Module, Attached
|
||||||
from frappy.protocol.dispatcher import Dispatcher
|
from frappy.protocol.dispatcher import Dispatcher
|
||||||
|
|
||||||
@ -29,6 +30,9 @@ class LoggerStub:
|
|||||||
info = warning = exception = debug
|
info = warning = exception = debug
|
||||||
handlers = []
|
handlers = []
|
||||||
|
|
||||||
|
def getChild(self, name):
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
logger = LoggerStub()
|
logger = LoggerStub()
|
||||||
|
|
||||||
@ -51,6 +55,7 @@ class ServerStub:
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.secnode = SecNodeStub()
|
self.secnode = SecNodeStub()
|
||||||
self.dispatcher = Dispatcher('dispatcher', logger, {}, self)
|
self.dispatcher = Dispatcher('dispatcher', logger, {}, self)
|
||||||
|
self.log = logger
|
||||||
|
|
||||||
|
|
||||||
def test_attach():
|
def test_attach():
|
||||||
@ -64,3 +69,22 @@ def test_attach():
|
|||||||
srv.secnode.add_module(a, 'a')
|
srv.secnode.add_module(a, 'a')
|
||||||
srv.secnode.add_module(m, 'm')
|
srv.secnode.add_module(m, 'm')
|
||||||
assert m.att == a
|
assert m.att == a
|
||||||
|
|
||||||
|
|
||||||
|
def test_attach_hasio_uri():
|
||||||
|
class TestIO(Module):
|
||||||
|
def __init__(self, name, logger, cfgdict, srv):
|
||||||
|
self._uri = cfgdict.pop('uri')
|
||||||
|
super().__init__(name, logger, cfgdict, srv)
|
||||||
|
|
||||||
|
class HasIOTest(HasIO):
|
||||||
|
ioClass = TestIO
|
||||||
|
|
||||||
|
srv = ServerStub()
|
||||||
|
m = HasIOTest('m', logger, {'description': '', 'uri': 'abc'}, srv)
|
||||||
|
assert srv.secnode.modules['m_io']._uri == 'abc'
|
||||||
|
assert m.io == srv.secnode.modules['m_io']
|
||||||
|
# two modules with the same IO should use the same io module
|
||||||
|
m2 = HasIOTest('m', logger, {'description': '', 'uri': 'abc'}, srv)
|
||||||
|
assert m2.io == srv.secnode.modules['m_io']
|
||||||
|
|
||||||
|
155
test/test_callbacks.py
Normal file
155
test/test_callbacks.py
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
# *****************************************************************************
|
||||||
|
#
|
||||||
|
# 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>
|
||||||
|
#
|
||||||
|
# *****************************************************************************
|
||||||
|
"""test parameter callbacks"""
|
||||||
|
|
||||||
|
from test.test_modules import LoggerStub, ServerStub
|
||||||
|
import pytest
|
||||||
|
from frappy.core import Module, Parameter, FloatRange
|
||||||
|
from frappy.errors import WrongTypeError
|
||||||
|
|
||||||
|
|
||||||
|
WRONG_TYPE = WrongTypeError()
|
||||||
|
|
||||||
|
|
||||||
|
class Mod(Module):
|
||||||
|
a = Parameter('', FloatRange())
|
||||||
|
b = Parameter('', FloatRange())
|
||||||
|
c = Parameter('', FloatRange())
|
||||||
|
|
||||||
|
def read_a(self):
|
||||||
|
raise WRONG_TYPE
|
||||||
|
|
||||||
|
def read_b(self):
|
||||||
|
raise WRONG_TYPE
|
||||||
|
|
||||||
|
def read_c(self):
|
||||||
|
raise WRONG_TYPE
|
||||||
|
|
||||||
|
|
||||||
|
class Dbl(Module):
|
||||||
|
a = Parameter('', FloatRange())
|
||||||
|
b = Parameter('', FloatRange())
|
||||||
|
c = Parameter('', FloatRange())
|
||||||
|
_error_a = None
|
||||||
|
_value_b = None
|
||||||
|
_error_c = None
|
||||||
|
|
||||||
|
def update_a(self, value, err=None):
|
||||||
|
# treat error updates
|
||||||
|
try:
|
||||||
|
self.a = value * 2
|
||||||
|
except TypeError: # value is None -> err
|
||||||
|
self.announceUpdate('a', None, err)
|
||||||
|
|
||||||
|
def update_b(self, value):
|
||||||
|
self._value_b = value
|
||||||
|
# error updates are ignored
|
||||||
|
self.b = value * 2
|
||||||
|
|
||||||
|
|
||||||
|
def make(cls):
|
||||||
|
logger = LoggerStub()
|
||||||
|
srv = ServerStub({})
|
||||||
|
return cls('mod1', logger, {'description': ''}, srv)
|
||||||
|
|
||||||
|
|
||||||
|
def test_simple_callback():
|
||||||
|
mod1 = make(Mod)
|
||||||
|
result = []
|
||||||
|
|
||||||
|
def cbfunc(arg1, arg2, value):
|
||||||
|
result[:] = arg1, arg2, value
|
||||||
|
|
||||||
|
mod1.addCallback('a', cbfunc, 'ARG1', 'arg2')
|
||||||
|
|
||||||
|
mod1.a = 1.5
|
||||||
|
assert result == ['ARG1', 'arg2', 1.5]
|
||||||
|
|
||||||
|
result.clear()
|
||||||
|
|
||||||
|
with pytest.raises(WrongTypeError):
|
||||||
|
mod1.read_a()
|
||||||
|
|
||||||
|
assert not result # callback function is NOT called
|
||||||
|
|
||||||
|
|
||||||
|
def test_combi_callback():
|
||||||
|
mod1 = make(Mod)
|
||||||
|
result = []
|
||||||
|
|
||||||
|
def cbfunc(arg1, arg2, value, err=None):
|
||||||
|
result[:] = arg1, arg2, value, err
|
||||||
|
|
||||||
|
mod1.addCallback('a', cbfunc, 'ARG1', 'arg2')
|
||||||
|
|
||||||
|
mod1.a = 1.5
|
||||||
|
assert result == ['ARG1', 'arg2', 1.5, None]
|
||||||
|
|
||||||
|
result.clear()
|
||||||
|
|
||||||
|
with pytest.raises(WrongTypeError):
|
||||||
|
mod1.read_a()
|
||||||
|
|
||||||
|
assert result[:3] == ['ARG1', 'arg2', None] # callback function called with value None
|
||||||
|
assert isinstance(result[3], WrongTypeError)
|
||||||
|
|
||||||
|
|
||||||
|
def test_autoupdate():
|
||||||
|
mod1 = make(Mod)
|
||||||
|
mod2 = make(Dbl)
|
||||||
|
mod1.registerCallbacks(mod2, autoupdate=['c'])
|
||||||
|
|
||||||
|
result = {}
|
||||||
|
|
||||||
|
def cbfunc(pname, *args):
|
||||||
|
result[pname] = args
|
||||||
|
|
||||||
|
for param in 'a', 'b', 'c':
|
||||||
|
mod2.addCallback(param, cbfunc, param)
|
||||||
|
|
||||||
|
# test update_a without error
|
||||||
|
mod1.a = 5
|
||||||
|
assert mod2.a == 10
|
||||||
|
assert result.pop('a') == (10,)
|
||||||
|
|
||||||
|
# test update_a with error
|
||||||
|
with pytest.raises(WrongTypeError):
|
||||||
|
mod1.read_a()
|
||||||
|
|
||||||
|
assert result.pop('a') == (None, WRONG_TYPE)
|
||||||
|
|
||||||
|
# test that update_b is ignored in case of error
|
||||||
|
mod1.b = 3
|
||||||
|
assert mod2.b == 6 # no error
|
||||||
|
assert result.pop('b') == (6,)
|
||||||
|
|
||||||
|
with pytest.raises(WrongTypeError):
|
||||||
|
mod1.read_b()
|
||||||
|
assert 'b' not in result
|
||||||
|
|
||||||
|
# test autoupdate
|
||||||
|
mod1.c = 3
|
||||||
|
assert mod2.c == 3
|
||||||
|
assert result['c'] == (3,)
|
||||||
|
|
||||||
|
with pytest.raises(WrongTypeError):
|
||||||
|
mod1.read_c()
|
||||||
|
assert result['c'] == (None, WRONG_TYPE)
|
@ -18,12 +18,14 @@
|
|||||||
# Markus Zolliker <markus.zolliker@psi.ch>
|
# Markus Zolliker <markus.zolliker@psi.ch>
|
||||||
#
|
#
|
||||||
# *****************************************************************************
|
# *****************************************************************************
|
||||||
"""test frappy.mixins.HasCtrlPars"""
|
"""test frappy.extparams"""
|
||||||
|
|
||||||
|
|
||||||
from test.test_modules import LoggerStub, ServerStub
|
from test.test_modules import LoggerStub, ServerStub
|
||||||
|
import pytest
|
||||||
from frappy.core import FloatRange, Module, Parameter
|
from frappy.core import FloatRange, Module, Parameter
|
||||||
from frappy.structparam import StructParam
|
from frappy.extparams import StructParam, FloatEnumParam
|
||||||
|
from frappy.errors import ProgrammingError
|
||||||
|
|
||||||
|
|
||||||
def test_with_read_ctrlpars():
|
def test_with_read_ctrlpars():
|
||||||
@ -130,3 +132,76 @@ def test_order_dependence1():
|
|||||||
def test_order_dependence2():
|
def test_order_dependence2():
|
||||||
test_with_read_ctrlpars()
|
test_with_read_ctrlpars()
|
||||||
test_without_read_ctrlpars()
|
test_without_read_ctrlpars()
|
||||||
|
|
||||||
|
|
||||||
|
def test_float_enum():
|
||||||
|
class Mod(Module):
|
||||||
|
vrange = FloatEnumParam('voltage range', [
|
||||||
|
(1, '50uV'), '200 µV', '1mV', ('5mV', 0.006), (9, 'max', 0.024)], 'V')
|
||||||
|
gain = FloatEnumParam('gain factor', ('1', '2', '4', '8'), idx_name='igain')
|
||||||
|
dist = FloatEnumParam('distance', ('1m', '1mm', '1µm'), unit='m')
|
||||||
|
|
||||||
|
_vrange_idx = None
|
||||||
|
|
||||||
|
def write_vrange_idx(self, value):
|
||||||
|
self._vrange_idx = value
|
||||||
|
|
||||||
|
logger = LoggerStub()
|
||||||
|
updates = {}
|
||||||
|
srv = ServerStub(updates)
|
||||||
|
|
||||||
|
m = Mod('m', logger, {'description': ''}, srv)
|
||||||
|
|
||||||
|
assert m.write_vrange_idx(1) == 1
|
||||||
|
assert m._vrange_idx == '50uV'
|
||||||
|
assert m._vrange_idx == 1
|
||||||
|
assert m.vrange == 5e-5
|
||||||
|
|
||||||
|
assert m.write_vrange_idx(2) == 2
|
||||||
|
assert m._vrange_idx == '200 µV'
|
||||||
|
assert m._vrange_idx == 2
|
||||||
|
assert m.vrange == 2e-4
|
||||||
|
|
||||||
|
assert m.write_vrange(6e-5) == 5e-5 # round to the next value
|
||||||
|
assert m._vrange_idx == '50uV'
|
||||||
|
assert m._vrange_idx == 1
|
||||||
|
assert m.write_vrange(20e-3) == 24e-3 # round to the next value
|
||||||
|
assert m._vrange_idx == 'max'
|
||||||
|
assert m._vrange_idx == 9
|
||||||
|
|
||||||
|
for idx in range(4):
|
||||||
|
value = 2 ** idx
|
||||||
|
updates.clear()
|
||||||
|
assert m.write_igain(idx) == idx
|
||||||
|
assert updates == {'m': {'igain': idx, 'gain': value}}
|
||||||
|
assert m.igain == idx
|
||||||
|
assert m.igain == str(value)
|
||||||
|
assert m.gain == value
|
||||||
|
|
||||||
|
for idx in range(4):
|
||||||
|
value = 2 ** idx
|
||||||
|
assert m.write_gain(value) == value
|
||||||
|
assert m.igain == idx
|
||||||
|
assert m.igain == str(value)
|
||||||
|
|
||||||
|
for idx in range(3):
|
||||||
|
value = 10 ** (-3 * idx)
|
||||||
|
assert m.write_dist(value) == value
|
||||||
|
assert m.dist_idx == idx
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('labels, unit, error', [
|
||||||
|
(FloatRange(), '', 'not a datatype'), # 2nd arg must not be a datatype
|
||||||
|
([(1, 2, 3)], '', 'must be strings'), # label is not a string
|
||||||
|
([(1, '1V', 3, 4)], 'V', 'labels or tuples'), # 4-tuple
|
||||||
|
([('1A', 3, 4)], 'A', 'labels or tuples'), # two values after label
|
||||||
|
(('1m', (0, '1k')), '', 'conflicts with'), # two times index 0
|
||||||
|
(['1mV', '10mA'], 'V', 'not the form'), # wrong unit
|
||||||
|
(['.mV'], 'V', 'not the form'), # bad number
|
||||||
|
(['mV'], 'V', 'not the form'), # missing number
|
||||||
|
(['1+mV'], 'V', 'not the form'), # bad number
|
||||||
|
])
|
||||||
|
def test_bad_float_enum(labels, unit, error):
|
||||||
|
with pytest.raises(ProgrammingError, match=error):
|
||||||
|
class Mod(Module): # pylint:disable=unused-variable
|
||||||
|
param = FloatEnumParam('', labels, unit)
|
@ -23,6 +23,8 @@
|
|||||||
|
|
||||||
import sys
|
import sys
|
||||||
import threading
|
import threading
|
||||||
|
import importlib
|
||||||
|
from glob import glob
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from frappy.datatypes import BoolType, FloatRange, StringType, IntRange, ScaledInteger
|
from frappy.datatypes import BoolType, FloatRange, StringType, IntRange, ScaledInteger
|
||||||
@ -440,12 +442,12 @@ def test_override():
|
|||||||
assert Mod.value.value == 5
|
assert Mod.value.value == 5
|
||||||
assert Mod.stop.description == "no decorator needed"
|
assert Mod.stop.description == "no decorator needed"
|
||||||
|
|
||||||
class Mod2(Drivable):
|
class Mod2(Mod):
|
||||||
@Command()
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
assert Mod2.stop.description == Drivable.stop.description
|
# inherit doc string
|
||||||
|
assert Mod2.stop.description == Mod.stop.description
|
||||||
|
|
||||||
|
|
||||||
def test_command_config():
|
def test_command_config():
|
||||||
@ -920,3 +922,24 @@ def test_interface_classes(bases, iface_classes):
|
|||||||
pass
|
pass
|
||||||
m = Mod('mod', LoggerStub(), {'description': 'test'}, srv)
|
m = Mod('mod', LoggerStub(), {'description': 'test'}, srv)
|
||||||
assert m.interface_classes == iface_classes
|
assert m.interface_classes == iface_classes
|
||||||
|
|
||||||
|
|
||||||
|
all_drivables = set()
|
||||||
|
for pyfile in glob('frappy_*/*.py'):
|
||||||
|
module = pyfile[:-3].replace('/', '.')
|
||||||
|
try:
|
||||||
|
importlib.import_module(module)
|
||||||
|
except Exception as e:
|
||||||
|
print(module, e)
|
||||||
|
continue
|
||||||
|
for obj_ in sys.modules[module].__dict__.values():
|
||||||
|
if isinstance(obj_, type) and issubclass(obj_, Drivable):
|
||||||
|
all_drivables.add(obj_)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('modcls', all_drivables)
|
||||||
|
def test_stop_doc(modcls):
|
||||||
|
# make sure that implemented stop methods have a doc string
|
||||||
|
if (modcls.stop.description == Drivable.stop.description
|
||||||
|
and modcls.stop.func != Drivable.stop.func):
|
||||||
|
assert modcls.stop.func.__doc__ # stop method needs a doc string
|
||||||
|
Reference in New Issue
Block a user