Files
sea/tcl/drivers/nvstep.tcl
2023-10-26 10:05:26 +02:00

416 lines
11 KiB
Tcl

namespace eval nvstep {
}
proc stdConfig::nvstep {args} {
variable name
controller syncedprot
pollperiod 1 1
obj NvStep wr
prop write nvstep::write
prop read nvstep::read
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
set sensirion 0
foreach arg $args {
if {$arg eq "1"} {
set sensirion 1
}
}
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 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 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
}
}
if {[hval [sct]/use_pressure]} {
set ist [hvali [sct]/flowp]
} else {
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 {} {
hsetprop /cc/fa nvpath [sct]
_cc updatescript /cc/fa nvstep::updatemode
hsetprop /cc/f nvpath [sct]
hsetprop /cc/f nvctrl [sct controller]
hsetprop /cc/f flowsource pressure
_cc updatescript /cc/f nvstep::updateflow
catch {
hsetprop /nvflow flowsource flow
hsetprop /nvflow nvpath [sct]
hsetprop /nvflow nvctrl [sct controller]
_hemot updatescript /nvflow nvstep::updateflow
}
return unpoll
}
proc nvstep::updateflow {value} {
# flowsource:
# pressure: this is a pressure
# flow: this is a flow from a flowmeter
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 "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
} else {
error "[sct] illegal flowsource: $source"
}
[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
}