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 }