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 }