Merge branch 'wip' of gitlab.psi.ch:samenv/frappy into wip

This commit is contained in:
l_samenv 2022-12-19 16:09:40 +01:00
commit 0820d537c3
15 changed files with 959 additions and 217 deletions

52
cfg/main/flamp.cfg Normal file
View File

@ -0,0 +1,52 @@
[NODE]
description = lamp oven control (from manuel knecht)
id = flamp.config.sea.psi.ch
[sea_main]
class = secop_psi.sea.SeaClient
description = main sea connection for flamp.config
config = flamp.config
service = main
[tt]
class = secop_psi.sea.SeaDrivable
io = sea_main
sea_object = tt
rel_paths = . t1
[t2]
class = secop_psi.sea.SeaReadable
io = sea_main
sea_object = tt
rel_paths = t2
[current]
class = secop_psi.sea.SeaReadable
io = sea_main
sea_object = current
extra_modules = i1, i2, i3, i4
[i1]
class = secop_psi.sea.SeaReadable
io = sea_main
single_module = current.i1
[i2]
class = secop_psi.sea.SeaReadable
io = sea_main
single_module = current.i2
[i3]
class = secop_psi.sea.SeaReadable
io = sea_main
single_module = current.i3
[i4]
class = secop_psi.sea.SeaReadable
io = sea_main
single_module = current.i4
[pv]
class = secop_psi.sea.SeaReadable
io = sea_main
sea_object = pv

21
cfg/main/fs.cfg Normal file
View File

@ -0,0 +1,21 @@
[NODE]
description = small furnace
id = fs.config.sea.psi.ch
[sea_main]
class = secop_psi.sea.SeaClient
description = main sea connection for fs.config
config = fs.config
service = main
[tt]
class = secop_psi.sea.SeaDrivable
io = sea_main
sea_object = tt
rel_paths = . tm
[ts]
class = secop_psi.sea.SeaDrivable
io = sea_main
sea_object = tt
rel_paths = ts

22
cfg/main/ft.cfg Normal file
View File

@ -0,0 +1,22 @@
[NODE]
description = FT tantalum furnace (1400 K)
id = ft.config.sea.psi.ch
[sea_main]
class = secop_psi.sea.SeaClient
description = main sea connection for fw.config
config = ft.config
service = main
[ts]
class = secop_psi.sea.SeaDrivable
io = sea_main
sea_object = tt
rel_paths = . ts
[t2]
class = secop_psi.sea.SeaReadable
io = sea_main
sea_object = tt
rel_paths = tm

View File

@ -68,5 +68,5 @@ description = stick rotation, typically used for omega
class = secop_psi.phytron.Motor class = secop_psi.phytron.Motor
io = om_io io = om_io
sign = -1 sign = -1
encoder_mode = CHECK encoder_mode = READ

80
cfg/sea/flamp.config.json Normal file
View File

@ -0,0 +1,80 @@
{"tt": {"base": "/tt", "params": [
{"path": "", "type": "float", "readonly": false, "cmd": "run tt", "description": "tt", "kids": 40},
{"path": "send", "type": "text", "readonly": false, "cmd": "tt send", "visibility": 3},
{"path": "status", "type": "text", "readonly": false, "cmd": "run tt", "visibility": 3},
{"path": "is_running", "type": "int", "readonly": false, "cmd": "tt is_running", "visibility": 3},
{"path": "set", "type": "float", "readonly": false, "cmd": "tt set"},
{"path": "target", "type": "float", "readonly": false, "cmd": "run tt"},
{"path": "running", "type": "int", "readonly": false, "cmd": "run tt"},
{"path": "tolerance", "type": "float", "readonly": false, "cmd": "tt tolerance"},
{"path": "maxwait", "type": "float", "readonly": false, "cmd": "tt maxwait"},
{"path": "settle", "type": "float", "readonly": false, "cmd": "tt settle"},
{"path": "log", "type": "text", "readonly": false, "cmd": "tt log", "visibility": 3, "kids": 4},
{"path": "log/mean", "type": "float", "readonly": false, "cmd": "run tt", "visibility": 3},
{"path": "log/m2", "type": "float", "readonly": false, "cmd": "run tt", "visibility": 3},
{"path": "log/stddev", "type": "float", "readonly": false, "cmd": "run tt", "visibility": 3},
{"path": "log/n", "type": "float", "readonly": false, "cmd": "run tt", "visibility": 3},
{"path": "limit", "type": "float", "readonly": false, "cmd": "tt limit"},
{"path": "t1", "type": "float", "readonly": false, "cmd": "run tt", "kids": 3},
{"path": "t1/raw", "type": "float", "readonly": false, "cmd": "run tt"},
{"path": "t1/curve", "type": "text", "readonly": false, "cmd": "tt t1/curve", "kids": 1},
{"path": "t1/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt t1/curve/points"},
{"path": "t1/valid", "type": "bool", "readonly": false, "cmd": "run tt"},
{"path": "t2", "type": "float", "readonly": false, "cmd": "run tt", "kids": 3},
{"path": "t2/raw", "type": "float", "readonly": false, "cmd": "run tt"},
{"path": "t2/curve", "type": "text", "readonly": false, "cmd": "tt t2/curve", "kids": 1},
{"path": "t2/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt t2/curve/points"},
{"path": "t2/valid", "type": "bool", "readonly": false, "cmd": "run tt"},
{"path": "t3", "type": "float", "readonly": false, "cmd": "run tt", "kids": 3},
{"path": "t3/raw", "type": "float", "readonly": false, "cmd": "run tt"},
{"path": "t3/curve", "type": "text", "readonly": false, "cmd": "tt t3/curve", "kids": 1},
{"path": "t3/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt t3/curve/points"},
{"path": "t3/valid", "type": "bool", "readonly": false, "cmd": "run tt"},
{"path": "t4", "type": "float", "readonly": false, "cmd": "run tt", "kids": 3},
{"path": "t4/raw", "type": "float", "readonly": false, "cmd": "run tt"},
{"path": "t4/curve", "type": "text", "readonly": false, "cmd": "tt t4/curve", "kids": 1},
{"path": "t4/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt t4/curve/points"},
{"path": "t4/valid", "type": "bool", "readonly": false, "cmd": "run tt"},
{"path": "tref", "type": "float", "readonly": false, "cmd": "run tt"},
{"path": "tout", "type": "float", "readonly": false, "cmd": "run tt"},
{"path": "toutmax", "type": "float", "readonly": false, "cmd": "tt toutmax"},
{"path": "toutmin", "type": "float", "readonly": false, "cmd": "tt toutmin"},
{"path": "ctrlmode", "type": "enum", "enum": {"ok": 0, "off": 1, "illegal_channel": 2, "no_sensor": 3, "no_waterflow": 4, "bad_vacuum": 5, "tc_overflow": 6, "wall_T_overflow": 7}, "readonly": false, "cmd": "tt ctrlmode"},
{"path": "ramp", "type": "float", "readonly": false, "cmd": "tt ramp"},
{"path": "smooth", "type": "float", "readonly": false, "cmd": "tt smooth"},
{"path": "prop", "type": "float", "readonly": false, "cmd": "tt prop", "description": "proportional gain for T slope control"},
{"path": "int", "type": "float", "readonly": false, "cmd": "tt int", "description": "time constant for T slope control"},
{"path": "powerset", "type": "float", "readonly": false, "cmd": "tt powerset"},
{"path": "power", "type": "float", "readonly": false, "cmd": "run tt"},
{"path": "resist", "type": "float", "readonly": false, "cmd": "run tt"},
{"path": "maxpower", "type": "float", "readonly": false, "cmd": "tt maxpower"},
{"path": "maxheater", "type": "float", "readonly": false, "cmd": "tt maxheater"},
{"path": "output", "type": "float", "readonly": false, "cmd": "tt output"},
{"path": "manualpower", "type": "bool", "readonly": false, "cmd": "tt manualpower"},
{"path": "ctrlchan", "type": "int", "readonly": false, "cmd": "tt ctrlchan"},
{"path": "interlock_state", "type": "enum", "enum": {"ok": 0, "no_waterflow": 1, "bad_vacuum": 2, "no_waterflow_bad_vacuum": 3}, "readonly": false, "cmd": "run tt"},
{"path": "interlock_mask", "type": "enum", "enum": {"no_check": 0, "check_water_only": 1, "check_vacuum_only": 2, "check_all": 3}, "readonly": false, "cmd": "tt interlock_mask"},
{"path": "sramp", "type": "float", "readonly": false, "cmd": "run tt"},
{"path": "slope", "type": "float", "readonly": false, "cmd": "run tt"},
{"path": "v_htr", "type": "float", "readonly": false, "cmd": "run tt"},
{"path": "i_htr", "type": "float", "readonly": false, "cmd": "run tt"},
{"path": "htr", "type": "float", "readonly": false, "cmd": "run tt"},
{"path": "powerprop", "type": "float", "readonly": false, "cmd": "run tt"}]},
"current": {"base": "/current", "params": [
{"path": "", "type": "float", "kids": 7},
{"path": "send", "type": "text", "readonly": false, "cmd": "current send", "visibility": 3},
{"path": "status", "type": "text", "visibility": 3},
{"path": "i1", "type": "float"},
{"path": "i2", "type": "float"},
{"path": "i3", "type": "float"},
{"path": "i4", "type": "float"},
{"path": "ib", "type": "float"}]},
"pv": {"base": "/pv", "params": [
{"path": "", "type": "float", "kids": 5},
{"path": "send", "type": "text", "readonly": false, "cmd": "pv send", "visibility": 3},
{"path": "status", "type": "text", "visibility": 3},
{"path": "sp1", "type": "text"},
{"path": "sp2", "type": "text"},
{"path": "sps", "type": "text"}]}}

160
cfg/sea/fs.config.json Normal file
View File

@ -0,0 +1,160 @@
{"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": "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": "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": "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": "dout", "type": "int", "readonly": false, "cmd": "tt dout"},
{"path": "dinp", "type": "int"},
{"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", "visibility": 3},
{"path": "fs", "type": "enum", "enum": {"ok": 0, "no_sens": 1}, "readonly": false, "cmd": "cc fs", "visibility": 3},
{"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}, "visibility": 3},
{"path": "fa", "type": "enum", "enum": {"fixed": 0, "controlled": 1, "automatic": 2, "offline": 3}, "readonly": false, "cmd": "cc fa", "visibility": 3},
{"path": "mp", "type": "float", "readonly": false, "cmd": "cc mp", "visibility": 3},
{"path": "msp", "type": "float", "visibility": 3},
{"path": "mmp", "type": "float", "visibility": 3},
{"path": "mc", "type": "float", "readonly": false, "cmd": "cc mc", "visibility": 3},
{"path": "mfc", "type": "float", "readonly": false, "cmd": "cc mfc", "visibility": 3},
{"path": "moc", "type": "float", "readonly": false, "cmd": "cc moc", "visibility": 3},
{"path": "mtc", "type": "float", "readonly": false, "cmd": "cc mtc", "visibility": 3},
{"path": "mtl", "type": "float", "visibility": 3},
{"path": "mft", "type": "float", "readonly": false, "cmd": "cc mft", "visibility": 3},
{"path": "mt", "type": "float", "visibility": 3},
{"path": "mo", "type": "float", "visibility": 3},
{"path": "mcr", "type": "float", "visibility": 3},
{"path": "mot", "type": "float", "visibility": 3},
{"path": "mw", "type": "float", "readonly": false, "cmd": "cc mw", "description": "correction pulse after automatic open", "visibility": 3},
{"path": "hav", "type": "bool", "readonly": false, "cmd": "cc hav"},
{"path": "h", "type": "float", "visibility": 3},
{"path": "hr", "type": "float", "visibility": 3},
{"path": "hc", "type": "float", "visibility": 3},
{"path": "hu", "type": "float", "visibility": 3},
{"path": "hh", "type": "float", "readonly": false, "cmd": "cc hh", "visibility": 3},
{"path": "hl", "type": "float", "readonly": false, "cmd": "cc hl", "visibility": 3},
{"path": "htf", "type": "float", "readonly": false, "cmd": "cc htf", "description": "meas. period in fast mode", "visibility": 3},
{"path": "hts", "type": "float", "readonly": false, "cmd": "cc hts", "description": "meas. period in slow mode", "visibility": 3},
{"path": "hd", "type": "float", "readonly": false, "cmd": "cc hd", "visibility": 3},
{"path": "hwr", "type": "float", "readonly": false, "cmd": "cc hwr", "visibility": 3},
{"path": "hem", "type": "float", "readonly": false, "cmd": "cc hem", "description": "sensor length in mm from top to empty pos.", "visibility": 3},
{"path": "hfu", "type": "float", "readonly": false, "cmd": "cc hfu", "description": "sensor length in mm from top to full pos.", "visibility": 3},
{"path": "hcd", "type": "enum", "enum": {"stop": 0, "fill": 1, "off": 2, "auto": 3, "manual": 7}, "readonly": false, "cmd": "cc hcd", "visibility": 3},
{"path": "hv", "type": "enum", "enum": {"fill_valve_off": 0, "filling": 1, "no_fill_valve": 2, "timeout": 3, "timeout1": 4}, "visibility": 3},
{"path": "hsf", "type": "enum", "enum": {"sens_ok": 0, "sens_warm": 1, "no_sens": 2, "timeout": 3, "not_yet_read": 4, "disabled": 5}, "visibility": 3},
{"path": "ha", "type": "bool", "readonly": false, "cmd": "cc ha", "visibility": 3},
{"path": "hm", "type": "bool", "visibility": 3},
{"path": "hf", "type": "enum", "enum": {"slow": 0, "fast": 1}, "readonly": false, "cmd": "cc hf", "visibility": 3},
{"path": "hbe", "type": "bool", "readonly": false, "cmd": "cc hbe", "visibility": 3},
{"path": "hmf", "type": "float", "visibility": 3},
{"path": "hms", "type": "float", "visibility": 3},
{"path": "hit", "type": "float", "readonly": false, "cmd": "cc hit", "visibility": 3},
{"path": "hft", "type": "int", "readonly": false, "cmd": "cc hft", "visibility": 3},
{"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", "visibility": 3},
{"path": "nav", "type": "bool", "readonly": false, "cmd": "cc nav"},
{"path": "nu", "type": "float", "visibility": 3},
{"path": "nl", "type": "float", "visibility": 3},
{"path": "nth", "type": "float", "readonly": false, "cmd": "cc nth", "visibility": 3},
{"path": "ntc", "type": "float", "readonly": false, "cmd": "cc ntc", "visibility": 3},
{"path": "ntm", "type": "float", "readonly": false, "cmd": "cc ntm", "visibility": 3},
{"path": "ns", "type": "enum", "enum": {"sens_ok": 0, "no_sens": 1, "short_circuit": 2, "upside_down": 3, "sens_warm": 4, "empty": 5}, "visibility": 3},
{"path": "na", "type": "bool", "readonly": false, "cmd": "cc na", "visibility": 3},
{"path": "nv", "type": "enum", "enum": {"fill_valve_off": 0, "filling": 1, "no_fill_valve": 2, "timeout": 3, "timeout1": 4, "boost": 5}, "visibility": 3},
{"path": "nc", "type": "enum", "enum": {"stop": 0, "fill": 1, "off": 2, "auto": 3}, "readonly": false, "cmd": "cc nc", "visibility": 3},
{"path": "nfb", "type": "float", "visibility": 3},
{"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"}]}}

183
cfg/sea/ft.config.json Normal file
View File

@ -0,0 +1,183 @@
{"tt": {"base": "/tt", "params": [
{"path": "", "type": "float", "readonly": false, "cmd": "run tt", "description": "tt", "kids": 20},
{"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", "visibility": 3, "kids": 9},
{"path": "dblctrl/tshift", "type": "float", "readonly": false, "cmd": "tt dblctrl/tshift", "visibility": 3},
{"path": "dblctrl/mode", "type": "enum", "enum": {"disabled": -1, "inactive": 0, "stable": 1, "up": 2, "down": 3}, "readonly": false, "cmd": "tt dblctrl/mode", "visibility": 3},
{"path": "dblctrl/shift_up", "type": "float", "visibility": 3},
{"path": "dblctrl/shift_lo", "type": "float", "visibility": 3},
{"path": "dblctrl/t_min", "type": "float", "visibility": 3},
{"path": "dblctrl/t_max", "type": "float", "visibility": 3},
{"path": "dblctrl/int2", "type": "float", "readonly": false, "cmd": "tt dblctrl/int2", "visibility": 3},
{"path": "dblctrl/prop_up", "type": "float", "readonly": false, "cmd": "tt dblctrl/prop_up", "visibility": 3},
{"path": "dblctrl/prop_lo", "type": "float", "readonly": false, "cmd": "tt dblctrl/prop_lo", "visibility": 3},
{"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": "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": "te", "type": "float", "kids": 4},
{"path": "te/curve", "type": "text", "readonly": false, "cmd": "tt te/curve", "kids": 1},
{"path": "te/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt te/curve/points", "visibility": 3},
{"path": "te/alarm", "type": "float", "readonly": false, "cmd": "tt te/alarm"},
{"path": "te/stddev", "type": "float"},
{"path": "te/raw", "type": "float"},
{"path": "p", "type": "float", "kids": 4},
{"path": "p/curve", "type": "text", "readonly": false, "cmd": "tt p/curve", "kids": 1},
{"path": "p/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt p/curve/points", "visibility": 3},
{"path": "p/alarm", "type": "float", "readonly": false, "cmd": "tt p/alarm"},
{"path": "p/stddev", "type": "float"},
{"path": "p/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": "display", "type": "text", "readonly": false, "cmd": "tt display"},
{"path": "dout", "type": "int", "readonly": false, "cmd": "tt dout"},
{"path": "dinp", "type": "int"},
{"path": "remote", "type": "bool"}]},
"table": {"base": "/table", "params": [
{"path": "", "type": "none", "kids": 8},
{"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 ..."}]},
"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", "visibility": 3},
{"path": "fs", "type": "enum", "enum": {"ok": 0, "no_sens": 1}, "readonly": false, "cmd": "cc fs", "visibility": 3},
{"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}, "visibility": 3},
{"path": "fa", "type": "enum", "enum": {"fixed": 0, "controlled": 1, "automatic": 2, "offline": 3}, "readonly": false, "cmd": "cc fa", "visibility": 3},
{"path": "mp", "type": "float", "readonly": false, "cmd": "cc mp", "visibility": 3},
{"path": "msp", "type": "float", "visibility": 3},
{"path": "mmp", "type": "float", "visibility": 3},
{"path": "mc", "type": "float", "readonly": false, "cmd": "cc mc", "visibility": 3},
{"path": "mfc", "type": "float", "readonly": false, "cmd": "cc mfc", "visibility": 3},
{"path": "moc", "type": "float", "readonly": false, "cmd": "cc moc", "visibility": 3},
{"path": "mtc", "type": "float", "readonly": false, "cmd": "cc mtc", "visibility": 3},
{"path": "mtl", "type": "float", "visibility": 3},
{"path": "mft", "type": "float", "readonly": false, "cmd": "cc mft", "visibility": 3},
{"path": "mt", "type": "float", "visibility": 3},
{"path": "mo", "type": "float", "visibility": 3},
{"path": "mcr", "type": "float", "visibility": 3},
{"path": "mot", "type": "float", "visibility": 3},
{"path": "mw", "type": "float", "readonly": false, "cmd": "cc mw", "description": "correction pulse after automatic open", "visibility": 3},
{"path": "hav", "type": "bool", "readonly": false, "cmd": "cc hav"},
{"path": "h", "type": "float", "visibility": 3},
{"path": "hr", "type": "float", "visibility": 3},
{"path": "hc", "type": "float", "visibility": 3},
{"path": "hu", "type": "float", "visibility": 3},
{"path": "hh", "type": "float", "readonly": false, "cmd": "cc hh", "visibility": 3},
{"path": "hl", "type": "float", "readonly": false, "cmd": "cc hl", "visibility": 3},
{"path": "htf", "type": "float", "readonly": false, "cmd": "cc htf", "description": "meas. period in fast mode", "visibility": 3},
{"path": "hts", "type": "float", "readonly": false, "cmd": "cc hts", "description": "meas. period in slow mode", "visibility": 3},
{"path": "hd", "type": "float", "readonly": false, "cmd": "cc hd", "visibility": 3},
{"path": "hwr", "type": "float", "readonly": false, "cmd": "cc hwr", "visibility": 3},
{"path": "hem", "type": "float", "readonly": false, "cmd": "cc hem", "description": "sensor length in mm from top to empty pos.", "visibility": 3},
{"path": "hfu", "type": "float", "readonly": false, "cmd": "cc hfu", "description": "sensor length in mm from top to full pos.", "visibility": 3},
{"path": "hcd", "type": "enum", "enum": {"stop": 0, "fill": 1, "off": 2, "auto": 3, "manual": 7}, "readonly": false, "cmd": "cc hcd", "visibility": 3},
{"path": "hv", "type": "enum", "enum": {"fill_valve_off": 0, "filling": 1, "no_fill_valve": 2, "timeout": 3, "timeout1": 4}, "visibility": 3},
{"path": "hsf", "type": "enum", "enum": {"sens_ok": 0, "sens_warm": 1, "no_sens": 2, "timeout": 3, "not_yet_read": 4, "disabled": 5}, "visibility": 3},
{"path": "ha", "type": "bool", "readonly": false, "cmd": "cc ha", "visibility": 3},
{"path": "hm", "type": "bool", "visibility": 3},
{"path": "hf", "type": "enum", "enum": {"slow": 0, "fast": 1}, "readonly": false, "cmd": "cc hf", "visibility": 3},
{"path": "hbe", "type": "bool", "readonly": false, "cmd": "cc hbe", "visibility": 3},
{"path": "hmf", "type": "float", "visibility": 3},
{"path": "hms", "type": "float", "visibility": 3},
{"path": "hit", "type": "float", "readonly": false, "cmd": "cc hit", "visibility": 3},
{"path": "hft", "type": "int", "readonly": false, "cmd": "cc hft", "visibility": 3},
{"path": "hea", "type": "enum", "enum": {"0": 0, "1": 1, "6": 2}, "readonly": false, "cmd": "cc hea"},
{"path": "hch", "type": "int", "readonly": false, "cmd": "cc hch", "visibility": 3},
{"path": "hwr0", "type": "float", "readonly": false, "cmd": "cc hwr0", "visibility": 3},
{"path": "hem0", "type": "float", "readonly": false, "cmd": "cc hem0", "description": "sensor length in mm from top to empty pos.", "visibility": 3},
{"path": "hfu0", "type": "float", "readonly": false, "cmd": "cc hfu0", "description": "sensor length in mm from top to full pos.", "visibility": 3},
{"path": "hd0", "type": "float", "readonly": false, "cmd": "cc hd0", "description": "external sensor drive current (mA)", "visibility": 3},
{"path": "h0", "type": "float", "visibility": 3},
{"path": "hs0", "type": "enum", "enum": {"sens_ok": 0, "sens_warm": 1, "no_sens": 2, "timeout": 3, "not_yet_read": 4, "disabled": 5}, "visibility": 3},
{"path": "h1", "type": "float", "visibility": 3},
{"path": "hs1", "type": "enum", "enum": {"sens_ok": 0, "sens_warm": 1, "no_sens": 2, "timeout": 3, "not_yet_read": 4, "disabled": 5}, "visibility": 3},
{"path": "h2", "type": "float", "visibility": 3},
{"path": "hs2", "type": "enum", "enum": {"sens_ok": 0, "sens_warm": 1, "no_sens": 2, "timeout": 3, "not_yet_read": 4, "disabled": 5}, "visibility": 3},
{"path": "h3", "type": "float", "visibility": 3},
{"path": "hs3", "type": "enum", "enum": {"sens_ok": 0, "sens_warm": 1, "no_sens": 2, "timeout": 3, "not_yet_read": 4, "disabled": 5}, "visibility": 3},
{"path": "h4", "type": "float", "visibility": 3},
{"path": "hs4", "type": "enum", "enum": {"sens_ok": 0, "sens_warm": 1, "no_sens": 2, "timeout": 3, "not_yet_read": 4, "disabled": 5}, "visibility": 3},
{"path": "h5", "type": "float", "visibility": 3},
{"path": "hs5", "type": "enum", "enum": {"sens_ok": 0, "sens_warm": 1, "no_sens": 2, "timeout": 3, "not_yet_read": 4, "disabled": 5}, "visibility": 3},
{"path": "hfb", "type": "float", "visibility": 3},
{"path": "nav", "type": "bool", "readonly": false, "cmd": "cc nav"},
{"path": "nu", "type": "float", "visibility": 3},
{"path": "nl", "type": "float", "visibility": 3},
{"path": "nth", "type": "float", "readonly": false, "cmd": "cc nth", "visibility": 3},
{"path": "ntc", "type": "float", "readonly": false, "cmd": "cc ntc", "visibility": 3},
{"path": "ntm", "type": "float", "readonly": false, "cmd": "cc ntm", "visibility": 3},
{"path": "ns", "type": "enum", "enum": {"sens_ok": 0, "no_sens": 1, "short_circuit": 2, "upside_down": 3, "sens_warm": 4, "empty": 5}, "visibility": 3},
{"path": "na", "type": "bool", "readonly": false, "cmd": "cc na", "visibility": 3},
{"path": "nv", "type": "enum", "enum": {"fill_valve_off": 0, "filling": 1, "no_fill_valve": 2, "timeout": 3, "timeout1": 4, "boost": 5}, "visibility": 3},
{"path": "nc", "type": "enum", "enum": {"stop": 0, "fill": 1, "off": 2, "auto": 3}, "readonly": false, "cmd": "cc nc", "visibility": 3},
{"path": "nfb", "type": "float", "visibility": 3},
{"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"}]}}

View File

@ -13,7 +13,26 @@ class = secop_psi.sea.SeaDrivable
io = sea_stick io = sea_stick
sea_object = ts sea_object = ts
[th] [T_sorb]
class = secop_psi.sea.SeaDrivable class = secop_psi.sea.SeaReadable
io = sea_stick io = sea_stick
sea_object = th sea_object = th
rel_paths = sorb
[T_plate]
class = secop_psi.sea.SeaReadable
io = sea_stick
sea_object = th
rel_paths = plate
[T_low]
class = secop_psi.sea.SeaReadable
io = sea_stick
sea_object = th
rel_paths = low
[T_pot]
class = secop_psi.sea.SeaReadable
io = sea_stick
sea_object = th
rel_paths = pot

View File

@ -31,7 +31,32 @@ from secop.errors import BadValueError, ConfigError
from secop.lib import clamp from secop.lib import clamp
# --- proposals, to be used at SINQ (not agreed as standard yet) --- class HasSimpleOffset(Feature):
"""has a client side offset parameter
this is just a storage!
"""
offset = PersistentParam('offset (physical value + offset = HW value)',
FloatRange(unit='deg'), readonly=False, default=0)
class HasTargetLimits(Feature):
"""user limits
implementation to be done in the subclass
according to standard
"""
target_limits = PersistentParam('user limits', readonly=False, default=(-9e99, 9e99),
datatype=TupleOf(FloatRange(unit='deg'), FloatRange(unit='deg')))
def check_limits(self, value):
"""check if value is valid"""
min_, max_ = self.target_limits
if not min_ <= value <= max_:
raise BadValueError('limits violation: %g outside [%g, %g]' % (value, min_, max_))
# --- legacy mixins, not agreed as standard ---
class HasOffset(Feature): class HasOffset(Feature):
"""has an offset parameter """has an offset parameter
@ -63,139 +88,3 @@ class HasLimits(Feature):
min_, max_ = self.target_limits min_, max_ = self.target_limits
if not min_ <= value <= max_: if not min_ <= value <= max_:
raise BadValueError('limits violation: %g outside [%g, %g]' % (value, min_, max_)) raise BadValueError('limits violation: %g outside [%g, %g]' % (value, min_, max_))
# --- not used, not tested yet ---
class HAS_PID(Feature):
# note: implementors should either use p,i,d or pid, but ECS must be handle both cases
# note: if both p,i,d and pid are implemented, it MUST NOT matter which one gets a change, the final result should be the same
# note: if there are additional custom accessibles with the same name as an element of the struct, the above applies
# note: (i would still but them in the same group, though)
# note: if extra elements are implemented in the pid struct they MUST BE
# properly described in the description of the pid Parameter
# parameters
use_pid = Parameter('use the pid mode', datatype=EnumType(openloop=0, pid_control=1), )
# pylint: disable=invalid-name
p = Parameter('proportional part of the regulation', datatype=FloatRange(0), )
i = Parameter('(optional) integral part', datatype=FloatRange(0), optional=True)
d = Parameter('(optional) derivative part', datatype=FloatRange(0), optional=True)
base_output = Parameter('(optional) minimum output value', datatype=FloatRange(0), optional=True)
pid = Parameter('(optional) Struct of p,i,d, minimum output value',
datatype=StructOf(p=FloatRange(0),
i=FloatRange(0),
d=FloatRange(0),
base_output=FloatRange(0),
), optional=True,
) # note: struct may be extended with custom elements (names should be prefixed with '_')
output = Parameter('(optional) output of pid-control', datatype=FloatRange(0), optional=True, readonly=False)
class Has_PIDTable(HAS_PID):
# parameters
use_pidtable = Parameter('use the zoning mode', datatype=EnumType(fixed_pid=0, zone_mode=1))
pidtable = Parameter('Table of pid-values vs. target temperature', datatype=ArrayOf(TupleOf(FloatRange(0),
StructOf(p=FloatRange(0),
i=FloatRange(0),
d=FloatRange(0),
_heater_range=FloatRange(0),
_base_output=FloatRange(0),),),), optional=True) # struct may include 'heaterrange'
class HAS_Persistent(Feature):
#extra_Status {
# 'decoupled' : Status.IDLE+1, # to be discussed.
# 'coupling' : Status.BUSY+1, # to be discussed.
# 'coupled' : Status.BUSY+2, # to be discussed.
# 'decoupling' : Status.BUSY+3, # to be discussed.
#}
# parameters
persistent_mode = Parameter('Use persistent mode',
datatype=EnumType(off=0,on=1),
default=0, readonly=False)
is_persistent = Parameter('current state of persistence',
datatype=BoolType(), optional=True)
# stored_value = Parameter('current persistence value, often used as the modules value',
# datatype='main', unit='$', optional=True)
# driven_value = Parameter('driven value (outside value, syncs with stored_value if non-persistent)',
# datatype='main', unit='$' )
class HAS_Tolerance(Feature):
# detects IDLE status by checking if the value lies in a given window:
# tolerance is the maximum allowed deviation from target, value must lie in this interval
# for at least ´timewindow´ seconds.
# parameters
tolerance = Parameter('Half height of the Window',
datatype=FloatRange(0), default=1, unit='$')
timewindow = Parameter('Length of the timewindow to check',
datatype=FloatRange(0), default=30, unit='s',
optional=True)
class HAS_Timeout(Feature):
# parameters
timeout = Parameter('timeout for movement',
datatype=FloatRange(0), default=0, unit='s')
class HAS_Pause(Feature):
# just a proposal, can't agree on it....
@Command(argument=None, result=None)
def pause(self):
"""pauses movement"""
@Command(argument=None, result=None)
def go(self):
"""continues movement or start a new one if target was change since the last pause"""
class HAS_Ramp(Feature):
# parameters
ramp =Parameter('speed of movement', unit='$/min',
datatype=FloatRange(0))
use_ramp = Parameter('use the ramping of the setpoint, or jump',
datatype=EnumType(disable_ramp=0, use_ramp=1),
optional=True)
setpoint = Parameter('currently active setpoint',
datatype=FloatRange(0), unit='$',
readonly=True, )
class HAS_Speed(Feature):
# parameters
speed = Parameter('(maximum) speed of movement (of the main value)',
unit='$/s', datatype=FloatRange(0))
class HAS_Accel(HAS_Speed):
# parameters
accel = Parameter('acceleration of movement', unit='$/s^2',
datatype=FloatRange(0))
decel = Parameter('deceleration of movement', unit='$/s^2',
datatype=FloatRange(0), optional=True)
class HAS_MotorCurrents(Feature):
# parameters
movecurrent = Parameter('Current while moving',
datatype=FloatRange(0))
idlecurrent = Parameter('Current while idle',
datatype=FloatRange(0), optional=True)
class HAS_Curve(Feature):
# proposed, not yet agreed upon!
# parameters
curve = Parameter('Calibration curve', datatype=StringType(), default='<unset>')

View File

@ -722,7 +722,10 @@ class Module(HasAccessibles):
for mobj in modules: for mobj in modules:
pinfo = mobj.pollInfo pinfo = mobj.pollInfo
if now > pinfo.last_main + pinfo.interval: if now > pinfo.last_main + pinfo.interval:
pinfo.last_main = (now // pinfo.interval) * pinfo.interval if pinfo.interval:
pinfo.last_main = (now // pinfo.interval) * pinfo.interval
else:
pinfo.last_main = now
mobj.callPollFunc(mobj.doPoll) mobj.callPollFunc(mobj.doPoll)
now = time.time() now = time.time()
# find ONE due slow poll and call it # find ONE due slow poll and call it

View File

@ -118,12 +118,32 @@ class HasStates:
self.setFastPoll(False) self.setFastPoll(False)
self.read_status() self.read_status()
def on_cleanup(self, sm):
if isinstance(sm.cleanup_reason, Exception):
return self.on_error(sm)
if isinstance(sm.cleanup_reason, Start):
return self.on_restart(sm)
if isinstance(sm.cleanup_reason, Stop):
return self.on_stop(sm)
self.log.error('bad cleanup reason %r', sm.cleanup_reason)
def on_error(self, sm):
self.log.error('handle error %r', sm.cleanup_reason)
self.final_status(ERROR, repr(sm.cleanup_reason))
return None
def on_restart(self, sm):
return None
def on_stop(self, sm):
return None
def start_machine(self, statefunc, fast_poll=True, **kwds): def start_machine(self, statefunc, fast_poll=True, **kwds):
sm = self._state_machine sm = self._state_machine
sm.status = self.get_status(statefunc, BUSY) sm.status = self.get_status(statefunc, BUSY)
if sm.statefunc: if sm.statefunc:
sm.status = sm.status[0], 'restarting' sm.status = sm.status[0], 'restarting'
sm.start(statefunc, **kwds) sm.start(statefunc, cleanup=kwds.pop('cleanup', self.on_cleanup), **kwds)
self.read_status() self.read_status()
if fast_poll: if fast_poll:
sm.reset_fast_poll = True sm.reset_fast_poll = True

264
secop_psi/attocube.py Normal file
View File

@ -0,0 +1,264 @@
# *****************************************************************************
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation; either version 2 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# Module authors:
# Markus Zolliker <markus.zolliker@psi.ch>
# *****************************************************************************
import sys
import time
from secop.core import Drivable, Parameter, Command, Property, ERROR, WARN, BUSY, IDLE, Done, nopoll
from secop.features import HasTargetLimits, HasSimpleOffset
from secop.datatypes import IntRange, FloatRange, StringType, BoolType
from secop.errors import ConfigError, BadValueError
sys.path.append('/home/l_samenv/Documents/anc350/Linux64/userlib/lib')
from PyANC350v4 import Positioner
DIRECTION_NAME = {1: 'forward', -1: 'backward'}
class FreezeStatus:
"""freeze status for some time
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
def __init_subclass__(cls):
def wrapped(self, inner=cls.read_status):
if time.time() < self.__freeze_status_until:
return Done
return inner(self)
cls.read_status = wrapped
super().__init_subclass__()
def freeze_status(self, delay, code=BUSY, text='changed target'):
"""freezze status to the given value for the given delay"""
self.__freeze_status_until = time.time() + delay
self.status = code, text
class Axis(HasTargetLimits, FreezeStatus, Drivable):
axis = Property('axis number', IntRange(0, 2), 0)
value = Parameter('axis position', FloatRange(unit='deg'))
frequency = Parameter('frequency', FloatRange(1, unit='Hz'), readonly=False)
amplitude = Parameter('amplitude', FloatRange(0, unit='V'), readonly=False)
gear = Parameter('gear factor', FloatRange(), readonly=False, default=1, initwrite=True)
tolerance = Parameter('positioning tolerance', FloatRange(0, unit='$'), readonly=False, default=0.01)
output = Parameter('enable output', BoolType(), readonly=False)
info = Parameter('axis info', StringType())
statusbits = Parameter('status bits', StringType())
_hw = Positioner()
_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}
_direction = 1 # move direction
_idle_status = IDLE, ''
_error_state = '' # empty string: no error
_history = None
_check_sensor = False
_try_count = 0
def __init__(self, name, logger, opts, srv):
unit = opts.pop('unit', 'deg')
opts['value.unit'] = unit
try:
self._scale = self.SCALES[unit] * opts.get('gear', 1)
except KeyError as e:
raise ConfigError('unsupported unit: %s' % unit)
super().__init__(name, logger, opts, srv)
def write_gear(self, value):
self._scale = self.SCALES[self.parameters['value'].datatype.unit] * self.gear
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):
return self._hw.getFrequency(self.axis)
def write_frequency(self, value):
self._hw.setFrequency(self.axis, value)
return self._hw.getFrequency(self.axis)
def read_amplitude(self):
return self._hw.getAmplitude(self.axis)
def write_amplitude(self, value):
self._hw.setAmplitude(self.axis, value)
return self._hw.getAmplitude(self.axis)
def write_tolerance(self, value):
self._hw.setTargetRange(self.axis, value / self._scale)
return value
def write_output(self, value):
self._hw.setAxisOutput(self.axis, enable=value, autoDisable=0)
return value
def read_status(self):
statusbits = self._hw.getAxisStatus(self.axis)
sensor, self.output, moving, attarget, eot_fwd, eot_bwd, sensor_error = statusbits
self.statusbits = ''.join((k for k, v in zip('SOMTFBE', statusbits) if v))
if self._move_steps:
if not (eot_fwd or eot_bwd):
return BUSY, 'moving by steps'
if not sensor:
self._error_state = 'no sensor connected'
elif sensor_error:
self._error_state = 'sensor error'
elif eot_fwd:
self._error_state = 'end of travel forward'
elif eot_bwd:
self._error_state = 'end of travel backward'
else:
if self._error_state and not DIRECTION_NAME[self._direction] in self._error_state:
self._error_state = ''
status_text = 'moving' if self._try_count == 0 else 'moving (retry %d)' % self._try_count
if moving and self._history is not None: # history None: moving by steps
self._history.append(self.value)
if len(self._history) < 5:
return BUSY, status_text
beg = self._history.pop(0)
if abs(beg - self.target) < self.tolerance:
# reset normal tolerance
self._stop()
self._idle_status = IDLE, 'in tolerance'
return self._idle_status
# self._hw.setTargetRange(self.axis, self.tolerance / self._scale)
if (self.value - beg) * self._direction > 0:
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):
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:
self._hw.setTargetPosition(self.axis, value / self._scale)
self._hw.startAutoMove(self.axis, enable=1, relative=0)
except Exception as e:
if itry == 4:
raise
self.log.warn('%r', e)
self._history = [self.value]
self._idle_status = IDLE, ''
self.freeze_status(1, BUSY, 'changed target')
self.setFastPoll(True, 1)
return value
def doPoll(self):
if self._move_steps == 0:
super().doPoll()
return
self._hw.startSingleStep(self.axis, self._direction < 0)
self._move_steps -= self._direction
if self._move_steps % int(self.frequency) == 0: # poll value and status every second
super().doPoll()
@nopoll
def read_info(self):
"""read info from controller"""
cap = self._hw.measureCapacitance(self.axis) * 1e9
axistype = ['linear', 'gonio', 'rotator'][self._hw.getActuatorType(self.axis)]
return '%s %s %.3gnF' % (self._hw.getActuatorName(self.axis), axistype, cap)
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)

View File

@ -26,7 +26,7 @@ from secop.lib.enum import Enum
from secop.errors import BadValueError, HardwareError from secop.errors import BadValueError, HardwareError
from secop_psi.magfield import Magfield, SimpleMagfield, Status from secop_psi.magfield import Magfield, SimpleMagfield, Status
from secop_psi.mercury import MercuryChannel, off_on, Mapped from secop_psi.mercury import MercuryChannel, off_on, Mapped
from secop.lib.statemachine import Retry from secop.states import Retry
Action = Enum(hold=0, run_to_set=1, run_to_zero=2, clamped=3) Action = Enum(hold=0, run_to_set=1, run_to_zero=2, clamped=3)
hold_rtoz_rtos_clmp = Mapped(HOLD=Action.hold, RTOS=Action.run_to_set, hold_rtoz_rtos_clmp = Mapped(HOLD=Action.hold, RTOS=Action.run_to_set,
@ -65,6 +65,13 @@ class SimpleField(MercuryChannel, SimpleMagfield):
obj = object.__new__(newclass) obj = object.__new__(newclass)
return obj return obj
def initModule(self):
super().initModule()
try:
self.write_action(Action.hold)
except Exception as e:
self.log.error('can not set to hold %r', e)
def read_value(self): def read_value(self):
return self.query('PSU:SIG:FLD') return self.query('PSU:SIG:FLD')
@ -94,12 +101,12 @@ class SimpleField(MercuryChannel, SimpleMagfield):
def set_and_go(self, value): def set_and_go(self, value):
self.setpoint = self.change('PSU:SIG:FSET', value) self.setpoint = self.change('PSU:SIG:FSET', value)
assert self.write_action('hold') == 'hold' assert self.write_action(Action.hold) == Action.hold
assert self.write_action('run_to_set') == 'run_to_set' assert self.write_action(Action.run_to_set) == Action.run_to_set
def start_ramp_to_target(self, sm): def start_ramp_to_target(self, sm):
# if self.action != 'hold': # if self.action != Action.hold:
# assert self.write_action('hold') == 'hold' # assert self.write_action(Action.hold) == Action.hold
# return Retry # return Retry
self.set_and_go(sm.target) self.set_and_go(sm.target)
sm.try_cnt = 5 sm.try_cnt = 5
@ -116,17 +123,17 @@ class SimpleField(MercuryChannel, SimpleMagfield):
return Retry return Retry
def final_status(self, *args, **kwds): def final_status(self, *args, **kwds):
print('FINAL-hold') self.write_action(Action.hold)
self.write_action('hold')
return super().final_status(*args, **kwds) return super().final_status(*args, **kwds)
def on_restart(self, sm): def on_restart(self, sm):
print('ON_RESTART-hold', sm.sm) self.write_action(Action.hold)
self.write_action('hold')
return super().on_restart(sm) return super().on_restart(sm)
class Field(SimpleField, Magfield): class Field(SimpleField, Magfield):
persistent_field = Parameter(
'persistent field', FloatRange(unit='$'), readonly=False)
wait_switch_on = Parameter( wait_switch_on = Parameter(
'wait time to ensure switch is on', FloatRange(0, unit='s'), readonly=True, default=60) 'wait time to ensure switch is on', FloatRange(0, unit='s'), readonly=True, default=60)
wait_switch_off = Parameter( wait_switch_off = Parameter(
@ -136,7 +143,7 @@ class Field(SimpleField, Magfield):
_field_mismatch = None _field_mismatch = None
__init = True __init = True
__switch_heater_fix = 0 __switch_fixed_until = 0
def doPoll(self): def doPoll(self):
super().doPoll() super().doPoll()
@ -208,7 +215,8 @@ class Field(SimpleField, Magfield):
value = self.query('PSU:SIG:SWHT', off_on) value = self.query('PSU:SIG:SWHT', off_on)
now = time.time() now = time.time()
if value != self.switch_heater: if value != self.switch_heater:
if now < self.__switch_heater_fix: if now < self.__switch_fixed_until:
self.log.debug('correct fixed switch time')
# probably switch heater was changed, but IPS reply is not yet updated # probably switch heater was changed, but IPS reply is not yet updated
if self.switch_heater: if self.switch_heater:
self.switch_on_time = time.time() self.switch_on_time = time.time()
@ -228,8 +236,10 @@ class Field(SimpleField, Magfield):
self.log.info('switch heater already %r', value) self.log.info('switch heater already %r', value)
# we do not want to restart the timer # we do not want to restart the timer
return value return value
self.__switch_heater_fix = time.time() + 10 self.__switch_fixed_until = time.time() + 10
return self.change('PSU:SIG:SWHT', value, off_on) self.log.debug('switch time fixed for 10 sec')
result = self.change('PSU:SIG:SWHT', value, off_on, n_retry=0) # no readback check
return result
def start_ramp_to_field(self, sm): def start_ramp_to_field(self, sm):
if abs(self.current - self.persistent_field) <= self.tolerance: if abs(self.current - self.persistent_field) <= self.tolerance:
@ -241,9 +251,7 @@ class Field(SimpleField, Magfield):
if self.switch_heater: if self.switch_heater:
self.log.warn('switch is already on!') self.log.warn('switch is already on!')
return self.ramp_to_field return self.ramp_to_field
self.log.warn('wait first for switch off current=%g pf=%g', self.current, self.persistent_field) self.log.warn('wait first for switch off current=%g pf=%g %r', self.current, self.persistent_field, e)
return Retry
self.status = Status.PREPARING, 'wait for switch off'
sm.after_wait = self.ramp_to_field sm.after_wait = self.ramp_to_field
return self.wait_for_switch return self.wait_for_switch
return self.ramp_to_field return self.ramp_to_field
@ -270,7 +278,7 @@ class Field(SimpleField, Magfield):
return Retry return Retry
def wait_for_switch(self, sm): def wait_for_switch(self, sm):
if not self.delay(10): if not sm.delta(10):
return Retry return Retry
try: try:
self.log.warn('try again') self.log.warn('try again')
@ -282,8 +290,8 @@ class Field(SimpleField, Magfield):
def start_ramp_to_zero(self, sm): def start_ramp_to_zero(self, sm):
try: try:
assert self.write_action('hold') == 'hold' assert self.write_action(Action.hold) == Action.hold
assert self.write_action('run_to_zero') == 'run_to_zero' assert self.write_action(Action.run_to_zero) == Action.run_to_zero
except (HardwareError, AssertionError) as e: except (HardwareError, AssertionError) as e:
self.log.warn('switch not yet ready %r', e) self.log.warn('switch not yet ready %r', e)
self.status = Status.PREPARING, 'wait for switch off' self.status = Status.PREPARING, 'wait for switch off'
@ -298,6 +306,6 @@ class Field(SimpleField, Magfield):
sm.try_cnt -= 1 sm.try_cnt -= 1
if sm.try_cnt < 0: if sm.try_cnt < 0:
raise raise
assert self.write_action('hold') == 'hold' assert self.write_action(Action.hold) == Action.hold
assert self.write_action('run_to_zero') == 'run_to_zero' assert self.write_action(Action.run_to_zero) == Action.run_to_zero
return Retry return Retry

View File

@ -23,9 +23,9 @@ import time
from secop.core import Drivable, Parameter, Done, IDLE, BUSY, ERROR from secop.core import Drivable, Parameter, Done, IDLE, BUSY, ERROR
from secop.datatypes import FloatRange, EnumType, ArrayOf, TupleOf, StatusType from secop.datatypes import FloatRange, EnumType, ArrayOf, TupleOf, StatusType
from secop.features import HasLimits from secop.features import HasLimits
from secop.errors import ConfigError, ProgrammingError, HardwareError from secop.errors import ConfigError, ProgrammingError, HardwareError, BadValueError
from secop.lib.enum import Enum from secop.lib.enum import Enum
from secop.states import Retry, HasStates, status_code from secop.states import Retry, HasStates, status_code, Start
UNLIMITED = FloatRange() UNLIMITED = FloatRange()
@ -130,8 +130,6 @@ class Magfield(SimpleMagfield):
'persistent mode', EnumType(Mode), readonly=False, default=Mode.PERSISTENT) 'persistent mode', EnumType(Mode), readonly=False, default=Mode.PERSISTENT)
switch_heater = Parameter('switch heater', EnumType(off=OFF, on=ON), switch_heater = Parameter('switch heater', EnumType(off=OFF, on=ON),
readonly=False, default=0) readonly=False, default=0)
persistent_field = Parameter(
'persistent field', FloatRange(unit='$'), readonly=False)
current = Parameter( current = Parameter(
'leads current (in units of field)', FloatRange(unit='$')) 'leads current (in units of field)', FloatRange(unit='$'))
# TODO: time_to_target # TODO: time_to_target
@ -157,54 +155,73 @@ class Magfield(SimpleMagfield):
ramp_tmo = Parameter( ramp_tmo = Parameter(
'timeout for field ramp progress', 'timeout for field ramp progress',
FloatRange(0, unit='s'), readonly=False, default=30) FloatRange(0, unit='s'), readonly=False, default=30)
__init = True __init_persistency = True
switch_on_time = None switch_on_time = None
switch_off_time = None switch_off_time = None
def doPoll(self): def doPoll(self):
if self.__init: if self.__init_persistency:
self.__init = False if self.__init_persistency is True:
if self.read_switch_heater() and self.mode == Mode.PERSISTENT: self._last_target = self.value
self.__init_persistency = time.time() + 60
self.read_value() # check for persistent field mismatch self.read_value() # check for persistent field mismatch
# switch off heater from previous live or manual intervention elif self.read_switch_heater() and self.mode != Mode.DRIVEN:
self.write_target(self.persistent_field) if time.time() > self.__init_persistency:
# switch off heater from previous live or manual intervention
self.log.info('fix mode after startup')
self.write_mode(self.mode)
else: else:
self._last_target = self.persistent_field self.__init_persistency = False
else: super().doPoll()
super().doPoll()
def initModule(self): def initModule(self):
super().initModule() super().initModule()
self.registerCallbacks(self) # for update_switch_heater self.registerCallbacks(self) # for update_switch_heater
def write_mode(self, value): def write_mode(self, value):
self.start_machine(self.start_field_change, cleanup=self.cleanup, target=self.target, mode=value) self.__init_persistency = False
target = self.value
func = self.start_field_change
if value == Mode.DISABLED:
target = 0
if abs(self.value) < self.tolerance:
func = self.start_switch_off
elif value == Mode.PERSISTENT:
func = self.start_switch_off
self.start_machine(func, target=target, mode=value)
return value return value
def write_target(self, target): def write_target(self, target):
self.__init_persistency = False
if self.mode == Mode.DISABLED:
if target == 0:
return 0
self.log.info('raise error %r', target)
raise BadValueError('disabled')
self.check_limits(target) self.check_limits(target)
self.start_machine(self.start_field_change, cleanup=self.cleanup, target=target, mode=self.mode) self.start_machine(self.start_field_change, target=target, mode=self.mode)
return target return target
def cleanup(self, sm): # sm is short for statemachine def on_error(self, sm): # sm is short for statemachine
if self.switch_heater != 0: if self.switch_heater == ON:
self.persistent_field = self.read_value() self.read_value()
if sm.mode != Mode.DRIVEN: if sm.mode != Mode.DRIVEN:
self.log.warning('turn switch heater off') self.log.warning('turn switch heater off')
self.write_switch_heater(0) self.write_switch_heater(OFF)
return self.on_error(sm)
@status_code('PREPARING') @status_code(Status.PREPARING)
def start_field_change(self, sm): def start_field_change(self, sm):
self.setFastPoll(True, 1.0) self.setFastPoll(True, 1.0)
if sm.target == self.persistent_field or ( if sm.target == self.value or (
sm.target == self._last_target and sm.target == self._last_target and
abs(sm.target - self.persistent_field) <= self.tolerance): # short cut abs(sm.target - self.value) <= self.tolerance): # short cut
return self.check_switch_off return self.check_switch_off
if self.switch_heater: if self.switch_heater == ON:
return self.start_switch_on return self.start_switch_on
return self.start_ramp_to_field return self.start_ramp_to_field
@status_code('PREPARING') @status_code(Status.PREPARING)
def start_ramp_to_field(self, sm): def start_ramp_to_field(self, sm):
"""start ramping current to persistent field """start ramping current to persistent field
@ -213,12 +230,12 @@ class Magfield(SimpleMagfield):
""" """
raise NotImplementedError raise NotImplementedError
@status_code('PREPARING', 'ramp leads to match field') @status_code(Status.PREPARING, 'ramp leads to match field')
def ramp_to_field(self, sm): def ramp_to_field(self, sm):
if sm.init: if sm.init:
sm.stabilize_start = 0 # in case current is already at field sm.stabilize_start = 0 # in case current is already at field
self.init_progress(sm, self.current) self.init_progress(sm, self.current)
dif = abs(self.current - self.persistent_field) dif = abs(self.current - self.value)
if dif > self.tolerance: if dif > self.tolerance:
tdif = self.get_progress(sm, self.current) tdif = self.get_progress(sm, self.current)
if tdif > self.leads_ramp_tmo: if tdif > self.leads_ramp_tmo:
@ -229,7 +246,7 @@ class Magfield(SimpleMagfield):
sm.stabilize_start = sm.now sm.stabilize_start = sm.now
return self.stabilize_current return self.stabilize_current
@status_code('PREPARING') @status_code(Status.PREPARING)
def stabilize_current(self, sm): def stabilize_current(self, sm):
if sm.now - sm.stabilize_start < self.wait_stable_leads: if sm.now - sm.stabilize_start < self.wait_stable_leads:
return Retry return Retry
@ -237,7 +254,6 @@ class Magfield(SimpleMagfield):
def update_switch_heater(self, value): def update_switch_heater(self, value):
"""is called whenever switch heater was changed""" """is called whenever switch heater was changed"""
print('SW', value)
if value == 0: if value == 0:
if self.switch_off_time is None: if self.switch_off_time is None:
self.log.info('restart switch_off_time') self.log.info('restart switch_off_time')
@ -249,12 +265,12 @@ class Magfield(SimpleMagfield):
self.switch_on_time = time.time() self.switch_on_time = time.time()
self.switch_off_time = None self.switch_off_time = None
@status_code('PREPARING') @status_code(Status.PREPARING)
def start_switch_on(self, sm): def start_switch_on(self, sm):
if self.read_switch_heater() == 0: if self.read_switch_heater() == OFF:
self.status = Status.PREPARING, 'turn switch heater on' self.status = Status.PREPARING, 'turn switch heater on'
try: try:
self.write_switch_heater(True) self.write_switch_heater(ON)
except Exception as e: except Exception as e:
self.log.warning('write_switch_heater %r', e) self.log.warning('write_switch_heater %r', e)
return Retry return Retry
@ -262,13 +278,15 @@ class Magfield(SimpleMagfield):
self.status = Status.PREPARING, 'wait for heater on' self.status = Status.PREPARING, 'wait for heater on'
return self.wait_for_switch_on return self.wait_for_switch_on
@status_code('PREPARING') @status_code(Status.PREPARING)
def wait_for_switch_on(self, sm): def wait_for_switch_on(self, sm):
if (sm.target == self._last_target and if (sm.target == self._last_target and
abs(sm.target - self.persistent_field) <= self.tolerance): # short cut abs(sm.target - self.value) <= self.tolerance): # short cut
return self.check_switch_off return self.check_switch_off
self.read_switch_heater() # trigger switch_on/off_time self.read_switch_heater() # trigger switch_on/off_time
if self.switch_heater == 0: if self.switch_heater == OFF:
if sm.init: # avoid too many states chained
return Retry
self.log.warning('switch turned off manually?') self.log.warning('switch turned off manually?')
return self.start_switch_on return self.start_switch_on
if sm.now - self.switch_on_time < self.wait_switch_on: if sm.now - self.switch_on_time < self.wait_switch_on:
@ -278,7 +296,7 @@ class Magfield(SimpleMagfield):
self._last_target = sm.target self._last_target = sm.target
return self.start_ramp_to_target return self.start_ramp_to_target
@status_code('RAMPING') @status_code(Status.RAMPING)
def start_ramp_to_target(self, sm): def start_ramp_to_target(self, sm):
"""start ramping current to target field """start ramping current to target field
@ -287,9 +305,8 @@ class Magfield(SimpleMagfield):
""" """
raise NotImplementedError raise NotImplementedError
@status_code('RAMPING') @status_code(Status.RAMPING)
def ramp_to_target(self, sm): def ramp_to_target(self, sm):
self.persistent_field = self.value
dif = abs(self.value - sm.target) dif = abs(self.value - sm.target)
if sm.init: if sm.init:
sm.stabilize_start = 0 # in case current is already at target sm.stabilize_start = 0 # in case current is already at target
@ -298,6 +315,7 @@ class Magfield(SimpleMagfield):
sm.stabilize_start = sm.now sm.stabilize_start = sm.now
tdif = self.get_progress(sm, self.value) tdif = self.get_progress(sm, self.value)
if tdif > self.workingramp / self.tolerance * 60 + self.ramp_tmo: if tdif > self.workingramp / self.tolerance * 60 + self.ramp_tmo:
self.log.warn('no progress')
raise HardwareError('no progress') raise HardwareError('no progress')
sm.stabilize_start = None sm.stabilize_start = None
return Retry return Retry
@ -305,10 +323,9 @@ class Magfield(SimpleMagfield):
sm.stabilize_start = sm.now sm.stabilize_start = sm.now
return self.stabilize_field return self.stabilize_field
@status_code('STABILIZING') @status_code(Status.STABILIZING)
def stabilize_field(self, sm): def stabilize_field(self, sm):
self.persistent_field = self.value if sm.now < sm.stabilize_start + self.wait_stable_field:
if sm.now > sm.stablize_start + self.wait_stable_field:
return Retry return Retry
return self.check_switch_off return self.check_switch_off
@ -317,17 +334,18 @@ class Magfield(SimpleMagfield):
return self.final_status(Status.PREPARED, 'driven') return self.final_status(Status.PREPARED, 'driven')
return self.start_switch_off return self.start_switch_off
@status_code('FINALIZING') @status_code(Status.FINALIZING)
def start_switch_off(self, sm): def start_switch_off(self, sm):
if self.switch_heater == 1: if self.switch_heater == ON:
self.write_switch_heater(False) self.write_switch_heater(OFF)
return self.wait_for_switch_off return self.wait_for_switch_off
@status_code('FINALIZING') @status_code(Status.FINALIZING)
def wait_for_switch_off(self, sm): def wait_for_switch_off(self, sm):
self.persistent_field = self.value
self.read_switch_heater() self.read_switch_heater()
if self.switch_off_time is None: if self.switch_heater == ON:
if sm.init: # avoid too many states chained
return Retry
self.log.warning('switch turned on manually?') self.log.warning('switch turned on manually?')
return self.start_switch_off return self.start_switch_off
if sm.now - self.switch_off_time < self.wait_switch_off: if sm.now - self.switch_off_time < self.wait_switch_off:
@ -336,7 +354,7 @@ class Magfield(SimpleMagfield):
return self.final_status(Status.IDLE, 'leads current at field, switch off') return self.final_status(Status.IDLE, 'leads current at field, switch off')
return self.start_ramp_to_zero return self.start_ramp_to_zero
@status_code('FINALIZING') @status_code(Status.FINALIZING)
def start_ramp_to_zero(self, sm): def start_ramp_to_zero(self, sm):
"""start ramping current to zero """start ramping current to zero
@ -345,15 +363,15 @@ class Magfield(SimpleMagfield):
""" """
raise NotImplementedError raise NotImplementedError
@status_code('FINALIZING') @status_code(Status.FINALIZING)
def ramp_to_zero(self, sm): def ramp_to_zero(self, sm):
"""ramp field to zero""" """ramp field to zero"""
if sm.init: if sm.init:
self.init_progress(sm, self.current) self.init_progress(sm, self.current)
if abs(self.current) > self.tolerance: if abs(self.current) > self.tolerance:
if self.get_progress(sm, self.current, self.ramp) > self.leads_ramp_tmo: if self.get_progress(sm, self.value) > self.leads_ramp_tmo:
raise HardwareError('no progress') raise HardwareError('no progress')
return Retry return Retry
if sm.mode == Mode.DISABLED and self.persistent_field == 0: if sm.mode == Mode.DISABLED and abs(self.value) < self.tolerance:
return self.final_status(Status.DISABLED, 'disabled') return self.final_status(Status.DISABLED, 'disabled')
return self.final_status(Status.IDLE, 'persistent mode') return self.final_status(Status.IDLE, 'persistent mode')

View File

@ -123,13 +123,14 @@ class MercuryChannel(HasIO):
else: else:
raise HardwareError(msg) from None raise HardwareError(msg) from None
def multichange(self, adr, values, convert=as_float, tolerance=0): def multichange(self, adr, values, convert=as_float, tolerance=0, n_retry=3):
"""set parameter(s) in mercury syntax """set parameter(s) in mercury syntax
:param adr: as in see multiquery method :param adr: as in see multiquery method
:param values: [(name1, value1), (name2, value2) ...] :param values: [(name1, value1), (name2, value2) ...]
:param convert: a converter function (converts given value to string and replied string to value) :param convert: a converter function (converts given value to string and replied string to value)
:param tolerance: tolerance for readback check :param tolerance: tolerance for readback check
:param n_retry: number of retries or 0 for no readback check
:return: the values as tuple :return: the values as tuple
Example: Example:
@ -143,7 +144,7 @@ class MercuryChannel(HasIO):
adr = self._complete_adr(adr) adr = self._complete_adr(adr)
params = ['%s:%s' % (k, convert(v)) for k, v in values] params = ['%s:%s' % (k, convert(v)) for k, v in values]
cmd = 'SET:%s:%s' % (adr, ':'.join(params)) cmd = 'SET:%s:%s' % (adr, ':'.join(params))
for _ in range(3): # try 3 times or until readback result matches for _ in range(max(1, n_retry)): # try n_retry times or until readback result matches
t = time.time() t = time.time()
reply = self.communicate(cmd) reply = self.communicate(cmd)
head = 'STAT:SET:%s:' % adr head = 'STAT:SET:%s:' % adr
@ -158,6 +159,8 @@ class MercuryChannel(HasIO):
except (AssertionError, AttributeError, ValueError) as e: except (AssertionError, AttributeError, ValueError) as e:
time.sleep(0.1) # in case of missed replies this might help to skip garbage time.sleep(0.1) # in case of missed replies this might help to skip garbage
raise HardwareError('invalid reply %r to cmd %r' % (reply, cmd)) from e raise HardwareError('invalid reply %r to cmd %r' % (reply, cmd)) from e
if n_retry == 0:
return [v[1] for v in values] # no readback check
keys = [v[0] for v in values] keys = [v[0] for v in values]
debug = [] debug = []
readback = self.multiquery(adr, keys, convert, debug) readback = self.multiquery(adr, keys, convert, debug)
@ -182,9 +185,9 @@ class MercuryChannel(HasIO):
adr, _, name = adr.rpartition(':') adr, _, name = adr.rpartition(':')
return self.multiquery(adr, [name], convert)[0] return self.multiquery(adr, [name], convert)[0]
def change(self, adr, value, convert=as_float, tolerance=0): def change(self, adr, value, convert=as_float, tolerance=0, n_retry=3):
adr, _, name = adr.rpartition(':') adr, _, name = adr.rpartition(':')
return self.multichange(adr, [(name, value)], convert, tolerance)[0] return self.multichange(adr, [(name, value)], convert, tolerance, n_retry)[0]
class TemperatureSensor(MercuryChannel, Readable): class TemperatureSensor(MercuryChannel, Readable):