diff --git a/cfg/main/flamp.cfg b/cfg/main/flamp.cfg new file mode 100644 index 0000000..f390498 --- /dev/null +++ b/cfg/main/flamp.cfg @@ -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 diff --git a/cfg/main/fs.cfg b/cfg/main/fs.cfg new file mode 100644 index 0000000..2721dfe --- /dev/null +++ b/cfg/main/fs.cfg @@ -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 diff --git a/cfg/main/ft.cfg b/cfg/main/ft.cfg new file mode 100644 index 0000000..5e82ee6 --- /dev/null +++ b/cfg/main/ft.cfg @@ -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 + diff --git a/cfg/main/ma10.cfg b/cfg/main/ma10.cfg index 67494cb..023e336 100644 --- a/cfg/main/ma10.cfg +++ b/cfg/main/ma10.cfg @@ -68,5 +68,5 @@ description = stick rotation, typically used for omega class = secop_psi.phytron.Motor io = om_io sign = -1 -encoder_mode = CHECK +encoder_mode = READ diff --git a/cfg/sea/flamp.config.json b/cfg/sea/flamp.config.json new file mode 100644 index 0000000..b610aa8 --- /dev/null +++ b/cfg/sea/flamp.config.json @@ -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"}]}} diff --git a/cfg/sea/fs.config.json b/cfg/sea/fs.config.json new file mode 100644 index 0000000..a62740b --- /dev/null +++ b/cfg/sea/fs.config.json @@ -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"}]}} diff --git a/cfg/sea/ft.config.json b/cfg/sea/ft.config.json new file mode 100644 index 0000000..853eace --- /dev/null +++ b/cfg/sea/ft.config.json @@ -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"}]}} diff --git a/cfg/stick/heliox.cfg b/cfg/stick/heliox.cfg index e0240f8..724167b 100644 --- a/cfg/stick/heliox.cfg +++ b/cfg/stick/heliox.cfg @@ -13,7 +13,26 @@ class = secop_psi.sea.SeaDrivable io = sea_stick sea_object = ts -[th] -class = secop_psi.sea.SeaDrivable +[T_sorb] +class = secop_psi.sea.SeaReadable io = sea_stick 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 diff --git a/secop/features.py b/secop/features.py index 9cfb4d8..f5324c9 100644 --- a/secop/features.py +++ b/secop/features.py @@ -31,7 +31,32 @@ from secop.errors import BadValueError, ConfigError 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): """has an offset parameter @@ -63,139 +88,3 @@ class HasLimits(Feature): min_, max_ = self.target_limits if not min_ <= value <= 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='') diff --git a/secop/modules.py b/secop/modules.py index d75c3b2..cbc0bdd 100644 --- a/secop/modules.py +++ b/secop/modules.py @@ -722,7 +722,10 @@ class Module(HasAccessibles): for mobj in modules: pinfo = mobj.pollInfo 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) now = time.time() # find ONE due slow poll and call it diff --git a/secop/states.py b/secop/states.py index be63d01..9767920 100644 --- a/secop/states.py +++ b/secop/states.py @@ -118,12 +118,32 @@ class HasStates: self.setFastPoll(False) 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): sm = self._state_machine sm.status = self.get_status(statefunc, BUSY) if sm.statefunc: sm.status = sm.status[0], 'restarting' - sm.start(statefunc, **kwds) + sm.start(statefunc, cleanup=kwds.pop('cleanup', self.on_cleanup), **kwds) self.read_status() if fast_poll: sm.reset_fast_poll = True diff --git a/secop_psi/attocube.py b/secop_psi/attocube.py new file mode 100644 index 0000000..d85d8c4 --- /dev/null +++ b/secop_psi/attocube.py @@ -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 +# ***************************************************************************** + +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) diff --git a/secop_psi/ips_mercury.py b/secop_psi/ips_mercury.py index c4fbf75..84758c5 100644 --- a/secop_psi/ips_mercury.py +++ b/secop_psi/ips_mercury.py @@ -26,7 +26,7 @@ from secop.lib.enum import Enum from secop.errors import BadValueError, HardwareError from secop_psi.magfield import Magfield, SimpleMagfield, Status 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) 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) 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): return self.query('PSU:SIG:FLD') @@ -94,12 +101,12 @@ class SimpleField(MercuryChannel, SimpleMagfield): def set_and_go(self, value): self.setpoint = self.change('PSU:SIG:FSET', value) - assert self.write_action('hold') == 'hold' - assert self.write_action('run_to_set') == 'run_to_set' + assert self.write_action(Action.hold) == Action.hold + assert self.write_action(Action.run_to_set) == Action.run_to_set def start_ramp_to_target(self, sm): - # if self.action != 'hold': - # assert self.write_action('hold') == 'hold' + # if self.action != Action.hold: + # assert self.write_action(Action.hold) == Action.hold # return Retry self.set_and_go(sm.target) sm.try_cnt = 5 @@ -116,17 +123,17 @@ class SimpleField(MercuryChannel, SimpleMagfield): return Retry def final_status(self, *args, **kwds): - print('FINAL-hold') - self.write_action('hold') + self.write_action(Action.hold) return super().final_status(*args, **kwds) def on_restart(self, sm): - print('ON_RESTART-hold', sm.sm) - self.write_action('hold') + self.write_action(Action.hold) return super().on_restart(sm) class Field(SimpleField, Magfield): + persistent_field = Parameter( + 'persistent field', FloatRange(unit='$'), readonly=False) wait_switch_on = Parameter( 'wait time to ensure switch is on', FloatRange(0, unit='s'), readonly=True, default=60) wait_switch_off = Parameter( @@ -136,7 +143,7 @@ class Field(SimpleField, Magfield): _field_mismatch = None __init = True - __switch_heater_fix = 0 + __switch_fixed_until = 0 def doPoll(self): super().doPoll() @@ -208,7 +215,8 @@ class Field(SimpleField, Magfield): value = self.query('PSU:SIG:SWHT', off_on) now = time.time() 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 if self.switch_heater: self.switch_on_time = time.time() @@ -228,8 +236,10 @@ class Field(SimpleField, Magfield): self.log.info('switch heater already %r', value) # we do not want to restart the timer return value - self.__switch_heater_fix = time.time() + 10 - return self.change('PSU:SIG:SWHT', value, off_on) + self.__switch_fixed_until = time.time() + 10 + 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): if abs(self.current - self.persistent_field) <= self.tolerance: @@ -241,9 +251,7 @@ class Field(SimpleField, Magfield): if self.switch_heater: self.log.warn('switch is already on!') return self.ramp_to_field - self.log.warn('wait first for switch off current=%g pf=%g', self.current, self.persistent_field) - return Retry - self.status = Status.PREPARING, 'wait for switch off' + self.log.warn('wait first for switch off current=%g pf=%g %r', self.current, self.persistent_field, e) sm.after_wait = self.ramp_to_field return self.wait_for_switch return self.ramp_to_field @@ -270,7 +278,7 @@ class Field(SimpleField, Magfield): return Retry def wait_for_switch(self, sm): - if not self.delay(10): + if not sm.delta(10): return Retry try: self.log.warn('try again') @@ -282,8 +290,8 @@ class Field(SimpleField, Magfield): def start_ramp_to_zero(self, sm): try: - assert self.write_action('hold') == 'hold' - assert self.write_action('run_to_zero') == 'run_to_zero' + assert self.write_action(Action.hold) == Action.hold + assert self.write_action(Action.run_to_zero) == Action.run_to_zero except (HardwareError, AssertionError) as e: self.log.warn('switch not yet ready %r', e) self.status = Status.PREPARING, 'wait for switch off' @@ -298,6 +306,6 @@ class Field(SimpleField, Magfield): sm.try_cnt -= 1 if sm.try_cnt < 0: raise - assert self.write_action('hold') == 'hold' - assert self.write_action('run_to_zero') == 'run_to_zero' + assert self.write_action(Action.hold) == Action.hold + assert self.write_action(Action.run_to_zero) == Action.run_to_zero return Retry diff --git a/secop_psi/magfield.py b/secop_psi/magfield.py index ee1ac82..4c04136 100644 --- a/secop_psi/magfield.py +++ b/secop_psi/magfield.py @@ -23,9 +23,9 @@ import time from secop.core import Drivable, Parameter, Done, IDLE, BUSY, ERROR from secop.datatypes import FloatRange, EnumType, ArrayOf, TupleOf, StatusType 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.states import Retry, HasStates, status_code +from secop.states import Retry, HasStates, status_code, Start UNLIMITED = FloatRange() @@ -130,8 +130,6 @@ class Magfield(SimpleMagfield): 'persistent mode', EnumType(Mode), readonly=False, default=Mode.PERSISTENT) switch_heater = Parameter('switch heater', EnumType(off=OFF, on=ON), readonly=False, default=0) - persistent_field = Parameter( - 'persistent field', FloatRange(unit='$'), readonly=False) current = Parameter( 'leads current (in units of field)', FloatRange(unit='$')) # TODO: time_to_target @@ -157,54 +155,73 @@ class Magfield(SimpleMagfield): ramp_tmo = Parameter( 'timeout for field ramp progress', FloatRange(0, unit='s'), readonly=False, default=30) - __init = True + __init_persistency = True switch_on_time = None switch_off_time = None def doPoll(self): - if self.__init: - self.__init = False - if self.read_switch_heater() and self.mode == Mode.PERSISTENT: + if self.__init_persistency: + if self.__init_persistency is True: + self._last_target = self.value + self.__init_persistency = time.time() + 60 self.read_value() # check for persistent field mismatch - # switch off heater from previous live or manual intervention - self.write_target(self.persistent_field) + elif self.read_switch_heater() and self.mode != Mode.DRIVEN: + 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: - self._last_target = self.persistent_field - else: - super().doPoll() + self.__init_persistency = False + super().doPoll() def initModule(self): super().initModule() self.registerCallbacks(self) # for update_switch_heater 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 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.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 - def cleanup(self, sm): # sm is short for statemachine - if self.switch_heater != 0: - self.persistent_field = self.read_value() + def on_error(self, sm): # sm is short for statemachine + if self.switch_heater == ON: + self.read_value() if sm.mode != Mode.DRIVEN: 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): self.setFastPoll(True, 1.0) - if sm.target == self.persistent_field or ( + if sm.target == self.value or ( 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 - if self.switch_heater: + if self.switch_heater == ON: return self.start_switch_on return self.start_ramp_to_field - @status_code('PREPARING') + @status_code(Status.PREPARING) def start_ramp_to_field(self, sm): """start ramping current to persistent field @@ -213,12 +230,12 @@ class Magfield(SimpleMagfield): """ 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): if sm.init: sm.stabilize_start = 0 # in case current is already at field self.init_progress(sm, self.current) - dif = abs(self.current - self.persistent_field) + dif = abs(self.current - self.value) if dif > self.tolerance: tdif = self.get_progress(sm, self.current) if tdif > self.leads_ramp_tmo: @@ -229,7 +246,7 @@ class Magfield(SimpleMagfield): sm.stabilize_start = sm.now return self.stabilize_current - @status_code('PREPARING') + @status_code(Status.PREPARING) def stabilize_current(self, sm): if sm.now - sm.stabilize_start < self.wait_stable_leads: return Retry @@ -237,7 +254,6 @@ class Magfield(SimpleMagfield): def update_switch_heater(self, value): """is called whenever switch heater was changed""" - print('SW', value) if value == 0: if self.switch_off_time is None: self.log.info('restart switch_off_time') @@ -249,12 +265,12 @@ class Magfield(SimpleMagfield): self.switch_on_time = time.time() self.switch_off_time = None - @status_code('PREPARING') + @status_code(Status.PREPARING) 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' try: - self.write_switch_heater(True) + self.write_switch_heater(ON) except Exception as e: self.log.warning('write_switch_heater %r', e) return Retry @@ -262,13 +278,15 @@ class Magfield(SimpleMagfield): self.status = Status.PREPARING, 'wait for heater on' return self.wait_for_switch_on - @status_code('PREPARING') + @status_code(Status.PREPARING) def wait_for_switch_on(self, sm): 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 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?') return self.start_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 return self.start_ramp_to_target - @status_code('RAMPING') + @status_code(Status.RAMPING) def start_ramp_to_target(self, sm): """start ramping current to target field @@ -287,9 +305,8 @@ class Magfield(SimpleMagfield): """ raise NotImplementedError - @status_code('RAMPING') + @status_code(Status.RAMPING) def ramp_to_target(self, sm): - self.persistent_field = self.value dif = abs(self.value - sm.target) if sm.init: sm.stabilize_start = 0 # in case current is already at target @@ -298,6 +315,7 @@ class Magfield(SimpleMagfield): sm.stabilize_start = sm.now tdif = self.get_progress(sm, self.value) if tdif > self.workingramp / self.tolerance * 60 + self.ramp_tmo: + self.log.warn('no progress') raise HardwareError('no progress') sm.stabilize_start = None return Retry @@ -305,10 +323,9 @@ class Magfield(SimpleMagfield): sm.stabilize_start = sm.now return self.stabilize_field - @status_code('STABILIZING') + @status_code(Status.STABILIZING) def stabilize_field(self, sm): - self.persistent_field = self.value - if sm.now > sm.stablize_start + self.wait_stable_field: + if sm.now < sm.stabilize_start + self.wait_stable_field: return Retry return self.check_switch_off @@ -317,17 +334,18 @@ class Magfield(SimpleMagfield): return self.final_status(Status.PREPARED, 'driven') return self.start_switch_off - @status_code('FINALIZING') + @status_code(Status.FINALIZING) def start_switch_off(self, sm): - if self.switch_heater == 1: - self.write_switch_heater(False) + if self.switch_heater == ON: + self.write_switch_heater(OFF) return self.wait_for_switch_off - @status_code('FINALIZING') + @status_code(Status.FINALIZING) def wait_for_switch_off(self, sm): - self.persistent_field = self.value 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?') return self.start_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.start_ramp_to_zero - @status_code('FINALIZING') + @status_code(Status.FINALIZING) def start_ramp_to_zero(self, sm): """start ramping current to zero @@ -345,15 +363,15 @@ class Magfield(SimpleMagfield): """ raise NotImplementedError - @status_code('FINALIZING') + @status_code(Status.FINALIZING) def ramp_to_zero(self, sm): """ramp field to zero""" if sm.init: self.init_progress(sm, self.current) 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') 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.IDLE, 'persistent mode') diff --git a/secop_psi/mercury.py b/secop_psi/mercury.py index 431d721..839a767 100644 --- a/secop_psi/mercury.py +++ b/secop_psi/mercury.py @@ -123,13 +123,14 @@ class MercuryChannel(HasIO): else: 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 :param adr: as in see multiquery method :param values: [(name1, value1), (name2, value2) ...] :param convert: a converter function (converts given value to string and replied string to value) :param tolerance: tolerance for readback check + :param n_retry: number of retries or 0 for no readback check :return: the values as tuple Example: @@ -143,7 +144,7 @@ class MercuryChannel(HasIO): adr = self._complete_adr(adr) params = ['%s:%s' % (k, convert(v)) for k, v in values] 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() reply = self.communicate(cmd) head = 'STAT:SET:%s:' % adr @@ -158,6 +159,8 @@ class MercuryChannel(HasIO): except (AssertionError, AttributeError, ValueError) as e: 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 + if n_retry == 0: + return [v[1] for v in values] # no readback check keys = [v[0] for v in values] debug = [] readback = self.multiquery(adr, keys, convert, debug) @@ -182,9 +185,9 @@ class MercuryChannel(HasIO): adr, _, name = adr.rpartition(':') 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(':') - return self.multichange(adr, [(name, value)], convert, tolerance)[0] + return self.multichange(adr, [(name, value)], convert, tolerance, n_retry)[0] class TemperatureSensor(MercuryChannel, Readable):