Files
sea/tcl/drivers/ipsmag.tcl

403 lines
10 KiB
Tcl

namespace eval ipsmag {
}
source drivers/magfield.tcl
proc stdConfig::ipsmag {{n_of_slaves 3}} {
controller std timeout=5
prop write ipsmag::write
prop read ipsmag::read
prop update ipsmag::update
prop startcmd *IDN?
variable node
set node $node/tasks
prop complete ipsmag::completeStart
variable name
magfield_obj IPS_MAGFIELD "ipsmag::cmd /$name/ips"
#magfield_obj IPS_MAGFIELD "node_cmd /$name/ips"
kids "magnetic field control" {
# 1: bipolar
magfield_kids 1
node ips rd
prop adr DEV:GRPZ:PSU:SIG:PFLD
prop label persistent field
kids "IPS settings" {
node ramp_slow wr
prop adr DEV:GRPZ:PSU:SIG:RFST
default 0.1
prop help "ramp rate for coils Tesla/min."
node ramp_fast upd
default 100
prop help "ramp rate for leads Tesla/min."
node set_field wr
prop adr DEV:GRPZ:PSU:SIG:FSET
prop write ipsmag::write_set
node heater wr
prop enum 1
prop adr DEV:GRPZ:PSU:SIG:SWHT
prop cvt "ON=1 OFF=0"
prop label "persistent switch heater"
prop update ipsmag::update_heater
node ramp_state wr -int
prop enum hold=0,to_zero=1,to_set=2,clamp=3
prop adr DEV:GRPZ:PSU:ACTN
prop cvt "RTOZ=1 RTOS=2 CLMP=3 HOLD=0"
prop read ipsmag::read_ramp_state
node leads_set rd 1
prop adr DEV:GRPZ:PSU:SIG:FLD
prop help "calculated current in the leads, converted to Tesla"
node show_internals -int par 1
prop enum 1
prop newline 1
prop show_more 1
node leads_meas rd
prop adr DEV:GRPZ:PSU:SIG:CURR
prop help {measured current in the leads, converted to Tesla}
prop update ipsmag::update_leads $n_of_slaves
for {set i 1} {$i <= $n_of_slaves} {incr i} {
node slave$i upd
}
node volt rd
prop adr DEV:GRPZ:PSU:SIG:VOLT
node symode -text upd
default "correct"
node engineering_password wr -text
default ""
prop adr SYS:USER:ENG
prop write ipsmag::write_eng
prop read ipsmag::read_eng
prop update ipsmag::update_eng
node atob wr
prop help {Amp/Tesla}
prop adr DEV:GRPZ:PSU:ATOB
prop write ipsmag::write_config
prop update ipsmag::update_config
prop check ipsmag::check_config
node inductance wr
prop help {henries}
prop adr DEV:GRPZ:PSU:IND
prop write ipsmag::write_config
prop update ipsmag::update_config
prop check ipsmag::check_config
node switch_heater_current wr
prop help {switch heater current [mA]}
prop adr DEV:GRPZ:PSU:SHTC
prop write ipsmag::write_config
prop update ipsmag::update_config
prop check ipsmag::check_config
}
}
}
proc ipsmag::completeStart {{try 3}} {
if {![string match "IDN:OXFORD INSTRUMENTS:MERCURY*" [sct result]]} {
if {$try > 0} {
sct send "*IDN?"
incr try -1
clientput "[sct sicsdev]: bad IDN: [sct result]"
clientput "[sct sicsdev]: try again"
return "ipsmag::completeStart $try"
}
clientput "[sct sicsdev]: bad IDN: [sct result]"
return unpoll
}
return [stdSct::completeStart]
}
proc ipsmag::cmd {node args} {
set cmd [linsert $args 0 node_cmd $node]
if {[llength $args] < 2} {
return [eval $cmd]
}
lassign $args var val
if {[silent 0 hgetpropval $node/$var redo]} {
hsetprop $node/$var redo 0
set old -1
} else {
set old [eval [list node_cmd $node $var]]
}
set tar [silent $old hgetpropval $node/$var target]
if {$val != $old || $val != $tar} {
#clientlog "CMD $node $args ($old -> $val)"
return [eval [list node_cmd $node $var $val]]
}
# clientlog "KEEP $node $args"
return $old
}
proc ipsmag::write {} {
set val [sct target]
set cvt [silent "" sct cvt]
if {$cvt ne ""} {
set done 0
foreach item $cvt {
lassign [split $item =] key value
if {$value eq $val} {
set val $key
set done 1
break
}
}
if {!$done} {
set val $key
}
}
sct send "SET:[sct adr]:$val"
return "ipsmag::read 1"
}
proc ipsmag::write_set {} {
if {abs([silent 99999 hval [sct]] - [sct target]) > 1e-6} {
if {[hval [sct parent]/ramp_state] == 2} {
# we are in ramp_state "to_set" and the target changed while running
# to a setpoint. We have to switch the ramp_state to hold. This will
# remind the IPS that the setpoint has changed really!
# ramp_state will be switched to "to_set" again by the magfield script
sct send SET:DEV:GRPZ:PSU:ACTN:HOLD
clientput "setpoint changed: stop and restart ramp"
return ipsmag::write
}
}
return [ipsmag::write]
}
proc ipsmag::write_eng {} {
if {[sct target] eq ""} {
sct send SET:SYS:USER:NORM
} else {
sct send "SET:SYS:USER:ENG:[sct target]"
sct changed_password [DoubleTime]
}
return ipsmag::confirm_eng
}
proc ipsmag::confirm_eng {} {
if {[string match {*:DENIED} [sct result]]} {
sct print "ERROR: invalid password"
return idle
}
sct send READ:SYS:USER
return ipsmag::update_eng
}
proc ipsmag::read_eng {} {
if {[silent 0 sct changed_password] ne "0"} {
if {[DoubleTime] > [sct changed_password] + 60} {
sct changed_password 0
sct send SET:SYS:USER:NORM
return ipsmag::confirm_eng
}
}
sct send READ:SYS:USER
return ipsmag::update_eng
}
proc ipsmag::update_eng {} {
if {[ipsmag::cvt] eq "ENG"} {
sct update "***"
} else {
sct update ""
}
return idle
}
proc ipsmag::read {{from_write 0}} {
# TODO: split this into ipsmag::confirm and ipsmag::read
if {$from_write} {
if {![string match {*:VALID} [sct result]]} {
# show invalid messsage only when target value is different than actual
set tar [sct target]
set val [silent $tar hval [sct]]
if {abs($val - $tar) > abs($val + $tar) / 20000.} {
hdelprop [sct] requested
error "[sct]: [sct result]"
}
}
sct update [sct target] ;# do we need this? should be done in update script ...
}
sct send "READ:[sct adr]"
return update
}
proc ipsmag::cvt {} {
set val [lindex [split [sct result] :] end]
set cvt [silent "" sct cvt]
if {$cvt ne ""} {
set done 0
foreach item $cvt {
lassign [split $item =] key value
if {$key eq $val} {
set val $value
set done 1
break
}
}
if {!$done} {
clientput "[sct] can not parse '[sct result]', use '$value' instead"
set val $value
}
} else {
scan $val %f val
}
return $val
}
proc ipsmag::update {} {
sct update [ipsmag::cvt]
return idle
}
proc ipsmag::update_heater {} {
sct update [ipsmag::cvt]
if {[hval [sct]]} {
hsetprop [sct parent] adr DEV:GRPZ:PSU:SIG:FLD
} else {
hsetprop [sct parent] adr DEV:GRPZ:PSU:SIG:PFLD
}
return idle
}
proc ipsmag::update_leads {{n_of_slaves 3}} {
set current [ipsmag::cvt]
catch {
sct update [format %.6f [expr $current / [hval [sct parent]/atob]]]
} msg
if {$n_of_slaves} {
set currents [string trim [silent "" sct currents]]
if {$currents ne ""} {
# check currents
set tol [expr abs($current - [sct last_current]) * 1.2 + 0.06]
set mincur [format %.2f [expr ($current - $tol) / $n_of_slaves]]
set maxcur [format %.2f [expr ($current + $tol) / $n_of_slaves]]
set bad 0
foreach cur $currents {
if {$cur < $mincur || $cur > $maxcur} {
set bad 1
}
}
set status [hval [sct objectPath]/status]
if {$bad} {
set msg "slave currents ($currents) are not within $mincur .. $maxcur"
hupdate [sct objectPath]/status $msg
if {$status eq ""} {
clientlog "WARNING: $msg"
}
} elseif {[string match {slave currents *} $status]} {
hupdate [sct objectPath]/status ""
}
}
sct last_current $current
sct currents ""
sct send READ:DEV:PSU.M${n_of_slaves}:PSU:SIG:CURR
return "ipsmag::slave_currents $n_of_slaves"
}
return idle
}
proc ipsmag::slave_currents {index} {
set value [format %.2f [ipsmag::cvt]]
sct currents "$value [sct currents]"
updateval [sct parent]/slave$index $value
incr index -1
if {$index > 0} {
sct send READ:DEV:PSU.M${index}:PSU:SIG:CURR
return "ipsmag::slave_currents $index"
}
return idle
}
proc ipsmag::write_config {} {
if {[hvali [sct parent]/engineering_password] eq ""} {
sct update [sct target]
sct send READ:[sct adr]
return ipsmag::confirm_config
}
return [ipsmag::write]
}
proc ipsmag::confirm_config {} {
set val [ipsmag::cvt]
if {[hvali [sct parent]/engineering_password] eq ""} {
# check only
if {$val != [format %.7g [silent $val sct target]]} {
hdelprop [sct] requested
error "can not change [sct] with empty engineering_password"
}
}
sct update $val
return idle
}
proc ipsmag::update_config {} {
set val [ipsmag::cvt]
if {[hvali [sct parent]/engineering_password] eq ""} {
# check only
set msghead "make sure cables are connected for"
if {$val != [format %.7g [silent $val sct target]]} {
set txt "$msghead [hval [sct parent]/symode] mode, enter eng. pwd. and select [result device name] again"
if {[hvali [sct objectPath]/status] ne $txt} {
hupdate [sct objectPath]/status $txt
clientput "ERROR: $txt"
}
hsetprop [sct] geterror "$val does not match configured value [sct target]"
return idle
} else {
if {[string match "$msghead*" [hvali [sct objectPath]/status]]} {
hupdate [sct objectPath]/status ""
catch {hdelprop [sct] geterror}
}
}
}
sct update $val
return idle
}
proc ipsmag::check_config {} {
set val [silent [sct target] hval [sct]]
if {[hvali [sct parent]/engineering_password] eq ""} {
# check only
if {$val != [format %.7g [silent $val sct target]]} {
error "can not change [sct] with empty engineering_password"
}
}
}
proc ipsmag::read_ramp_state {{from_write 0}} {
if {$from_write} {
if {[string match {*:DENIED} [sct result]]} {
if {[hval [sct]] == 3} {
error "[sct]: power supply is clamped - please check reason"
}
error "[sct]: access not allowed: IPS in local mode?"
}
sct update [sct target] ;# do we need this? should be done in update
}
sct send "READ:[sct adr]"
return update
}