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 }