Files
sea/tcl/drivers/k2450.tcl
2022-08-18 15:04:28 +02:00

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
}