namespace eval ccu4flow { } proc stdConfig::ccu4flow {args} { variable node controller syncedprot obj ccu4flow wr prop write ccu4flow::setmode prop read ccu4flow::read prop dir 1 prop enum fixed=0,controlled=1,automatic=2,close=3,open=4 prop filter 0 prop regstate reg prop measured_openpulse 0 prop pulse 0 prop olddif 0 prop settime 0 prop soll 0 prop adjustdelay 10 prop maxhysteresis 1 prop hystlim 1 prop period 5 prop lastpulse 0 prop totalmin 0 prop minpulse 0 prop lastmov 0 prop hystpulse 0 prop lastist 0 set sensirion 0 foreach arg $args { if {$arg eq "release_blocked"} { prop release_blocked 1 } if {$arg eq "1"} { set sensirion 1 } } default 0 kids "needle valve" { node motstat alias /cc/fm prop enum idle,opening,closing,opened,closed,no_motor node flow upd -secop=nvflow node set out default 1.0 prop check ccu4flow::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 span upd node use_pressure par -int [expr !$sensirion] prop enum 1 prop help "use pressure instead of flow meter for control" node ctrl -none kids "control parameters" { node regtext upd -text default regulate prop label status prop width 40 node prop_o par 0.05 prop help {prop [sec/mbar] when opening. above 4 mbar a 10 times lower value is used} node prop_c par 0.03 prop help {prop [sec/mbar] when closing. above 4 mbar a 10 times lower value is used} node deriv_o par 30 prop help {convergence target time [sec] when opening} node deriv_c par 30 prop help {convergence target time [sec] when closing} node minpulse_o out default 0.05 prop help {minimum close pulse [sec]} prop check "ccu4flow::check_minpulse 1" prop write stdSct::complete node minpulse_c out default 0.05 prop help {standard close pulse [sec]} prop check "ccu4flow::check_minpulse -1" prop write stdSct::complete node hystpulse_o par 0 prop help {motor pulse to overcome hysteresis when opening} node hystpulse_c par 0 prop help {motor pulse to overcome hysteresis when closing} node tol par 0.25 prop label tolerance prop help {valid below 3 mbar} node tolhigh par 0.5 prop label tol. above 4 prop help {valid above 4 mbar} node openpulse par 60 prop help {time to open from completely closed to a significant opening} node adjust_minpulse par 1 prop enum 1 prop help {adjust minpulse automatically} } node autoflow -none kids "autoflow control parameters" { flow::make ccu4flow::tmts {result tt set/reg} } node calib -none kids calib { node ln_per_min_per_mbar par 0.9 node mbar_offset par 0.8 } } } proc ccu4flow::tmts {} { global tt set tm [silent 1 result tt tm] set ts [silent $tm result tt ts] if {$ts < $tm} { return $ts } else { return $tm } } proc ccu4flow::checkset {} { set dir [hgetpropval [sct parent] dir] # if {([sct target] - [hvali [sct]]) * $dir < 0} { # # we probably initiate a dir change # if {$invcnt > 0} { # incr invcnt -1 # hsetprop [sct parent] invcnt $invcnt # logtext invcnt -1 by set # } # } if {[hvali [sct]] != [sct target]} { hsetprop [sct parent] setchanged 1 hsetprop [sct parent] settime [DoubleTime] } sct update [sct target] #hupdate /cc/fm 0 set s [hvali [sct parent]] if {[hvali /cc/fa] == 0 || ($s != 1 && $s != 2)} { hset [sct parent] 1 } } proc ccu4flow::check_minpulse {dir} { if {$dir == [hgetpropval [sct objectPath] dir]} { hsetprop [sct objectPath] minpulse [sct target] } sct update [sct target] } proc ccu4flow::read {} { hsetprop /cc/fa nvpath [sct] _cc updatescript /cc/fa ccu4flow::updatemode hsetprop /cc/f nvpath [sct] hsetprop /cc/f nvctrl [sct controller] hsetprop /cc/f flowsource pressure _cc updatescript /cc/f ccu4flow::updateflow catch { hsetprop /nvflow flowsource flow hsetprop /nvflow nvpath [sct] hsetprop /nvflow nvctrl [sct controller] _hemot updatescript /nvflow ccu4flow::updateflow } return unpoll } proc ccu4flow::setmode {} { sctsync { sct update [sct target] if {[sct target] == 3} { # close cc fa 0 cc mp -[hvali /cc/mot] # set motstat to opening (until it gets updated) hupdate /cc/fm 2 } elseif {[sct target] == 4} { # open cc fa 0 cc mp [hvali /cc/mot] # set motstat to closing (until it gets updated) hupdate /cc/fm 1 } else { if {[sct target] == 2} { hupdate [sct]/autoflow/suspended 0 } cc fa [sct target] cc mp 0 } } return stdSct::complete } proc ccu4flow::updatemode {value} { if {$value == 3} { set mode [hvali [sct nvpath]] clientput "[sct parent] was offline $mode" if {$mode >= 3} { set mode 0 } hset [sct] $mode } elseif {[hvali [sct nvpath]] < 3 || $value > 0} { if {$value ne [hvali [sct nvpath]]} { clientput "[sct] changed to $value -> $nvpath" } hupdate [sct nvpath] $value } } proc ccu4flow::updateflow {value} { # flowsource: # pressure: this is a pressure # flow: this is a flow from a flowmeter set source [silent none sct flowsource] set n [silent 9 sct filter_n] set filter [lrange "[silent $value sct filter] $value" end-$n end] sct filter $filter # filter out values which are within the m highest or m lowest values # out of n+1 last values (i.e. within 5 seconds) (m was 3) set m [silent 0 sct filter_m] set filter [lsort -real $filter] if {[llength $filter] < $n} { set flow $value set flow0 $value set span 0 } else { set min [expr [lindex $filter $m] - 0.1] set max [expr [lindex $filter end-$m] + 0.1] set mean [expr double([join $filter +]) / [llength $filter]] set span [expr $max - $min - 0.2] set flow [silent 0 sct filtered] if {$max < $flow} { # if {[sct] ne "/cc/f"} { # clientput "[sct] fast decrease $flow $max" # } set flow $max } elseif {$min > $flow} { # if {[sct] ne "/cc/f"} { # clientput "[sct] fast increase $flow $min" # } set flow $min } else { set flow [expr $flow * 0.98 + $mean * 0.02] } } sct filtered $flow if {$source eq "flow"} { updateval [sct nvpath]/flow $flow } elseif {$source eq "pressure"} { 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] } updateval_e [sct nvpath]/flowp $flow -62.5 no_sensor hupdate [sct nvpath]/span $span } else { error "[sct] illegal flowsource: $source" } [sct nvctrl] queue [sct nvpath] read ccu4flow::ctrl } proc ccu4flow::increase {val by min max} { if {$val < $min} { set val $min } set lg [expr round(log10($val) * 10 + $by) * 0.1] set dig [expr int(1.85 - $lg)] if {$dig < 0} { set dig 0 } set v [format "%.${dig}f" [expr pow(10.0, $lg)]] if {$v > $max} { return $max } elseif {$v < $min} { return $min } else { return $v } } proc ccu4flow::trf {value} { if {$value <= 1.0} { return [expr $value - 1.0] } return [format %.2f [expr log($value)]] } proc ccu4flow::setstate {state {text ""}} { upvar regstate r if {$text eq ""} { set text $state } # if {$text ne [hvali [sct]/ctrl/regtext]} { # clientput "R $text" # } set r $state hset [sct]/ctrl/regtext $text } proc ccu4flow::logtext args { # clientput [concat $args] } proc ccu4flow::ctrl {} { set umsg "automatic needle valve not activated - set temperature undefined" set mode [sctval [sct]] set oldmode $mode # pump_is_off is defined in startup/hepump.tcl if {[info command pump_is_off] ne "" && ($mode == 2 || $mode == 1)} { if {[pump_is_off]} { set mode 0 } } if {$mode == 2 && [sctval [sct]/autoflow/suspended]} { set mode 1 } switch -- $mode { 0 - 3 - 4 { # fixed, close, open if {$oldmode == $mode} { switch -- [hvali /cc/fm] { 2 - 4 { # closing or closed sct update 3 } 1 - 3 { # opening or opened sct update 4 } default { # sct update 0 } } } logsetup [sct]/set clear logsetup [sct]/autoflow/flowtarget clear return idle } 1 { # controlled set soll [hvali [sct]/set] # the following line is for graphic (using flowtarget as nv_set curve) hupdate [sct]/autoflow/flowtarget $soll } 2 { # automatic 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 } flow::task /nv/autoflow [hvali [sct]/set] $flowmax set soll [hvali [sct]/autoflow/flowset] if {[sct soll] != $soll} { sct soll $soll sct settime [DoubleTime] } if {[hgetpropval [sct]/autoflow/getTset t_set_undefined]} { if {[hvali [sct]/status] ne $umsg} { clientput "ERROR: $umsg" } hupdate [sct]/status $umsg } } } 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 "" } set regstate [sct regstate] set now [DoubleTime] if {[hval [sct]/use_pressure]} { set ist [hvali [sct]/flowp] } else { set ist [hvali [sct]/flow] } set tol [hvali [sct]/ctrl/tol] set tolhigh [hvali [sct]/ctrl/tolhigh] set closestate 0 if {[hvali /cc/fm] == 4} { # n.v. is closed if {$ist < $soll} { if {$regstate ne "open"} { set closestate 2 set regstate 0 } } else { if {[silent 0 sct release_blocked] && $ist > $soll + $tol} { # hack for releasing n.v. at MA7 setstate closed set closecnt [silent 1 sct closecnt] incr closecnt if {$closecnt > 20} { cc mp 0.3 clientput "open pulse for blocked n.v." set closecnt 0 } sct closecnt $closecnt } return idle } } elseif {$ist > $soll + 80} { # n.v. to be closed if {$regstate ne "close" && $regstate ne "open"} { set closestate 1 set regstate 0 } } elseif {[hvali /cc/fm] == 3} { # n.v. opened if {$soll > $ist} { hset [sct]/ctrl/regtext "fully opened" return idle } } if {[silent 1 sct closecnt] == 0 && [silent 0 sct release_blocked]} { cc mp -0.3 clientput "close pulse for blocked n.v." } sct closecnt 1 if {$tol < 0.01} { set tol 0.01 hupdate [sct]/ctrl/tol $tol } if {$tolhigh < $tol} { set tolhigh $tol hupdate [sct]/ctrl/tolhigh $tolhigh } set f 10.0 if {$soll > 4} { set tol $tolhigh set fact $f } elseif {$soll > 3} { set tol [expr ($soll - 3) * ($tolhigh - $tol) + $tol] set fact [expr ($soll - 3.0) * ($f - 1.0) + 1.0] } else { set fact 1.0 } if {$ist > 4} { set fi $f } elseif {$ist > 3} { set fi [expr ($ist - 3) * ($f - 1.0) + 1.0] } else { set fi 1.0 } if {[sct dir] > 0} { if {$ist > [sct lastist] + 0.1 * $fi} { sct hystpulse -1 } elseif {$ist < [sct lastist]} { sct lastist $ist } } else { if {$ist < [sct lastist] - 0.1 * $fi} { sct hystpulse 1 } elseif {$ist > [sct lastist]} { sct lastist $ist } } set fact [expr sqrt($fi * $fact)] set dif [expr $soll - $ist] if {[sct dir] > 0} { set _dir _o } else { set _dir _c } set lastim [silent 0 sct lastim] set period [sct period] switch $regstate { close { set closestate 0 if {[hvali /cc/fm] == 2} { # still closing } elseif {[hvali /cc/fm] != 4} { # stopped: reg again? setstate reg } elseif {$dif < 0} { # flow still too high if {[hvali /cc/mp] != 0} { cc mp 0 } } elseif {[hvali [sct]/ctrl/openpulse] > 0} { setstate open } else { setstate reg } } open { set closestate 0 if {$ist < [sct minflow]} { sct minflow $ist } if {[hvali /cc/fm] == 1} { # still opening sct lastmov $now } else { if {$now > [sct lastmov] + $period * 2} { set mp [silent [hvali /cc/mo] hvali /cc/mmp] if {$mp > 0.21} { #clientput "ccu4flow: measured open pulse $mp" hupdate [sct]/ctrl/openpulse [expr $mp - 0.2] } else { #clientput "ccu4flow: open pulse unchanged" } sct dir -1 setstate reg } # stabilize } } within_tolerance { if {$dif * [sct dir] < -$tol} { setstate reg_hyst } elseif {$dif * [sct dir] > $tol} { setstate reg } elseif {$now > [silent $now sct last_out_of_tol] + 120} { set cm [silent 0 sct corr_minpulse] if {$cm > 0} { #clientput "ccu4flow: save increased minpulse$_dir [sct minpulse]" if {[hvali [sct]/ctrl/adjust_minpulse]} { hupdate [sct]/ctrl/minpulse$_dir [sct minpulse] } } elseif {$cm < 0} { #clientput "ccu4flow: clear corr_minpulse" } sct corr_minpulse 0 } } reg - reg_hyst - reg_in_tol { if {$dif * [sct dir] < -$tol} { # force init of reg_hyst state sct regstate reg setstate reg_hyst } elseif {$dif * [sct dir] <= 0 || abs($dif) < $tol * 0.5} { setstate within_tolerance } elseif {$now < $lastim + $period} { # wait } else { set slope [expr 0.00 + abs($dif) * $period / [hvali [sct]/ctrl/deriv$_dir]] set totalmin "" set ddif [format %.2f [expr -($dif - [sct olddif]) * [sct dir]]] sct filtered_ddif [expr max(0,[silent 0 sct filtered_ddif] * 0.9 + $ddif * 0.1)] if {$ddif < 0} { set ddif 0 } if {$ddif > 2 * $slope} { sct olddif [expr $dif + [sct dir] * $slope] #clientput "outof olddif [sct olddif]" } else { sct olddif [expr [sct olddif] - [sct dir] * $slope] } set prop [expr [hvali [sct]/ctrl/prop$_dir] / $fact] set minpulse [sct minpulse] set minpulse_fact [silent 1 sct minpulse_fact] if {$minpulse_fact > 1.45} { set minpulse_fact 0.6 } set minpulse [expr $minpulse * $minpulse_fact] sct minpulse_fact [expr $minpulse_fact + 0.2] if {$ddif != 0} { #clientput "[expr abs($dif) * $prop] - [expr abs($dif) * $prop * $ddif / $slope] d [expr $period * $dif / $ddif]" } set mpabs [format %.3f [expr abs($dif) * $prop * (1 - $ddif / $slope) + $minpulse]] # hupdate [sct]/span [sct filtered_ddif] if {$mpabs < 0} { set mpabs 0 } elseif {$mpabs > 0} { if {[sct filtered_ddif] > $slope} { set mpabs 0 } } # if {[sct dir] > 0 && [sct olddir] <= 0} { # } set mp [expr $mpabs * [sct dir]] set progress [expr [silent 0 sct progress] - 0.01] if {$dif * [sct dir] > $progress} { set progress [expr $dif * [sct dir]] } sct progress $progress set pstep 0.2 if {$slope < $pstep} { set pstep $slope } #clientput "ccu4flow: $dif ?< $progress - $pstep" if {$dif * [sct dir] < $progress - $pstep} { if {$now > [silent 0 sct lastprogress] + 1.5 * $period} { #clientput "ccu4flow: progress $progress dif $dif" sct progress [expr $dif * [sct dir]] # we got over hysteresis sct hstat "" sct totalmin 0 sct hystlim 0.3 if {$regstate eq "reg_hyst"} { setstate reg } } elseif {$regstate eq "reg_hyst"} { set mp 0 } } else { sct lastprogress $now } if {[sct hystpulse] eq [sct dir]} { if {$regstate eq "reg_hyst"} { set hp [hvali [sct]/ctrl/hystpulse$_dir] set mp [expr $mp + [sct dir] * $hp] } sct hystpulse 0 } if {$mp == 0} { cc mp 0 } else { if {$ddif < 0} { #clientput "worse $dif" sct olddif $dif } if {abs($dif) < $tol} { setstate reg_in_tol set mp [format %.3f [expr ($minpulse / $tol + $prop) * $dif]] if {$now > [silent $now sct last_out_of_tol] + 120} { set cm [silent 0 sct corr_minpulse] if {$cm > 0} { #clientput "ccu4flow: save increased minpulse$_dir [sct minpulse]" if {[hvali [sct]/ctrl/adjust_minpulse]} { hupdate [sct]/ctrl/minpulse$_dir [sct minpulse] } } elseif {$cm < 0} { #clientput "ccu4flow: clear corr_minpulse $cm" } sct corr_minpulse 0 } } else { sct last_out_of_tol $now if {$regstate eq "reg_in_tol"} { setstate reg } # set mpmax [sct dir] set totalmin [sct totalmin] set hstat [silent "" sct hstat] if {abs($mp) > $minpulse} { lappend hstat [expr abs($mp)] set totalmin [expr $totalmin + abs($mp) - $minpulse] if {$totalmin > [sct hystlim]} { # set minpulse [format %.3f [expr $minpulse * 1.25 + 0.0003]] set minpulse [format %.3f [expr $minpulse + 0.001]] sct minpulse $minpulse sct corr_minpulse 1 set h $hstat set hstat [list] set totalmin 0 foreach v $h { if {$v > $minpulse} { set totalmin [expr $totalmin + $v - $minpulse] lappend hstat $v } } # clientput "increase minpulse$_dir $minpulse totalmin $totalmin" } set totalmin [format %.3f $totalmin] sct totalmin $totalmin sct hstat $hstat } } cc mp $mp #logtext mp $mp sct olddif $dif sct lastpulse $now } #clientput "mp $mp t $totalmin" set lastim $now } } } if {$closestate != 0} { if {$closestate == 1} { setstate close "close / flow too high" } elseif {[hvali [sct]/ctrl/openpulse] > 0} { setstate open "open / n.v. closed" #clientput "[sct regstate] $regstate" } else { setstate reg } } if {$regstate != [sct regstate]} { #clientput "goto $regstate" switch $regstate { close { set mp -[hvali /cc/mot] cc mp $mp logtext mp $mp sct dir -1 } open { sct dir 1 set mft [expr [hvali /cc/f] + 1.0] cc mft $mft set mp [hvali [sct]/ctrl/openpulse] set pulse $mp set mw [expr $mp * 0.8] if {$mw > 1} { set mw 1 } cc mw $mw cc mp $mp logtext mp $mp mft $mft sct lastmov $now sct minflow $ist } reg_hyst - reg { if {$regstate eq "reg_hyst"} { sct totalmin 0 sct hystlim [sct maxhysteresis] if {$now < [sct settime] + 1} { #clientput "ccu4flow: reg_hyst after set" } else { set corr [silent 0 sct corr_minpulse] if {$corr != 0} { set minpulse [sct minpulse] if {$minpulse > 0 && $corr < 0} { set minpulse [format %.3f [expr $minpulse * 0.8 - 0.0004]] #clientput "ccu4flow: decrease minpulse$_dir $minpulse" } elseif {$corr == 1} { #clientput "ccu4flow: save increased minpulse$_dir $minpulse" } if {[hvali [sct]/ctrl/adjust_minpulse]} { hupdate [sct]/ctrl/minpulse$_dir $minpulse } } } sct corr_minpulse -1 # } elseif {[sct regstate] eq "reg_hyst"} { # sct hystpulse [expr -[sct dir]] # clientput "enable hystpulse [sct hystpulse]" # } else { # sct hystpulse 0 } if {$dif > 0} { sct dir 1 set _dir _o } else { sct dir -1 set _dir _c } sct minpulse [hvali [sct]/ctrl/minpulse$_dir] set lastim $now sct hstat "" sct lastpulse $now sct progress 0 } within_tolerance { cc mp 0 } } sct regstate $regstate } sct setchanged 0 sct lastim [expr int($lastim / $period + 0.5) * $period] hdelprop [sct] geterror return idle }