Files
sea/tcl/drivers/ccu4ext.tcl

420 lines
10 KiB
Tcl

namespace eval ccu4ext {
}
# automatic fill with level reading from external source
proc stdConfig::ccu4ext {type {readpath 0}} {
variable node
variable ctrl
controller syncedprot
obj fillccu wr -int
default -1
prop write ccu4ext::writeExt $type
prop read ccu4ext::fillExt $type
prop enum watching=0,filling=1,inactive=2,manualfill=3
prop label "filling state"
if {$type eq "n2"} {
set text LN2
} else {
set text LHe
}
kids "$text fill settings" {
node state upd -text
node readpath -text par $readpath
prop visible false
node lowlevel par 10
node highlevel par 100
node smooth upd
prop fmt %.7g
prop geterror undefined
default -1
node minfillminutes par 3.0
node maxfillminutes par 30.0
node minholdhours par 12.0
node maxholdhours par 120.0
node tolerance par 20.0
node badreadingminutes par 30.0
node tubecoolingminutes par 3.0
if {$type eq "he"} {
node vessellimit par 10.0
node vext upd
}
}
set node /sics/$ctrl
return "$text fill with CCU4 and external level meter"
}
proc ccu4ext::changestate {type state} {
switch $type {
n2 {
set cmd nc
set auto na
}
he {
set cmd hcd
set auto ha
}
default {
error "illegal level meter type: $type"
}
}
switch $state {
stop {
cc $cmd 0
cc $auto 1
}
fill {
cc $cmd 1
cc $auto 1
}
off {
cc $cmd 2
cc $auto 0
}
watch {
cc $cmd 3
cc $auto 1
}
}
sct change_time [DoubleTime]
}
proc ccu4ext::calcsmooth {level minspeed maxspeed} {
if {![string is double $level]} {
return 199
}
set now [clock seconds]
set lasttime [silent 0 sct lasttime]
if {$lasttime == 0} {
set lasttime $now
}
set delta [expr $now - $lasttime]
sct lasttime $now
set smooth [silent $level hvali [sct]/smooth]
set gs [expr $smooth + $delta * $maxspeed]
if {$level > $gs} {
set smooth $gs
} else {
set gs [expr $smooth + $delta * $minspeed]
if {$level < $gs} {
set smooth $gs
} else {
set smooth $level
}
}
set badtime [silent 0 sct badtime]
if {abs($smooth - $level) > [hvali [sct]/tolerance]} {
if {$badtime < [hvali [sct]/badreadingminutes] * 60 * 2} {
sct badtime [expr $badtime + $delta]
}
} else {
if {$badtime > $delta} {
sct badtime [expr $badtime - $delta]
} else {
sct badtime 0
}
}
hupdate [sct]/smooth $smooth
hdelprop [sct]/smooth geterror
return $smooth
}
proc ccu4ext::setmode {type state} {
if {[sctval [sct]] != $state} {
sct target $state
writeExt $type 0
}
}
proc ccu4ext::fillExt {type} {
if {$type eq "n2"} {
set valve [sctval /cc/nv]
set auto [sctval /cc/na]
} else {
set valve [sctval /cc/hv]
set auto [sctval /cc/ha]
if {[sctval /cc/hav] != 2} { # must be on 2=ext
hset /cc/hav 2
}
}
set errtxt ""
set now [DoubleTime]
set sm [expr $now > [silent 0 sct change_time] + 10]
switch -- $valve {
0 {
set txt "fill valve off"
if {$sm} {
if {$auto} {
setmode $type 0
} else {
setmode $type 2
}
}
}
1 {
set txt "filling"
if {$sm} {setmode $type 1}
}
2 {
set txt "no fill valve"
if {$sm} {setmode $type 2}
}
3 {
set errtxt timeout
}
4 {
set errtxt timeout1
}
default {
set errtxt "unknown error"
}
}
set rdpath [hvali [sct]/readpath]
set level [hvali $rdpath]
set levelerror [silent "" hgetpropval $rdpath geterror]
if {$levelerror ne "" && $errtxt eq "" && [hvali [sct]] < 2} {
set errtxt "$txt ($rdpath $levelerror)"
}
if {$errtxt eq ""} {
updateval [sct]/state $txt
hupdate [sct]/status ""
} else {
hupdate [sct]/status $errtxt
updateval [sct]/state $errtxt
}
if {[sctval [sct]] == 3} { # manualfill
changestate $type fill
return idle
}
set state [sctval [sct]]
set now [clock seconds]
set lowlevel [hvali [sct]/lowlevel]
if {$lowlevel < 0.0} {
hupdate [sct]/lowlevel 0.0
}
if {$lowlevel > 90.0} {
hupdate [sct]/lowlevel 90.0
}
set highlevel [hvali [sct]/highlevel]
if {$highlevel > 100.0} {
hupdate [sct]/highlevel 100.0
}
if {$highlevel < $lowlevel + 10.0} {
hupdate [sct]/highlevel [expr $lowlevel + 10.0]
}
switch -- $state {
-1 { # initializing
hupdate [sct]/status "automatic $type filling off"
hset [sct] 0
sct lasttime 0
changestate $type watch
return idle
}
0 { # watching
validated_level
set minspeed [expr - 100.0 / [hvali [sct]/minholdhours] / 3600.]
set maxspeed [expr - 100.0 / [hvali [sct]/maxholdhours] / 3600.]
set s [calcsmooth $level $minspeed $maxspeed]
if {$s < $lowlevel} {
if {[sct badtime] > [hvali [sct]/badreadingminutes] * 60} {
hupdate [sct]/status "automatic $type filling off - continuos bad reading"
hset [sct] 2
changestate $type off
return idle
}
# start fill
hset [sct] 1
clientput "$type level low ($level smooth $s) - start fill"
} else {
changestate $type stop
}
return idle
}
1 { # filling
if {[validated_level] eq ""} {
hset [sct] 2
changestate $type off
return idle
}
set vcmd [silent 0 sct vessel_cmd]
# check that vessel command works
if {$vcmd ne "0" && [catch {[result $vcmd]}]} {
clientput "WARNING: He vessel reading is configured, but not connected"
sct vessel_cmd 0
set vmd 0
}
set now [clock seconds]
set s [silent $level hval [sct]/smooth]
sct minlevel [silent 999 sct minlevel]
if {$s < [sct minlevel]} {
sct minlevel $s
}
set vessel 999
set maxspeed [expr 100.0 / [hvali [sct]/minfillminutes] / 60.]
if {[sct tubecooling] && $now < [sct startfill] + [hvali [sct]/tubecoolingminutes] * 60 && $s < [sct minlevel] + 5} {
# allow fill tube to get cold
set minspeed [expr - 100.0 / [hvali [sct]/maxfillminutes] / 60.]
} else {
if {[sct tubecooling]} {
sct tubecooling 0
if {$vcmd ne "0"} {
sct vstart_level [expr [result $vcmd]]
# start time - 2 minutes
sct vstart_time [expr $now - 120]
sct vlast_time $now
hupdate [sct]/vext [sct vstart_level]
}
}
set minspeed [expr + 100.0 / [hvali [sct]/maxfillminutes] / 60.]
if {$vcmd ne "0"} {
set dt [expr $now - [sct vlast_time]]
if {$dt > 0} {
set slope [expr ([hvali [sct]/vext] - [sct vstart_level])/([sct vlast_time] - [sct vstart_time])]
} else {
set slope 0
}
set vessel [result $vcmd]
set vext [expr [hvali [sct]/vext] + $slope * $dt]
if {$vext < $vessel} {
if {$vext < $vessel - 2.0} {
set errcnt [silent 0 sct errcnt]
incr errcnt
if {$errcnt > 3} {
hupdate [sct]/status "not enough progress on $type vessel - automatic ${type} filling off ($vext < $vessel - 2)"
hset [sct] 2
changestate $type off
return idle
}
sct errcnt $errcnt
} else {
set vessel $vext
}
}
hupdate [sct]/vext $vessel
sct vlast_time $now
}
}
if {$vessel < [silent 0 hvali [sct]/vessellimit]} {
hupdate [sct]/status "$type vessel empty - automatic ${type} filling off"
hset [sct] 2
changestate $type off
return idle
}
if {[calcsmooth $level $minspeed $maxspeed] <= $highlevel} {
# continue filling
changestate $type fill
return idle
}
clientput "$type level high - stop fill"
hset [sct] 0
changestate $type watch
return idle
}
2 {
set minspeed [expr - 100.0 / [hvali [sct]/minholdhours] / 3600.]
set maxspeed [expr + 100.0 / [hvali [sct]/minfillminutes] / 60.]
calcsmooth $level $minspeed $maxspeed
}
}
return idle
}
proc ccu4ext::validated_level {} {
set rdpath [hval [sct]/readpath]
if {[DoubleTime] < [silent 0 hgetpropval $rdpath read_time] + 30} {
return [hval $rdpath]
}
if {[silent "" hgetpropval $rdpath geterror] eq ""} {
clientlog "ERROR: no reading of $rdpath within 30 sec"
}
hsetprop $rdpath geterror not_available
return ""
}
proc ccu4ext::writeExt {type {activate 1}} {
sct update [sct target]
switch -- [sct target] {
# watching
0 {
set level [validated_level]
if {$level eq ""} {
changestate $type off
sct update 2
return idle
}
hupdate [sct]/smooth $level
catch {logsetup [sct]/vext clear}
hupdate [sct]/status ""
eval [silent "expr 0" sct slow_cmd]
if {$activate} {
changestate $type watch
}
return idle
}
# inactive
2 {
catch {logsetup [sct]/vext clear}
eval [silent "expr 0" sct slow_cmd]
if {[hvali [sct]/status] eq ""} {
hupdate [sct]/status "automatic ${type} filling off"
}
clientput "ERROR: [hvali [sct]/status]"
if {$activate} {
changestate $type off
}
return idle
}
# fill
1 {
set level [validated_level]
if {$level eq ""} {
changestate $type off
sct update 2
return idle
}
hupdate [sct]/smooth $level
hupdate [sct]/status ""
set vcmd [silent 0 sct vessel_cmd]
if {$vcmd ne "0"} {
set vlev [silent 60 result $vcmd]
if {$vlev == 60} {
clientput "WARNING: vessel level meter not connected - proceed without it"
sct vessel_cmd 0
} elseif {$vlev < [hvali [sct]/vessellimit] + 3.0} {
hupdate [sct]/status "vessel not full enough - automatic ${type} filling off"
clientput [hvali [sct]/status]
sct update 2
if {$activate} {
changestate $type off
}
return idle
}
}
sct minlevel 100
sct errcnt 0
sct startfill [clock seconds]
sct tubecooling 1
# eval [split [silent "expr 0" sct fast_cmd]]
eval [silent "expr 0" sct fast_cmd]
if {$activate} {
changestate $type fill
}
return idle
}
# manualfill
3 {
changestate $type fill
}
}
return idle
}