# flow control module based on hipadaba

namespace eval flow {
}

proc flow::loop {difget flowget flowset path flowstd flowlim} {
  $difget $path difmin difmax
  $flowget $path flowmin flowmax
  set setval [hvali $path/flowset]
  set setmin [expr [hvali $path/prop] * $difmin + $flowmin - $setval]
  set setmax [expr [hvali $path/prop] * $difmax + $flowmax - $setval]
  hupdate $path/setmin $setmin
  hupdate $path/setmax $setmax
  set c [hvali $path/smooth]
  if {$setmin > $setval} {
    set setval [expr $setmin * $c + $setval * (1 - $c)]
  } elseif {$setmax < $setval} {
    set setval [expr $setmax * $c + $setval * (1 - $c)]
  }
  putIntoLimits setval $flowstd $flowlim
  $flowset $path $setval
# the above calculation has the disantvantage, that $path/flowset might be higher
# than the flowstd, when the actual flow is very high. We calculate a
# more meaningful target just for make the user happy.
  set flowtarget [expr [hvali $path/prop] * ($difmin + $difmax) * 0.5]
#clientput "$flowtarget := [hvali $path/prop] * ($difmin + $difmax) * 0.5"
  if {$flowtarget < $flowstd && $setval < $flowmin} {
    set flowtarget [expr $c * $flowstd + (1 - $c) * [hvali $path/flowtarget]]
  } else {
    set flowtarget $setval
  }
  putIntoLimits flowtarget $flowstd $flowlim
  hupdate $path/flowtarget $flowtarget
}

proc flow::bufferPut {path name var value} {
  upvar $var buf
  set buf [hvali $path/${name}Buf]
  lappend buf [format %.11g $value]
  set buf [lrange $buf end-[expr [hvali $path/${name}Size] - 1] end]
  hupdate $path/${name}Buf $buf
}

proc flow::difget {path minname maxname} {
  upvar $minname min
  upvar $maxname max
  
  set t [eval [hvali $path/getTemp]]
  if {$t eq "NaN"} {
    set t 0
  }
  set s $t
  if {[catch {
      set s [eval [hvali $path/getTset]]
      hsetprop $path/getTset t_set_undefined 0
    }]} {
    hsetprop $path/getTset t_set_undefined 1
  }
  if {$s eq "NaN"} {
    set s $t
  }
  if {$s < [hvali $path/Tmin]} {
    set s [hvali $path/Tmin]
  }
#clientput [format {set %.11g ist %.11g} $s $t]
  flow::bufferPut $path dif buf [expr $t - $s]
  set last [lindex $buf end]
  set first [lindex $buf 0]
  set l [hvali $path/difSize]
  set slope1 [expr ($last - $first) / $l]
  set slope2 [expr ([lindex $buf [hvali $path/difRange]] - $first) / $l]
#clientput [format {dif %.11g %.11g slope %.11g %.11g} $t $s $slope1 $slope2]
  if {$slope1 > $slope2} {
    set max [expr $last + $slope1 * [hvali $path/convTime]]
#    set min $last
    set min [expr $last + $slope2 * [hvali $path/convTime]]
  } else {
    set min [expr $last + $slope1 * [hvali $path/convTime]]
    set max [expr $last + $slope2 * [hvali $path/convTime]]
  }
#clientput [format {last %.11g min %.11g max %.11g} $last $min $max]
  hupdate $path/difmax $max
  hupdate $path/difmin $min
}

proc flow::flowget {path flowmin flowmax} {
  upvar $flowmin min
  upvar $flowmax max
  
  flow::bufferPut $path flow buf [eval [hvali $path/getFlow]]
  set s [lsort -real $buf]
  set max [lindex $s end]
  set min [lindex $s 0]
  hupdate $path/flowmin $min
  hupdate $path/flowmax $max
}

proc flow::flowset {path setval} {
  set val [format "%.1f" $setval]
#  if {[hvali $path/flowset] != $val} {
#    eval "[hvali $path/setFlow] $val"
#  }
  hupdate $path/flowset $setval
}

proc flow::task {path flowstd flowlim} {
  flow::loop flow::difget flow::flowget flow::flowset $path $flowstd $flowlim
}

proc flow::pitask {path flowstd flowlim} {
  set tt [silent 1 result tt tm]
  set p [silent 0 result tt set/power]
  set tt [expr $tt - $p]
  set st $tt
  if {[catch {
      set st [eval [hvali $path/getTset]]
      hsetprop $path/getTset t_set_undefined 0
    }]} {
    hsetprop $path/getTset t_set_undefined 1
  }
  if {$st < [hvali $path/Tmin]} {
    set st [hvali $path/Tmin]
  }
  set y [hvali $path/flowmin]
  set dif [expr $tt - $st]
  set now [DoubleTime]
  set lastim [silent $now hgetpropval $path lastim]
  hsetprop $path lastim $now
  set lasdif [silent 0 hgetpropval $path lasdif]
  hsetprop $path lasdif $dif
  set dt [expr $now - $lastim]
  set prop [hvali $path/prop]
  set convTime [hvali $path/convTime]
  set y [expr $y + $prop * (($dif - $lasdif) + ($dif - 5 * $p) * $dt / double($convTime))]
  putIntoLimits y [expr -$flowlim] $flowlim
  hupdate $path/flowmin $y
  putIntoLimits y $flowstd $flowlim
  hupdate $path/flowset $y  
  hupdate $path/flowtarget $y  
}

proc flow::make {{getT flow::tmts} {getS "result tt tr"} {getF "result nv flow"}} {
  namespace eval ::stdConfig [subst {
    node suspended par 0
    prop enum 1
    node prop par 5
    node flowstd par 2
    node flowlim par 20
    node smooth par 0.05
    node difSize par 10
    node difRange par 7
    node flowSize par 10
    node convTime par 30
    node Tmin par 1.5
    node script -text par "flow::task"
    prop width 24
    node getTemp -text par "$getT"
    prop width 24
    node getTset -text par "$getS"
    prop t_set_undefined 0
    prop width 24
    node getFlow -text par "$getF"
    prop width 24
    node difBuf -nolog -text upd
    default ""
    node flowBuf -nolog -text upd
    default ""
    node flowset upd
    default 2
    node flowmin upd
    node flowmax upd
    node difmin upd
    node difmax upd
    node setmin upd
    node setmax upd
    node flowtarget upd
  }]
}

proc flow::tmts {} {
  set tm [silent 1 result tt tm]
  set ts [silent $tm result tt ts]
  if {$ts < $tm} {
    return $ts
  } else {
    return $tm
  }
}

proc flow::tmts2 {} {
  set tm [silent 1 result tt tm]
#  set ts [silent $tm result tt ts]
  set p [silent 0 result tt set/power]
  set pr [silent 0 hvali /nv/autoflow/prop]
  return [expr $tm - $p / $pr]
}

proc flow::ma15tmts {} {
  set tm [result te]
  set ts [result tt]
  if {$ts < 0} {
    return $tm
  }
  if {$ts < $tm} {
    return $ts
  } else {
    return $tm
  }
}

