471 lines
11 KiB
Tcl
471 lines
11 KiB
Tcl
namespace eval k2450 {} {
|
|
variable luaCode
|
|
set luaCode 0
|
|
}
|
|
|
|
proc stdConfig::k2450 {parameter {path_res ""}} {
|
|
# parameter: either current or voltage
|
|
# respath: the path of a resitivity node / object
|
|
variable base
|
|
variable name
|
|
|
|
controller std
|
|
prop startcmd *IDN?
|
|
prop control_parameter CURRENT
|
|
prop changed_control 0
|
|
prop path_$parameter $base$name
|
|
prop vsum 0
|
|
prop isum 0
|
|
prop nsum 0
|
|
prop path_res $path_res
|
|
|
|
obj K2450 -drive wr
|
|
poll 1
|
|
prop checklimits k2450::checklimits
|
|
prop halt k2450::on_halt
|
|
prop write k2450::write_main
|
|
prop read k2450::read_main
|
|
prop laststep 0
|
|
prop status idle
|
|
prop @parameter [string toupper $parameter]
|
|
prop @otherpath ""
|
|
prop startrun 0
|
|
|
|
kids $parameter {
|
|
node target upd
|
|
|
|
node output out -int
|
|
prop check k2450::check_output
|
|
prop write k2450::write_output
|
|
prop label output_on
|
|
prop enum 1
|
|
|
|
node set out
|
|
prop check k2450::check_set
|
|
prop write k2450::write_set
|
|
|
|
node limited upd
|
|
default 0
|
|
if {$parameter eq "current"} {
|
|
prop label "voltage limited"
|
|
} else {
|
|
prop label "current limited"
|
|
}
|
|
|
|
node limit out
|
|
prop check k2450::check_limit
|
|
prop write stdSct::completeUpdate
|
|
if {$parameter eq "current"} {
|
|
default 1
|
|
} else {
|
|
default 20
|
|
}
|
|
|
|
node lastmsg rd -text
|
|
prop read k2450::geterrors
|
|
prop geterror clear
|
|
prop errcnt 0
|
|
|
|
node script -text par "linear_ramp.tcl"
|
|
prop help "scriptfile containing 'while' and 'halt' scripts"
|
|
prop width 32
|
|
|
|
node delay par 1
|
|
|
|
node step par 0
|
|
|
|
node codeversion rd -text
|
|
prop read k2450::load_code
|
|
prop visible false
|
|
|
|
# force load_code to be run first
|
|
variable ctrl
|
|
variable node
|
|
$ctrl queue $node write read
|
|
}
|
|
}
|
|
|
|
proc k2450::start {} {
|
|
sct send *IDN?
|
|
return complete
|
|
}
|
|
|
|
proc k2450::geterrors {{followup 0}} {
|
|
if {[silent "" sct geterror] eq "clear"} {
|
|
sct update ""
|
|
}
|
|
if {$followup} {
|
|
if {[lindex [sct result] 0] == 0} {
|
|
return [silent idle sct after_geterrors]
|
|
}
|
|
set txt [lrange [sct result] 1 end-4]
|
|
if {[lindex [sct result] end-3] < 4} {
|
|
clientput "K2450 message: $txt"
|
|
if {[sct control_parameter] eq [sct @parameter]} {
|
|
updateval [sct objectPath]/lastmsg $txt
|
|
}
|
|
} else {
|
|
set errcnt [sct errcnt]
|
|
incr errcnt
|
|
if {[sct errcnt] > 10} {
|
|
sct errcnt 0
|
|
sct send print(eventlog.clear())
|
|
return [silent stdSct::complete sct after_geterrors]
|
|
}
|
|
sct errcnt $errcnt
|
|
}
|
|
}
|
|
sct send print(eventlog.next())
|
|
return "k2450::geterrors 1"
|
|
}
|
|
|
|
proc k2450::check_limit {} {
|
|
if {[hvali [sct @otherpath]/output]} {
|
|
sct update [sct target]
|
|
hset [sct @otherpath]/set [hvali [sct @otherpath]/set]
|
|
return idle
|
|
}
|
|
if {![hvali [sct objectPath]/output]} {
|
|
sct update [sct target]
|
|
return idle
|
|
}
|
|
set s [hvali [sct objectPath]/set]
|
|
set l [expr abs([sct target])]
|
|
sct update $l
|
|
if {$s > $l} {
|
|
set s $l
|
|
} elseif {$s < -$l} {
|
|
set s -$l
|
|
} else {
|
|
return
|
|
}
|
|
clientput "changed [sct objectPath]/set to $s"
|
|
hset [sct objectPath]/set $s
|
|
}
|
|
|
|
proc k2450::luacode {code} {
|
|
variable luaCode
|
|
variable luaCodeVersion
|
|
if {$code ne [silent 0 set luaCode]} {
|
|
set luaCode $code
|
|
set luaCodeVersion [clock format [clock seconds] -format %Y-%m-%dT%H:%M:%S]
|
|
}
|
|
}
|
|
|
|
k2450::luacode {{
|
|
setSource = function(sfunc, mfunc, set, lim)
|
|
if sfunc != smu.source.func then
|
|
smu.source.output = smu.OFF
|
|
end
|
|
smu.source.func = sfunc
|
|
smu.measure.func = mfunc
|
|
smu.source.level = set
|
|
if lim then
|
|
if sfunc == smu.FUNC_DC_VOLTAGE then
|
|
smu.source.ilimit.level = lim
|
|
else
|
|
smu.source.vlimit.level = lim
|
|
end
|
|
end
|
|
smu.source.output = smu.ON
|
|
end
|
|
} {
|
|
setVoltage = function(set, lim)
|
|
setSource(smu.FUNC_DC_VOLTAGE, smu.FUNC_DC_CURRENT, set, lim)
|
|
print("setVoltage", smu.source.level)
|
|
end
|
|
} {
|
|
setCurrent = function(set, lim)
|
|
setSource(smu.FUNC_DC_CURRENT, smu.FUNC_DC_VOLTAGE, set, lim)
|
|
print("setCurrent", smu.source.level)
|
|
end
|
|
} {
|
|
viBuf = buffer.make(10)
|
|
getValues = function()
|
|
viBuf.clear()
|
|
smu.measure.read(viBuf)
|
|
if smu.source.func == smu.FUNC_DC_CURRENT then
|
|
u = viBuf.readings[1]
|
|
i = viBuf.sourcevalues[1]
|
|
tripped = tonumber(smu.source.vlimit.tripped)
|
|
else
|
|
i = viBuf.readings[1]
|
|
u = viBuf.sourcevalues[1]
|
|
tripped = tonumber(smu.source.ilimit.tripped)
|
|
end
|
|
print(string.format("%.7g %.7g %.7g %d %d %d end", u, i, smu.source.level,
|
|
tonumber(smu.source.func), tonumber(smu.source.output), tripped))
|
|
end
|
|
}}
|
|
|
|
|
|
proc k2450::load_code {} {
|
|
if {[sct @parameter] eq "CURRENT"} {
|
|
sct @otherpath [sct path_voltage]
|
|
} else {
|
|
sct @otherpath [sct path_current]
|
|
}
|
|
sct send {print(codeVersion, "codeVersion")}
|
|
return k2450::load_code_chk
|
|
}
|
|
|
|
proc k2450::load_code_chk {} {
|
|
variable luaCodeVersion
|
|
lassign [sct result] versionOnDevice chk
|
|
if {$chk ne "codeVersion"} {
|
|
return idle
|
|
}
|
|
sct update $versionOnDevice
|
|
if {$versionOnDevice == $luaCodeVersion} {
|
|
return idle
|
|
}
|
|
clientput "load $luaCodeVersion (!= [sct result])"
|
|
# sct send "reset() print()"
|
|
sct send "print()"
|
|
return "k2450::load_code_do 0"
|
|
}
|
|
|
|
proc k2450::load_code_do {index} {
|
|
variable luaCode
|
|
sct send "[string map [list "\n" " "] [lindex $luaCode $index]] print()"
|
|
incr index
|
|
if {$index > [llength $luaCode]} {
|
|
return k2450::load_code_end
|
|
}
|
|
return "k2450::load_code_do $index"
|
|
}
|
|
|
|
proc k2450::load_code_end {} {
|
|
variable luaCodeVersion
|
|
hupdate [sct parent]/codeversion $luaCodeVersion
|
|
sct send "codeVersion = '$luaCodeVersion' print()"
|
|
return k2450::geterrors
|
|
}
|
|
|
|
proc k2450::write_set {} {
|
|
if {[sct control_parameter] ne [sct @parameter]} {
|
|
return idle
|
|
}
|
|
set lim nil
|
|
catch {
|
|
set lim [sctval [sct @otherpath]/limit]
|
|
}
|
|
if {[sct @parameter] eq "CURRENT"} {
|
|
sct send setCurrent([sct target], $lim)
|
|
} else {
|
|
sct send setVoltage([sct target], $lim)
|
|
}
|
|
sct changed_control 0
|
|
updateval [sct @otherpath]/output 0
|
|
updateval [sct parent]/output 1
|
|
hupdate [sct @otherpath]/set 0
|
|
logsetup [sct @otherpath]/set clear
|
|
return complete
|
|
}
|
|
|
|
proc k2450::get_old {obj} {
|
|
set old [hvali $obj/set]
|
|
set meas [hvali $obj]
|
|
clientput "old $old meas $meas"
|
|
set new [silent $meas hgetpropval $obj/set requested]
|
|
clientput "new $new"
|
|
if {abs($new - $old) > 1e-2 * abs($new + $old)} {
|
|
return $new
|
|
}
|
|
return $old
|
|
}
|
|
|
|
proc k2450::check_output {} {
|
|
if {[sct target]} {
|
|
hset [sct parent]/set [get_old [sct parent]]
|
|
}
|
|
}
|
|
|
|
proc k2450::write_output {} {
|
|
if {[sct target]} {
|
|
return idle
|
|
}
|
|
# switch off
|
|
sct send "smu.source.output = smu.OFF print()"
|
|
sct update 0
|
|
return complete
|
|
}
|
|
|
|
proc k2450::checklimits {} {
|
|
# checklimits is called twice! avoid that
|
|
if {[sct startrun]} return
|
|
hdelprop [sct @otherpath] target
|
|
set script [hval [sct]/script]
|
|
if {$script ne ""} {
|
|
clientput "$script [pwd]"
|
|
set file [result exe batchpath]/$script
|
|
if {[file exists $file]} {
|
|
source $file
|
|
} elseif {[file exists $script]} {
|
|
source $script
|
|
}
|
|
}
|
|
first [get_old [sct]] [sct target]
|
|
sct lastset [sctval [sct]/set]
|
|
sct laststep [DoubleTime]
|
|
updateval [sct]/target [sct target]
|
|
sct startrun 1
|
|
}
|
|
|
|
proc k2450::check_set {} {
|
|
sct control_parameter [sct @parameter]
|
|
sct changed_control 2
|
|
if {abs([sct target]) > [hval [sct objectPath]/limit]} {
|
|
error "abs([sct]) must be <= [hval [sct objectPath]/limit]"
|
|
}
|
|
}
|
|
|
|
proc k2450::on_halt {{startpath ""} {startscript ""}} {
|
|
hdelprop [sct] target
|
|
sct status posfault
|
|
clientput "HALT [sct]"
|
|
if {$startpath ne ""} {
|
|
sct status idle
|
|
[sct controller] queue $startpath read $startscript
|
|
} else {
|
|
# do not call halt script when restarting
|
|
if {[catch {halt} msg]} {
|
|
clientput "ERROR: in halt: $msg"
|
|
}
|
|
}
|
|
return idle
|
|
}
|
|
|
|
proc k2450::read_main {} {
|
|
if {[sct control_parameter] ne [sct @parameter]} {
|
|
if {[sct status] eq "run"} {
|
|
clientput "[sct] stopped"
|
|
hdelprop [sct] target
|
|
sct status idle
|
|
}
|
|
return idle
|
|
}
|
|
sct send "getValues()"
|
|
return k2450::update_main
|
|
}
|
|
|
|
proc k2450::update_main {} {
|
|
lassign [sct result] voltage current set func output tripped end
|
|
if {$end ne "end"} {
|
|
clientput "bad result to getValues(): [sct result]"
|
|
return idle
|
|
}
|
|
if {$func} {
|
|
set func CURRENT
|
|
} else {
|
|
set func VOLTAGE
|
|
}
|
|
if {$func ne [sct control_parameter]} {
|
|
if {[sct changed_control]} {
|
|
# function to be changed, but not yet sent to the device
|
|
sct changed_control [expr [sct changed_control] - 1]
|
|
} else {
|
|
# function was changed on the device
|
|
sct control_parameter $func
|
|
}
|
|
return idle
|
|
}
|
|
if {!$output && [hvali [sct]/output]} {
|
|
clientput "[sct] output switched off manually"
|
|
}
|
|
hupdate [sct]/output $output
|
|
if {!$output || ![sctval [sct]/output]} {
|
|
# output switched off
|
|
sct update 0
|
|
updateval [sct]/set 0
|
|
updateval [sct @otherpath] 0
|
|
updateval [sct @otherpath]/set 0
|
|
logsetup [sct]/set clear
|
|
logsetup [sct] clear
|
|
logsetup [sct @otherpath] clear
|
|
logsetup [sct @otherpath]/set clear
|
|
hdelprop [sct] target
|
|
hdelprop [sct @otherpath] target
|
|
if {[sct status] eq "run"} {
|
|
clientput "[sct] stopped"
|
|
sct status idle
|
|
}
|
|
return idle
|
|
}
|
|
updateval [sct]/limited $tripped
|
|
set now [DoubleTime]
|
|
sct vsum [expr [sct vsum] + $voltage]
|
|
sct isum [expr [sct isum] + $current]
|
|
sct nsum [expr [sct nsum] + 1]
|
|
if {[sct nsum] >= 5 + (int($now) % 5 != 0)} {
|
|
updateval [sct path_voltage] [expr [sct vsum] / double([sct nsum])]
|
|
updateval [sct path_current] [expr [sct isum] / double([sct nsum])]
|
|
catch {
|
|
updateval [sct path_res] [expr [sct vsum] / double([sct isum])]
|
|
}
|
|
updateval [sct]/set $set
|
|
sct vsum 0
|
|
sct isum 0
|
|
sct nsum 0
|
|
}
|
|
if {[sct status] eq "run"} {
|
|
get_values [sct] delay
|
|
if {$delay <= 0} {
|
|
set delay 1
|
|
}
|
|
if {[catch {set res [check $voltage $current]} msg]} {
|
|
clientput "ERROR: in check: $msg"
|
|
set res -1
|
|
}
|
|
while {$res > 0 && $now > [sct laststep] + $delay} {
|
|
get_values [sct] set step target
|
|
set lastset [silent $set sct lastset]
|
|
if {abs($lastset - $set) > 1e-2 * abs($lastset + $set)} {
|
|
clientput "lastset ($lastset) set to readback ($set)"
|
|
set lastset $set
|
|
}
|
|
if {[catch {set res [step $lastset $step $target]} msg]} {
|
|
clientput "ERROR: in step: $msg"
|
|
set res -1
|
|
}
|
|
catch {
|
|
sct lastset [hgetpropval [sct]/set target]
|
|
}
|
|
sct laststep [expr [sct laststep] + $delay]
|
|
}
|
|
if {$res < 0} {
|
|
hdelprop [sct] target
|
|
if {[catch {halt} msg]} {
|
|
clientput "ERROR: in halt: $msg"
|
|
}
|
|
sct status posfault
|
|
} elseif {$res == 0} {
|
|
sct last_step 0
|
|
clientput "[sct] finished"
|
|
sct status idle
|
|
}
|
|
}
|
|
return idle
|
|
}
|
|
|
|
proc k2450::write_main {} {
|
|
if {[sct startrun]} {
|
|
if {[hgetpropval [sct @otherpath] status] eq "run"} {
|
|
[sct controller] queue [sct @otherpath] start "k2450::on_halt [sct objectPath] k2450::write_main"
|
|
return idle
|
|
}
|
|
sct startrun 0
|
|
sct status run
|
|
set set [silent [hval [sct]/set] sct lastset]
|
|
sct print "[sct] running from $set to [hval [sct]/target]"
|
|
} else {
|
|
if {[sct status] eq "run"} {
|
|
clientput "[sct] stopped"
|
|
sct status idle
|
|
}
|
|
sct print "[sct] set to [sct target]"
|
|
hset [sct]/set [sct target]
|
|
}
|
|
return idle
|
|
}
|