namespace eval nvstep { } proc stdConfig::nvstep {} { variable name controller syncedprot pollperiod 1 1 obj NvStep wr prop write nvstep::write prop read "nvstep::read [silent 0 result instconfig sensirion]" prop enum fixed=0,controlled=1,automatic=2,close=3,open=4 prop write nvstep::write prop lastpulse 0 prop filter 0 prop closetest 0 prop amplitude 0 prop t_after_delay 0 prop fixed_to_auto 0 prop initialize 0 prop maxflow 0 default 0 kids "needle value" { node flow upd node motstat -text upd node set out default 2.5 prop check nvstep::checkset prop write stdSct::complete prop label "flow set" node flowmax par 20 prop label "flow maximum" node flowp upd prop help "flow calculated from pressure before pump" node ctrl -none kids "control parameters" { node prop par 10 prop help bigger means higher feedback node int par 15 prop help bigger means reacting slower (unit: sec) node delay par 5 prop help minimum time to wait before next motor step node tol par 0.1 } node autoflow -none kids "autoflow control parameters" { flow::make flow::tmts {result tt set/reg} } node calib -none kids calib { node ln_per_min_per_mbar par 0.9 node mbar_offset par 0.5 } node initialize -int out default 0 prop visible false prop enum check=1,adjust=2,stop=3 prop label "limit switch" prop write nvstep::startinit node initstate -text upd prop visible false default "" } } proc nvstep::checkset {} { sct update [sct target] set s [hvali [sct parent]] if {[hvali /cc/fa] == 0 || ($s != 1 && $s != 2)} { if {$s != 2} { set s 1 } hset [sct parent] $s } } proc nvstep::write {} { switch [sct target] { 3 { sct rangeshift 0 if {[hvali [sct]/nvmot] > [hvali [sct]/nvmot/lowerlimit]} { nvmot [hvali [sct]/nvmot/lowerlimit] updateval [sct]/motstat closing } } 4 { nvmot [hvali [sct]/nvmot/upperlimit] updateval [sct]/motstat opening } 2 { hupdate [sct]/autoflow/suspended 0 } } sct fixed_to_auto 0 if {[sct target] == 0 || [sct target] > 2} { logsetup [sct]/set clear logsetup [sct]/autoflow/flowtarget clear } sct update [sct target] return idle } proc nvstep::calcstep {from to prop} { # for the decreasing step (to < from), we use a linear function above 20 foreach var {from to} { if {[set $var] > 20 && $to < $from} { set $var [expr 1.9 + 0.0025 * [set $var]] } elseif {[set $var] > 1} { set $var [expr 2 - 1.0 / [set $var]] } } set res [expr ($to - $from) * $prop] return $res } proc nvstep::poll {} { set now [DoubleTime] set mode [sctval [sct]] if {$mode != 0} { set encpos [hval [sct]/nvmot/encoder] if {$encpos < [hval [sct]/nvmot/lowerlimit] - 15 || $encpos > [hval [sct]/nvmot/upperlimit] + 15} { hsetprop [sct]/nvmot/encoder adjust_zero 1 return idle } } # if {[silent 0 sct initialize] > 0} { # return nvstep::init # } hset [sct]/nvmot/disablelimits 0 set umsg "automatic needle valve not activated - set temperature undefined" switch -- $mode { 1 - 2 { # pump_is_off is defined in startup/hepump.tcl if {[info command pump_is_off] ne "" && [pump_is_off]} { return idle } if {$mode == 1 || [sctval [sct]/autoflow/suspended]} { set soll [hvali [sct]/set] hupdate [sct]/autoflow/flowtarget $soll } else { if {$now > [silent 0 sct lastauto] + 4.7} { sct lastauto $now set script [hvali [sct]/autoflow/script] set flowmax [hvali [sct]/flowmax] if {$flowmax > 20} { set flowmax 20 clientput "WARNING: reduced flowmax to $flowmax ln/min for less stress to the pump" hupdate [sct]/flowmax $flowmax } $script [sct]/autoflow [hvali [sct]/set] $flowmax } set soll [hvali [sct]/autoflow/flowset] if {[hgetpropval [sct]/autoflow/getTset t_set_undefined]} { if {[hvali [sct]/status] ne $umsg} { clientput "ERROR: $umsg" } hupdate [sct]/status $umsg } } set ist [hvali [sct]/flow] set tol [hvali [sct]/ctrl/tol] set a [sct amplitude] set t [sct t_after_delay] if {$t != 0} { set t [expr $now - $t] } set i [hvali [sct]/ctrl/int] # lim is an exponential curve converging to soll. # the range between lim and soll is a window shrinking with time # before t_after_delay, the last 'ist' value is between lim and soll # if the 'ist' value does not change it will reach lim at t_after_delay set lim [expr $soll + $a * exp(-$t/double($i))] # calculate expected limits for no further step needed if {$lim > $soll} { set uplim [expr $lim + $tol] set lolim [expr $soll - $tol] } else { set uplim [expr $soll + $tol] set lolim [expr $lim - $tol] } if {$ist > $uplim || $ist < $lolim} { # we have to do a next step set step [nvstep::calcstep $ist $soll [hvali [sct]/ctrl/prop]] sct t_after_delay [expr $now + [hvali [sct]/ctrl/delay]] sct amplitude [expr $ist - $soll] set v [format %.2f [expr [hvali [sct]/nvmot] + $step]] set u [hvali [sct]/nvmot/upperlimit] set ll [hvali [sct]/nvmot/lowerlimit] ;# ll was 0 before! if {$v > $u} { set v $u updateval [sct]/motstat opened } elseif {$v < $ll} { set v $ll updateval [sct]/motstat closed } elseif {$step > 0} { updateval [sct]/motstat opening } else { updateval [sct]/motstat closing } #clientput "$step $v" nvmot $v } else { # no step needed yet updateval [sct]/motstat idle if {$t > 0} { if {$ist < $uplim - $tol && $ist > $lolim + $tol} { # the ist value is making more progress then expected at worst # so we narrow the window accordingly sct amplitude [expr $ist - $soll] sct t_after_delay $now } } } } 0 { # fixed set cnt [sct fixed_to_auto] if {[silent 0 hval [sct]/flow] > [hvali [sct]/flowmax] + 10} { incr cnt if {$cnt > 60} { clientput "pressure high - switch to automatic mode" hset [sct] 2 } } else { if {$cnt > 0} { incr cnt -1 } } sct fixed_to_auto $cnt switch [hvali [sct]/motstat] { opening - closing { if {[hgetpropval [sct]/nvmot status] ne "run"} { updateval [sct]/motstat idle } } } } 3 { # close set stat [hvali [sct]/motstat] if {[hgetpropval [sct]/nvmot status] ne "run" && $stat eq "closing"} { updateval [sct]/motstat closed } } 4 { # open set stat [hvali [sct]/motstat] if {[hgetpropval [sct]/nvmot status] ne "run" && $stat eq "opening"} { updateval [sct]/motstat opened } } } if {[hvali [sct]/status] eq $umsg && \ ([sctval [sct]] != 2 || [hgetpropval [sct]/autoflow/getTset t_set_undefined] == 0)} { hsetprop [sct]/autoflow/getTset t_set_undefined 0 hupdate [sct]/status "" } return idle } proc nvstep::read {{sensirion 0}} { hsetprop /cc/fa nvpath [sct] _cc updatescript /cc/fa nvstep::updatemode hsetprop /cc/f nvpath [sct] if {$sensirion} { hsetprop /nvflow flowsource flowmeter hsetprop /nvflow nvpath [sct] hsetprop /nvflow nvctrl [sct controller] _hemot updatescript /nvflow nvstep::updateflow hsetprop /cc/f flowsource pressref } else { hsetprop /cc/f nvctrl [sct controller] hsetprop /cc/f flowsource flowpress } _cc updatescript /cc/f nvstep::updateflow return unpoll } proc nvstep::updateflow {value} { # flowsource: # pressref: just update nv/flowp # flowmeter: update nv/flow # flowpress: flow from pressure set source [silent pressure sct flowsource] set filter [lrange "[silent $value sct filter] $value" end-9 10] sct filter $filter # filter out values which are within the 3 highest or 3 lowest values # out of 10 last values (i.e. within 5 seconds) set m 3 set filter [lsort -real $filter] if {[llength $filter] < 10} { set flow $value } else { set min [lindex $filter $m] set max [lindex $filter end-$m] set mean [expr double([join $filter +]) / [llength $filter]] set flow [silent 0 sct filtered] if {$max < $flow} { set flow $max } elseif {$min > $flow} { set flow $min } else { set flow [expr $flow * 0.98 + $mean * 0.02] } } sct filtered $flow if {$source eq "flowmeter"} { updateval [sct nvpath]/flow $flow } else { if {$flow < -50} { set flow -62.5 } else { set off [hval [sct nvpath]/calib/mbar_offset] set fpm [hval [sct nvpath]/calib/ln_per_min_per_mbar] set flow [expr ($flow - $off) * $fpm] } if {$source eq "pressref"} { hupdate [sct nvpath]/flowp $flow return } updateval_e [sct nvpath]/flow $flow -62.5 no_sensor } [sct nvctrl] queue [sct nvpath] read nvstep::poll } proc nvstep::startinit {} { set i [hgetpropval [sct parent] initialize] switch [sct target] { 1 { if {$i != 0} { error "checking already" } # hsetprop [sct parent] initialize 10 hsetprop [sct parent] initialize 13 } 2 { if {$i != 0} { error "adjusting already" } hsetprop [sct parent] initialize 20 } 3 { if {$i >= 10 && $i < 13} { hsetprop [sct parent] initialize 13 } elseif {$i >= 20} { hsetprop [sct parent] initialize 28 } else { hsetprop [sct parent] initialize 29 } } } sct update [sct target] return idle } proc nvstep::ierror {msg} { updateerror [sct]/initstate "$msg" clientput "[sct]: $msg" } proc nvstep::imsg {msg} { if {$msg ne [hvali [sct]/initstate]} { hupdate [sct]/initstate $msg clientput "[sct]: $msg" catch {hdelprop [sct]/initstate geterror} } } proc nvstep::init {} { set now [DoubleTime] if {[hgetpropval [sct]/nvmot status] eq "run"} { return idle } set phase [sct initialize] set pos [hvali [sct]/nvmot/encoder] set goto "" switch $phase { 10 { # start check ierror "limit switch no longer supported" } 20 { # start adjust ierror "limit switch no longer supported" } } if {$goto ne ""} { sct goto $goto clientput "goto $goto" run [hgetpropval [sct]/nvmot sicsdev] $goto } sct initialize $phase return idle }