Files
sea/tcl/drivers/flctrl.tcl
2022-08-18 15:04:28 +02:00

441 lines
10 KiB
Tcl

# furnace control, slightly modified version for lamp oven
namespace eval flctrl {
}
if {![namespace exists lsc]} {
source drivers/lsc.tcl
}
proc stdConfig::flctrl {body} {
variable name
variable node
variable path
controller std
prop startcmd "s=1"
pollperiod 5 5
array set arguments $body
# arguments(voltscale) voltage at 100 %
# arguments(min_resist) switch off when resistivity is lower than this
# arguments(max_resist) switch off when resistivity is bigger than this
obj FICTRL -drive wr
# @switch_on: set by check_set for switching on control
prop @switch_on 0
prop @voltscale $arguments(voltscale)
prop @min_resist $arguments(min_resist)
prop @max_resist $arguments(max_resist)
if {[info proc stdConfig::tdrive] eq ""} {
namespace eval :: {
source drivers/trun.tcl
}
}
tdrive $name -log 1
hsetprop $path/set check flctrl::check_set
kids "fi control ($name)" {
node limit par 1000
foreach ch {1 2 3 4} {
node t$ch upd
kids "settings channel $ch" {
node raw rd
prop readcmd v$ch
prop readfmt v$ch=%g
prop update flctrl::update_v
prop channel $ch
node curve out -text
prop width 32
prop model 0
prop check flctrl::curve
prop write stdSct::completeUpdate
kids "calibration" {
hfactory $path/points plain mugger floatvarar 1
}
node valid rd -int
prop enum 1
prop readcmd s$ch
prop readfmt s$ch=%d
}
}
node tref rd
prop readcmd tr
prop update flctrl::update_ref
prop old 0
node tout rd
prop readcmd tw
prop update flctrl::update_out
node toutmax wr
prop write flctrl::write_lim
prop readcmd wh
prop update flctrl::update_out
node toutmin wr
prop readcmd wl
prop write flctrl::write_lim
prop update flctrl::update_out
node ctrlmode wr
prop writecmd s=%d
prop readcmd s
prop readfmt s=%d
prop enum ok,off,illegal_channel,no_sensor,no_waterflow,bad_vacuum,tc_overflow,wall_T_overflow
prop update flctrl::update_c
node ramp par 20
node smooth par 60
node prop par 0.005
prop help "proportional gain for T slope control"
node int par 0.2
prop help "time constant for T slope control"
node powerset out
default 0
prop check flctrl::check_power
prop write stdSct::completeUpdate
node power upd
node resist upd
default 0.025
node maxpower par 150
node maxheater wr
prop writecmd m=%.7g
prop readcmd m
prop readfmt m=%g
node output wr
prop writecmd o=%.7g
prop readcmd o
prop readfmt o=%g
node manualpower -int par 0
prop enum 1
node ctrlchan wr -int
default 1
prop writecmd c=%d
prop readcmd c
prop readfmt c=%d
node interlock_state rd -int
prop readcmd is
prop readfmt is=%d
prop enum ok=0,no_waterflow=1,bad_vacuum=2,no_waterflow_bad_vacuum=3
node interlock_mask wr -int
prop readcmd im
prop readfmt im=%d
prop writecmd im=%d
prop enum no_check=0,check_water_only=1,check_vacuum_only=2,check_all=3
node sramp upd
node slope upd
node v_htr rd
prop readcmd U
prop readfmt U=%g
prop update flctrl::update_offset
prop offset 0
node i_htr rd
prop readcmd I
prop readfmt I=%g
prop update flctrl::update_offset
prop offset 0
node htr rd
prop readcmd h
prop readfmt h=%g
prop update flctrl::update_power
node powerprop upd
}
}
proc flctrl::check_power {} {
set p [sct target]
if {$p > 0} {
set r [hval [sct parent]/resist]
set s [sct @voltscale]
hset [sct parent]/output [format %.2f [expr 100 * sqrt($p * $r) / $s]]
} else {
hset [sct parent]/output 0
}
}
proc flctrl::update_power {} {
stdSct::update
set v [silent 0 hval [sct parent]/v_htr]
set i [silent 0 hval [sct parent]/i_htr]
set p [expr $i * $v]
updateval [sct parent]/power $p
if {$i > 1 && $v > 0.1} {
set r [hval [sct parent]/resist]
set r1 [expr ($v - 0.005) / double($i + 0.1)]
set r2 [expr ($v + 0.005) / double($i - 0.1)]
if {$r1 > [sct @max_resist]} {
error "resistance $r too high"
}
if {$r2 < [sct @min_resist]} {
error "resistance $r too low"
}
if {$r < $r1} {
set r $r1
# set r [expr $r * 0.9 + $r1 * 0.1]
} elseif {$r > $r2} {
# set r [expr $r * 0.9 + $r2 * 0.1]
set r $r2
}
hupdate [sct parent]/resist $r
}
updateval [sct parent]/power $p
return idle
}
proc flctrl::check_set {} {
sct @switch_on 1
if {[sct target] > [hval [sct parent]/limit]} {
error "set T > limit ([hval [sct parent]/limit])"
}
hupdate [sct parent]/target [sct target]
if {[string match switched* [hval [sct parent]/status]]} {
hupdate [sct parent]/status ""
}
sct update [sct target]
}
proc flctrl::cmds {} {
set base [sct objectPath]
sct cmd_p $base
foreach node [hlist $base] {
catch {
set cmd [hgetpropval $base/$node cmd]
sct cmd_$cmd $base/$node
lappend query $cmd
}
}
return [join $query " "]
}
proc flctrl::to_kelvin {ch value} {
if {[hvali [sct objectPath]/t$ch/curve] eq ""} {
return $value
}
set tref [hvali [sct objectPath]/tref]
set vref [interpolate [hvali [sct objectPath]/t$ch/curve/points] 1 $tref]
set v [expr $value + $vref]
return [interpolate [hvali [sct objectPath]/t$ch/curve/points] 0 $v extrapolate]
}
proc flctrl::to_mvolt {ch value} {
if {[hvali [sct objectPath]/t$ch/curve] eq ""} {
return $value
}
set tref [hvali [sct objectPath]/tref]
set vref [interpolate [hvali [sct objectPath]/t$ch/curve/points] 1 $tref]
set vval [interpolate [hvali [sct objectPath]/t$ch/curve/points] 1 $value]
return [expr $vval - $vref]
}
proc flctrl::ctrl_slope {} {
if {[sct @switch_on]} {
set init "s=0 "
} elseif {[hvali [sct parent]/ctrlmode]} {
# error state
return idle
} else {
set init ""
}
set ch [hvali [sct parent]/ctrlchan]
if {$ch == 0} {
return idle
}
# alarm when T is increasing by more than 50 K within 5 sec or T > limit + 20
set ta [expr min([hval [sct parent]/t$ch] + 50, [hval [sct parent]/limit] + 20)]
set valarm [format %g [to_mvolt $ch $ta]]
sct @switch_on 0
#clientput "ctrl talarm=$ta"
sct send "${init}a=$valarm c=$ch dx vx sx"
return flctrl::ctrl_do
}
proc flctrl::ctrl_do {} {
array set keyval {dx 0 vx 0 sx 0}
foreach item [sct result] {
lassign [split $item =] key val
set keyval($key) $val
}
set dt $keyval(dx)
if {$dt == 0} {
# no delta time available (happend on init)
return idle
}
set ch [hvali [sct parent]/ctrlchan]
# clientput [array get keyval]
set tist [to_kelvin $ch $keyval(vx)]
# convert voltage slope to T slope
set v1 [to_mvolt $ch [expr $tist - 10]]
set v2 [to_mvolt $ch [expr $tist + 10]]
set slope [expr $keyval(sx) * 20.0 / ($v2 - $v1)]
set tg [hval [sct parent]/target]
set ramp [hval [sct parent]/ramp]
set out [hval [sct parent]/powerset]
set prop [hval [sct parent]/prop]
set int [hval [sct parent]/int]
updateval [sct parent] $tist
set sramp [expr abs($tist - $tg) * 60.0 / [hval [sct parent]/smooth]]
if {$sramp > $ramp} {
set sramp $ramp
}
if {$tg < $tist} {
set sramp [expr -$sramp]
}
hupdate [sct parent]/sramp $sramp
hupdate [sct parent]/slope $slope
set last_dif [silent 0 sct last_dif]
set dif [expr $sramp - $slope]
# clientput "$dif slope $slope sramp $sramp"
sct last_dif $dif
set out [expr $out + ($dif * $dt / double($int) + $dif - $last_dif) * $prop]
set maxpower [hval [sct parent]/maxpower]
set r [hval [sct parent]/resist]
set s [sct @voltscale]
hset [sct parent]/maxheater [format %.5g [expr sqrt($maxpower * $r) * 100.0 / $s]]
if {$out < 0} {
if {$dif > 0} {
set out 0
} elseif {$out < -$maxpower*10} {
set out [expr -$maxpower*10]
}
} elseif {$out > $maxpower} {
if {$dif < 0} {
set out $maxpower
} elseif {$out > $maxpower * 11} {
set out [expr $maxpower * 11]
}
}
# clientput "dif $dif powerset $out"
hset [sct parent]/powerset [format %.5g $out]
hupdate [sct parent]/powerprop [expr $prop * $dif]
return idle
}
proc flctrl::update_ref {} {
if {[hval [sct objectPath]/manualpower] == 0} {
[sct controller] queue [sct] slow flctrl::ctrl_slope
}
set v [lindex [split [sct result] =] 1]
set t [format %.1f [expr $v + 273.15]]
sct update $t
return idle
}
proc flctrl::update_out {} {
set v [lindex [split [sct result] =] 1]
sct update [format %.1f [expr $v + 273.15]]
return idle
}
proc flctrl::write_lim {} {
sct send [format "[sct readcmd]=%.1f" [expr [sct target] - 273.15]]
return read
}
proc flctrl::update_v {} {
stdSct::update
set temp_k [to_kelvin [sct channel] [hvali [sct]]]
updateval [sct parent] $temp_k
if {([hval [sct objectPath]/manualpower] || [hvali [sct objectPath]/ctrlmode]) &&
[sct channel] == [hval [sct objectPath]/ctrlchan]} {
updateval [sct objectPath] $temp_k
}
return idle
}
proc flctrl::update_offset {} {
lassign [split [sct result] =] name v
if {$v > 0} {
set v [expr $v + [silent 0 sct offset]]
}
if {[silent "" sct geterror] ne "powersupply_switched_off"} {
sct update $v
}
return idle
}
proc flctrl::update_c {} {
stdSct::update
if {[sct @switch_on]} {
return idle
}
set o [sct objectPath]
set old [hvali $o/status]
set new [enum_txt [sct]]
if {$new ne "ok"} {
if {$new != [silent 0 sct oldval]} {
clientput "ERROR: $new"
sct oldval $new
}
set interlock_state [expr [hval $o/interlock_state] & [hval $o/interlock_mask]]
set summary [list]
if {$interlock_state & 1} {
lappend summary "no waterflow"
}
if {$interlock_state & 2} {
lappend summary "bad vacuum"
}
if {[hval $o/tout] > [hval $o/toutmax]} {
lappend summary "wall T too high"
} elseif {[hval $o/tout] < [hval $o/toutmin]} {
lappend summary "missing wall sensor"
}
set summary [join $summary ", "]
if {$summary eq ""} {
if {$new eq "off"} {
set summary "switched off"
} else {
set summary "switched off because of $new"
}
}
hupdate [sct objectPath]/status $summary
} elseif {$old != ""} {
hupdate [sct objectPath]/status ""
}
return idle
}
proc flctrl::write {} {
set p [expr [sct target]/double([silent 1 sct fact])]
sct send "[sct cmd]=$p [flctrl::cmds]"
#return flctrl::update
return stdSct::complete
}
proc flctrl::curve {} {
if {[sct requested] ne "" && [sct requested] ne "raw"} {
lsc::read_curve
}
}