Files
sea/tcl/drivers/ccu4flow.tcl

779 lines
21 KiB
Tcl

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
}