Merge branch 'wip' of gitlab.psi.ch:samenv/frappy into wip
This commit is contained in:
31
cfg/cryosim.cfg
Normal file
31
cfg/cryosim.cfg
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
[NODE]
|
||||||
|
id = cyrosim.psi.ch
|
||||||
|
description = cryo simulation (similar ppms simulation)
|
||||||
|
|
||||||
|
[INTERFACE]
|
||||||
|
uri = tcp://5000
|
||||||
|
|
||||||
|
[tt]
|
||||||
|
class = secop_psi.ppms.Temp
|
||||||
|
description = main temperature
|
||||||
|
io = ppms
|
||||||
|
|
||||||
|
[lev]
|
||||||
|
class = secop_psi.ppms.Level
|
||||||
|
description = helium level
|
||||||
|
io = ppms
|
||||||
|
|
||||||
|
[ts]
|
||||||
|
class = secop_psi.ppms.UserChannel
|
||||||
|
description = sample temperature
|
||||||
|
enabled = 1
|
||||||
|
value.unit = K
|
||||||
|
io = ppms
|
||||||
|
|
||||||
|
[ppms]
|
||||||
|
class = secop_psi.ppms.Main
|
||||||
|
description = the main and poller module
|
||||||
|
class_id = QD.MULTIVU.PPMS.1
|
||||||
|
visibility = 3
|
||||||
|
pollinterval = 2
|
||||||
|
export = False
|
@ -1,5 +1,5 @@
|
|||||||
[NODE]
|
[NODE]
|
||||||
description = sea client (communication only)
|
description = sea client (tool for creating cfg)
|
||||||
id = comm.sea.psi.ch
|
id = comm.sea.psi.ch
|
||||||
|
|
||||||
[seaconn]
|
[seaconn]
|
||||||
|
38
cfg/magsim.cfg
Normal file
38
cfg/magsim.cfg
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
[NODE]
|
||||||
|
id = magsim.psi.ch
|
||||||
|
description = cryo magnet simulation (similar to ppms simulation)
|
||||||
|
|
||||||
|
[INTERFACE]
|
||||||
|
uri = tcp://5000
|
||||||
|
|
||||||
|
[tt]
|
||||||
|
class = secop_psi.ppms.Temp
|
||||||
|
description = main temperature
|
||||||
|
io = ppms
|
||||||
|
|
||||||
|
[mf]
|
||||||
|
class = secop_psi.ppms.Field
|
||||||
|
target.min = -9
|
||||||
|
target.max = 9
|
||||||
|
description = magnetic field
|
||||||
|
io = ppms
|
||||||
|
|
||||||
|
[lev]
|
||||||
|
class = secop_psi.ppms.Level
|
||||||
|
description = helium level
|
||||||
|
io = ppms
|
||||||
|
|
||||||
|
[ts]
|
||||||
|
class = secop_psi.ppms.UserChannel
|
||||||
|
description = sample temperature
|
||||||
|
enabled = 1
|
||||||
|
value.unit = K
|
||||||
|
io = ppms
|
||||||
|
|
||||||
|
[ppms]
|
||||||
|
class = secop_psi.ppms.Main
|
||||||
|
description = the main and poller module
|
||||||
|
class_id = QD.MULTIVU.PPMS.1
|
||||||
|
visibility = 3
|
||||||
|
pollinterval = 2
|
||||||
|
export = False
|
57
cfg/main/ill4.cfg
Normal file
57
cfg/main/ill4.cfg
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
[NODE]
|
||||||
|
description = orange cryostat with 70 mm sample space (FOCUS)
|
||||||
|
id = ill4.config.sea.psi.ch
|
||||||
|
|
||||||
|
[sea_main]
|
||||||
|
class = secop_psi.sea.SeaClient
|
||||||
|
description = main sea connection for ill4.config
|
||||||
|
config = ill4.config
|
||||||
|
service = main
|
||||||
|
|
||||||
|
[tt]
|
||||||
|
class = secop_psi.sea.SeaDrivable
|
||||||
|
iodev = sea_main
|
||||||
|
sea_object = tt
|
||||||
|
|
||||||
|
[cc]
|
||||||
|
class = secop_psi.sea.SeaReadable
|
||||||
|
iodev = sea_main
|
||||||
|
sea_object = cc
|
||||||
|
extra_modules = h
|
||||||
|
visibility = 3
|
||||||
|
|
||||||
|
[lev]
|
||||||
|
class = secop_psi.sea.SeaReadable
|
||||||
|
iodev = sea_main
|
||||||
|
single_module = cc.h
|
||||||
|
|
||||||
|
[nv]
|
||||||
|
class = secop_psi.sea.SeaWritable
|
||||||
|
iodev = sea_main
|
||||||
|
sea_object = nv
|
||||||
|
|
||||||
|
[ln2fill]
|
||||||
|
class = secop_psi.sea.SeaWritable
|
||||||
|
iodev = sea_main
|
||||||
|
sea_object = ln2fill
|
||||||
|
|
||||||
|
[hefill]
|
||||||
|
class = secop_psi.sea.SeaWritable
|
||||||
|
iodev = sea_main
|
||||||
|
sea_object = hefill
|
||||||
|
|
||||||
|
[hepump]
|
||||||
|
class = secop_psi.sea.SeaWritable
|
||||||
|
iodev = sea_main
|
||||||
|
sea_object = hepump
|
||||||
|
|
||||||
|
[hemot]
|
||||||
|
class = secop_psi.sea.SeaDrivable
|
||||||
|
iodev = sea_main
|
||||||
|
sea_object = hemot
|
||||||
|
visibility = 3
|
||||||
|
|
||||||
|
[table]
|
||||||
|
class = secop_psi.sea.SeaModule
|
||||||
|
iodev = sea_main
|
||||||
|
sea_object = table
|
@ -1,4 +1,5 @@
|
|||||||
{"cryo": {"base": "/cryo", "params": [{"path": "", "type": "float", "kids": 14},
|
{"cryo": {"base": "/cryo", "params": [
|
||||||
|
{"path": "", "type": "float", "kids": 14},
|
||||||
{"path": "send", "type": "text", "readonly": false, "cmd": "cryo send", "visibility": 3},
|
{"path": "send", "type": "text", "readonly": false, "cmd": "cryo send", "visibility": 3},
|
||||||
{"path": "status", "type": "text", "visibility": 3},
|
{"path": "status", "type": "text", "visibility": 3},
|
||||||
{"path": "cool", "type": "enum", "enum": {"on": 0, "off": 1}, "readonly": false, "cmd": "cryo cool"},
|
{"path": "cool", "type": "enum", "enum": {"on": 0, "off": 1}, "readonly": false, "cmd": "cryo cool"},
|
||||||
@ -17,4 +18,7 @@
|
|||||||
{"path": "filter/amplitude", "type": "float", "readonly": false, "cmd": "cryo filter/amplitude", "description": "oszillation amplitude / K (+/-)"},
|
{"path": "filter/amplitude", "type": "float", "readonly": false, "cmd": "cryo filter/amplitude", "description": "oszillation amplitude / K (+/-)"},
|
||||||
{"path": "filter/precision", "type": "float", "readonly": false, "cmd": "cryo filter/precision"},
|
{"path": "filter/precision", "type": "float", "readonly": false, "cmd": "cryo filter/precision"},
|
||||||
{"path": "filter/raw", "type": "float"},
|
{"path": "filter/raw", "type": "float"},
|
||||||
{"path": "filter/intdif", "type": "float"}]}, "addonlock_focus-be-filter": {"base": "/addonlock_focus-be-filter", "params": [{"path": "", "type": "text", "readonly": false, "cmd": "addonlock_focus-be-filter = "}]}}
|
{"path": "filter/intdif", "type": "float"}]},
|
||||||
|
|
||||||
|
"addonlock_focus-be-filter": {"base": "/addonlock_focus-be-filter", "params": [
|
||||||
|
{"path": "", "type": "text", "readonly": false, "cmd": "addonlock_focus-be-filter = "}]}}
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
|
[NODE]
|
||||||
|
description = FOCUS CryoTel Be-filter
|
||||||
|
id = focus-be-filter.addon.sea.psi.ch
|
||||||
|
|
||||||
[sea_addons]
|
[sea_addons]
|
||||||
class = secop_psi.sea.SeaClient
|
class = secop_psi.sea.SeaClient
|
||||||
description = SEA connection to ccr2ht_focus-be-filter
|
description = addons sea connection for focus-be-filter.addon
|
||||||
config = focus-be-filter.addon
|
config = focus-be-filter.addon
|
||||||
service = addons
|
service = addons
|
||||||
|
|
||||||
|
49
cfg/sea/ill4.cfg
Normal file
49
cfg/sea/ill4.cfg
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
[NODE]
|
||||||
|
description = orange cryostat with 70 mm sample space (FOCUS)
|
||||||
|
id = ill4.config.sea.psi.ch
|
||||||
|
|
||||||
|
[sea_main]
|
||||||
|
class = secop_psi.sea.SeaClient
|
||||||
|
description = main sea connection for ill4.config
|
||||||
|
config = ill4.config
|
||||||
|
service = main
|
||||||
|
|
||||||
|
[tt]
|
||||||
|
class = secop_psi.sea.SeaDrivable
|
||||||
|
iodev = sea_main
|
||||||
|
sea_object = tt
|
||||||
|
|
||||||
|
[cc]
|
||||||
|
class = secop_psi.sea.SeaReadable
|
||||||
|
iodev = sea_main
|
||||||
|
sea_object = cc
|
||||||
|
|
||||||
|
[nv]
|
||||||
|
class = secop_psi.sea.SeaWritable
|
||||||
|
iodev = sea_main
|
||||||
|
sea_object = nv
|
||||||
|
|
||||||
|
[ln2fill]
|
||||||
|
class = secop_psi.sea.SeaWritable
|
||||||
|
iodev = sea_main
|
||||||
|
sea_object = ln2fill
|
||||||
|
|
||||||
|
[hefill]
|
||||||
|
class = secop_psi.sea.SeaWritable
|
||||||
|
iodev = sea_main
|
||||||
|
sea_object = hefill
|
||||||
|
|
||||||
|
[hepump]
|
||||||
|
class = secop_psi.sea.SeaWritable
|
||||||
|
iodev = sea_main
|
||||||
|
sea_object = hepump
|
||||||
|
|
||||||
|
[hemot]
|
||||||
|
class = secop_psi.sea.SeaDrivable
|
||||||
|
iodev = sea_main
|
||||||
|
sea_object = hemot
|
||||||
|
|
||||||
|
[table]
|
||||||
|
class = secop_psi.sea.SeaReadable
|
||||||
|
iodev = sea_main
|
||||||
|
sea_object = table
|
299
cfg/sea/ill4.config.json
Normal file
299
cfg/sea/ill4.config.json
Normal file
@ -0,0 +1,299 @@
|
|||||||
|
{"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": "target", "type": "float"},
|
||||||
|
{"path": "running", "type": "int"},
|
||||||
|
{"path": "tolerance", "type": "float", "readonly": false, "cmd": "tt tolerance"},
|
||||||
|
{"path": "maxwait", "type": "float", "readonly": false, "cmd": "tt maxwait"},
|
||||||
|
{"path": "settle", "type": "float", "readonly": false, "cmd": "tt settle"},
|
||||||
|
{"path": "log", "type": "text", "readonly": false, "cmd": "tt log", "visibility": 3, "kids": 4},
|
||||||
|
{"path": "log/mean", "type": "float", "visibility": 3},
|
||||||
|
{"path": "log/m2", "type": "float", "visibility": 3},
|
||||||
|
{"path": "log/stddev", "type": "float", "visibility": 3},
|
||||||
|
{"path": "log/n", "type": "float", "visibility": 3},
|
||||||
|
{"path": "dblctrl", "type": "bool", "readonly": false, "cmd": "tt dblctrl", "kids": 9},
|
||||||
|
{"path": "dblctrl/tshift", "type": "float", "readonly": false, "cmd": "tt dblctrl/tshift"},
|
||||||
|
{"path": "dblctrl/mode", "type": "enum", "enum": {"disabled": -1, "inactive": 0, "stable": 1, "up": 2, "down": 3}, "readonly": false, "cmd": "tt dblctrl/mode"},
|
||||||
|
{"path": "dblctrl/shift_up", "type": "float"},
|
||||||
|
{"path": "dblctrl/shift_lo", "type": "float"},
|
||||||
|
{"path": "dblctrl/t_min", "type": "float"},
|
||||||
|
{"path": "dblctrl/t_max", "type": "float"},
|
||||||
|
{"path": "dblctrl/int2", "type": "float", "readonly": false, "cmd": "tt dblctrl/int2"},
|
||||||
|
{"path": "dblctrl/prop_up", "type": "float", "readonly": false, "cmd": "tt dblctrl/prop_up"},
|
||||||
|
{"path": "dblctrl/prop_lo", "type": "float", "readonly": false, "cmd": "tt dblctrl/prop_lo"},
|
||||||
|
{"path": "tm", "type": "float", "kids": 4},
|
||||||
|
{"path": "tm/curve", "type": "text", "readonly": false, "cmd": "tt tm/curve", "kids": 1},
|
||||||
|
{"path": "tm/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt tm/curve/points", "visibility": 3},
|
||||||
|
{"path": "tm/alarm", "type": "float", "readonly": false, "cmd": "tt tm/alarm"},
|
||||||
|
{"path": "tm/stddev", "type": "float"},
|
||||||
|
{"path": "tm/raw", "type": "float"},
|
||||||
|
{"path": "ts", "type": "float", "kids": 4},
|
||||||
|
{"path": "ts/curve", "type": "text", "readonly": false, "cmd": "tt ts/curve", "kids": 1},
|
||||||
|
{"path": "ts/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt ts/curve/points", "visibility": 3},
|
||||||
|
{"path": "ts/alarm", "type": "float", "readonly": false, "cmd": "tt ts/alarm"},
|
||||||
|
{"path": "ts/stddev", "type": "float"},
|
||||||
|
{"path": "ts/raw", "type": "float"},
|
||||||
|
{"path": "ts_2", "type": "float", "visibility": 3, "kids": 4},
|
||||||
|
{"path": "ts_2/curve", "type": "text", "readonly": false, "cmd": "tt ts_2/curve", "visibility": 3, "kids": 1},
|
||||||
|
{"path": "ts_2/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt ts_2/curve/points", "visibility": 3},
|
||||||
|
{"path": "ts_2/alarm", "type": "float", "readonly": false, "cmd": "tt ts_2/alarm", "visibility": 3},
|
||||||
|
{"path": "ts_2/stddev", "type": "float", "visibility": 3},
|
||||||
|
{"path": "ts_2/raw", "type": "float", "visibility": 3},
|
||||||
|
{"path": "set", "type": "float", "readonly": false, "cmd": "tt set", "kids": 18},
|
||||||
|
{"path": "set/mode", "type": "enum", "enum": {"disabled": -1, "off": 0, "controlling": 1, "manual": 2}, "readonly": false, "cmd": "tt set/mode"},
|
||||||
|
{"path": "set/reg", "type": "float"},
|
||||||
|
{"path": "set/ramp", "type": "float", "readonly": false, "cmd": "tt set/ramp", "description": "maximum ramp in K/min (0: ramp off)"},
|
||||||
|
{"path": "set/wramp", "type": "float", "readonly": false, "cmd": "tt set/wramp"},
|
||||||
|
{"path": "set/smooth", "type": "float", "readonly": false, "cmd": "tt set/smooth", "description": "smooth time (minutes)"},
|
||||||
|
{"path": "set/channel", "type": "text", "readonly": false, "cmd": "tt set/channel"},
|
||||||
|
{"path": "set/limit", "type": "float", "readonly": false, "cmd": "tt set/limit"},
|
||||||
|
{"path": "set/resist", "type": "float", "readonly": false, "cmd": "tt set/resist"},
|
||||||
|
{"path": "set/maxheater", "type": "text", "readonly": false, "cmd": "tt set/maxheater", "description": "maximum heater limit, units should be given without space: W, mW, A, mA"},
|
||||||
|
{"path": "set/linearpower", "type": "float", "readonly": false, "cmd": "tt set/linearpower", "description": "when not 0, it is the maximum effective power, and the power is linear to the heater output"},
|
||||||
|
{"path": "set/maxpowerlim", "type": "float", "description": "the maximum power limit (before any booster or converter)"},
|
||||||
|
{"path": "set/maxpower", "type": "float", "readonly": false, "cmd": "tt set/maxpower", "description": "maximum power [W]"},
|
||||||
|
{"path": "set/maxcurrent", "type": "float", "description": "the maximum current before any booster or converter"},
|
||||||
|
{"path": "set/manualpower", "type": "float", "readonly": false, "cmd": "tt set/manualpower"},
|
||||||
|
{"path": "set/power", "type": "float"},
|
||||||
|
{"path": "set/prop", "type": "float", "readonly": false, "cmd": "tt set/prop", "description": "bigger means more gain"},
|
||||||
|
{"path": "set/integ", "type": "float", "readonly": false, "cmd": "tt set/integ", "description": "bigger means faster"},
|
||||||
|
{"path": "set/deriv", "type": "float", "readonly": false, "cmd": "tt set/deriv"},
|
||||||
|
{"path": "display", "type": "text", "readonly": false, "cmd": "tt display"},
|
||||||
|
{"path": "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"},
|
||||||
|
{"path": "fs", "type": "enum", "enum": {"ok": 0, "no_sens": 1}, "readonly": false, "cmd": "cc fs"},
|
||||||
|
{"path": "mav", "type": "bool", "readonly": false, "cmd": "cc mav"},
|
||||||
|
{"path": "fm", "type": "enum", "enum": {"idle": 0, "opening": 1, "closing": 2, "opened": 3, "closed": 4, "no_motor": 5}},
|
||||||
|
{"path": "fa", "type": "enum", "enum": {"fixed": 0, "controlled": 1, "automatic": 2, "offline": 3}, "readonly": false, "cmd": "cc fa"},
|
||||||
|
{"path": "mp", "type": "float", "readonly": false, "cmd": "cc mp"},
|
||||||
|
{"path": "msp", "type": "float"},
|
||||||
|
{"path": "mmp", "type": "float"},
|
||||||
|
{"path": "mc", "type": "float", "readonly": false, "cmd": "cc mc"},
|
||||||
|
{"path": "mfc", "type": "float", "readonly": false, "cmd": "cc mfc"},
|
||||||
|
{"path": "moc", "type": "float", "readonly": false, "cmd": "cc moc"},
|
||||||
|
{"path": "mtc", "type": "float", "readonly": false, "cmd": "cc mtc"},
|
||||||
|
{"path": "mtl", "type": "float"},
|
||||||
|
{"path": "mft", "type": "float", "readonly": false, "cmd": "cc mft"},
|
||||||
|
{"path": "mt", "type": "float"},
|
||||||
|
{"path": "mo", "type": "float"},
|
||||||
|
{"path": "mcr", "type": "float"},
|
||||||
|
{"path": "mot", "type": "float"},
|
||||||
|
{"path": "mw", "type": "float", "readonly": false, "cmd": "cc mw", "description": "correction pulse after automatic open"},
|
||||||
|
{"path": "hav", "type": "bool", "readonly": false, "cmd": "cc hav"},
|
||||||
|
{"path": "h", "type": "float"},
|
||||||
|
{"path": "hr", "type": "float"},
|
||||||
|
{"path": "hc", "type": "float"},
|
||||||
|
{"path": "hu", "type": "float"},
|
||||||
|
{"path": "hh", "type": "float", "readonly": false, "cmd": "cc hh"},
|
||||||
|
{"path": "hl", "type": "float", "readonly": false, "cmd": "cc hl"},
|
||||||
|
{"path": "htf", "type": "float", "readonly": false, "cmd": "cc htf", "description": "meas. period in fast mode"},
|
||||||
|
{"path": "hts", "type": "float", "readonly": false, "cmd": "cc hts", "description": "meas. period in slow mode"},
|
||||||
|
{"path": "hd", "type": "float", "readonly": false, "cmd": "cc hd"},
|
||||||
|
{"path": "hwr", "type": "float", "readonly": false, "cmd": "cc hwr"},
|
||||||
|
{"path": "hem", "type": "float", "readonly": false, "cmd": "cc hem", "description": "sensor length in mm from top to empty pos."},
|
||||||
|
{"path": "hfu", "type": "float", "readonly": false, "cmd": "cc hfu", "description": "sensor length in mm from top to full pos."},
|
||||||
|
{"path": "hcd", "type": "enum", "enum": {"stop": 0, "fill": 1, "off": 2, "auto": 3, "manual": 7}, "readonly": false, "cmd": "cc hcd"},
|
||||||
|
{"path": "hv", "type": "enum", "enum": {"fill_valve_off": 0, "filling": 1, "no_fill_valve": 2, "timeout": 3, "timeout1": 4}},
|
||||||
|
{"path": "hsf", "type": "enum", "enum": {"sens_ok": 0, "sens_warm": 1, "no_sens": 2, "timeout": 3, "not_yet_read": 4, "disabled": 5}},
|
||||||
|
{"path": "ha", "type": "bool", "readonly": false, "cmd": "cc ha"},
|
||||||
|
{"path": "hm", "type": "bool"},
|
||||||
|
{"path": "hf", "type": "enum", "enum": {"slow": 0, "fast": 1}, "readonly": false, "cmd": "cc hf"},
|
||||||
|
{"path": "hbe", "type": "bool", "readonly": false, "cmd": "cc hbe"},
|
||||||
|
{"path": "hmf", "type": "float"},
|
||||||
|
{"path": "hms", "type": "float"},
|
||||||
|
{"path": "hit", "type": "float", "readonly": false, "cmd": "cc hit"},
|
||||||
|
{"path": "hft", "type": "int", "readonly": false, "cmd": "cc hft"},
|
||||||
|
{"path": "hea", "type": "enum", "enum": {"0": 0, "1": 1, "6": 2}, "readonly": false, "cmd": "cc hea"},
|
||||||
|
{"path": "hch", "type": "int", "readonly": false, "cmd": "cc hch"},
|
||||||
|
{"path": "hwr0", "type": "float", "readonly": false, "cmd": "cc hwr0"},
|
||||||
|
{"path": "hem0", "type": "float", "readonly": false, "cmd": "cc hem0", "description": "sensor length in mm from top to empty pos."},
|
||||||
|
{"path": "hfu0", "type": "float", "readonly": false, "cmd": "cc hfu0", "description": "sensor length in mm from top to full pos."},
|
||||||
|
{"path": "hd0", "type": "float", "readonly": false, "cmd": "cc hd0", "description": "external sensor drive current (mA)"},
|
||||||
|
{"path": "h0", "type": "float"},
|
||||||
|
{"path": "hs0", "type": "enum", "enum": {"sens_ok": 0, "sens_warm": 1, "no_sens": 2, "timeout": 3, "not_yet_read": 4, "disabled": 5}},
|
||||||
|
{"path": "h1", "type": "float"},
|
||||||
|
{"path": "hs1", "type": "enum", "enum": {"sens_ok": 0, "sens_warm": 1, "no_sens": 2, "timeout": 3, "not_yet_read": 4, "disabled": 5}},
|
||||||
|
{"path": "h2", "type": "float"},
|
||||||
|
{"path": "hs2", "type": "enum", "enum": {"sens_ok": 0, "sens_warm": 1, "no_sens": 2, "timeout": 3, "not_yet_read": 4, "disabled": 5}},
|
||||||
|
{"path": "h3", "type": "float"},
|
||||||
|
{"path": "hs3", "type": "enum", "enum": {"sens_ok": 0, "sens_warm": 1, "no_sens": 2, "timeout": 3, "not_yet_read": 4, "disabled": 5}},
|
||||||
|
{"path": "h4", "type": "float"},
|
||||||
|
{"path": "hs4", "type": "enum", "enum": {"sens_ok": 0, "sens_warm": 1, "no_sens": 2, "timeout": 3, "not_yet_read": 4, "disabled": 5}},
|
||||||
|
{"path": "h5", "type": "float"},
|
||||||
|
{"path": "hs5", "type": "enum", "enum": {"sens_ok": 0, "sens_warm": 1, "no_sens": 2, "timeout": 3, "not_yet_read": 4, "disabled": 5}},
|
||||||
|
{"path": "hfb", "type": "float"},
|
||||||
|
{"path": "nav", "type": "bool", "readonly": false, "cmd": "cc nav"},
|
||||||
|
{"path": "nu", "type": "float"},
|
||||||
|
{"path": "nl", "type": "float"},
|
||||||
|
{"path": "nth", "type": "float", "readonly": false, "cmd": "cc nth"},
|
||||||
|
{"path": "ntc", "type": "float", "readonly": false, "cmd": "cc ntc"},
|
||||||
|
{"path": "ntm", "type": "float", "readonly": false, "cmd": "cc ntm"},
|
||||||
|
{"path": "ns", "type": "enum", "enum": {"sens_ok": 0, "no_sens": 1, "short_circuit": 2, "upside_down": 3, "sens_warm": 4, "empty": 5}},
|
||||||
|
{"path": "na", "type": "bool", "readonly": false, "cmd": "cc na"},
|
||||||
|
{"path": "nv", "type": "enum", "enum": {"fill_valve_off": 0, "filling": 1, "no_fill_valve": 2, "timeout": 3, "timeout1": 4, "boost": 5}},
|
||||||
|
{"path": "nc", "type": "enum", "enum": {"stop": 0, "fill": 1, "off": 2, "auto": 3}, "readonly": false, "cmd": "cc nc"},
|
||||||
|
{"path": "nfb", "type": "float"},
|
||||||
|
{"path": "cda", "type": "float"},
|
||||||
|
{"path": "cdb", "type": "float"},
|
||||||
|
{"path": "cba", "type": "float"},
|
||||||
|
{"path": "cbb", "type": "float"},
|
||||||
|
{"path": "cvs", "type": "int"},
|
||||||
|
{"path": "csp", "type": "int"},
|
||||||
|
{"path": "cdv", "type": "text", "readonly": false, "cmd": "cc cdv"},
|
||||||
|
{"path": "cic", "type": "text", "readonly": false, "cmd": "cc cic"},
|
||||||
|
{"path": "cin", "type": "text"},
|
||||||
|
{"path": "cds", "type": "enum", "enum": {"local": 0, "remote": 1, "loading": 2, "by_code": 3, "by_touch": 4}, "readonly": false, "cmd": "cc cds"},
|
||||||
|
{"path": "timing", "type": "bool", "readonly": false, "cmd": "cc timing"},
|
||||||
|
{"path": "tc", "type": "float", "visibility": 3},
|
||||||
|
{"path": "tn", "type": "float", "visibility": 3},
|
||||||
|
{"path": "th", "type": "float", "visibility": 3},
|
||||||
|
{"path": "tf", "type": "float", "visibility": 3},
|
||||||
|
{"path": "tm", "type": "float", "visibility": 3},
|
||||||
|
{"path": "tv", "type": "float", "visibility": 3},
|
||||||
|
{"path": "tq", "type": "float", "visibility": 3},
|
||||||
|
{"path": "bdl", "type": "float", "readonly": false, "cmd": "cc bdl"}]},
|
||||||
|
|
||||||
|
"nv": {"base": "/nv", "params": [
|
||||||
|
{"path": "", "type": "enum", "enum": {"fixed": 0, "controlled": 1, "automatic": 2, "close": 3, "open": 4}, "readonly": false, "cmd": "nv", "kids": 11},
|
||||||
|
{"path": "send", "type": "text", "readonly": false, "cmd": "nv send", "visibility": 3},
|
||||||
|
{"path": "status", "type": "text", "visibility": 3},
|
||||||
|
{"path": "motstat", "type": "enum", "enum": {"idle": 0, "opening": 1, "closing": 2, "opened": 3, "closed": 4, "no_motor": 5}},
|
||||||
|
{"path": "flow", "type": "float"},
|
||||||
|
{"path": "set", "type": "float", "readonly": false, "cmd": "nv set"},
|
||||||
|
{"path": "flowmax", "type": "float", "readonly": false, "cmd": "nv flowmax"},
|
||||||
|
{"path": "flowp", "type": "float"},
|
||||||
|
{"path": "span", "type": "float"},
|
||||||
|
{"path": "ctrl", "type": "none", "kids": 13},
|
||||||
|
{"path": "ctrl/regtext", "type": "text"},
|
||||||
|
{"path": "ctrl/prop_o", "type": "float", "readonly": false, "cmd": "nv ctrl/prop_o", "description": "prop [sec/mbar] when opening. above 4 mbar a 10 times lower value is used"},
|
||||||
|
{"path": "ctrl/prop_c", "type": "float", "readonly": false, "cmd": "nv ctrl/prop_c", "description": "prop [sec/mbar] when closing. above 4 mbar a 10 times lower value is used"},
|
||||||
|
{"path": "ctrl/deriv_o", "type": "float", "readonly": false, "cmd": "nv ctrl/deriv_o", "description": "convergence target time [sec] when opening"},
|
||||||
|
{"path": "ctrl/deriv_c", "type": "float", "readonly": false, "cmd": "nv ctrl/deriv_c", "description": "convergence target time [sec] when closing"},
|
||||||
|
{"path": "ctrl/minpulse_o", "type": "float", "readonly": false, "cmd": "nv ctrl/minpulse_o", "description": "minimum close pulse [sec]"},
|
||||||
|
{"path": "ctrl/minpulse_c", "type": "float", "readonly": false, "cmd": "nv ctrl/minpulse_c", "description": "standard close pulse [sec]"},
|
||||||
|
{"path": "ctrl/hystpulse_o", "type": "float", "readonly": false, "cmd": "nv ctrl/hystpulse_o", "description": "motor pulse to overcome hysteresis when opening"},
|
||||||
|
{"path": "ctrl/hystpulse_c", "type": "float", "readonly": false, "cmd": "nv ctrl/hystpulse_c", "description": "motor pulse to overcome hysteresis when closing"},
|
||||||
|
{"path": "ctrl/tol", "type": "float", "readonly": false, "cmd": "nv ctrl/tol", "description": "valid below 3 mbar"},
|
||||||
|
{"path": "ctrl/tolhigh", "type": "float", "readonly": false, "cmd": "nv ctrl/tolhigh", "description": "valid above 4 mbar"},
|
||||||
|
{"path": "ctrl/openpulse", "type": "float", "readonly": false, "cmd": "nv ctrl/openpulse", "description": "time to open from completely closed to a significant opening"},
|
||||||
|
{"path": "ctrl/adjust_minpulse", "type": "bool", "readonly": false, "cmd": "nv ctrl/adjust_minpulse", "description": "adjust minpulse automatically"},
|
||||||
|
{"path": "autoflow", "type": "none", "kids": 24},
|
||||||
|
{"path": "autoflow/suspended", "type": "bool", "readonly": false, "cmd": "nv autoflow/suspended"},
|
||||||
|
{"path": "autoflow/prop", "type": "float", "readonly": false, "cmd": "nv autoflow/prop"},
|
||||||
|
{"path": "autoflow/flowstd", "type": "float", "readonly": false, "cmd": "nv autoflow/flowstd"},
|
||||||
|
{"path": "autoflow/flowlim", "type": "float", "readonly": false, "cmd": "nv autoflow/flowlim"},
|
||||||
|
{"path": "autoflow/smooth", "type": "float", "readonly": false, "cmd": "nv autoflow/smooth"},
|
||||||
|
{"path": "autoflow/difSize", "type": "float", "readonly": false, "cmd": "nv autoflow/difSize"},
|
||||||
|
{"path": "autoflow/difRange", "type": "float", "readonly": false, "cmd": "nv autoflow/difRange"},
|
||||||
|
{"path": "autoflow/flowSize", "type": "float", "readonly": false, "cmd": "nv autoflow/flowSize"},
|
||||||
|
{"path": "autoflow/convTime", "type": "float", "readonly": false, "cmd": "nv autoflow/convTime"},
|
||||||
|
{"path": "autoflow/Tmin", "type": "float", "readonly": false, "cmd": "nv autoflow/Tmin"},
|
||||||
|
{"path": "autoflow/script", "type": "text", "readonly": false, "cmd": "nv autoflow/script"},
|
||||||
|
{"path": "autoflow/getTemp", "type": "text", "readonly": false, "cmd": "nv autoflow/getTemp"},
|
||||||
|
{"path": "autoflow/getTset", "type": "text", "readonly": false, "cmd": "nv autoflow/getTset"},
|
||||||
|
{"path": "autoflow/getFlow", "type": "text", "readonly": false, "cmd": "nv autoflow/getFlow"},
|
||||||
|
{"path": "autoflow/difBuf", "type": "text"},
|
||||||
|
{"path": "autoflow/flowBuf", "type": "text"},
|
||||||
|
{"path": "autoflow/flowset", "type": "float"},
|
||||||
|
{"path": "autoflow/flowmin", "type": "float"},
|
||||||
|
{"path": "autoflow/flowmax", "type": "float"},
|
||||||
|
{"path": "autoflow/difmin", "type": "float"},
|
||||||
|
{"path": "autoflow/difmax", "type": "float"},
|
||||||
|
{"path": "autoflow/setmin", "type": "float"},
|
||||||
|
{"path": "autoflow/setmax", "type": "float"},
|
||||||
|
{"path": "autoflow/flowtarget", "type": "float"},
|
||||||
|
{"path": "calib", "type": "none", "kids": 2},
|
||||||
|
{"path": "calib/ln_per_min_per_mbar", "type": "float", "readonly": false, "cmd": "nv calib/ln_per_min_per_mbar"},
|
||||||
|
{"path": "calib/mbar_offset", "type": "float", "readonly": false, "cmd": "nv calib/mbar_offset"}]},
|
||||||
|
|
||||||
|
"ln2fill": {"base": "/ln2fill", "params": [
|
||||||
|
{"path": "", "type": "enum", "enum": {"watching": 0, "fill": 1, "inactive": 2}, "readonly": false, "cmd": "ln2fill", "kids": 3},
|
||||||
|
{"path": "send", "type": "text", "readonly": false, "cmd": "ln2fill send", "visibility": 3},
|
||||||
|
{"path": "status", "type": "text", "visibility": 3},
|
||||||
|
{"path": "state", "type": "text"}]},
|
||||||
|
|
||||||
|
"hefill": {"base": "/hefill", "params": [
|
||||||
|
{"path": "", "type": "enum", "enum": {"watching": 0, "fill": 1, "timeout": 2}, "readonly": false, "cmd": "hefill", "kids": 6},
|
||||||
|
{"path": "send", "type": "text", "readonly": false, "cmd": "hefill send", "visibility": 3},
|
||||||
|
{"path": "status", "type": "text", "visibility": 3},
|
||||||
|
{"path": "fast", "type": "enum", "enum": {"slow": 0, "fast": 1}, "readonly": false, "cmd": "cc hf"},
|
||||||
|
{"path": "state", "type": "text"},
|
||||||
|
{"path": "hefull", "type": "float", "readonly": false, "cmd": "cc hh"},
|
||||||
|
{"path": "helow", "type": "float", "readonly": false, "cmd": "cc hl"}]},
|
||||||
|
|
||||||
|
"hepump": {"base": "/hepump", "params": [
|
||||||
|
{"path": "", "type": "enum", "enum": {"xds35_auto": 0, "xds35_manual": 1, "sv65": 2, "other": 3, "no": -1}, "readonly": false, "cmd": "hepump", "description": "xds35: scroll pump, sv65: leybold", "kids": 9},
|
||||||
|
{"path": "send", "type": "text", "readonly": false, "cmd": "hepump send", "visibility": 3},
|
||||||
|
{"path": "status", "type": "text", "visibility": 3},
|
||||||
|
{"path": "running", "type": "bool", "readonly": false, "cmd": "hepump running"},
|
||||||
|
{"path": "eco", "type": "bool", "readonly": false, "cmd": "hepump eco"},
|
||||||
|
{"path": "auto", "type": "bool", "readonly": false, "cmd": "hepump auto"},
|
||||||
|
{"path": "valve", "type": "enum", "enum": {"closed": 0, "closing": 1, "opening": 2, "opened": 3, "undefined": 4}, "readonly": false, "cmd": "hepump valve"},
|
||||||
|
{"path": "eco_t_lim", "type": "float", "readonly": false, "cmd": "hepump eco_t_lim", "description": "switch off eco mode when T_set < eco_t_lim and T < eco_t_lim * 2"},
|
||||||
|
{"path": "calib", "type": "float", "readonly": false, "cmd": "hepump calib", "visibility": 3}]},
|
||||||
|
|
||||||
|
"hemot": {"base": "/hepump/hemot", "params": [
|
||||||
|
{"path": "", "type": "float", "readonly": false, "cmd": "run hemot", "kids": 32},
|
||||||
|
{"path": "send", "type": "text", "readonly": false, "cmd": "hemot send", "visibility": 3},
|
||||||
|
{"path": "status", "type": "text", "visibility": 3},
|
||||||
|
{"path": "is_running", "type": "int", "readonly": false, "cmd": "hemot is_running", "visibility": 3},
|
||||||
|
{"path": "pos", "type": "float"},
|
||||||
|
{"path": "encoder", "type": "float"},
|
||||||
|
{"path": "zero", "type": "float", "readonly": false, "cmd": "hemot zero"},
|
||||||
|
{"path": "lowerlimit", "type": "float", "readonly": false, "cmd": "hemot lowerlimit"},
|
||||||
|
{"path": "upperlimit", "type": "float", "readonly": false, "cmd": "hemot upperlimit"},
|
||||||
|
{"path": "disablelimits", "type": "bool", "readonly": false, "cmd": "hemot disablelimits"},
|
||||||
|
{"path": "verbose", "type": "bool", "readonly": false, "cmd": "hemot verbose"},
|
||||||
|
{"path": "target", "type": "float"},
|
||||||
|
{"path": "runstate", "type": "enum", "enum": {"idle": 0, "running": 1, "finished": 2, "error": 3}},
|
||||||
|
{"path": "precision", "type": "float", "readonly": false, "cmd": "hemot precision"},
|
||||||
|
{"path": "maxencdif", "type": "float", "readonly": false, "cmd": "hemot maxencdif"},
|
||||||
|
{"path": "id", "type": "float", "readonly": false, "cmd": "hemot id"},
|
||||||
|
{"path": "pump_number", "type": "float", "readonly": false, "cmd": "hemot pump_number"},
|
||||||
|
{"path": "init", "type": "float", "readonly": false, "cmd": "hemot init"},
|
||||||
|
{"path": "maxspeed", "type": "float", "readonly": false, "cmd": "hemot maxspeed"},
|
||||||
|
{"path": "acceleration", "type": "float", "readonly": false, "cmd": "hemot acceleration"},
|
||||||
|
{"path": "maxcurrent", "type": "float", "readonly": false, "cmd": "hemot maxcurrent"},
|
||||||
|
{"path": "standbycurrent", "type": "float", "readonly": false, "cmd": "hemot standbycurrent"},
|
||||||
|
{"path": "freewheeling", "type": "bool", "readonly": false, "cmd": "hemot freewheeling"},
|
||||||
|
{"path": "output0", "type": "bool", "readonly": false, "cmd": "hemot output0"},
|
||||||
|
{"path": "output1", "type": "bool", "readonly": false, "cmd": "hemot output1"},
|
||||||
|
{"path": "input3", "type": "bool"},
|
||||||
|
{"path": "input0", "type": "float"},
|
||||||
|
{"path": "input0raw", "type": "float"},
|
||||||
|
{"path": "pullup", "type": "float", "readonly": false, "cmd": "hemot pullup"},
|
||||||
|
{"path": "nopumpfeedback", "type": "bool", "readonly": false, "cmd": "hemot nopumpfeedback"},
|
||||||
|
{"path": "eeprom", "type": "enum", "enum": {"ok": 0, "dirty": 1, "save": 2, "load": 3}, "readonly": false, "cmd": "hemot eeprom"},
|
||||||
|
{"path": "customadr", "type": "text", "readonly": false, "cmd": "hemot customadr"},
|
||||||
|
{"path": "custompar", "type": "float", "readonly": false, "cmd": "hemot custompar"}]},
|
||||||
|
|
||||||
|
"table": {"base": "/table", "params": [
|
||||||
|
{"path": "", "type": "none", "kids": 17},
|
||||||
|
{"path": "send", "type": "text", "readonly": false, "cmd": "table send", "visibility": 3},
|
||||||
|
{"path": "status", "type": "text", "visibility": 3},
|
||||||
|
{"path": "fix_tt_set_prop", "type": "bool", "readonly": false, "cmd": "table fix_tt_set_prop"},
|
||||||
|
{"path": "val_tt_set_prop", "type": "float"},
|
||||||
|
{"path": "tbl_tt_set_prop", "type": "text", "readonly": false, "cmd": "table tbl_tt_set_prop", "description": "enter value pair separated with colon T1:par1 T2:par2 ..."},
|
||||||
|
{"path": "fix_tt_set_integ", "type": "bool", "readonly": false, "cmd": "table fix_tt_set_integ"},
|
||||||
|
{"path": "val_tt_set_integ", "type": "float"},
|
||||||
|
{"path": "tbl_tt_set_integ", "type": "text", "readonly": false, "cmd": "table tbl_tt_set_integ", "description": "enter value pair separated with colon T1:par1 T2:par2 ..."},
|
||||||
|
{"path": "fix_tt_dblctrl_int2", "type": "bool", "readonly": false, "cmd": "table fix_tt_dblctrl_int2"},
|
||||||
|
{"path": "val_tt_dblctrl_int2", "type": "float"},
|
||||||
|
{"path": "tbl_tt_dblctrl_int2", "type": "text", "readonly": false, "cmd": "table tbl_tt_dblctrl_int2", "description": "enter value pair separated with colon T1:par1 T2:par2 ..."},
|
||||||
|
{"path": "fix_tt_dblctrl_prop_up", "type": "bool", "readonly": false, "cmd": "table fix_tt_dblctrl_prop_up"},
|
||||||
|
{"path": "val_tt_dblctrl_prop_up", "type": "float"},
|
||||||
|
{"path": "tbl_tt_dblctrl_prop_up", "type": "text", "readonly": false, "cmd": "table tbl_tt_dblctrl_prop_up", "description": "enter value pair separated with colon T1:par1 T2:par2 ..."},
|
||||||
|
{"path": "fix_tt_dblctrl_prop_lo", "type": "bool", "readonly": false, "cmd": "table fix_tt_dblctrl_prop_lo"},
|
||||||
|
{"path": "val_tt_dblctrl_prop_lo", "type": "float"},
|
||||||
|
{"path": "tbl_tt_dblctrl_prop_lo", "type": "text", "readonly": false, "cmd": "table tbl_tt_dblctrl_prop_lo", "description": "enter value pair separated with colon T1:par1 T2:par2 ..."}]}}
|
@ -6,4 +6,307 @@
|
|||||||
{"path": "limit", "type": "float", "readonly": false, "cmd": "warmup limit", "description": "do not use warmup when target < limit"},
|
{"path": "limit", "type": "float", "readonly": false, "cmd": "warmup limit", "description": "do not use warmup when target < limit"},
|
||||||
{"path": "timef", "type": "float", "readonly": false, "cmd": "warmup timef", "description": "reset double control after (timef * int2) sec"},
|
{"path": "timef", "type": "float", "readonly": false, "cmd": "warmup timef", "description": "reset double control after (timef * int2) sec"},
|
||||||
{"path": "abruptstop", "type": "bool", "readonly": false, "cmd": "warmup abruptstop", "description": "switch off sample heater immediately after tm > treg"},
|
{"path": "abruptstop", "type": "bool", "readonly": false, "cmd": "warmup abruptstop", "description": "switch off sample heater immediately after tm > treg"},
|
||||||
{"path": "warmup", "type": "enum", "enum": {"off": 0, "warmup": 1, "htroff": 2, "settle": 3}, "readonly": false, "cmd": "warmup warmup"}]}}
|
{"path": "warmup", "type": "enum", "enum": {"off": 0, "warmup": 1, "htroff": 2, "settle": 3}, "readonly": false, "cmd": "warmup warmup"}]},
|
||||||
|
"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": "target", "type": "float"},
|
||||||
|
{"path": "running", "type": "int"},
|
||||||
|
{"path": "tolerance", "type": "float", "readonly": false, "cmd": "tt tolerance"},
|
||||||
|
{"path": "maxwait", "type": "float", "readonly": false, "cmd": "tt maxwait"},
|
||||||
|
{"path": "settle", "type": "float", "readonly": false, "cmd": "tt settle"},
|
||||||
|
{"path": "log", "type": "text", "readonly": false, "cmd": "tt log", "visibility": 3, "kids": 4},
|
||||||
|
{"path": "log/mean", "type": "float", "visibility": 3},
|
||||||
|
{"path": "log/m2", "type": "float", "visibility": 3},
|
||||||
|
{"path": "log/stddev", "type": "float", "visibility": 3},
|
||||||
|
{"path": "log/n", "type": "float", "visibility": 3},
|
||||||
|
{"path": "dblctrl", "type": "bool", "readonly": false, "cmd": "tt dblctrl", "kids": 9},
|
||||||
|
{"path": "dblctrl/tshift", "type": "float", "readonly": false, "cmd": "tt dblctrl/tshift"},
|
||||||
|
{"path": "dblctrl/mode", "type": "enum", "enum": {"disabled": -1, "inactive": 0, "stable": 1, "up": 2, "down": 3}, "readonly": false, "cmd": "tt dblctrl/mode"},
|
||||||
|
{"path": "dblctrl/shift_up", "type": "float"},
|
||||||
|
{"path": "dblctrl/shift_lo", "type": "float"},
|
||||||
|
{"path": "dblctrl/t_min", "type": "float"},
|
||||||
|
{"path": "dblctrl/t_max", "type": "float"},
|
||||||
|
{"path": "dblctrl/int2", "type": "float", "readonly": false, "cmd": "tt dblctrl/int2"},
|
||||||
|
{"path": "dblctrl/prop_up", "type": "float", "readonly": false, "cmd": "tt dblctrl/prop_up"},
|
||||||
|
{"path": "dblctrl/prop_lo", "type": "float", "readonly": false, "cmd": "tt dblctrl/prop_lo"},
|
||||||
|
{"path": "tm", "type": "float", "kids": 4},
|
||||||
|
{"path": "tm/curve", "type": "text", "readonly": false, "cmd": "tt tm/curve", "kids": 1},
|
||||||
|
{"path": "tm/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt tm/curve/points", "visibility": 3},
|
||||||
|
{"path": "tm/alarm", "type": "float", "readonly": false, "cmd": "tt tm/alarm"},
|
||||||
|
{"path": "tm/stddev", "type": "float"},
|
||||||
|
{"path": "tm/raw", "type": "float"},
|
||||||
|
{"path": "ts", "type": "float", "kids": 4},
|
||||||
|
{"path": "ts/curve", "type": "text", "readonly": false, "cmd": "tt ts/curve", "kids": 1},
|
||||||
|
{"path": "ts/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt ts/curve/points", "visibility": 3},
|
||||||
|
{"path": "ts/alarm", "type": "float", "readonly": false, "cmd": "tt ts/alarm"},
|
||||||
|
{"path": "ts/stddev", "type": "float"},
|
||||||
|
{"path": "ts/raw", "type": "float"},
|
||||||
|
{"path": "ts_2", "type": "float", "visibility": 3, "kids": 4},
|
||||||
|
{"path": "ts_2/curve", "type": "text", "readonly": false, "cmd": "tt ts_2/curve", "visibility": 3, "kids": 1},
|
||||||
|
{"path": "ts_2/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt ts_2/curve/points", "visibility": 3},
|
||||||
|
{"path": "ts_2/alarm", "type": "float", "readonly": false, "cmd": "tt ts_2/alarm", "visibility": 3},
|
||||||
|
{"path": "ts_2/stddev", "type": "float", "visibility": 3},
|
||||||
|
{"path": "ts_2/raw", "type": "float", "visibility": 3},
|
||||||
|
{"path": "set", "type": "float", "readonly": false, "cmd": "tt set", "kids": 18},
|
||||||
|
{"path": "set/mode", "type": "enum", "enum": {"disabled": -1, "off": 0, "controlling": 1, "manual": 2}, "readonly": false, "cmd": "tt set/mode"},
|
||||||
|
{"path": "set/reg", "type": "float"},
|
||||||
|
{"path": "set/ramp", "type": "float", "readonly": false, "cmd": "tt set/ramp", "description": "maximum ramp in K/min (0: ramp off)"},
|
||||||
|
{"path": "set/wramp", "type": "float", "readonly": false, "cmd": "tt set/wramp"},
|
||||||
|
{"path": "set/smooth", "type": "float", "readonly": false, "cmd": "tt set/smooth", "description": "smooth time (minutes)"},
|
||||||
|
{"path": "set/channel", "type": "text", "readonly": false, "cmd": "tt set/channel"},
|
||||||
|
{"path": "set/limit", "type": "float", "readonly": false, "cmd": "tt set/limit"},
|
||||||
|
{"path": "set/resist", "type": "float", "readonly": false, "cmd": "tt set/resist"},
|
||||||
|
{"path": "set/maxheater", "type": "text", "readonly": false, "cmd": "tt set/maxheater", "description": "maximum heater limit, units should be given without space: W, mW, A, mA"},
|
||||||
|
{"path": "set/linearpower", "type": "float", "readonly": false, "cmd": "tt set/linearpower", "description": "when not 0, it is the maximum effective power, and the power is linear to the heater output"},
|
||||||
|
{"path": "set/maxpowerlim", "type": "float", "description": "the maximum power limit (before any booster or converter)"},
|
||||||
|
{"path": "set/maxpower", "type": "float", "readonly": false, "cmd": "tt set/maxpower", "description": "maximum power [W]"},
|
||||||
|
{"path": "set/maxcurrent", "type": "float", "description": "the maximum current before any booster or converter"},
|
||||||
|
{"path": "set/manualpower", "type": "float", "readonly": false, "cmd": "tt set/manualpower"},
|
||||||
|
{"path": "set/power", "type": "float"},
|
||||||
|
{"path": "set/prop", "type": "float", "readonly": false, "cmd": "tt set/prop", "description": "bigger means more gain"},
|
||||||
|
{"path": "set/integ", "type": "float", "readonly": false, "cmd": "tt set/integ", "description": "bigger means faster"},
|
||||||
|
{"path": "set/deriv", "type": "float", "readonly": false, "cmd": "tt set/deriv"},
|
||||||
|
{"path": "display", "type": "text", "readonly": false, "cmd": "tt display"},
|
||||||
|
{"path": "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"},
|
||||||
|
{"path": "fs", "type": "enum", "enum": {"ok": 0, "no_sens": 1}, "readonly": false, "cmd": "cc fs"},
|
||||||
|
{"path": "mav", "type": "bool", "readonly": false, "cmd": "cc mav"},
|
||||||
|
{"path": "fm", "type": "enum", "enum": {"idle": 0, "opening": 1, "closing": 2, "opened": 3, "closed": 4, "no_motor": 5}},
|
||||||
|
{"path": "fa", "type": "enum", "enum": {"fixed": 0, "controlled": 1, "automatic": 2, "offline": 3}, "readonly": false, "cmd": "cc fa"},
|
||||||
|
{"path": "mp", "type": "float", "readonly": false, "cmd": "cc mp"},
|
||||||
|
{"path": "msp", "type": "float"},
|
||||||
|
{"path": "mmp", "type": "float"},
|
||||||
|
{"path": "mc", "type": "float", "readonly": false, "cmd": "cc mc"},
|
||||||
|
{"path": "mfc", "type": "float", "readonly": false, "cmd": "cc mfc"},
|
||||||
|
{"path": "moc", "type": "float", "readonly": false, "cmd": "cc moc"},
|
||||||
|
{"path": "mtc", "type": "float", "readonly": false, "cmd": "cc mtc"},
|
||||||
|
{"path": "mtl", "type": "float"},
|
||||||
|
{"path": "mft", "type": "float", "readonly": false, "cmd": "cc mft"},
|
||||||
|
{"path": "mt", "type": "float"},
|
||||||
|
{"path": "mo", "type": "float"},
|
||||||
|
{"path": "mcr", "type": "float"},
|
||||||
|
{"path": "mot", "type": "float"},
|
||||||
|
{"path": "mw", "type": "float", "readonly": false, "cmd": "cc mw", "description": "correction pulse after automatic open"},
|
||||||
|
{"path": "hav", "type": "bool", "readonly": false, "cmd": "cc hav"},
|
||||||
|
{"path": "h", "type": "float"},
|
||||||
|
{"path": "hr", "type": "float"},
|
||||||
|
{"path": "hc", "type": "float"},
|
||||||
|
{"path": "hu", "type": "float"},
|
||||||
|
{"path": "hh", "type": "float", "readonly": false, "cmd": "cc hh"},
|
||||||
|
{"path": "hl", "type": "float", "readonly": false, "cmd": "cc hl"},
|
||||||
|
{"path": "htf", "type": "float", "readonly": false, "cmd": "cc htf", "description": "meas. period in fast mode"},
|
||||||
|
{"path": "hts", "type": "float", "readonly": false, "cmd": "cc hts", "description": "meas. period in slow mode"},
|
||||||
|
{"path": "hd", "type": "float", "readonly": false, "cmd": "cc hd"},
|
||||||
|
{"path": "hwr", "type": "float", "readonly": false, "cmd": "cc hwr"},
|
||||||
|
{"path": "hem", "type": "float", "readonly": false, "cmd": "cc hem", "description": "sensor length in mm from top to empty pos."},
|
||||||
|
{"path": "hfu", "type": "float", "readonly": false, "cmd": "cc hfu", "description": "sensor length in mm from top to full pos."},
|
||||||
|
{"path": "hcd", "type": "enum", "enum": {"stop": 0, "fill": 1, "off": 2, "auto": 3, "manual": 7}, "readonly": false, "cmd": "cc hcd"},
|
||||||
|
{"path": "hv", "type": "enum", "enum": {"fill_valve_off": 0, "filling": 1, "no_fill_valve": 2, "timeout": 3, "timeout1": 4}},
|
||||||
|
{"path": "hsf", "type": "enum", "enum": {"sens_ok": 0, "sens_warm": 1, "no_sens": 2, "timeout": 3, "not_yet_read": 4, "disabled": 5}},
|
||||||
|
{"path": "ha", "type": "bool", "readonly": false, "cmd": "cc ha"},
|
||||||
|
{"path": "hm", "type": "bool"},
|
||||||
|
{"path": "hf", "type": "enum", "enum": {"slow": 0, "fast": 1}, "readonly": false, "cmd": "cc hf"},
|
||||||
|
{"path": "hbe", "type": "bool", "readonly": false, "cmd": "cc hbe"},
|
||||||
|
{"path": "hmf", "type": "float"},
|
||||||
|
{"path": "hms", "type": "float"},
|
||||||
|
{"path": "hit", "type": "float", "readonly": false, "cmd": "cc hit"},
|
||||||
|
{"path": "hft", "type": "int", "readonly": false, "cmd": "cc hft"},
|
||||||
|
{"path": "hea", "type": "enum", "enum": {"0": 0, "1": 1, "6": 2}, "readonly": false, "cmd": "cc hea"},
|
||||||
|
{"path": "hch", "type": "int", "readonly": false, "cmd": "cc hch", "visibility": 3},
|
||||||
|
{"path": "hwr0", "type": "float", "readonly": false, "cmd": "cc hwr0", "visibility": 3},
|
||||||
|
{"path": "hem0", "type": "float", "readonly": false, "cmd": "cc hem0", "description": "sensor length in mm from top to empty pos.", "visibility": 3},
|
||||||
|
{"path": "hfu0", "type": "float", "readonly": false, "cmd": "cc hfu0", "description": "sensor length in mm from top to full pos.", "visibility": 3},
|
||||||
|
{"path": "hd0", "type": "float", "readonly": false, "cmd": "cc hd0", "description": "external sensor drive current (mA)", "visibility": 3},
|
||||||
|
{"path": "h0", "type": "float", "visibility": 3},
|
||||||
|
{"path": "hs0", "type": "enum", "enum": {"sens_ok": 0, "sens_warm": 1, "no_sens": 2, "timeout": 3, "not_yet_read": 4, "disabled": 5}, "visibility": 3},
|
||||||
|
{"path": "h1", "type": "float", "visibility": 3},
|
||||||
|
{"path": "hs1", "type": "enum", "enum": {"sens_ok": 0, "sens_warm": 1, "no_sens": 2, "timeout": 3, "not_yet_read": 4, "disabled": 5}, "visibility": 3},
|
||||||
|
{"path": "h2", "type": "float", "visibility": 3},
|
||||||
|
{"path": "hs2", "type": "enum", "enum": {"sens_ok": 0, "sens_warm": 1, "no_sens": 2, "timeout": 3, "not_yet_read": 4, "disabled": 5}, "visibility": 3},
|
||||||
|
{"path": "h3", "type": "float", "visibility": 3},
|
||||||
|
{"path": "hs3", "type": "enum", "enum": {"sens_ok": 0, "sens_warm": 1, "no_sens": 2, "timeout": 3, "not_yet_read": 4, "disabled": 5}, "visibility": 3},
|
||||||
|
{"path": "h4", "type": "float", "visibility": 3},
|
||||||
|
{"path": "hs4", "type": "enum", "enum": {"sens_ok": 0, "sens_warm": 1, "no_sens": 2, "timeout": 3, "not_yet_read": 4, "disabled": 5}, "visibility": 3},
|
||||||
|
{"path": "h5", "type": "float", "visibility": 3},
|
||||||
|
{"path": "hs5", "type": "enum", "enum": {"sens_ok": 0, "sens_warm": 1, "no_sens": 2, "timeout": 3, "not_yet_read": 4, "disabled": 5}, "visibility": 3},
|
||||||
|
{"path": "hfb", "type": "float"},
|
||||||
|
{"path": "nav", "type": "bool", "readonly": false, "cmd": "cc nav"},
|
||||||
|
{"path": "nu", "type": "float"},
|
||||||
|
{"path": "nl", "type": "float"},
|
||||||
|
{"path": "nth", "type": "float", "readonly": false, "cmd": "cc nth"},
|
||||||
|
{"path": "ntc", "type": "float", "readonly": false, "cmd": "cc ntc"},
|
||||||
|
{"path": "ntm", "type": "float", "readonly": false, "cmd": "cc ntm"},
|
||||||
|
{"path": "ns", "type": "enum", "enum": {"sens_ok": 0, "no_sens": 1, "short_circuit": 2, "upside_down": 3, "sens_warm": 4, "empty": 5}},
|
||||||
|
{"path": "na", "type": "bool", "readonly": false, "cmd": "cc na"},
|
||||||
|
{"path": "nv", "type": "enum", "enum": {"fill_valve_off": 0, "filling": 1, "no_fill_valve": 2, "timeout": 3, "timeout1": 4, "boost": 5}},
|
||||||
|
{"path": "nc", "type": "enum", "enum": {"stop": 0, "fill": 1, "off": 2, "auto": 3}, "readonly": false, "cmd": "cc nc"},
|
||||||
|
{"path": "nfb", "type": "float"},
|
||||||
|
{"path": "cda", "type": "float"},
|
||||||
|
{"path": "cdb", "type": "float"},
|
||||||
|
{"path": "cba", "type": "float"},
|
||||||
|
{"path": "cbb", "type": "float"},
|
||||||
|
{"path": "cvs", "type": "int"},
|
||||||
|
{"path": "csp", "type": "int"},
|
||||||
|
{"path": "cdv", "type": "text", "readonly": false, "cmd": "cc cdv"},
|
||||||
|
{"path": "cic", "type": "text", "readonly": false, "cmd": "cc cic"},
|
||||||
|
{"path": "cin", "type": "text"},
|
||||||
|
{"path": "cds", "type": "enum", "enum": {"local": 0, "remote": 1, "loading": 2, "by_code": 3, "by_touch": 4}, "readonly": false, "cmd": "cc cds"},
|
||||||
|
{"path": "timing", "type": "bool", "readonly": false, "cmd": "cc timing"},
|
||||||
|
{"path": "tc", "type": "float", "visibility": 3},
|
||||||
|
{"path": "tn", "type": "float", "visibility": 3},
|
||||||
|
{"path": "th", "type": "float", "visibility": 3},
|
||||||
|
{"path": "tf", "type": "float", "visibility": 3},
|
||||||
|
{"path": "tm", "type": "float", "visibility": 3},
|
||||||
|
{"path": "tv", "type": "float", "visibility": 3},
|
||||||
|
{"path": "tq", "type": "float", "visibility": 3},
|
||||||
|
{"path": "bdl", "type": "float", "readonly": false, "cmd": "cc bdl"}]}, "nv": {"base": "/nv", "params": [{"path": "", "type": "enum", "enum": {"fixed": 0, "controlled": 1, "automatic": 2, "close": 3, "open": 4}, "readonly": false, "cmd": "nv", "kids": 12},
|
||||||
|
{"path": "send", "type": "text", "readonly": false, "cmd": "nv send", "visibility": 3},
|
||||||
|
{"path": "status", "type": "text", "visibility": 3},
|
||||||
|
{"path": "flow", "type": "float"},
|
||||||
|
{"path": "motstat", "type": "text"},
|
||||||
|
{"path": "set", "type": "float", "readonly": false, "cmd": "nv set"},
|
||||||
|
{"path": "flowmax", "type": "float", "readonly": false, "cmd": "nv flowmax"},
|
||||||
|
{"path": "ctrl", "type": "none", "kids": 4},
|
||||||
|
{"path": "ctrl/prop", "type": "float", "readonly": false, "cmd": "nv ctrl/prop"},
|
||||||
|
{"path": "ctrl/int", "type": "float", "readonly": false, "cmd": "nv ctrl/int"},
|
||||||
|
{"path": "ctrl/delay", "type": "float", "readonly": false, "cmd": "nv ctrl/delay"},
|
||||||
|
{"path": "ctrl/tol", "type": "float", "readonly": false, "cmd": "nv ctrl/tol"},
|
||||||
|
{"path": "autoflow", "type": "none", "kids": 24},
|
||||||
|
{"path": "autoflow/suspended", "type": "bool", "readonly": false, "cmd": "nv autoflow/suspended"},
|
||||||
|
{"path": "autoflow/prop", "type": "float", "readonly": false, "cmd": "nv autoflow/prop"},
|
||||||
|
{"path": "autoflow/flowstd", "type": "float", "readonly": false, "cmd": "nv autoflow/flowstd"},
|
||||||
|
{"path": "autoflow/flowlim", "type": "float", "readonly": false, "cmd": "nv autoflow/flowlim"},
|
||||||
|
{"path": "autoflow/smooth", "type": "float", "readonly": false, "cmd": "nv autoflow/smooth"},
|
||||||
|
{"path": "autoflow/difSize", "type": "float", "readonly": false, "cmd": "nv autoflow/difSize"},
|
||||||
|
{"path": "autoflow/difRange", "type": "float", "readonly": false, "cmd": "nv autoflow/difRange"},
|
||||||
|
{"path": "autoflow/flowSize", "type": "float", "readonly": false, "cmd": "nv autoflow/flowSize"},
|
||||||
|
{"path": "autoflow/convTime", "type": "float", "readonly": false, "cmd": "nv autoflow/convTime"},
|
||||||
|
{"path": "autoflow/Tmin", "type": "float", "readonly": false, "cmd": "nv autoflow/Tmin"},
|
||||||
|
{"path": "autoflow/script", "type": "text", "readonly": false, "cmd": "nv autoflow/script"},
|
||||||
|
{"path": "autoflow/getTemp", "type": "text", "readonly": false, "cmd": "nv autoflow/getTemp"},
|
||||||
|
{"path": "autoflow/getTset", "type": "text", "readonly": false, "cmd": "nv autoflow/getTset"},
|
||||||
|
{"path": "autoflow/getFlow", "type": "text", "readonly": false, "cmd": "nv autoflow/getFlow"},
|
||||||
|
{"path": "autoflow/difBuf", "type": "text"},
|
||||||
|
{"path": "autoflow/flowBuf", "type": "text"},
|
||||||
|
{"path": "autoflow/flowset", "type": "float"},
|
||||||
|
{"path": "autoflow/flowmin", "type": "float"},
|
||||||
|
{"path": "autoflow/flowmax", "type": "float"},
|
||||||
|
{"path": "autoflow/difmin", "type": "float"},
|
||||||
|
{"path": "autoflow/difmax", "type": "float"},
|
||||||
|
{"path": "autoflow/setmin", "type": "float"},
|
||||||
|
{"path": "autoflow/setmax", "type": "float"},
|
||||||
|
{"path": "autoflow/flowtarget", "type": "float"},
|
||||||
|
{"path": "calib", "type": "none", "kids": 2},
|
||||||
|
{"path": "calib/ln_per_min_per_mbar", "type": "float", "readonly": false, "cmd": "nv calib/ln_per_min_per_mbar"},
|
||||||
|
{"path": "calib/mbar_offset", "type": "float", "readonly": false, "cmd": "nv calib/mbar_offset"},
|
||||||
|
{"path": "initialize", "type": "enum", "enum": {"check": 1, "adjust": 2, "stop": 3}, "readonly": false, "cmd": "nv initialize", "visibility": 3},
|
||||||
|
{"path": "initstate", "type": "text", "visibility": 3}]}, "nvmot": {"base": "/nv/nvmot", "params": [{"path": "", "type": "float", "readonly": false, "cmd": "run nvmot", "kids": 32},
|
||||||
|
{"path": "send", "type": "text", "readonly": false, "cmd": "nvmot send", "visibility": 3},
|
||||||
|
{"path": "status", "type": "text", "visibility": 3},
|
||||||
|
{"path": "is_running", "type": "int", "readonly": false, "cmd": "nvmot is_running", "visibility": 3},
|
||||||
|
{"path": "pos", "type": "float"},
|
||||||
|
{"path": "encoder", "type": "float"},
|
||||||
|
{"path": "zero", "type": "float", "readonly": false, "cmd": "nvmot zero"},
|
||||||
|
{"path": "lowerlimit", "type": "float", "readonly": false, "cmd": "nvmot lowerlimit"},
|
||||||
|
{"path": "upperlimit", "type": "float", "readonly": false, "cmd": "nvmot upperlimit"},
|
||||||
|
{"path": "disablelimits", "type": "bool", "readonly": false, "cmd": "nvmot disablelimits"},
|
||||||
|
{"path": "verbose", "type": "bool", "readonly": false, "cmd": "nvmot verbose"},
|
||||||
|
{"path": "target", "type": "float"},
|
||||||
|
{"path": "runstate", "type": "enum", "enum": {"idle": 0, "running": 1, "finished": 2, "error": 3}},
|
||||||
|
{"path": "precision", "type": "float", "readonly": false, "cmd": "nvmot precision"},
|
||||||
|
{"path": "maxencdif", "type": "float", "readonly": false, "cmd": "nvmot maxencdif"},
|
||||||
|
{"path": "id", "type": "float", "readonly": false, "cmd": "nvmot id"},
|
||||||
|
{"path": "pump_number", "type": "float", "readonly": false, "cmd": "nvmot pump_number"},
|
||||||
|
{"path": "init", "type": "float", "readonly": false, "cmd": "nvmot init"},
|
||||||
|
{"path": "maxspeed", "type": "float", "readonly": false, "cmd": "nvmot maxspeed"},
|
||||||
|
{"path": "acceleration", "type": "float", "readonly": false, "cmd": "nvmot acceleration"},
|
||||||
|
{"path": "maxcurrent", "type": "float", "readonly": false, "cmd": "nvmot maxcurrent"},
|
||||||
|
{"path": "standbycurrent", "type": "float", "readonly": false, "cmd": "nvmot standbycurrent"},
|
||||||
|
{"path": "freewheeling", "type": "bool", "readonly": false, "cmd": "nvmot freewheeling"},
|
||||||
|
{"path": "output0", "type": "bool", "readonly": false, "cmd": "nvmot output0"},
|
||||||
|
{"path": "output1", "type": "bool", "readonly": false, "cmd": "nvmot output1"},
|
||||||
|
{"path": "input3", "type": "bool"},
|
||||||
|
{"path": "input0", "type": "float"},
|
||||||
|
{"path": "input0raw", "type": "float"},
|
||||||
|
{"path": "pullup", "type": "float", "readonly": false, "cmd": "nvmot pullup"},
|
||||||
|
{"path": "nopumpfeedback", "type": "bool", "readonly": false, "cmd": "nvmot nopumpfeedback"},
|
||||||
|
{"path": "eeprom", "type": "enum", "enum": {"ok": 0, "dirty": 1, "save": 2, "load": 3}, "readonly": false, "cmd": "nvmot eeprom"},
|
||||||
|
{"path": "customadr", "type": "text", "readonly": false, "cmd": "nvmot customadr"},
|
||||||
|
{"path": "custompar", "type": "float", "readonly": false, "cmd": "nvmot custompar"}]}, "ln2fill": {"base": "/ln2fill", "params": [{"path": "", "type": "enum", "enum": {"watching": 0, "fill": 1, "inactive": 2}, "readonly": false, "cmd": "ln2fill", "kids": 3},
|
||||||
|
{"path": "send", "type": "text", "readonly": false, "cmd": "ln2fill send", "visibility": 3},
|
||||||
|
{"path": "status", "type": "text", "visibility": 3},
|
||||||
|
{"path": "state", "type": "text"}]}, "hefill": {"base": "/hefill", "params": [{"path": "", "type": "enum", "enum": {"no fill valve": 2}, "readonly": false, "cmd": "hefill", "kids": 6},
|
||||||
|
{"path": "send", "type": "text", "readonly": false, "cmd": "hefill send", "visibility": 3},
|
||||||
|
{"path": "status", "type": "text", "visibility": 3},
|
||||||
|
{"path": "fast", "type": "enum", "enum": {"slow": 0, "fast": 1}, "readonly": false, "cmd": "cc hf"},
|
||||||
|
{"path": "state", "type": "text"},
|
||||||
|
{"path": "hefull", "type": "float", "readonly": false, "cmd": "cc hh"},
|
||||||
|
{"path": "helow", "type": "float", "readonly": false, "cmd": "cc hl"}]}, "hepump": {"base": "/hepump", "params": [{"path": "", "type": "enum", "enum": {"xds35_auto": 0, "xds35_manual": 1, "sv65": 2, "other": 3, "no": -1}, "readonly": false, "cmd": "hepump", "description": "xds35: scroll pump, sv65: leybold", "kids": 9},
|
||||||
|
{"path": "send", "type": "text", "readonly": false, "cmd": "hepump send", "visibility": 3},
|
||||||
|
{"path": "status", "type": "text", "visibility": 3},
|
||||||
|
{"path": "running", "type": "bool", "readonly": false, "cmd": "hepump running"},
|
||||||
|
{"path": "eco", "type": "bool", "readonly": false, "cmd": "hepump eco"},
|
||||||
|
{"path": "auto", "type": "bool", "readonly": false, "cmd": "hepump auto"},
|
||||||
|
{"path": "valve", "type": "enum", "enum": {"closed": 0, "closing": 1, "opening": 2, "opened": 3, "undefined": 4}, "readonly": false, "cmd": "hepump valve"},
|
||||||
|
{"path": "eco_t_lim", "type": "float", "readonly": false, "cmd": "hepump eco_t_lim", "description": "switch off eco mode when T_set < eco_t_lim and T < eco_t_lim * 2"},
|
||||||
|
{"path": "calib", "type": "float", "readonly": false, "cmd": "hepump calib", "visibility": 3}]}, "hemot": {"base": "/hepump/hemot", "params": [{"path": "", "type": "float", "readonly": false, "cmd": "run hemot", "kids": 32},
|
||||||
|
{"path": "send", "type": "text", "readonly": false, "cmd": "hemot send", "visibility": 3},
|
||||||
|
{"path": "status", "type": "text", "visibility": 3},
|
||||||
|
{"path": "is_running", "type": "int", "readonly": false, "cmd": "hemot is_running", "visibility": 3},
|
||||||
|
{"path": "pos", "type": "float"},
|
||||||
|
{"path": "encoder", "type": "float"},
|
||||||
|
{"path": "zero", "type": "float", "readonly": false, "cmd": "hemot zero"},
|
||||||
|
{"path": "lowerlimit", "type": "float", "readonly": false, "cmd": "hemot lowerlimit"},
|
||||||
|
{"path": "upperlimit", "type": "float", "readonly": false, "cmd": "hemot upperlimit"},
|
||||||
|
{"path": "disablelimits", "type": "bool", "readonly": false, "cmd": "hemot disablelimits"},
|
||||||
|
{"path": "verbose", "type": "bool", "readonly": false, "cmd": "hemot verbose"},
|
||||||
|
{"path": "target", "type": "float"},
|
||||||
|
{"path": "runstate", "type": "enum", "enum": {"idle": 0, "running": 1, "finished": 2, "error": 3}},
|
||||||
|
{"path": "precision", "type": "float", "readonly": false, "cmd": "hemot precision"},
|
||||||
|
{"path": "maxencdif", "type": "float", "readonly": false, "cmd": "hemot maxencdif"},
|
||||||
|
{"path": "id", "type": "float", "readonly": false, "cmd": "hemot id"},
|
||||||
|
{"path": "pump_number", "type": "float", "readonly": false, "cmd": "hemot pump_number"},
|
||||||
|
{"path": "init", "type": "float", "readonly": false, "cmd": "hemot init"},
|
||||||
|
{"path": "maxspeed", "type": "float", "readonly": false, "cmd": "hemot maxspeed"},
|
||||||
|
{"path": "acceleration", "type": "float", "readonly": false, "cmd": "hemot acceleration"},
|
||||||
|
{"path": "maxcurrent", "type": "float", "readonly": false, "cmd": "hemot maxcurrent"},
|
||||||
|
{"path": "standbycurrent", "type": "float", "readonly": false, "cmd": "hemot standbycurrent"},
|
||||||
|
{"path": "freewheeling", "type": "bool", "readonly": false, "cmd": "hemot freewheeling"},
|
||||||
|
{"path": "output0", "type": "bool", "readonly": false, "cmd": "hemot output0"},
|
||||||
|
{"path": "output1", "type": "bool", "readonly": false, "cmd": "hemot output1"},
|
||||||
|
{"path": "input3", "type": "bool"},
|
||||||
|
{"path": "input0", "type": "float"},
|
||||||
|
{"path": "input0raw", "type": "float"},
|
||||||
|
{"path": "pullup", "type": "float", "readonly": false, "cmd": "hemot pullup"},
|
||||||
|
{"path": "nopumpfeedback", "type": "bool", "readonly": false, "cmd": "hemot nopumpfeedback"},
|
||||||
|
{"path": "eeprom", "type": "enum", "enum": {"ok": 0, "dirty": 1, "save": 2, "load": 3}, "readonly": false, "cmd": "hemot eeprom"},
|
||||||
|
{"path": "customadr", "type": "text", "readonly": false, "cmd": "hemot customadr"},
|
||||||
|
{"path": "custompar", "type": "float", "readonly": false, "cmd": "hemot custompar"}]}, "table": {"base": "/table", "params": [{"path": "", "type": "none", "kids": 20},
|
||||||
|
{"path": "send", "type": "text", "readonly": false, "cmd": "table send", "visibility": 3},
|
||||||
|
{"path": "status", "type": "text", "visibility": 3},
|
||||||
|
{"path": "fix_tt_set_prop", "type": "bool", "readonly": false, "cmd": "table fix_tt_set_prop"},
|
||||||
|
{"path": "val_tt_set_prop", "type": "float"},
|
||||||
|
{"path": "tbl_tt_set_prop", "type": "text", "readonly": false, "cmd": "table tbl_tt_set_prop", "description": "enter value pair separated with colon T1:par1 T2:par2 ..."},
|
||||||
|
{"path": "fix_tt_set_integ", "type": "bool", "readonly": false, "cmd": "table fix_tt_set_integ"},
|
||||||
|
{"path": "val_tt_set_integ", "type": "float"},
|
||||||
|
{"path": "tbl_tt_set_integ", "type": "text", "readonly": false, "cmd": "table tbl_tt_set_integ", "description": "enter value pair separated with colon T1:par1 T2:par2 ..."},
|
||||||
|
{"path": "fix_tt_dblctrl_int2", "type": "bool", "readonly": false, "cmd": "table fix_tt_dblctrl_int2"},
|
||||||
|
{"path": "val_tt_dblctrl_int2", "type": "float"},
|
||||||
|
{"path": "tbl_tt_dblctrl_int2", "type": "text", "readonly": false, "cmd": "table tbl_tt_dblctrl_int2", "description": "enter value pair separated with colon T1:par1 T2:par2 ..."},
|
||||||
|
{"path": "fix_tt_dblctrl_prop_up", "type": "bool", "readonly": false, "cmd": "table fix_tt_dblctrl_prop_up"},
|
||||||
|
{"path": "val_tt_dblctrl_prop_up", "type": "float"},
|
||||||
|
{"path": "tbl_tt_dblctrl_prop_up", "type": "text", "readonly": false, "cmd": "table tbl_tt_dblctrl_prop_up", "description": "enter value pair separated with colon T1:par1 T2:par2 ..."},
|
||||||
|
{"path": "fix_tt_dblctrl_prop_lo", "type": "bool", "readonly": false, "cmd": "table fix_tt_dblctrl_prop_lo"},
|
||||||
|
{"path": "val_tt_dblctrl_prop_lo", "type": "float"},
|
||||||
|
{"path": "tbl_tt_dblctrl_prop_lo", "type": "text", "readonly": false, "cmd": "table tbl_tt_dblctrl_prop_lo", "description": "enter value pair separated with colon T1:par1 T2:par2 ..."},
|
||||||
|
{"path": "fix_warmup_weight", "type": "bool", "readonly": false, "cmd": "table fix_warmup_weight"},
|
||||||
|
{"path": "val_warmup_weight", "type": "float"},
|
||||||
|
{"path": "tbl_warmup_weight", "type": "text", "readonly": false, "cmd": "table tbl_warmup_weight", "description": "enter value pair separated with colon T1:par1 T2:par2 ..."}]}}
|
||||||
|
17
cfg/stick/ill4stick.cfg
Normal file
17
cfg/stick/ill4stick.cfg
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
[NODE]
|
||||||
|
description = ILL4 standard sample stick
|
||||||
|
id = ill4.stick.sea.psi.ch
|
||||||
|
|
||||||
|
[sea_stick]
|
||||||
|
class = secop_psi.sea.SeaClient
|
||||||
|
description = SEA stick connection
|
||||||
|
config = ill4.stick
|
||||||
|
service = stick
|
||||||
|
|
||||||
|
[ts]
|
||||||
|
class = secop_psi.sea.SeaReadable
|
||||||
|
iodev = sea_stick
|
||||||
|
sea_object = tt
|
||||||
|
json_file = ill4.config.json
|
||||||
|
rel_paths = ts
|
||||||
|
|
@ -2,7 +2,7 @@
|
|||||||
description = sample changer for 100 mm orange cryo (5 samples, HRPT)
|
description = sample changer for 100 mm orange cryo (5 samples, HRPT)
|
||||||
id = sch5.stick.sea.psi.ch
|
id = sch5.stick.sea.psi.ch
|
||||||
|
|
||||||
[seaconn]
|
[sea_stick]
|
||||||
class = secop_psi.sea.SeaClient
|
class = secop_psi.sea.SeaClient
|
||||||
description = SEA connection to sch5
|
description = SEA connection to sch5
|
||||||
config = sch5.stick
|
config = sch5.stick
|
||||||
@ -10,12 +10,13 @@ service = stick
|
|||||||
|
|
||||||
[warmup]
|
[warmup]
|
||||||
class = secop_psi.sea.SeaReadable
|
class = secop_psi.sea.SeaReadable
|
||||||
iodev = seaconn
|
iodev = sea_stick
|
||||||
sea_object = warmup
|
sea_object = warmup
|
||||||
|
|
||||||
[ts]
|
[ts]
|
||||||
class = secop_psi.sea.SeaReadable
|
class = secop_psi.sea.SeaReadable
|
||||||
iodev = seaconn
|
iodev = sea_stick
|
||||||
sea_object = tt
|
sea_object = tt
|
||||||
|
json_file = ori4.config.json
|
||||||
rel_paths = ts
|
rel_paths = ts
|
||||||
|
|
||||||
|
@ -17,12 +17,14 @@
|
|||||||
#
|
#
|
||||||
# Module authors:
|
# Module authors:
|
||||||
# Enrico Faulhaber <enrico.faulhaber@frm2.tum.de>
|
# Enrico Faulhaber <enrico.faulhaber@frm2.tum.de>
|
||||||
|
# Markus Zolliker <markus.zolliker@psi.ch>
|
||||||
#
|
#
|
||||||
# *****************************************************************************
|
# *****************************************************************************
|
||||||
"""Define (internal) SECoP Errors"""
|
"""Define (internal) SECoP Errors"""
|
||||||
|
|
||||||
|
|
||||||
class SECoPError(RuntimeError):
|
class SECoPError(RuntimeError):
|
||||||
|
silent = False # silent = True indicates that the error is already logged
|
||||||
|
|
||||||
def __init__(self, *args, **kwds):
|
def __init__(self, *args, **kwds):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
@ -32,7 +34,8 @@ class SECoPError(RuntimeError):
|
|||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
args = ', '.join(map(repr, self.args))
|
args = ', '.join(map(repr, self.args))
|
||||||
kwds = ', '.join(['%s=%r' % i for i in list(self.__dict__.items())])
|
kwds = ', '.join(['%s=%r' % i for i in list(self.__dict__.items())
|
||||||
|
if i[0] != 'silent'])
|
||||||
res = []
|
res = []
|
||||||
if args:
|
if args:
|
||||||
res.append(args)
|
res.append(args)
|
||||||
@ -102,14 +105,6 @@ class CommunicationFailedError(SECoPError):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class SilentError(SECoPError):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class CommunicationSilentError(SilentError, CommunicationFailedError):
|
|
||||||
name = 'CommunicationFailed'
|
|
||||||
|
|
||||||
|
|
||||||
class IsBusyError(SECoPError):
|
class IsBusyError(SECoPError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@ -123,7 +118,7 @@ class DisabledError(SECoPError):
|
|||||||
|
|
||||||
|
|
||||||
class HardwareError(SECoPError):
|
class HardwareError(SECoPError):
|
||||||
pass
|
name = 'HardwareError'
|
||||||
|
|
||||||
|
|
||||||
def make_secop_error(name, text):
|
def make_secop_error(name, text):
|
||||||
|
49
secop/io.py
49
secop/io.py
@ -31,8 +31,7 @@ import threading
|
|||||||
from secop.lib.asynconn import AsynConn, ConnectionClosed
|
from secop.lib.asynconn import AsynConn, ConnectionClosed
|
||||||
from secop.datatypes import ArrayOf, BLOBType, BoolType, FloatRange, IntRange, \
|
from secop.datatypes import ArrayOf, BLOBType, BoolType, FloatRange, IntRange, \
|
||||||
StringType, TupleOf, ValueType
|
StringType, TupleOf, ValueType
|
||||||
from secop.errors import CommunicationFailedError, CommunicationSilentError, \
|
from secop.errors import CommunicationFailedError, ConfigError, ProgrammingError
|
||||||
ConfigError, ProgrammingError
|
|
||||||
from secop.modules import Attached, Command, \
|
from secop.modules import Attached, Command, \
|
||||||
Communicator, Done, Module, Parameter, Property
|
Communicator, Done, Module, Parameter, Property
|
||||||
from secop.lib import generalConfig
|
from secop.lib import generalConfig
|
||||||
@ -42,6 +41,10 @@ generalConfig.set_default('legacy_hasiodev', False)
|
|||||||
HEX_CODE = re.compile(r'[0-9a-fA-F][0-9a-fA-F]$')
|
HEX_CODE = re.compile(r'[0-9a-fA-F][0-9a-fA-F]$')
|
||||||
|
|
||||||
|
|
||||||
|
class SilentError(CommunicationFailedError):
|
||||||
|
silent = True
|
||||||
|
|
||||||
|
|
||||||
class HasIO(Module):
|
class HasIO(Module):
|
||||||
"""Mixin for modules using a communicator"""
|
"""Mixin for modules using a communicator"""
|
||||||
io = Attached()
|
io = Attached()
|
||||||
@ -151,11 +154,10 @@ class IOBase(Communicator):
|
|||||||
self.callCallbacks()
|
self.callCallbacks()
|
||||||
return Done
|
return Done
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if str(e) == self._last_error:
|
if str(e) != self._last_error:
|
||||||
raise CommunicationSilentError(str(e)) from e
|
self._last_error = str(e)
|
||||||
self._last_error = str(e)
|
self.log.error(self._last_error)
|
||||||
self.log.error(self._last_error)
|
raise SilentError(repr(e)) from e
|
||||||
raise
|
|
||||||
return Done
|
return Done
|
||||||
|
|
||||||
def write_is_connected(self, value):
|
def write_is_connected(self, value):
|
||||||
@ -249,10 +251,10 @@ class StringIO(IOBase):
|
|||||||
"""
|
"""
|
||||||
command = command.encode(self.encoding)
|
command = command.encode(self.encoding)
|
||||||
if not self.is_connected:
|
if not self.is_connected:
|
||||||
|
# do not try to reconnect here
|
||||||
|
# read_is_connected is doing this when called by its poller
|
||||||
self.read_is_connected() # try to reconnect
|
self.read_is_connected() # try to reconnect
|
||||||
if not self._conn:
|
raise SilentError('disconnected') from None
|
||||||
self.log.debug('can not connect to %r' % self.uri)
|
|
||||||
raise CommunicationSilentError('can not connect to %r' % self.uri)
|
|
||||||
try:
|
try:
|
||||||
with self._lock:
|
with self._lock:
|
||||||
# read garbage and wait before send
|
# read garbage and wait before send
|
||||||
@ -279,11 +281,12 @@ class StringIO(IOBase):
|
|||||||
self.comLog('< %s', reply)
|
self.comLog('< %s', reply)
|
||||||
return reply
|
return reply
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if str(e) == self._last_error:
|
if self._conn is None:
|
||||||
raise CommunicationSilentError(str(e)) from None
|
raise SilentError('disconnected') from None
|
||||||
self._last_error = str(e)
|
if repr(e) != self._last_error:
|
||||||
self.log.error(self._last_error)
|
self._last_error = str(e)
|
||||||
raise
|
self.log.error(self._last_error)
|
||||||
|
raise SilentError(repr(e)) from e
|
||||||
|
|
||||||
@Command(ArrayOf(StringType()), result=ArrayOf(StringType()))
|
@Command(ArrayOf(StringType()), result=ArrayOf(StringType()))
|
||||||
def multicomm(self, commands):
|
def multicomm(self, commands):
|
||||||
@ -357,9 +360,10 @@ class BytesIO(IOBase):
|
|||||||
def communicate(self, request, replylen): # pylint: disable=arguments-differ
|
def communicate(self, request, replylen): # pylint: disable=arguments-differ
|
||||||
"""send a request and receive (at least) <replylen> bytes as reply"""
|
"""send a request and receive (at least) <replylen> bytes as reply"""
|
||||||
if not self.is_connected:
|
if not self.is_connected:
|
||||||
|
# do not try to reconnect here
|
||||||
|
# read_is_connected is doing this when called by its poller
|
||||||
self.read_is_connected() # try to reconnect
|
self.read_is_connected() # try to reconnect
|
||||||
if not self._conn:
|
raise SilentError('disconnected') from None
|
||||||
raise CommunicationSilentError('can not connect to %r' % self.uri)
|
|
||||||
try:
|
try:
|
||||||
with self._lock:
|
with self._lock:
|
||||||
# read garbage and wait before send
|
# read garbage and wait before send
|
||||||
@ -378,11 +382,12 @@ class BytesIO(IOBase):
|
|||||||
self.comLog('< %s', hexify(reply))
|
self.comLog('< %s', hexify(reply))
|
||||||
return self.getFullReply(request, reply)
|
return self.getFullReply(request, reply)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if str(e) == self._last_error:
|
if self._conn is None:
|
||||||
raise CommunicationSilentError(str(e)) from None
|
raise SilentError('disconnected') from None
|
||||||
self._last_error = str(e)
|
if repr(e) != self._last_error:
|
||||||
self.log.error(self._last_error)
|
self._last_error = str(e)
|
||||||
raise
|
self.log.error(self._last_error)
|
||||||
|
raise SilentError(repr(e)) from e
|
||||||
|
|
||||||
@Command((ArrayOf(TupleOf(BLOBType(), IntRange(0)))), result=ArrayOf(BLOBType()))
|
@Command((ArrayOf(TupleOf(BLOBType(), IntRange(0)))), result=ArrayOf(BLOBType()))
|
||||||
def multicomm(self, requests):
|
def multicomm(self, requests):
|
||||||
|
@ -167,7 +167,7 @@ class AsynTcp(AsynConn):
|
|||||||
uri = uri[6:]
|
uri = uri[6:]
|
||||||
try:
|
try:
|
||||||
self.connection = tcpSocket(uri, self.defaultport, self.timeout)
|
self.connection = tcpSocket(uri, self.defaultport, self.timeout)
|
||||||
except (ConnectionRefusedError, socket.gaierror) as e:
|
except (ConnectionRefusedError, socket.gaierror, socket.timeout) as e:
|
||||||
# indicate that retrying might make sense
|
# indicate that retrying might make sense
|
||||||
raise CommunicationFailedError(str(e)) from None
|
raise CommunicationFailedError(str(e)) from None
|
||||||
|
|
||||||
|
@ -177,9 +177,9 @@ class StateMachine:
|
|||||||
|
|
||||||
:return: a delay or None when idle
|
:return: a delay or None when idle
|
||||||
"""
|
"""
|
||||||
if self.state is None:
|
|
||||||
return None
|
|
||||||
with self._lock:
|
with self._lock:
|
||||||
|
if self.state is None:
|
||||||
|
return None
|
||||||
for _ in range(999):
|
for _ in range(999):
|
||||||
self.now = time.time()
|
self.now = time.time()
|
||||||
try:
|
try:
|
||||||
@ -236,7 +236,7 @@ class StateMachine:
|
|||||||
pass
|
pass
|
||||||
delay = self.cycle()
|
delay = self.cycle()
|
||||||
|
|
||||||
def _start(self, state, first_delay, **kwds):
|
def _start(self, state, **kwds):
|
||||||
self._restart = None
|
self._restart = None
|
||||||
self._idle_event.clear()
|
self._idle_event.clear()
|
||||||
self.last_error = None
|
self.last_error = None
|
||||||
@ -245,10 +245,12 @@ class StateMachine:
|
|||||||
self._new_state(state)
|
self._new_state(state)
|
||||||
self.start_time = self.now
|
self.start_time = self.now
|
||||||
self._last_time = self.now
|
self._last_time = self.now
|
||||||
|
first_delay = self.cycle() # important: call once (e.g. set status to busy)
|
||||||
if self._threaded:
|
if self._threaded:
|
||||||
if self._thread is None or not self._thread.is_alive():
|
if self._thread is None or not self._thread.is_alive():
|
||||||
# restart thread if dead (may happen when cleanup failed)
|
# restart thread if dead (may happen when cleanup failed)
|
||||||
self._thread = mkthread(self._run, first_delay)
|
if first_delay is not None:
|
||||||
|
self._thread = mkthread(self._run, first_delay)
|
||||||
else:
|
else:
|
||||||
self.trigger(first_delay)
|
self.trigger(first_delay)
|
||||||
|
|
||||||
@ -269,11 +271,9 @@ class StateMachine:
|
|||||||
self.last_error = self.stopped
|
self.last_error = self.stopped
|
||||||
self.cleanup(self) # ignore return state on restart
|
self.cleanup(self) # ignore return state on restart
|
||||||
self.stopped = False
|
self.stopped = False
|
||||||
delay = self.cycle()
|
self._start(state, **kwds)
|
||||||
self._start(state, delay, **kwds)
|
|
||||||
else:
|
else:
|
||||||
delay = self.cycle() # important: call once (e.g. set status to busy)
|
self._start(state, **kwds)
|
||||||
self._start(state, delay, **kwds)
|
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
"""stop machine, go to idle state
|
"""stop machine, go to idle state
|
||||||
|
@ -86,7 +86,6 @@ class RemoteLogHandler(mlzlog.Handler):
|
|||||||
class LogfileHandler(mlzlog.LogfileHandler):
|
class LogfileHandler(mlzlog.LogfileHandler):
|
||||||
|
|
||||||
def __init__(self, logdir, rootname, max_days=0):
|
def __init__(self, logdir, rootname, max_days=0):
|
||||||
self.logdir = logdir
|
|
||||||
self.rootname = rootname
|
self.rootname = rootname
|
||||||
self.max_days = max_days
|
self.max_days = max_days
|
||||||
super().__init__(logdir, rootname)
|
super().__init__(logdir, rootname)
|
||||||
@ -95,6 +94,11 @@ class LogfileHandler(mlzlog.LogfileHandler):
|
|||||||
if record.levelno != COMLOG:
|
if record.levelno != COMLOG:
|
||||||
super().emit(record)
|
super().emit(record)
|
||||||
|
|
||||||
|
def getChild(self, name):
|
||||||
|
child = type(self)(dirname(self.baseFilename), name, self.max_days)
|
||||||
|
child.setLevel(self.level)
|
||||||
|
return child
|
||||||
|
|
||||||
def doRollover(self):
|
def doRollover(self):
|
||||||
super().doRollover()
|
super().doRollover()
|
||||||
if self.max_days:
|
if self.max_days:
|
||||||
@ -157,8 +161,6 @@ class MainLogger:
|
|||||||
if self.logdir:
|
if self.logdir:
|
||||||
logfile_days = generalConfig.getint('logfile_days')
|
logfile_days = generalConfig.getint('logfile_days')
|
||||||
logfile_handler = LogfileHandler(self.logdir, self.rootname, max_days=logfile_days)
|
logfile_handler = LogfileHandler(self.logdir, self.rootname, max_days=logfile_days)
|
||||||
if generalConfig.logfile_days:
|
|
||||||
logfile_handler.max_days = int(generalConfig.logfile_days)
|
|
||||||
logfile_handler.setLevel(LOG_LEVELS[generalConfig.get('logfile_level', 'info')])
|
logfile_handler.setLevel(LOG_LEVELS[generalConfig.get('logfile_level', 'info')])
|
||||||
self.log.addHandler(logfile_handler)
|
self.log.addHandler(logfile_handler)
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ from functools import wraps
|
|||||||
from secop.datatypes import ArrayOf, BoolType, EnumType, FloatRange, \
|
from secop.datatypes import ArrayOf, BoolType, EnumType, FloatRange, \
|
||||||
IntRange, StatusType, StringType, TextType, TupleOf, DiscouragedConversion
|
IntRange, StatusType, StringType, TextType, TupleOf, DiscouragedConversion
|
||||||
from secop.errors import BadValueError, ConfigError, \
|
from secop.errors import BadValueError, ConfigError, \
|
||||||
ProgrammingError, SECoPError, SilentError, secop_error
|
ProgrammingError, SECoPError, secop_error
|
||||||
from secop.lib import formatException, mkthread, UniqueObject, generalConfig
|
from secop.lib import formatException, mkthread, UniqueObject, generalConfig
|
||||||
from secop.lib.enum import Enum
|
from secop.lib.enum import Enum
|
||||||
from secop.params import Accessible, Command, Parameter
|
from secop.params import Accessible, Command, Parameter
|
||||||
@ -233,7 +233,7 @@ class PollInfo:
|
|||||||
self.interval = pollinterval
|
self.interval = pollinterval
|
||||||
self.last_main = 0
|
self.last_main = 0
|
||||||
self.last_slow = 0
|
self.last_slow = 0
|
||||||
self.last_error = None
|
self.last_error = {} # dict [<name of poll func>] of (None or str(last exception))
|
||||||
self.polled_parameters = []
|
self.polled_parameters = []
|
||||||
self.fast_flag = False
|
self.fast_flag = False
|
||||||
self.trigger_event = trigger_event
|
self.trigger_event = trigger_event
|
||||||
@ -318,7 +318,8 @@ class Module(HasAccessibles):
|
|||||||
self.initModuleDone = False
|
self.initModuleDone = False
|
||||||
self.startModuleDone = False
|
self.startModuleDone = False
|
||||||
self.remoteLogHandler = None
|
self.remoteLogHandler = None
|
||||||
self.accessLock = threading.RLock()
|
self.accessLock = threading.RLock() # for read_* / write_* methods
|
||||||
|
self.updateLock = threading.RLock() # for announceUpdate
|
||||||
self.polledModules = [] # modules polled by thread started in self.startModules
|
self.polledModules = [] # modules polled by thread started in self.startModules
|
||||||
errors = []
|
errors = []
|
||||||
|
|
||||||
@ -495,7 +496,7 @@ class Module(HasAccessibles):
|
|||||||
def announceUpdate(self, pname, value=None, err=None, timestamp=None):
|
def announceUpdate(self, pname, value=None, err=None, timestamp=None):
|
||||||
"""announce a changed value or readerror"""
|
"""announce a changed value or readerror"""
|
||||||
|
|
||||||
with self.accessLock:
|
with self.updateLock:
|
||||||
# TODO: remove readerror 'property' and replace value with exception
|
# TODO: remove readerror 'property' and replace value with exception
|
||||||
pobj = self.parameters[pname]
|
pobj = self.parameters[pname]
|
||||||
timestamp = timestamp or time.time()
|
timestamp = timestamp or time.time()
|
||||||
@ -632,12 +633,19 @@ class Module(HasAccessibles):
|
|||||||
"""call read method with proper error handling"""
|
"""call read method with proper error handling"""
|
||||||
try:
|
try:
|
||||||
rfunc()
|
rfunc()
|
||||||
except SilentError:
|
self.pollInfo.last_error[rfunc.__name__] = None
|
||||||
pass
|
except Exception as e:
|
||||||
except SECoPError as e:
|
name = rfunc.__name__
|
||||||
self.log.error(str(e))
|
if str(e) != self.pollInfo.last_error.get(name):
|
||||||
except Exception:
|
self.pollInfo.last_error[name] = str(e)
|
||||||
self.log.error(formatException())
|
if isinstance(e, SECoPError):
|
||||||
|
if e.silent:
|
||||||
|
self.log.debug('%s: %s', name, str(e))
|
||||||
|
else:
|
||||||
|
self.log.error('%s: %s', name, str(e))
|
||||||
|
else:
|
||||||
|
# uncatched error: this is more serious
|
||||||
|
self.log.error('%s: %s', name, formatException())
|
||||||
|
|
||||||
def __pollThread(self, modules, started_callback):
|
def __pollThread(self, modules, started_callback):
|
||||||
"""poll thread body
|
"""poll thread body
|
||||||
@ -692,25 +700,14 @@ class Module(HasAccessibles):
|
|||||||
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
|
pinfo.last_main = (now // pinfo.interval) * pinfo.interval
|
||||||
try:
|
mobj.callPollFunc(mobj.doPoll)
|
||||||
mobj.doPoll()
|
|
||||||
pinfo.last_error = None
|
|
||||||
except Exception as e:
|
|
||||||
if str(e) != str(pinfo.last_error) and not isinstance(e, SilentError):
|
|
||||||
mobj.log.error('doPoll: %r', e)
|
|
||||||
pinfo.last_error = e
|
|
||||||
now = time.time()
|
now = time.time()
|
||||||
# find ONE due slow poll and call it
|
# find ONE due slow poll and call it
|
||||||
loop = True
|
loop = True
|
||||||
while loop: # loops max. 2 times, when to_poll is at end
|
while loop: # loops max. 2 times, when to_poll is at end
|
||||||
for mobj, rfunc, pobj in to_poll:
|
for mobj, rfunc, pobj in to_poll:
|
||||||
if now > pobj.timestamp + mobj.slowinterval * 0.5:
|
if now > pobj.timestamp + mobj.slowinterval * 0.5:
|
||||||
try:
|
mobj.callPollFunc(rfunc)
|
||||||
prev_err = pobj.readerror
|
|
||||||
rfunc()
|
|
||||||
except Exception as e:
|
|
||||||
if not isinstance(e, SilentError) and str(pobj.readerror) != str(prev_err):
|
|
||||||
mobj.log.error('%s: %r', pobj.name, e)
|
|
||||||
loop = False # one poll done
|
loop = False # one poll done
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
@ -736,15 +733,20 @@ class Module(HasAccessibles):
|
|||||||
value = self.writeDict.pop(pname, Done)
|
value = self.writeDict.pop(pname, Done)
|
||||||
# in the mean time, a poller or handler might already have done it
|
# in the mean time, a poller or handler might already have done it
|
||||||
if value is not Done:
|
if value is not Done:
|
||||||
try:
|
wfunc = getattr(self, 'write_' + pname, None)
|
||||||
self.log.debug('initialize parameter %s', pname)
|
if wfunc is None:
|
||||||
getattr(self, 'write_' + pname)(value)
|
setattr(self, pname, value)
|
||||||
except SilentError:
|
else:
|
||||||
pass
|
try:
|
||||||
except SECoPError as e:
|
self.log.debug('initialize parameter %s', pname)
|
||||||
self.log.error(str(e))
|
wfunc(value)
|
||||||
except Exception:
|
except SECoPError as e:
|
||||||
self.log.error(formatException())
|
if e.silent:
|
||||||
|
self.log.debug('%s: %s', pname, str(e))
|
||||||
|
else:
|
||||||
|
self.log.error('%s: %s', pname, str(e))
|
||||||
|
except Exception:
|
||||||
|
self.log.error(formatException())
|
||||||
if started_callback:
|
if started_callback:
|
||||||
started_callback()
|
started_callback()
|
||||||
|
|
||||||
|
@ -72,15 +72,16 @@ class PersistentMixin(HasAccessibles):
|
|||||||
persistentdir = os.path.join(generalConfig.logdir, 'persistent')
|
persistentdir = os.path.join(generalConfig.logdir, 'persistent')
|
||||||
os.makedirs(persistentdir, exist_ok=True)
|
os.makedirs(persistentdir, exist_ok=True)
|
||||||
self.persistentFile = os.path.join(persistentdir, '%s.%s.json' % (self.DISPATCHER.equipment_id, self.name))
|
self.persistentFile = os.path.join(persistentdir, '%s.%s.json' % (self.DISPATCHER.equipment_id, self.name))
|
||||||
self.initData = {}
|
self.initData = {} # "factory" settings
|
||||||
for pname in self.parameters:
|
for pname in self.parameters:
|
||||||
pobj = self.parameters[pname]
|
pobj = self.parameters[pname]
|
||||||
if hasattr(self, 'write_' + pname) and getattr(pobj, 'persistent', 0):
|
flag = getattr(pobj, 'persistent', 0)
|
||||||
self.initData[pname] = pobj.value
|
if flag:
|
||||||
if pobj.persistent == 'auto':
|
if flag == 'auto':
|
||||||
def cb(value, m=self):
|
def cb(value, m=self):
|
||||||
m.saveParameters()
|
m.saveParameters()
|
||||||
self.valueCallbacks[pname].append(cb)
|
self.valueCallbacks[pname].append(cb)
|
||||||
|
self.initData[pname] = pobj.value
|
||||||
self.writeDict.update(self.loadParameters(write=False))
|
self.writeDict.update(self.loadParameters(write=False))
|
||||||
|
|
||||||
def loadParameters(self, write=True):
|
def loadParameters(self, write=True):
|
||||||
|
@ -33,6 +33,7 @@ from secop.io import HasIO
|
|||||||
|
|
||||||
class ProxyModule(HasIO, Module):
|
class ProxyModule(HasIO, Module):
|
||||||
module = Property('remote module name', datatype=StringType(), default='')
|
module = Property('remote module name', datatype=StringType(), default='')
|
||||||
|
status = Parameter('connection status', Readable.status.datatype) # add status even when not a Readable
|
||||||
|
|
||||||
_consistency_check_done = False
|
_consistency_check_done = False
|
||||||
_secnode = None
|
_secnode = None
|
||||||
@ -182,11 +183,12 @@ def proxy_class(remote_class, name=None):
|
|||||||
|
|
||||||
for aname, aobj in rcls.accessibles.items():
|
for aname, aobj in rcls.accessibles.items():
|
||||||
if isinstance(aobj, Parameter):
|
if isinstance(aobj, Parameter):
|
||||||
pobj = aobj.merge(dict(handler=None, needscfg=False))
|
pobj = aobj.copy()
|
||||||
|
pobj.merge(dict(handler=None, needscfg=False))
|
||||||
attrs[aname] = pobj
|
attrs[aname] = pobj
|
||||||
|
|
||||||
def rfunc(self, pname=aname):
|
def rfunc(self, pname=aname):
|
||||||
value, _, readerror = self._secnode.getParameter(self.name, pname)
|
value, _, readerror = self._secnode.getParameter(self.name, pname, True)
|
||||||
if readerror:
|
if readerror:
|
||||||
raise readerror
|
raise readerror
|
||||||
return value
|
return value
|
||||||
|
@ -25,6 +25,7 @@ import math
|
|||||||
import re
|
import re
|
||||||
import time
|
import time
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
from secop.core import Drivable, HasIodev, \
|
from secop.core import Drivable, HasIodev, \
|
||||||
Parameter, Property, Readable, StringIO
|
Parameter, Property, Readable, StringIO
|
||||||
from secop.datatypes import EnumType, FloatRange, StringType, StructOf
|
from secop.datatypes import EnumType, FloatRange, StringType, StructOf
|
||||||
@ -97,12 +98,153 @@ class MercuryChannel(HasIodev):
|
|||||||
|
|
||||||
def read_channel_name(self):
|
def read_channel_name(self):
|
||||||
return self.query('')
|
return self.query('')
|
||||||
|
=======
|
||||||
|
from secop.core import Drivable, HasIO, Writable, \
|
||||||
|
Parameter, Property, Readable, StringIO, Attached, Done, IDLE, nopoll
|
||||||
|
from secop.datatypes import EnumType, FloatRange, StringType, StructOf, BoolType
|
||||||
|
from secop.errors import HardwareError
|
||||||
|
from secop_psi.convergence import HasConvergence
|
||||||
|
from secop.lib.enum import Enum
|
||||||
|
|
||||||
|
|
||||||
|
VALUE_UNIT = re.compile(r'(.*\d|inf)([A-Za-z/%]*)$')
|
||||||
|
SELF = 0
|
||||||
|
|
||||||
|
|
||||||
|
def as_float(value):
|
||||||
|
if isinstance(value, str):
|
||||||
|
return float(VALUE_UNIT.match(value).group(1))
|
||||||
|
return '%g' % value
|
||||||
|
|
||||||
|
|
||||||
|
def as_string(value):
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
class Mapped:
|
||||||
|
def __init__(self, **kwds):
|
||||||
|
self.mapping = kwds
|
||||||
|
self.mapping.update({v: k for k, v in kwds.items()})
|
||||||
|
|
||||||
|
def __call__(self, value):
|
||||||
|
return self.mapping[value]
|
||||||
|
|
||||||
|
|
||||||
|
off_on = Mapped(OFF=False, ON=True)
|
||||||
|
fast_slow = Mapped(ON=0, OFF=1) # maps OIs slow=ON/fast=OFF to sample_rate.slow=0/sample_rate.fast=1
|
||||||
|
|
||||||
|
|
||||||
|
class IO(StringIO):
|
||||||
|
identification = [('*IDN?', r'IDN:OXFORD INSTRUMENTS:MERCURY*')]
|
||||||
|
|
||||||
|
|
||||||
|
class MercuryChannel(HasIO):
|
||||||
|
slot = Property('''slot uids
|
||||||
|
|
||||||
|
example: DB6.T1,DB1.H1
|
||||||
|
slot ids for sensor (and control output)''',
|
||||||
|
StringType())
|
||||||
|
channel_name = Parameter('mercury nick name', StringType(), default='')
|
||||||
|
channel_type = '' #: channel type(s) for sensor (and control) e.g. TEMP,HTR or PRES,AUX
|
||||||
|
|
||||||
|
def _complete_adr(self, adr):
|
||||||
|
"""complete address from channel_type and slot"""
|
||||||
|
head, sep, tail = adr.partition(':')
|
||||||
|
for i, (channel_type, slot) in enumerate(zip(self.channel_type.split(','), self.slot.split(','))):
|
||||||
|
if head == str(i):
|
||||||
|
return 'DEV:%s:%s%s%s' % (slot, channel_type, sep, tail)
|
||||||
|
if head == channel_type:
|
||||||
|
return 'DEV:%s:%s%s%s' % (slot, head, sep, tail)
|
||||||
|
return adr
|
||||||
|
|
||||||
|
def multiquery(self, adr, names=(), convert=as_float):
|
||||||
|
"""get parameter(s) in mercury syntax
|
||||||
|
|
||||||
|
:param adr: the 'address part' of the SCPI command
|
||||||
|
the DEV:<slot> is added automatically, when adr starts with the channel type
|
||||||
|
in addition, when adr starts with '0:' or '1:', channel type and slot are added
|
||||||
|
:param names: the SCPI names of the parameter(s), for example ['TEMP']
|
||||||
|
:param convert: a converter function (converts replied string to value)
|
||||||
|
:return: the values as tuple
|
||||||
|
|
||||||
|
Example:
|
||||||
|
adr='AUX:SIG'
|
||||||
|
names = ('PERC',)
|
||||||
|
self.channel_type='PRES,AUX' # adr starts with 'AUX'
|
||||||
|
self.slot='DB5.P1,DB3.G1' # -> take second slot
|
||||||
|
-> query command will be READ:DEV:DB3.G1:PRES:SIG:PERC
|
||||||
|
"""
|
||||||
|
adr = self._complete_adr(adr)
|
||||||
|
cmd = 'READ:%s:%s' % (adr, ':'.join(names))
|
||||||
|
reply = self.communicate(cmd)
|
||||||
|
head = 'STAT:%s:' % adr
|
||||||
|
try:
|
||||||
|
assert reply.startswith(head)
|
||||||
|
replyiter = iter(reply[len(head):].split(':'))
|
||||||
|
keys, result = zip(*zip(replyiter, replyiter))
|
||||||
|
assert keys == tuple(names)
|
||||||
|
return tuple(convert(r) for r in result)
|
||||||
|
except (AssertionError, AttributeError, ValueError):
|
||||||
|
raise HardwareError('invalid reply %r to cmd %r' % (reply, cmd)) from None
|
||||||
|
|
||||||
|
def multichange(self, adr, values, convert=as_float):
|
||||||
|
"""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)
|
||||||
|
:return: the values as tuple
|
||||||
|
|
||||||
|
Example:
|
||||||
|
adr='0:LOOP'
|
||||||
|
values = [('P', 5), ('I', 2), ('D', 0)]
|
||||||
|
self.channel_type='TEMP,HTR' # adr starts with 0: take TEMP
|
||||||
|
self.slot='DB6.T1,DB1.H1' # and take first slot
|
||||||
|
-> change command will be SET:DEV:DB6.T1:TEMP:LOOP:P:5:I:2:D:0
|
||||||
|
"""
|
||||||
|
adr = self._complete_adr(adr)
|
||||||
|
params = ['%s:%s' % (k, convert(v)) for k, v in values]
|
||||||
|
cmd = 'SET:%s:%s' % (adr, ':'.join(params))
|
||||||
|
reply = self.communicate(cmd)
|
||||||
|
head = 'STAT:SET:%s:' % adr
|
||||||
|
|
||||||
|
try:
|
||||||
|
assert reply.startswith(head)
|
||||||
|
replyiter = iter(reply[len(head):].split(':'))
|
||||||
|
keys, result, valid = zip(*zip(replyiter, replyiter, replyiter))
|
||||||
|
assert keys == tuple(k for k, _ in values)
|
||||||
|
assert any(v == 'VALID' for v in valid)
|
||||||
|
return tuple(convert(r) for r in result)
|
||||||
|
except (AssertionError, AttributeError, ValueError) as e:
|
||||||
|
raise HardwareError('invalid reply %r to cmd %r' % (reply, cmd)) from e
|
||||||
|
|
||||||
|
def query(self, adr, convert=as_float):
|
||||||
|
"""query a single parameter
|
||||||
|
|
||||||
|
'adr' and 'convert' areg
|
||||||
|
"""
|
||||||
|
adr, _, name = adr.rpartition(':')
|
||||||
|
return self.multiquery(adr, [name], convert)[0]
|
||||||
|
|
||||||
|
def change(self, adr, value, convert=as_float):
|
||||||
|
adr, _, name = adr.rpartition(':')
|
||||||
|
return self.multichange(adr, [(name, value)], convert)[0]
|
||||||
|
|
||||||
|
def read_channel_name(self):
|
||||||
|
if self.channel_name:
|
||||||
|
return Done # channel name will not change
|
||||||
|
return self.query('0:NICK', as_string)
|
||||||
|
>>>>>>> d3379d5... support for OI mercury series
|
||||||
|
|
||||||
|
|
||||||
class TemperatureSensor(MercuryChannel, Readable):
|
class TemperatureSensor(MercuryChannel, Readable):
|
||||||
channel_type = 'TEMP'
|
channel_type = 'TEMP'
|
||||||
value = Parameter(unit='K')
|
value = Parameter(unit='K')
|
||||||
|
<<<<<<< HEAD
|
||||||
raw = Parameter('raw value', FloatRange())
|
raw = Parameter('raw value', FloatRange())
|
||||||
|
=======
|
||||||
|
raw = Parameter('raw value', FloatRange(unit='Ohm'))
|
||||||
|
>>>>>>> d3379d5... support for OI mercury series
|
||||||
|
|
||||||
def read_value(self):
|
def read_value(self):
|
||||||
return self.query('TEMP:SIG:TEMP')
|
return self.query('TEMP:SIG:TEMP')
|
||||||
@ -111,6 +253,7 @@ class TemperatureSensor(MercuryChannel, Readable):
|
|||||||
return self.query('TEMP:SIG:RES')
|
return self.query('TEMP:SIG:RES')
|
||||||
|
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
class HasProgressCheck:
|
class HasProgressCheck:
|
||||||
"""mixin for progress checks
|
"""mixin for progress checks
|
||||||
|
|
||||||
@ -345,6 +488,252 @@ class TemperatureLoop(Loop, TemperatureSensor, Drivable):
|
|||||||
self.log.warning('switch to manual heater mode')
|
self.log.warning('switch to manual heater mode')
|
||||||
self.write_mode('manual')
|
self.write_mode('manual')
|
||||||
return self.query('HTR:SIG:VOLT', math.sqrt(value * self.heater_resistivity))
|
return self.query('HTR:SIG:VOLT', math.sqrt(value * self.heater_resistivity))
|
||||||
|
=======
|
||||||
|
class HasInput(MercuryChannel):
|
||||||
|
controlled_by = Parameter('source of target value', EnumType(members={'self': SELF}), default=0)
|
||||||
|
target = Parameter(readonly=False)
|
||||||
|
input_modules = ()
|
||||||
|
|
||||||
|
def add_input(self, modobj):
|
||||||
|
if not self.input_modules:
|
||||||
|
self.input_modules = []
|
||||||
|
self.input_modules.append(modobj)
|
||||||
|
prev_enum = self.parameters['controlled_by'].datatype._enum
|
||||||
|
# add enum member, using autoincrement feature of Enum
|
||||||
|
self.parameters['controlled_by'].datatype = EnumType(Enum(prev_enum, **{modobj.name: None}))
|
||||||
|
|
||||||
|
def write_controlled_by(self, value):
|
||||||
|
if self.controlled_by == value:
|
||||||
|
return Done
|
||||||
|
self.controlled_by = value
|
||||||
|
if value == SELF:
|
||||||
|
self.log.warning('switch to manual mode')
|
||||||
|
for input_module in self.input_modules:
|
||||||
|
if input_module.control_active:
|
||||||
|
input_module.write_control_active(False)
|
||||||
|
return Done
|
||||||
|
|
||||||
|
|
||||||
|
class Loop(HasConvergence, MercuryChannel, Drivable):
|
||||||
|
"""common base class for loops"""
|
||||||
|
control_active = Parameter('control mode', BoolType())
|
||||||
|
output_module = Attached(HasInput, mandatory=False)
|
||||||
|
ctrlpars = Parameter(
|
||||||
|
'pid (proportional band, integral time, differential time',
|
||||||
|
StructOf(p=FloatRange(0, unit='$'), i=FloatRange(0, unit='min'), d=FloatRange(0, unit='min')),
|
||||||
|
readonly=False,
|
||||||
|
)
|
||||||
|
enable_pid_table = Parameter('', BoolType(), readonly=False)
|
||||||
|
|
||||||
|
def initModule(self):
|
||||||
|
super().initModule()
|
||||||
|
if self.output_module:
|
||||||
|
self.output_module.add_input(self)
|
||||||
|
|
||||||
|
def set_output(self, active):
|
||||||
|
if active:
|
||||||
|
if self.output_module and self.output_module.controlled_by != self.name:
|
||||||
|
self.output_module.controlled_by = self.name
|
||||||
|
else:
|
||||||
|
if self.output_module and self.output_module.controlled_by != SELF:
|
||||||
|
self.output_module.write_controlled_by(SELF)
|
||||||
|
status = IDLE, 'control inactive'
|
||||||
|
if self.status != status:
|
||||||
|
self.status = status
|
||||||
|
|
||||||
|
def set_target(self, target):
|
||||||
|
if self.control_active:
|
||||||
|
self.set_output(True)
|
||||||
|
else:
|
||||||
|
self.log.warning('switch loop control on')
|
||||||
|
self.write_control_active(True)
|
||||||
|
self.target = target
|
||||||
|
self.start_state()
|
||||||
|
|
||||||
|
def read_enable_pid_table(self):
|
||||||
|
return self.query('0:LOOP:PIDT', off_on)
|
||||||
|
|
||||||
|
def write_enable_pid_table(self, value):
|
||||||
|
return self.change('0:LOOP:PIDT', value, off_on)
|
||||||
|
|
||||||
|
def read_ctrlpars(self):
|
||||||
|
# read all in one go, in order to reduce comm. traffic
|
||||||
|
pid = self.multiquery('0:LOOP', ('P', 'I', 'D'))
|
||||||
|
return {k: float(v) for k, v in zip('pid', pid)}
|
||||||
|
|
||||||
|
def write_ctrlpars(self, value):
|
||||||
|
pid = self.multichange('0:LOOP', [(k, value[k.lower()]) for k in 'PID'])
|
||||||
|
return {k.lower(): v for k, v in zip('PID', pid)}
|
||||||
|
|
||||||
|
|
||||||
|
class HeaterOutput(HasInput, MercuryChannel, Writable):
|
||||||
|
"""heater output
|
||||||
|
|
||||||
|
Remark:
|
||||||
|
The hardware calculates the power from the voltage and the configured
|
||||||
|
resistivity. As the measured heater current is available, the resistivity
|
||||||
|
will be adjusted automatically, when true_power is True.
|
||||||
|
"""
|
||||||
|
channel_type = 'HTR'
|
||||||
|
value = Parameter('heater output', FloatRange(unit='W'), readonly=False)
|
||||||
|
target = Parameter('heater output', FloatRange(0, 100, unit='$'), readonly=False)
|
||||||
|
resistivity = Parameter('heater resistivity', FloatRange(10, 1000, unit='Ohm'),
|
||||||
|
readonly=False)
|
||||||
|
true_power = Parameter('calculate power from measured current', BoolType(), readonly=False, default=True)
|
||||||
|
limit = Parameter('heater output limit', FloatRange(0, 1000, unit='W'), readonly=False)
|
||||||
|
volt = 0.0 # target voltage
|
||||||
|
_last_target = None
|
||||||
|
_volt_target = None
|
||||||
|
|
||||||
|
def read_limit(self):
|
||||||
|
return self.query('HTR:VLIM') ** 2 / self.resistivity
|
||||||
|
|
||||||
|
def write_limit(self, value):
|
||||||
|
result = self.change('HTR:VLIM', math.sqrt(value * self.resistivity))
|
||||||
|
return result ** 2 / self.resistivity
|
||||||
|
|
||||||
|
def read_resistivity(self):
|
||||||
|
if self.true_power:
|
||||||
|
return self.resistivity
|
||||||
|
return max(10, self.query('HTR:RES'))
|
||||||
|
|
||||||
|
def write_resistivity(self, value):
|
||||||
|
self.resistivity = self.change('HTR:RES', max(10, value))
|
||||||
|
if self._last_target is not None:
|
||||||
|
if not self.true_power:
|
||||||
|
self._volt_target = math.sqrt(self._last_target * self.resistivity)
|
||||||
|
self.change('HTR:SIG:VOLT', self._volt_target)
|
||||||
|
return Done
|
||||||
|
|
||||||
|
def read_status(self):
|
||||||
|
status = IDLE, ('true power' if self.true_power else 'fixed resistivity')
|
||||||
|
if self.status != status:
|
||||||
|
return status
|
||||||
|
return Done
|
||||||
|
|
||||||
|
def read_value(self):
|
||||||
|
if self._last_target is None: # on init
|
||||||
|
self.read_target()
|
||||||
|
if not self.true_power:
|
||||||
|
volt = self.query('HTR:SIG:VOLT')
|
||||||
|
return volt ** 2 / max(10, self.resistivity)
|
||||||
|
volt, current = self.multiquery('HTR:SIG', ('VOLT', 'CURR'))
|
||||||
|
if volt > 0 and current > 0.0001 and self._last_target:
|
||||||
|
res = volt / current
|
||||||
|
tol = res * max(max(0.0003, abs(volt - self._volt_target)) / volt, 0.0001 / current, 0.0001)
|
||||||
|
if abs(res - self.resistivity) > tol + 0.07 and self._last_target:
|
||||||
|
self.write_resistivity(round(res, 1))
|
||||||
|
if self.controlled_by == 0:
|
||||||
|
self._volt_target = math.sqrt(self._last_target * self.resistivity)
|
||||||
|
self.change('HTR:SIG:VOLT', self._volt_target)
|
||||||
|
return volt * current
|
||||||
|
|
||||||
|
def read_target(self):
|
||||||
|
if self.controlled_by != 0 and self.target:
|
||||||
|
return 0
|
||||||
|
if self._last_target is not None:
|
||||||
|
return Done
|
||||||
|
self._volt_target = self.query('HTR:SIG:VOLT')
|
||||||
|
self.resistivity = max(10, self.query('HTR:RES'))
|
||||||
|
self._last_target = self._volt_target ** 2 / max(10, self.resistivity)
|
||||||
|
return self._last_target
|
||||||
|
|
||||||
|
def set_target(self, value):
|
||||||
|
"""set the target without switching to manual
|
||||||
|
|
||||||
|
might be used by a software loop
|
||||||
|
"""
|
||||||
|
self._volt_target = math.sqrt(value * self.resistivity)
|
||||||
|
self.change('HTR:SIG:VOLT', self._volt_target)
|
||||||
|
self._last_target = value
|
||||||
|
return value
|
||||||
|
|
||||||
|
def write_target(self, value):
|
||||||
|
self.write_controlled_by(SELF)
|
||||||
|
return self.set_target(value)
|
||||||
|
|
||||||
|
|
||||||
|
class TemperatureLoop(TemperatureSensor, Loop, Drivable):
|
||||||
|
channel_type = 'TEMP,HTR'
|
||||||
|
output_module = Attached(HeaterOutput, mandatory=False)
|
||||||
|
ramp = Parameter('ramp rate', FloatRange(0, unit='K/min'), readonly=False)
|
||||||
|
enable_ramp = Parameter('enable ramp rate', BoolType(), readonly=False)
|
||||||
|
setpoint = Parameter('working setpoint (differs from target when ramping)', FloatRange(0, unit='$'))
|
||||||
|
auto_flow = Parameter('enable auto flow', BoolType(), readonly=False)
|
||||||
|
_last_setpoint_change = None
|
||||||
|
|
||||||
|
def doPoll(self):
|
||||||
|
super().doPoll()
|
||||||
|
self.read_setpoint()
|
||||||
|
|
||||||
|
def read_control_active(self):
|
||||||
|
active = self.query('TEMP:LOOP:ENAB', off_on)
|
||||||
|
self.set_output(active)
|
||||||
|
return active
|
||||||
|
|
||||||
|
def write_control_active(self, value):
|
||||||
|
self.set_output(value)
|
||||||
|
return self.change('TEMP:LOOP:ENAB', value, off_on)
|
||||||
|
|
||||||
|
@nopoll # polled by read_setpoint
|
||||||
|
def read_target(self):
|
||||||
|
if self.read_enable_ramp():
|
||||||
|
return self.target
|
||||||
|
self.setpoint = self.query('TEMP:LOOP:TSET')
|
||||||
|
return self.setpoint
|
||||||
|
|
||||||
|
def read_setpoint(self):
|
||||||
|
setpoint = self.query('TEMP:LOOP:TSET')
|
||||||
|
if self.enable_ramp:
|
||||||
|
if setpoint == self.setpoint:
|
||||||
|
# update target when working setpoint does no longer change
|
||||||
|
if setpoint != self.target and self._last_setpoint_change is not None:
|
||||||
|
unchanged_since = time.time() - self._last_setpoint_change
|
||||||
|
if unchanged_since > max(12.0, 0.06 / max(1e-4, self.ramp)):
|
||||||
|
self.target = self.setpoint
|
||||||
|
return setpoint
|
||||||
|
self._last_setpoint_change = time.time()
|
||||||
|
else:
|
||||||
|
self.target = setpoint
|
||||||
|
return setpoint
|
||||||
|
|
||||||
|
def write_target(self, value):
|
||||||
|
target = self.change('TEMP:LOOP:TSET', value)
|
||||||
|
if self.enable_ramp:
|
||||||
|
self._last_setpoint_change = None
|
||||||
|
self.set_target(value)
|
||||||
|
else:
|
||||||
|
self.set_target(target)
|
||||||
|
return Done
|
||||||
|
|
||||||
|
def read_enable_ramp(self):
|
||||||
|
return self.query('TEMP:LOOP:RENA', off_on)
|
||||||
|
|
||||||
|
def write_enable_ramp(self, value):
|
||||||
|
return self.change('TEMP:LOOP:RENA', value, off_on)
|
||||||
|
|
||||||
|
def read_auto_flow(self):
|
||||||
|
return self.query('TEMP:LOOP:FAUT', off_on)
|
||||||
|
|
||||||
|
def write_auto_flow(self, value):
|
||||||
|
return self.change('TEMP:LOOP:FAUT', value, off_on)
|
||||||
|
|
||||||
|
def read_ramp(self):
|
||||||
|
result = self.query('TEMP:LOOP:RSET')
|
||||||
|
return min(9e99, result)
|
||||||
|
|
||||||
|
def write_ramp(self, value):
|
||||||
|
# use 0 or a very big value for switching off ramp
|
||||||
|
if not value:
|
||||||
|
self.write_enable_ramp(0)
|
||||||
|
return 0
|
||||||
|
if value >= 9e99:
|
||||||
|
self.change('TEMP:LOOP:RSET', 'inf', as_string)
|
||||||
|
self.write_enable_ramp(0)
|
||||||
|
return 9e99
|
||||||
|
self.write_enable_ramp(1)
|
||||||
|
return self.change('TEMP:LOOP:RSET', max(1e-4, value))
|
||||||
|
>>>>>>> d3379d5... support for OI mercury series
|
||||||
|
|
||||||
|
|
||||||
class PressureSensor(MercuryChannel, Readable):
|
class PressureSensor(MercuryChannel, Readable):
|
||||||
@ -355,6 +744,7 @@ class PressureSensor(MercuryChannel, Readable):
|
|||||||
return self.query('PRES:SIG:PRES')
|
return self.query('PRES:SIG:PRES')
|
||||||
|
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
class PressureLoop(Loop, PressureSensor, Drivable):
|
class PressureLoop(Loop, PressureSensor, Drivable):
|
||||||
channel_type = 'PRES,AUX'
|
channel_type = 'PRES,AUX'
|
||||||
|
|
||||||
@ -381,6 +771,69 @@ class HeLevel(MercuryChannel, Readable):
|
|||||||
fast_timeout = Parameter('timeout for switching to slow', FloatRange(0, unit='sec'), readonly=False)
|
fast_timeout = Parameter('timeout for switching to slow', FloatRange(0, unit='sec'), readonly=False)
|
||||||
_min_level = 200
|
_min_level = 200
|
||||||
_max_level = -100
|
_max_level = -100
|
||||||
|
=======
|
||||||
|
class ValvePos(HasInput, MercuryChannel, Drivable):
|
||||||
|
channel_type = 'PRES,AUX'
|
||||||
|
value = Parameter('value pos', FloatRange(unit='%'), readonly=False)
|
||||||
|
target = Parameter('valve pos target', FloatRange(0, 100, unit='$'), readonly=False)
|
||||||
|
|
||||||
|
def doPoll(self):
|
||||||
|
self.read_status()
|
||||||
|
|
||||||
|
def read_value(self):
|
||||||
|
return self.query('AUX:SIG:PERC')
|
||||||
|
|
||||||
|
def read_status(self):
|
||||||
|
self.read_value()
|
||||||
|
if abs(self.value - self.target) < 0.01:
|
||||||
|
return 'IDLE', 'at target'
|
||||||
|
return 'BUSY', 'moving'
|
||||||
|
|
||||||
|
def read_target(self):
|
||||||
|
return self.query('PRES:LOOP:FSET')
|
||||||
|
|
||||||
|
def write_target(self, value):
|
||||||
|
self.write_controlled_by(SELF)
|
||||||
|
return self.change('PRES:LOOP:FSET', value)
|
||||||
|
|
||||||
|
|
||||||
|
class PressureLoop(PressureSensor, Loop, Drivable):
|
||||||
|
channel_type = 'PRES,AUX'
|
||||||
|
output_module = Attached(ValvePos, mandatory=False)
|
||||||
|
|
||||||
|
def read_control_active(self):
|
||||||
|
active = self.query('PRES:LOOP:FAUT', off_on)
|
||||||
|
self.set_output(active)
|
||||||
|
return active
|
||||||
|
|
||||||
|
def write_control_active(self, value):
|
||||||
|
self.set_output(value)
|
||||||
|
return self.change('PRES:LOOP:FAUT', value, off_on)
|
||||||
|
|
||||||
|
def read_target(self):
|
||||||
|
return self.query('PRES:LOOP:PRST')
|
||||||
|
|
||||||
|
def write_target(self, value):
|
||||||
|
target = self.change('PRES:LOOP:PRST', value)
|
||||||
|
self.set_target(target)
|
||||||
|
return Done
|
||||||
|
|
||||||
|
|
||||||
|
class HeLevel(MercuryChannel, Readable):
|
||||||
|
"""He level meter channel
|
||||||
|
|
||||||
|
The Mercury system does not support automatic switching between fast
|
||||||
|
(when filling) and slow (when consuming). We have to handle this by software.
|
||||||
|
"""
|
||||||
|
channel_type = 'LVL'
|
||||||
|
sample_rate = Parameter('_', EnumType(slow=0, fast=1), readonly=False)
|
||||||
|
hysteresis = Parameter('hysteresis for detection of increase', FloatRange(0, 100, unit='%'),
|
||||||
|
default=5, readonly=False)
|
||||||
|
fast_timeout = Parameter('time to switch to slow after last increase', FloatRange(0, unit='sec'),
|
||||||
|
default=300, readonly=False)
|
||||||
|
_min_level = 999
|
||||||
|
_max_level = -999
|
||||||
|
>>>>>>> d3379d5... support for OI mercury series
|
||||||
_last_increase = None # None when in slow mode, last increase time in fast mode
|
_last_increase = None # None when in slow mode, last increase time in fast mode
|
||||||
|
|
||||||
def check_rate(self, sample_rate):
|
def check_rate(self, sample_rate):
|
||||||
@ -392,6 +845,7 @@ class HeLevel(MercuryChannel, Readable):
|
|||||||
if sample_rate != 0: # fast
|
if sample_rate != 0: # fast
|
||||||
if not self._last_increase:
|
if not self._last_increase:
|
||||||
self._last_increase = time.time()
|
self._last_increase = time.time()
|
||||||
|
<<<<<<< HEAD
|
||||||
self._max_level = -100
|
self._max_level = -100
|
||||||
elif self._last_increase:
|
elif self._last_increase:
|
||||||
self._last_increase = None
|
self._last_increase = None
|
||||||
@ -404,6 +858,20 @@ class HeLevel(MercuryChannel, Readable):
|
|||||||
def write_sample_rate(self, value):
|
def write_sample_rate(self, value):
|
||||||
self.check_rate(value)
|
self.check_rate(value)
|
||||||
return SAMPLE_RATE[self.query('LVL:HEL:PULS:SLOW', SAMPLE_RATE[value])]
|
return SAMPLE_RATE[self.query('LVL:HEL:PULS:SLOW', SAMPLE_RATE[value])]
|
||||||
|
=======
|
||||||
|
self._max_level = -999
|
||||||
|
elif self._last_increase:
|
||||||
|
self._last_increase = None
|
||||||
|
self._min_level = 999
|
||||||
|
return sample_rate
|
||||||
|
|
||||||
|
def read_sample_rate(self):
|
||||||
|
return self.check_rate(self.query('LVL:HEL:PULS:SLOW', fast_slow))
|
||||||
|
|
||||||
|
def write_sample_rate(self, value):
|
||||||
|
self.check_rate(value)
|
||||||
|
return self.change('LVL:HEL:PULS:SLOW', value, fast_slow)
|
||||||
|
>>>>>>> d3379d5... support for OI mercury series
|
||||||
|
|
||||||
def read_value(self):
|
def read_value(self):
|
||||||
level = self.query('LVL:SIG:HEL:LEV')
|
level = self.query('LVL:SIG:HEL:LEV')
|
||||||
@ -415,11 +883,19 @@ class HeLevel(MercuryChannel, Readable):
|
|||||||
self._max_level = level
|
self._max_level = level
|
||||||
elif now > self._last_increase + self.fast_timeout:
|
elif now > self._last_increase + self.fast_timeout:
|
||||||
# no increase since fast timeout -> slow
|
# no increase since fast timeout -> slow
|
||||||
|
<<<<<<< HEAD
|
||||||
self.write_sample_rate('slow')
|
self.write_sample_rate('slow')
|
||||||
else:
|
else:
|
||||||
if level > self._min_level + self.hysteresis:
|
if level > self._min_level + self.hysteresis:
|
||||||
# substantial increase -> fast
|
# substantial increase -> fast
|
||||||
self.write_sample_rate('fast')
|
self.write_sample_rate('fast')
|
||||||
|
=======
|
||||||
|
self.write_sample_rate(self.sample_rate.slow)
|
||||||
|
else:
|
||||||
|
if level > self._min_level + self.hysteresis:
|
||||||
|
# substantial increase -> fast
|
||||||
|
self.write_sample_rate(self.sample_rate.fast)
|
||||||
|
>>>>>>> d3379d5... support for OI mercury series
|
||||||
else:
|
else:
|
||||||
self._min_level = min(self._min_level, level)
|
self._min_level = min(self._min_level, level)
|
||||||
return level
|
return level
|
||||||
@ -432,5 +908,9 @@ class N2Level(MercuryChannel, Readable):
|
|||||||
return self.query('LVL:SIG:NIT:LEV')
|
return self.query('LVL:SIG:NIT:LEV')
|
||||||
|
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
class MagnetOutput(MercuryChannel, Drivable):
|
class MagnetOutput(MercuryChannel, Drivable):
|
||||||
pass
|
pass
|
||||||
|
=======
|
||||||
|
# TODO: magnet power supply
|
||||||
|
>>>>>>> d3379d5... support for OI mercury series
|
||||||
|
267
secop_psi/motorvalve.py
Normal file
267
secop_psi/motorvalve.py
Normal file
@ -0,0 +1,267 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# *****************************************************************************
|
||||||
|
#
|
||||||
|
# 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>
|
||||||
|
#
|
||||||
|
# *****************************************************************************
|
||||||
|
|
||||||
|
"""motor valve using a trinamic PD-1161 motor
|
||||||
|
|
||||||
|
the valve has an end switch connected to the 'home' digital input
|
||||||
|
of the motor controller. Motor settings for the currently used valve:
|
||||||
|
|
||||||
|
[valve_motor]
|
||||||
|
description = valve motor
|
||||||
|
class = secop_psi.trinamic.Motor
|
||||||
|
maxcurrent=0.3 # a value corresponding to the torque needed to firmly close the valve.
|
||||||
|
move_limit=9999 # no move limit needed
|
||||||
|
acceleration=150
|
||||||
|
encoder_tolerance=3.6 # typical value
|
||||||
|
auto_reset=True # motor stalls on closing
|
||||||
|
|
||||||
|
[valve]
|
||||||
|
description = trinamic angular motor valve
|
||||||
|
class = secop_psi.motorvalve.MotorValve
|
||||||
|
motor = valve_motor
|
||||||
|
turns = 9 # number of turns needed to open fully
|
||||||
|
speed = 400 # close to max. speed
|
||||||
|
lowspeed = 50 # speed for final closing / reference run
|
||||||
|
"""
|
||||||
|
|
||||||
|
from secop.core import Drivable, Parameter, EnumType, Attached, FloatRange, \
|
||||||
|
Command, IDLE, BUSY, WARN, ERROR, Done, PersistentParam, PersistentMixin
|
||||||
|
from secop.errors import HardwareError
|
||||||
|
from secop_psi.trinamic import Motor
|
||||||
|
from secop.lib.statemachine import StateMachine, Retry, Stop
|
||||||
|
|
||||||
|
|
||||||
|
class MotorValve(PersistentMixin, Drivable):
|
||||||
|
motor = Attached(Motor)
|
||||||
|
value = Parameter('current state', EnumType(
|
||||||
|
closed=0, opened=1, undefined=-1), default=-1)
|
||||||
|
target = Parameter('target state', EnumType(close=0, open=1))
|
||||||
|
turns = Parameter('number of turns to open', FloatRange(), readonly=False, group='settings')
|
||||||
|
speed = Parameter('speed for far moves', FloatRange(), readonly=False, group='settings')
|
||||||
|
lowspeed = Parameter('speed for finding closed position', FloatRange(), readonly=False, group='settings')
|
||||||
|
closed_pos = PersistentParam('fully closed position', FloatRange(),
|
||||||
|
persistent='auto', export=True, default=-999) # TODO: export=False
|
||||||
|
pollinterval = Parameter(group='settings')
|
||||||
|
|
||||||
|
_state = None
|
||||||
|
|
||||||
|
# remark: the home button must be touched when the motor is at zero
|
||||||
|
|
||||||
|
def earlyInit(self):
|
||||||
|
super().earlyInit()
|
||||||
|
self._state = StateMachine(logger=self.log, count=3, cleanup=self.handle_error)
|
||||||
|
|
||||||
|
def write_target(self, target):
|
||||||
|
if self.status[0] == ERROR:
|
||||||
|
raise HardwareError('%s: need refrun' % self.status[1])
|
||||||
|
self.target = target
|
||||||
|
self._state.start(self.goto_target, count=3)
|
||||||
|
return Done
|
||||||
|
|
||||||
|
def goto_target(self, state):
|
||||||
|
self.value = 'undefined'
|
||||||
|
if self.motor.isBusy():
|
||||||
|
mot_target = 0 if self.target == self.target.close else self.turns * 360
|
||||||
|
if abs(mot_target - self.motor.target) > self.motor.tolerance:
|
||||||
|
self.motor.stop()
|
||||||
|
return self.open_valve if self.target == self.target.open else self.close_valve
|
||||||
|
|
||||||
|
def read_value(self):
|
||||||
|
"""determine value and status"""
|
||||||
|
if self.status[0] == ERROR:
|
||||||
|
return 'undefined'
|
||||||
|
if self.motor.isBusy():
|
||||||
|
return Done
|
||||||
|
motpos = self.motor.read_value()
|
||||||
|
if self.motor.read_home():
|
||||||
|
if motpos > 360:
|
||||||
|
self.status = ERROR, 'home button must be released at this position'
|
||||||
|
elif motpos > 5:
|
||||||
|
if self.status[0] != ERROR:
|
||||||
|
self.status = WARN, 'position undefined'
|
||||||
|
elif motpos < -360:
|
||||||
|
self.status = ERROR, 'motor must not reach -1 turn!'
|
||||||
|
elif abs(motpos - self.closed_pos) < self.motor.tolerance:
|
||||||
|
self.status = IDLE, 'closed'
|
||||||
|
return 'closed'
|
||||||
|
self.status = WARN, 'nearly closed'
|
||||||
|
return 'undefined'
|
||||||
|
if abs(motpos - self.turns * 360) < 5:
|
||||||
|
self.status = IDLE, 'opened'
|
||||||
|
return 'opened'
|
||||||
|
if motpos < 5:
|
||||||
|
self.status = ERROR, 'home button must be engaged at this position'
|
||||||
|
elif self.status[0] != ERROR:
|
||||||
|
self.status = WARN, 'position undefined'
|
||||||
|
return 'undefined'
|
||||||
|
|
||||||
|
def open_valve(self, state):
|
||||||
|
if state.init:
|
||||||
|
self.closed_pos = -999
|
||||||
|
self.value = 'undefined'
|
||||||
|
self.status = BUSY, 'opening'
|
||||||
|
self.motor.write_speed(self.speed)
|
||||||
|
self.motor.write_target(self.turns * 360)
|
||||||
|
if self.motor.isBusy():
|
||||||
|
if self.motor.home and self.motor.value > 360:
|
||||||
|
self.motor.stop()
|
||||||
|
self.status = ERROR, 'opening valve failed (home switch not released)'
|
||||||
|
return None
|
||||||
|
return Retry()
|
||||||
|
motvalue = self.motor.read_value()
|
||||||
|
if abs(motvalue - self.turns * 360) < 5:
|
||||||
|
self.read_value() # value = opened, status = IDLE
|
||||||
|
else:
|
||||||
|
if state.count > 0:
|
||||||
|
state.count -= 1
|
||||||
|
self.log.info('target %g not reached, try again', motvalue)
|
||||||
|
return self.goto_target
|
||||||
|
self.status = ERROR, 'opening valve failed (motor target not reached)'
|
||||||
|
return None
|
||||||
|
|
||||||
|
def close_valve(self, state):
|
||||||
|
if state.init:
|
||||||
|
self.closed_pos = -999
|
||||||
|
self.status = BUSY, 'closing'
|
||||||
|
self.motor.write_speed(self.speed)
|
||||||
|
self.motor.write_target(0)
|
||||||
|
if self.motor.isBusy():
|
||||||
|
if self.motor.home:
|
||||||
|
return self.find_closed
|
||||||
|
return Retry()
|
||||||
|
motvalue = self.motor.read_value()
|
||||||
|
if abs(motvalue) > 5:
|
||||||
|
if state.count > 0:
|
||||||
|
state.count -= 1
|
||||||
|
self.log.info('target %g not reached, try again', motvalue)
|
||||||
|
return self.goto_target
|
||||||
|
self.status = ERROR, 'closing valve failed (zero not reached)'
|
||||||
|
return None
|
||||||
|
if self.read_value() == self.value.undefined:
|
||||||
|
if self.status[0] != ERROR:
|
||||||
|
return self.find_closed
|
||||||
|
return None
|
||||||
|
|
||||||
|
def find_closed(self, state):
|
||||||
|
"""drive with low speed until motor stalls"""
|
||||||
|
if state.init:
|
||||||
|
self.motor.write_speed(self.lowspeed)
|
||||||
|
state.prev = self.motor.value
|
||||||
|
self.motor.write_target(-360)
|
||||||
|
if self.motor.isBusy():
|
||||||
|
if not self.motor.home:
|
||||||
|
self.motor.stop()
|
||||||
|
return None
|
||||||
|
return Retry()
|
||||||
|
motvalue = self.motor.read_value()
|
||||||
|
if motvalue < -360:
|
||||||
|
self.read_value() # status -> error
|
||||||
|
return None
|
||||||
|
if motvalue < state.prev - 5:
|
||||||
|
# moved by more than 5 deg
|
||||||
|
state.prev = self.motor.value
|
||||||
|
self.motor.write_target(-360)
|
||||||
|
return Retry()
|
||||||
|
if motvalue > 5:
|
||||||
|
self.status = ERROR, 'closing valve failed (zero not reached)'
|
||||||
|
return None
|
||||||
|
if motvalue < -355:
|
||||||
|
self.status = ERROR, 'closing valve failed (does not stop)'
|
||||||
|
return None
|
||||||
|
self.closed_pos = motvalue
|
||||||
|
self.read_value() # value = closed, status = IDLE
|
||||||
|
return None
|
||||||
|
|
||||||
|
@Command
|
||||||
|
def ref_run(self):
|
||||||
|
"""start reference run"""
|
||||||
|
self.target = 'close'
|
||||||
|
self._state.start(self.ref_home, count=3)
|
||||||
|
|
||||||
|
@Command
|
||||||
|
def stop(self):
|
||||||
|
self._state.stop()
|
||||||
|
self.motor.stop()
|
||||||
|
|
||||||
|
def ref_home(self, state):
|
||||||
|
if state.init:
|
||||||
|
self.closed_pos = -999
|
||||||
|
self.motor.write_speed(self.lowspeed)
|
||||||
|
if self.motor.read_home():
|
||||||
|
self.status = BUSY, 'refrun: release home'
|
||||||
|
self.motor.write_target(self.motor.read_value() + 360)
|
||||||
|
return self.ref_released
|
||||||
|
self.status = BUSY, 'refrun: find home'
|
||||||
|
self.motor.write_target(self.motor.read_value() - (self.turns + 1) * 360)
|
||||||
|
if not self.motor.isBusy():
|
||||||
|
self.status = ERROR, 'ref run failed, can not find home switch'
|
||||||
|
return None
|
||||||
|
if not self.motor.home:
|
||||||
|
return Retry()
|
||||||
|
self.motor.write_speed(self.lowspeed)
|
||||||
|
state.prev = self.motor.read_value()
|
||||||
|
self.motor.write_target(state.prev - 360)
|
||||||
|
self.status = BUSY, 'refrun: find closed'
|
||||||
|
return self.ref_closed
|
||||||
|
|
||||||
|
def ref_released(self, state):
|
||||||
|
if self.motor.isBusy():
|
||||||
|
if self.motor.home:
|
||||||
|
return Retry()
|
||||||
|
elif self.motor.read_home():
|
||||||
|
if state.count > 0:
|
||||||
|
state.count -= 1
|
||||||
|
self.log.info('home switch not released, try again')
|
||||||
|
self.motor.write_target(self.motor.target)
|
||||||
|
return Retry()
|
||||||
|
self.status = ERROR, 'ref run failed, can not release home switch'
|
||||||
|
return None
|
||||||
|
return self.ref_home
|
||||||
|
|
||||||
|
def ref_closed(self, state):
|
||||||
|
if self.motor.isBusy():
|
||||||
|
if not self.motor.home:
|
||||||
|
self.motor.stop()
|
||||||
|
return None
|
||||||
|
return Retry()
|
||||||
|
self.motor.set_zero(max(-50, (self.motor.read_value() - state.prev) * 0.5))
|
||||||
|
self.read_value() # check home button is valid
|
||||||
|
if abs(self.motor.target - self.motor.value) < 5:
|
||||||
|
self.status = ERROR, 'ref run failed, does not stop'
|
||||||
|
if self.status[0] == ERROR:
|
||||||
|
return None
|
||||||
|
self.log.info('refrun successful')
|
||||||
|
return self.close_valve
|
||||||
|
|
||||||
|
def handle_error(self, state):
|
||||||
|
if state.stopped: # stop or restart case
|
||||||
|
if state.stopped is Stop:
|
||||||
|
self.status = WARN, 'stopped'
|
||||||
|
return None
|
||||||
|
if state.count > 0:
|
||||||
|
state.count -= 1
|
||||||
|
self.log.info('error %r, try again', state.last_error)
|
||||||
|
state.default_cleanup(state) # log error cause
|
||||||
|
state.last_error = None
|
||||||
|
return self.goto_target # try again
|
||||||
|
self.status = ERROR, str(state.last_error)
|
||||||
|
return state.default_cleanup(state)
|
@ -406,7 +406,9 @@ class SeaModule(Module):
|
|||||||
# with open(join(seaconfdir, json_file + '.json')) as fp:
|
# with open(join(seaconfdir, json_file + '.json')) as fp:
|
||||||
# sea_object, descr = json.load(fp)
|
# sea_object, descr = json.load(fp)
|
||||||
with open(join(seaconfdir, json_file)) as fp:
|
with open(join(seaconfdir, json_file)) as fp:
|
||||||
descr = json.load(fp)[sea_object]
|
content = json.load(fp)
|
||||||
|
# print(json_file, content.keys())
|
||||||
|
descr = content[sea_object]
|
||||||
if rel_paths == '*' or not rel_paths:
|
if rel_paths == '*' or not rel_paths:
|
||||||
# take all
|
# take all
|
||||||
main = descr['params'][0]
|
main = descr['params'][0]
|
||||||
@ -419,13 +421,14 @@ class SeaModule(Module):
|
|||||||
# filter by relative paths
|
# filter by relative paths
|
||||||
rel_paths = rel_paths.split()
|
rel_paths = rel_paths.split()
|
||||||
result = []
|
result = []
|
||||||
|
is_running = None
|
||||||
for rpath in rel_paths:
|
for rpath in rel_paths:
|
||||||
include = True
|
include = True
|
||||||
for paramdesc in descr['params']:
|
for paramdesc in descr['params']:
|
||||||
path = paramdesc['path']
|
path = paramdesc['path']
|
||||||
if path.endswith('is_running'):
|
if path.endswith('is_running'):
|
||||||
# take this always
|
# take this always
|
||||||
result.append(paramdesc)
|
is_running = paramdesc
|
||||||
continue
|
continue
|
||||||
if paramdesc.get('visibility', 1) > visibility_level:
|
if paramdesc.get('visibility', 1) > visibility_level:
|
||||||
continue
|
continue
|
||||||
@ -437,6 +440,8 @@ class SeaModule(Module):
|
|||||||
result.append(paramdesc)
|
result.append(paramdesc)
|
||||||
elif sub[0] == rpath:
|
elif sub[0] == rpath:
|
||||||
result.append(paramdesc)
|
result.append(paramdesc)
|
||||||
|
if is_running: # take this at end
|
||||||
|
result.append(is_running)
|
||||||
descr['params'] = result
|
descr['params'] = result
|
||||||
rel0 = '' if rel_paths[0] == '.' else rel_paths[0]
|
rel0 = '' if rel_paths[0] == '.' else rel_paths[0]
|
||||||
if result[0]['path'] == rel0:
|
if result[0]['path'] == rel0:
|
||||||
|
@ -191,7 +191,7 @@ class Sensor(Readable):
|
|||||||
|
|
||||||
def initModule(self):
|
def initModule(self):
|
||||||
super().initModule()
|
super().initModule()
|
||||||
self._rawsensor.registerCallbacks(self, ['status']) # auto update status
|
self.rawsensor.registerCallbacks(self, ['status']) # auto update status
|
||||||
self._calib = CalCurve(self.calib)
|
self._calib = CalCurve(self.calib)
|
||||||
if self.description == '_':
|
if self.description == '_':
|
||||||
self.description = '%r calibrated with curve %r' % (self.rawsensor, self.calib)
|
self.description = '%r calibrated with curve %r' % (self.rawsensor, self.calib)
|
||||||
@ -220,4 +220,4 @@ class Sensor(Readable):
|
|||||||
self.status = self.Status.ERROR, self._value_error
|
self.status = self.Status.ERROR, self._value_error
|
||||||
|
|
||||||
def read_value(self):
|
def read_value(self):
|
||||||
return self._calib(self._rawsensor.read_value())
|
return self._calib(self.rawsensor.read_value())
|
||||||
|
@ -26,10 +26,12 @@ import time
|
|||||||
import struct
|
import struct
|
||||||
|
|
||||||
from secop.core import BoolType, Command, EnumType, FloatRange, IntRange, \
|
from secop.core import BoolType, Command, EnumType, FloatRange, IntRange, \
|
||||||
HasIO, Parameter, Property, Drivable, PersistentMixin, PersistentParam, Done
|
HasIO, Parameter, Property, Drivable, PersistentMixin, PersistentParam, Done, \
|
||||||
|
IDLE, BUSY, ERROR
|
||||||
from secop.io import BytesIO
|
from secop.io import BytesIO
|
||||||
from secop.errors import CommunicationFailedError, HardwareError, BadValueError, IsBusyError
|
from secop.errors import CommunicationFailedError, HardwareError, BadValueError, IsBusyError
|
||||||
from secop.rwhandler import ReadHandler, WriteHandler
|
from secop.rwhandler import ReadHandler, WriteHandler
|
||||||
|
from secop.lib import formatStatusBits
|
||||||
|
|
||||||
MOTOR_STOP = 3
|
MOTOR_STOP = 3
|
||||||
MOVE = 4
|
MOVE = 4
|
||||||
@ -37,6 +39,8 @@ SET_AXIS_PAR = 5
|
|||||||
GET_AXIS_PAR = 6
|
GET_AXIS_PAR = 6
|
||||||
SET_GLOB_PAR = 9
|
SET_GLOB_PAR = 9
|
||||||
GET_GLOB_PAR = 10
|
GET_GLOB_PAR = 10
|
||||||
|
SET_IO = 14
|
||||||
|
GET_IO = 15
|
||||||
# STORE_GLOB_PAR = 11
|
# STORE_GLOB_PAR = 11
|
||||||
|
|
||||||
BAUDRATES = [9600, 0, 19200, 0, 38400, 57600, 0, 115200]
|
BAUDRATES = [9600, 0, 19200, 0, 38400, 57600, 0, 115200]
|
||||||
@ -80,7 +84,8 @@ def writable(*args, **kwds):
|
|||||||
class Motor(PersistentMixin, HasIO, Drivable):
|
class Motor(PersistentMixin, HasIO, Drivable):
|
||||||
address = Property('module address', IntRange(0, 255), default=1)
|
address = Property('module address', IntRange(0, 255), default=1)
|
||||||
|
|
||||||
value = Parameter('motor position', FloatRange(unit='deg', fmtstr='%.3f'))
|
value = Parameter('motor position', FloatRange(unit='deg', fmtstr='%.3f'),
|
||||||
|
needscfg=False)
|
||||||
zero = PersistentParam('zero point', FloatRange(unit='$'), readonly=False, default=0)
|
zero = PersistentParam('zero point', FloatRange(unit='$'), readonly=False, default=0)
|
||||||
encoder = PersistentParam('encoder reading', FloatRange(unit='$', fmtstr='%.1f'),
|
encoder = PersistentParam('encoder reading', FloatRange(unit='$', fmtstr='%.1f'),
|
||||||
readonly=True, initwrite=False)
|
readonly=True, initwrite=False)
|
||||||
@ -88,12 +93,17 @@ class Motor(PersistentMixin, HasIO, Drivable):
|
|||||||
readonly=True, initwrite=False)
|
readonly=True, initwrite=False)
|
||||||
target = Parameter('', FloatRange(unit='$'), default=0)
|
target = Parameter('', FloatRange(unit='$'), default=0)
|
||||||
|
|
||||||
move_limit = Parameter('max. angle to drive in one go', FloatRange(unit='$'),
|
move_limit = Parameter('max. angle to drive in one go when current above safe_current',
|
||||||
|
FloatRange(unit='$'),
|
||||||
readonly=False, default=360, group='more')
|
readonly=False, default=360, group='more')
|
||||||
|
safe_current = Parameter('motor current allowed for big steps', FloatRange(unit='A'),
|
||||||
|
readonly=False, default=0.2, group='more')
|
||||||
tolerance = Parameter('positioning tolerance', FloatRange(unit='$'),
|
tolerance = Parameter('positioning tolerance', FloatRange(unit='$'),
|
||||||
readonly=False, default=0.9)
|
readonly=False, default=0.9)
|
||||||
encoder_tolerance = writable('the allowed deviation between steppos and encoder\n\nmust be > tolerance',
|
encoder_tolerance = writable('the allowed deviation between steppos and encoder\n\nmust be > tolerance',
|
||||||
FloatRange(0, 360., unit='$', fmtstr='%.3f'), group='more')
|
FloatRange(0, 360., unit='$', fmtstr='%.3f'), group='more')
|
||||||
|
has_encoder = Parameter('whether encoder is used or not', BoolType(),
|
||||||
|
readonly=False, default=True, group='more')
|
||||||
speed = writable('max. speed', FloatRange(0, MAX_SPEED, unit='$/sec', fmtstr='%.1f'), default=40)
|
speed = writable('max. speed', FloatRange(0, MAX_SPEED, unit='$/sec', fmtstr='%.1f'), default=40)
|
||||||
minspeed = writable('min. speed', FloatRange(0, MAX_SPEED, unit='$/sec', fmtstr='%.1f'),
|
minspeed = writable('min. speed', FloatRange(0, MAX_SPEED, unit='$/sec', fmtstr='%.1f'),
|
||||||
default=SPEED_SCALE, group='motorparam')
|
default=SPEED_SCALE, group='motorparam')
|
||||||
@ -106,10 +116,16 @@ class Motor(PersistentMixin, HasIO, Drivable):
|
|||||||
acceleration = writable('', FloatRange(4.6 * ACCEL_SCALE, MAX_ACCEL, unit='deg/s^2', fmtstr='%.1f'),
|
acceleration = writable('', FloatRange(4.6 * ACCEL_SCALE, MAX_ACCEL, unit='deg/s^2', fmtstr='%.1f'),
|
||||||
default=150., group='motorparam')
|
default=150., group='motorparam')
|
||||||
target_reached = Parameter('', BoolType(), group='hwstatus')
|
target_reached = Parameter('', BoolType(), group='hwstatus')
|
||||||
move_status = Parameter('', IntRange(0, 3), group='hwstatus')
|
move_status = Parameter('', EnumType(ok=0, stalled=1, encoder_deviation=2, stalled_and_encoder_deviation=3),
|
||||||
|
group='hwstatus')
|
||||||
error_bits = Parameter('', IntRange(0, 255), group='hwstatus')
|
error_bits = Parameter('', IntRange(0, 255), group='hwstatus')
|
||||||
|
home = Parameter('state of home switch (input 3)', BoolType())
|
||||||
|
has_home = Parameter('enable home and activate pullup resistor', BoolType(),
|
||||||
|
default=True, initwrite=True, group='more')
|
||||||
|
auto_reset = Parameter('automatic reset after failure', BoolType(), readonly=False, default=False)
|
||||||
free_wheeling = writable('', FloatRange(0, 60., unit='sec', fmtstr='%.2f'),
|
free_wheeling = writable('', FloatRange(0, 60., unit='sec', fmtstr='%.2f'),
|
||||||
default=0.1, group='motorparam')
|
default=0.1, group='motorparam')
|
||||||
|
|
||||||
power_down_delay = writable('', FloatRange(0, 60., unit='sec', fmtstr='%.2f'),
|
power_down_delay = writable('', FloatRange(0, 60., unit='sec', fmtstr='%.2f'),
|
||||||
default=0.1, group='motorparam')
|
default=0.1, group='motorparam')
|
||||||
baudrate = Parameter('', EnumType({'%d' % v: i for i, v in enumerate(BAUDRATES)}),
|
baudrate = Parameter('', EnumType({'%d' % v: i for i, v in enumerate(BAUDRATES)}),
|
||||||
@ -117,22 +133,24 @@ class Motor(PersistentMixin, HasIO, Drivable):
|
|||||||
pollinterval = Parameter(group='more')
|
pollinterval = Parameter(group='more')
|
||||||
|
|
||||||
ioClass = BytesIO
|
ioClass = BytesIO
|
||||||
fast_pollfactor = 0.001 # not used any more, TODO: use a statemachine for running
|
fast_poll = 0.05
|
||||||
_started = 0
|
_run_mode = 0 # 0: idle, 1: driving, 2: stopping
|
||||||
_calcTimeout = True
|
_calc_timeout = True
|
||||||
_need_reset = None
|
_need_reset = None
|
||||||
|
_last_change = 0
|
||||||
|
_loading = False # True when loading parameters
|
||||||
|
|
||||||
def comm(self, cmd, adr, value=0, bank=0):
|
def comm(self, cmd, adr, value=0, bank=0):
|
||||||
"""set or get a parameter
|
"""set or get a parameter
|
||||||
|
|
||||||
:param adr: parameter number
|
:param adr: parameter number
|
||||||
:param cmd: SET command (in the GET case, 1 is added to this)
|
:param cmd: instruction number (SET_* or GET_*)
|
||||||
:param bank: the bank
|
:param bank: the bank
|
||||||
:param value: if given, the parameter is written, else it is returned
|
:param value: if given, the parameter is written, else it is returned
|
||||||
:return: the returned value
|
:return: the returned value
|
||||||
"""
|
"""
|
||||||
if self._calcTimeout and self.io._conn:
|
if self._calc_timeout and self.io._conn:
|
||||||
self._calcTimeout = False
|
self._calc_timeout = False
|
||||||
baudrate = getattr(self.io._conn.connection, 'baudrate', None)
|
baudrate = getattr(self.io._conn.connection, 'baudrate', None)
|
||||||
if baudrate:
|
if baudrate:
|
||||||
if baudrate not in BAUDRATES:
|
if baudrate not in BAUDRATES:
|
||||||
@ -167,6 +185,8 @@ class Motor(PersistentMixin, HasIO, Drivable):
|
|||||||
super().startModule(start_events)
|
super().startModule(start_events)
|
||||||
|
|
||||||
def fix_encoder(self=self):
|
def fix_encoder(self=self):
|
||||||
|
if not self.has_encoder:
|
||||||
|
return
|
||||||
try:
|
try:
|
||||||
# get encoder value from motor. at this stage self.encoder contains the persistent value
|
# get encoder value from motor. at this stage self.encoder contains the persistent value
|
||||||
encoder = self._read_axispar(ENCODER_ADR, ANGLE_SCALE) + self.zero
|
encoder = self._read_axispar(ENCODER_ADR, ANGLE_SCALE) + self.zero
|
||||||
@ -174,7 +194,8 @@ class Motor(PersistentMixin, HasIO, Drivable):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.log.error('fix_encoder failed with %r', e)
|
self.log.error('fix_encoder failed with %r', e)
|
||||||
|
|
||||||
start_events.queue(fix_encoder)
|
if self.has_encoder:
|
||||||
|
start_events.queue(fix_encoder)
|
||||||
|
|
||||||
def fix_encoder(self, encoder_from_hw):
|
def fix_encoder(self, encoder_from_hw):
|
||||||
"""fix encoder value
|
"""fix encoder value
|
||||||
@ -194,7 +215,7 @@ class Motor(PersistentMixin, HasIO, Drivable):
|
|||||||
if adjusted_encoder != encoder_from_hw:
|
if adjusted_encoder != encoder_from_hw:
|
||||||
self.log.info('take next closest encoder value (%.2f)' % adjusted_encoder)
|
self.log.info('take next closest encoder value (%.2f)' % adjusted_encoder)
|
||||||
self._need_reset = True
|
self._need_reset = True
|
||||||
self.status = self.Status.ERROR, 'saved encoder value does not match reading'
|
self.status = ERROR, 'saved encoder value does not match reading'
|
||||||
self._write_axispar(adjusted_encoder - self.zero, ENCODER_ADR, ANGLE_SCALE, readback=False)
|
self._write_axispar(adjusted_encoder - self.zero, ENCODER_ADR, ANGLE_SCALE, readback=False)
|
||||||
|
|
||||||
def _read_axispar(self, adr, scale=1):
|
def _read_axispar(self, adr, scale=1):
|
||||||
@ -235,71 +256,123 @@ class Motor(PersistentMixin, HasIO, Drivable):
|
|||||||
"""handler write for HwParam"""
|
"""handler write for HwParam"""
|
||||||
return self._write_axispar(value, *HW_ARGS[pname])
|
return self._write_axispar(value, *HW_ARGS[pname])
|
||||||
|
|
||||||
|
def doPoll(self):
|
||||||
|
self.read_status() # read_value is called by read_status
|
||||||
|
|
||||||
def read_value(self):
|
def read_value(self):
|
||||||
encoder = self.read_encoder()
|
|
||||||
steppos = self.read_steppos()
|
steppos = self.read_steppos()
|
||||||
|
encoder = self.read_encoder() if self.has_encoder else steppos
|
||||||
|
if self.has_home:
|
||||||
|
self.read_home()
|
||||||
initialized = self.comm(GET_GLOB_PAR, 255, bank=2)
|
initialized = self.comm(GET_GLOB_PAR, 255, bank=2)
|
||||||
if initialized: # no power loss
|
if initialized: # no power loss
|
||||||
self.saveParameters()
|
self.saveParameters()
|
||||||
else: # just powered up
|
elif not self._loading: # just powered up
|
||||||
# get persistent values
|
try:
|
||||||
writeDict = self.loadParameters()
|
self._loading = True
|
||||||
|
# get persistent values
|
||||||
|
writeDict = self.loadParameters()
|
||||||
|
finally:
|
||||||
|
self._loading = False
|
||||||
self.log.info('set to previous saved values %r', writeDict)
|
self.log.info('set to previous saved values %r', writeDict)
|
||||||
# self.encoder now contains the last known (persistent) value
|
# self.encoder now contains the last known (persistent) value
|
||||||
if self._need_reset is None:
|
if self._need_reset is None:
|
||||||
if self.status[0] == self.Status.IDLE:
|
if self.status[0] == IDLE:
|
||||||
# server started, power cycled and encoder value matches last one
|
# server started, power cycled and encoder value matches last one
|
||||||
self.reset()
|
self.reset()
|
||||||
else:
|
else:
|
||||||
self.fix_encoder(encoder)
|
if self.has_encoder:
|
||||||
|
self.fix_encoder(encoder)
|
||||||
self._need_reset = True
|
self._need_reset = True
|
||||||
self.status = self.Status.ERROR, 'power loss'
|
self.status = ERROR, 'power loss'
|
||||||
# or should we just fix instead of error status?
|
# or should we just fix instead of error status?
|
||||||
# self._write_axispar(self.steppos - self.zero, readback=False)
|
# self._write_axispar(self.steppos - self.zero, readback=False)
|
||||||
self.comm(SET_GLOB_PAR, 255, 1, bank=2) # set initialized flag
|
self.comm(SET_GLOB_PAR, 255, 1, bank=2) # set initialized flag
|
||||||
self._started = 0
|
self._run_mode = 0
|
||||||
|
self.setFastPoll(False)
|
||||||
|
|
||||||
return encoder if abs(encoder - steppos) > self.tolerance else steppos
|
return encoder if abs(encoder - steppos) > self.tolerance else steppos
|
||||||
|
|
||||||
def read_status(self):
|
def read_status(self):
|
||||||
oldpos = self.steppos
|
oldpos = self.steppos
|
||||||
self.read_value() # make sure encoder and steppos are fresh
|
self.read_value() # make sure encoder and steppos are fresh
|
||||||
if not self._started:
|
if not self._run_mode:
|
||||||
if abs(self.encoder - self.steppos) > self.encoder_tolerance:
|
if self.has_encoder and abs(self.encoder - self.steppos) > self.encoder_tolerance:
|
||||||
self._need_reset = True
|
self._need_reset = True
|
||||||
if self.status[0] != self.Status.ERROR:
|
if self.auto_reset:
|
||||||
|
return IDLE, 'encoder does not match internal pos'
|
||||||
|
if self.status[0] != ERROR:
|
||||||
self.log.error('encoder (%.2f) does not match internal pos (%.2f)', self.encoder, self.steppos)
|
self.log.error('encoder (%.2f) does not match internal pos (%.2f)', self.encoder, self.steppos)
|
||||||
return self.Status.ERROR, 'encoder does not match internal pos'
|
return ERROR, 'encoder does not match internal pos'
|
||||||
return self.status
|
return self.status
|
||||||
if oldpos != self.steppos or not (self.read_target_reached() or self.read_move_status()
|
now = self.parameters['steppos'].timestamp
|
||||||
or self.read_error_bits()):
|
if self.steppos != oldpos:
|
||||||
return self.Status.BUSY, 'moving'
|
self._last_change = now
|
||||||
# TODO: handle the different errors from move_status and error_bits
|
return BUSY, 'stopping' if self._run_mode == 2 else 'moving'
|
||||||
|
if now < self._last_change + 0.3 and not (self.read_target_reached() or self.read_move_status()):
|
||||||
|
return BUSY, 'stopping' if self._run_mode == 2 else 'moving'
|
||||||
|
if self.target_reached:
|
||||||
|
reason = ''
|
||||||
|
elif self.move_status:
|
||||||
|
reason = self.move_status.name
|
||||||
|
elif self.error_bits:
|
||||||
|
reason = formatStatusBits(self.error_bits, (
|
||||||
|
'stallGuard', 'over_temp', 'over_temp_warn', 'short_A', 'short_B',
|
||||||
|
'open_load_A', 'open_load_B', 'standstill'))
|
||||||
|
else:
|
||||||
|
reason = 'unknown'
|
||||||
|
self.setFastPoll(False)
|
||||||
|
if self._run_mode == 2:
|
||||||
|
self.target = self.value # indicate to customers that this was stopped
|
||||||
|
self._run_mode = 0
|
||||||
|
return IDLE, 'stopped'
|
||||||
|
self._run_mode = 0
|
||||||
diff = self.target - self.encoder
|
diff = self.target - self.encoder
|
||||||
if abs(diff) <= self.tolerance:
|
if abs(diff) <= self.tolerance:
|
||||||
self._started = 0
|
if reason:
|
||||||
return self.Status.IDLE, ''
|
self.log.warning('target reached, but move_status = %s', reason)
|
||||||
self.log.error('out of tolerance by %.3g', diff)
|
return IDLE, ''
|
||||||
self._started = 0
|
if self.auto_reset:
|
||||||
return self.Status.ERROR, 'out of tolerance'
|
self._need_reset = True
|
||||||
|
return IDLE, 'stalled: %s' % reason
|
||||||
|
self.log.error('out of tolerance by %.3g (%s)', diff, reason)
|
||||||
|
return ERROR, 'out of tolerance (%s)' % reason
|
||||||
|
|
||||||
def write_target(self, target):
|
def write_target(self, target):
|
||||||
self.read_value() # make sure encoder and steppos are fresh
|
for _ in range(2): # for auto reset
|
||||||
if abs(target - self.encoder) > self.move_limit:
|
self.read_value() # make sure encoder and steppos are fresh
|
||||||
raise BadValueError('can not move more than %s deg' % self.move_limit)
|
if self.maxcurrent >= self.safe_current + CURRENT_SCALE and (
|
||||||
diff = self.encoder - self.steppos
|
abs(target - self.encoder) > self.move_limit + self.tolerance):
|
||||||
if self._need_reset:
|
# pylint: disable=bad-string-format-type
|
||||||
raise HardwareError('need reset (%s)' % self.status[1])
|
# pylint wrongly does not recognise encoder as a descriptor
|
||||||
|
raise BadValueError('can not move more than %g deg (%g -> %g)' %
|
||||||
|
(self.move_limit, self.encoder, target))
|
||||||
|
diff = self.encoder - self.steppos
|
||||||
|
if self._need_reset:
|
||||||
|
if self.auto_reset:
|
||||||
|
if self.isBusy():
|
||||||
|
self.stop()
|
||||||
|
while self.isBusy():
|
||||||
|
time.sleep(0.1)
|
||||||
|
self.read_value()
|
||||||
|
self.reset()
|
||||||
|
if self.status[0] == IDLE:
|
||||||
|
continue
|
||||||
|
raise HardwareError('auto reset failed')
|
||||||
|
raise HardwareError('need reset (%s)' % self.status[1])
|
||||||
|
break
|
||||||
if abs(diff) > self.tolerance:
|
if abs(diff) > self.tolerance:
|
||||||
if abs(diff) > self.encoder_tolerance:
|
if abs(diff) > self.encoder_tolerance and self.has_encoder:
|
||||||
self._need_reset = True
|
self._need_reset = True
|
||||||
self.status = self.Status.ERROR, 'encoder does not match internal pos'
|
self.status = ERROR, 'encoder does not match internal pos'
|
||||||
raise HardwareError('need reset (encoder does not match internal pos)')
|
raise HardwareError('need reset (encoder does not match internal pos)')
|
||||||
self._write_axispar(self.encoder - self.zero, STEPPOS_ADR, ANGLE_SCALE)
|
self._write_axispar(self.encoder - self.zero, STEPPOS_ADR, ANGLE_SCALE, readback=False)
|
||||||
self._started = time.time()
|
self._last_change = time.time()
|
||||||
self.log.info('move to %.1f', target)
|
self._run_mode = 1 # driving
|
||||||
|
self.setFastPoll(True, self.fast_poll)
|
||||||
|
self.log.debug('move to %.1f', target)
|
||||||
self.comm(MOVE, 0, (target - self.zero) / ANGLE_SCALE)
|
self.comm(MOVE, 0, (target - self.zero) / ANGLE_SCALE)
|
||||||
self.status = self.Status.BUSY, 'changed target'
|
self.status = BUSY, 'changed target'
|
||||||
return target
|
return target
|
||||||
|
|
||||||
def write_zero(self, value):
|
def write_zero(self, value):
|
||||||
@ -308,71 +381,67 @@ class Motor(PersistentMixin, HasIO, Drivable):
|
|||||||
return Done
|
return Done
|
||||||
|
|
||||||
def read_encoder(self):
|
def read_encoder(self):
|
||||||
return self._read_axispar(ENCODER_ADR, ANGLE_SCALE) + self.zero
|
if self.has_encoder:
|
||||||
|
return self._read_axispar(ENCODER_ADR, ANGLE_SCALE) + self.zero
|
||||||
|
return self.read_steppos()
|
||||||
|
|
||||||
def read_steppos(self):
|
def read_steppos(self):
|
||||||
return self._read_axispar(STEPPOS_ADR, ANGLE_SCALE) + self.zero
|
return self._read_axispar(STEPPOS_ADR, ANGLE_SCALE) + self.zero
|
||||||
|
|
||||||
|
def read_home(self):
|
||||||
|
return not self.comm(GET_IO, 255) & 8
|
||||||
|
|
||||||
|
def write_has_home(self, value):
|
||||||
|
"""activate pullup resistor"""
|
||||||
|
return bool(self.comm(SET_IO, 0, value))
|
||||||
|
|
||||||
@Command(FloatRange())
|
@Command(FloatRange())
|
||||||
def set_zero(self, value):
|
def set_zero(self, value):
|
||||||
"""adjust zero"""
|
"""adapt zero to make current position equal to given value"""
|
||||||
self.write_zero(value - self.read_value())
|
raw = self.read_value() - self.zero
|
||||||
|
self.write_zero(value - raw)
|
||||||
|
|
||||||
def read_baudrate(self):
|
def read_baudrate(self):
|
||||||
return self.comm(GET_GLOB_PAR, 65)
|
return self.comm(GET_GLOB_PAR, 65)
|
||||||
|
|
||||||
def write_baudrate(self, value):
|
def write_baudrate(self, value):
|
||||||
self.comm(SET_GLOB_PAR, 65, int(value))
|
"""a baudrate change takes effect only after power cycle"""
|
||||||
|
return self.comm(SET_GLOB_PAR, 65, int(value))
|
||||||
|
|
||||||
@Command()
|
@Command()
|
||||||
def reset(self):
|
def reset(self):
|
||||||
"""set steppos to encoder value, if not within tolerance"""
|
"""set steppos to encoder value, if not within tolerance"""
|
||||||
if self._started:
|
if self._run_mode:
|
||||||
raise IsBusyError('can not reset while moving')
|
raise IsBusyError('can not reset while moving')
|
||||||
tol = ENCODER_RESOLUTION * 1.1
|
tol = ENCODER_RESOLUTION * 1.1
|
||||||
for itry in range(10):
|
for itry in range(10):
|
||||||
diff = self.read_encoder() - self.read_steppos()
|
diff = self.read_encoder() - self.read_steppos()
|
||||||
if abs(diff) <= tol:
|
if abs(diff) <= tol:
|
||||||
self._need_reset = False
|
self._need_reset = False
|
||||||
self.status = self.Status.IDLE, 'ok'
|
self.status = IDLE, 'ok'
|
||||||
return
|
return
|
||||||
self._write_axispar(self.encoder - self.zero, STEPPOS_ADR, ANGLE_SCALE, readback=False)
|
self._write_axispar(self.encoder - self.zero, STEPPOS_ADR, ANGLE_SCALE, readback=False)
|
||||||
self.comm(MOVE, 0, (self.encoder - self.zero) / ANGLE_SCALE)
|
self.comm(MOVE, 0, (self.encoder - self.zero) / ANGLE_SCALE)
|
||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
if itry > 5:
|
if itry > 5:
|
||||||
tol = self.tolerance
|
tol = self.tolerance
|
||||||
self.status = self.Status.ERROR, 'reset failed'
|
self.status = ERROR, 'reset failed'
|
||||||
return
|
return
|
||||||
|
|
||||||
@Command()
|
@Command()
|
||||||
def stop(self):
|
def stop(self):
|
||||||
"""stop motor immediately"""
|
"""stop motor immediately"""
|
||||||
|
self._run_mode = 2 # stopping
|
||||||
self.comm(MOTOR_STOP, 0)
|
self.comm(MOTOR_STOP, 0)
|
||||||
self.status = self.Status.IDLE, 'stopped'
|
self.status = BUSY, 'stopping'
|
||||||
self._started = 0
|
self.setFastPoll(False)
|
||||||
|
|
||||||
@Command()
|
@Command(IntRange(), result=IntRange(), export=False)
|
||||||
def step_forward(self):
|
|
||||||
"""move one full step forwards
|
|
||||||
|
|
||||||
for quick tests
|
|
||||||
"""
|
|
||||||
self.comm(MOVE, 1, FULL_STEP / ANGLE_SCALE)
|
|
||||||
|
|
||||||
@Command()
|
|
||||||
def step_back(self):
|
|
||||||
"""move one full step backwards
|
|
||||||
|
|
||||||
for quick tests
|
|
||||||
"""
|
|
||||||
self.comm(MOVE, 1, - FULL_STEP / ANGLE_SCALE)
|
|
||||||
|
|
||||||
@Command(IntRange(), result=IntRange())
|
|
||||||
def get_axis_par(self, adr):
|
def get_axis_par(self, adr):
|
||||||
"""get arbitrary motor parameter"""
|
"""get arbitrary motor parameter"""
|
||||||
return self.comm(GET_AXIS_PAR, adr)
|
return self.comm(GET_AXIS_PAR, adr)
|
||||||
|
|
||||||
@Command((IntRange(), IntRange()), result=IntRange())
|
@Command((IntRange(), IntRange()), result=IntRange(), export=False)
|
||||||
def set_axis_par(self, adr, value):
|
def set_axis_par(self, adr, value):
|
||||||
"""set arbitrary motor parameter"""
|
"""set arbitrary motor parameter"""
|
||||||
return self.comm(SET_AXIS_PAR, adr, value)
|
return self.comm(SET_AXIS_PAR, adr, value)
|
||||||
|
30
test/test_errors.py
Normal file
30
test/test_errors.py
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# *****************************************************************************
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify it under
|
||||||
|
# the terms of the GNU General Public License as published by the Free Software
|
||||||
|
# Foundation; either version 2 of the License, or (at your option) any later
|
||||||
|
# version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||||
|
# details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License along with
|
||||||
|
# this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
#
|
||||||
|
# Module authors:
|
||||||
|
# Markus Zolliker <markus.zolliker@psi.ch>
|
||||||
|
#
|
||||||
|
# *****************************************************************************
|
||||||
|
"""test data types."""
|
||||||
|
|
||||||
|
import secop.errors
|
||||||
|
|
||||||
|
|
||||||
|
def test_errors():
|
||||||
|
"""check consistence of secop.errors.EXCEPTIONS"""
|
||||||
|
for e in secop.errors.EXCEPTIONS.values():
|
||||||
|
assert secop.errors.EXCEPTIONS[e().name] == e
|
@ -78,7 +78,6 @@ def test_fun():
|
|||||||
s.cycle() # do nothing
|
s.cycle() # do nothing
|
||||||
assert s.step == 0
|
assert s.step == 0
|
||||||
s.start(rise, level=0, direction=0)
|
s.start(rise, level=0, direction=0)
|
||||||
s.cycle()
|
|
||||||
for i in range(1, 4):
|
for i in range(1, 4):
|
||||||
assert s.status == 'rise'
|
assert s.status == 'rise'
|
||||||
assert s.step == i
|
assert s.step == i
|
||||||
@ -100,7 +99,6 @@ def test_fun():
|
|||||||
def test_max_chain():
|
def test_max_chain():
|
||||||
s = StateMachine(step=0, status='', threaded=False, logger=LoggerStub())
|
s = StateMachine(step=0, status='', threaded=False, logger=LoggerStub())
|
||||||
s.start(fall, level=999+1, direction=0)
|
s.start(fall, level=999+1, direction=0)
|
||||||
s.cycle()
|
|
||||||
assert isinstance(s.last_error, RuntimeError)
|
assert isinstance(s.last_error, RuntimeError)
|
||||||
assert s.state is None
|
assert s.state is None
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user