# auto range for lsc 370
namespace eval auto370 {
}

proc stdConfig::auto370 {lsc370 {getcmd 0}} {
  variable path
  variable name

  controller syncedprot 10

  pollperiod 1 1
  
  obj auto370 wr
  prop enum 1
  prop label "auto heater on"
  prop read auto370::poll
  prop write stdSct::completeUpdate
  prop lsc370 /$lsc370/set
  prop lastdown 0
  prop lastup 0
  prop resist 0
  prop @rangetarget 0
  prop getcmd $getcmd
  prop heat 1
  kids "$lsc370 autorange settings" {
    node mode -int out
    prop enum 0,1
    prop check auto370::checkmode
    prop write stdSct::complete
    default 0

    node heaterrange -int out
    prop check auto370::checkrange
    prop write stdSct::complete
    prop enum 1,2,3,4,5,6,7,8
    prop label heaterrange

    node minrange par 3
    prop enum 1,2,3,4,5,6,7,8
    prop label minrange

    node maxrange par 6
    prop enum 1,2,3,4,5,6,7,8
    prop label maxrange

    node switchup par 90

    node switchupdelay par 60
    prop label "switch up delay"
    prop help "min. time to be above the threshold before switching up"

    node switchdown par 10

    node switchdowndelay par 300
    prop label "switch down delay"
    prop help "min. time to be below the threshold before switching down"

    node heater upd

    node autoprop par 0
    prop enum 1
    prop newline 1

    node calcpow upd

    node ttable -text par {0.01 0.02 0.064 0.2}
    prop width 40
    prop label T power table
    prop help "T for 1uW, 10uW, 100uW, 1mW ..."
    prop newline 1
   
    node prop par 5
    prop label "max. prop"

    node propmin par 0.2
    prop label "min. prop"
  }

}

proc auto370::neededPower {tem table} {
  set pow 1.0e-7
  set last [expr [lindex $table 0] * sqrt(0.1)]
  foreach t $table {
    if {$tem < $t} {
      if {$last == 0} {
        return $pow
      }
      return [expr $pow * pow($tem / $last, 1.0 / log10($t/$last))]
    }
    set pow [expr $pow * 10]
    set last $t
  }
  return [expr $pow * pow($tem / $last, 2)]
}

proc auto370::poll {} {
  catch {hdelprop [sct] geterror}
  set now [clock seconds]
  set p [sct lsc370]
  set resist [sctval $p/resist]
  set pow [sctval $p/maxpower]
  if {$pow <= 0} {
    return idle
  }
  set htrrng [expr round(log10($pow / $resist) + 10)]
  set htr [silent 0 hgetpropval $p/power htr]
  updateval [sct]/heater $htr
  set minr [sctval [sct]/minrange]
  set minp [expr $resist * pow(10, $minr - 10)]
  set maxr [sctval [sct]/maxrange]
  set maxp [expr $resist * pow(10, $maxr - 10)]
  set maxpowerlim [sctval $p/maxpowerlim]
  set maxl [expr round(log10($maxpowerlim / $resist) + 10)]
  set w1 [expr $resist * 0.01]
  set w2 [expr $resist * 0.1]
  set w3 $resist
  set plist [format {%gnW=1 %guW=2 %guW=3 %guW=4 %gmW=5 %gmW=6 %gmW=7 %gW=8} $w3 $w1 $w2 $w3 $w1 $w2 $w3 $w1]
  set plist [lrange $plist 0 [expr $maxl-1]]
  set htxt [join [lrange $plist [expr $minr-1] [expr $maxr-1]] ,]
  if {$htxt ne [hgetpropval [sct]/heaterrange enum]} {
    hsetprop [sct]/minrange enum [join $plist ,]
    hsetprop [sct]/maxrange enum [join $plist ,]
    hsetprop [sct]/heaterrange enum $htxt
  }

  set soll [hvali $p/reg]

  set psoll [neededPower $soll [hvali [sct]/ttable]]
  hupdate [sct]/calcpow $psoll
  if {[hvali [sct]/autoprop]} {
    set prop [hvali $p/prop]
    set f [expr sqrt($psoll / $pow)]
    if {$f > 1} {
      set f 1
    }
    set pnew [expr [hvali [sct]/prop] * $f]
    set propmin [hvali [sct]/propmin]
    if {$pnew < $propmin} {
      set pnew $propmin
    }
    if {$prop != $pnew} {
      hset $p/prop $pnew
    }
    hsetprop $p/prop priv internal
  } else {
    hsetprop $p/prop priv user
  }


  set switchup [hvali [sct]/switchup]
  if {$switchup > 100} {
    set switchup 100
    hupdate [sct]/switchup $switchup
  } elseif {$switchup < 10} {
    set switchup 10
    hupdate [sct]/switchup $switchup
  }
  set switchdown [hvali [sct]/switchdown]
  if {$switchdown > $switchup * 0.3} {
    set switchdown [expr $switchup * 0.3]
    hupdate [sct]/switchdown $switchdown
  } elseif {$switchdown < 0.1} {
    set switchdown 0.1
    hupdate [sct]/switchdown $switchdown
  }
  set rangetarget [sct @rangetarget]
  if {$rangetarget == 0} {
    updateval [sct]/heaterrange $htrrng
  } else {
    sct @rangetarget 0
    if {$rangetarget != $htrrng} {
      set newpow [expr $pow * pow(10, $rangetarget - $htrrng)]
      sctsync {
        hset $p/maxpower $newpow
      }
      return "auto370::complete $pow $newpow [hvali $p/power]"
    }
  }
  if {$htr > $switchup * 0.3 && $htrrng < $maxr} {
    sct lastdown $now
    if {$htr >= $switchup} {
      set newpow [expr $pow * 10]
      if {$newpow > $maxp * 1.1} {
        set newpow $maxp
      }
      if {$now > [sct lastup] + [hvali [sct]/switchupdelay]} {
        if {[sctval [sct]] == 0} {
          return idle
        }
        sct lastup $now
        sctsync {
          hset $p/maxpower $newpow
        }
        return "auto370::complete $pow $newpow [hvali $p/power]"
      }
    } else {
      sct lastup $now
    }
    set r [expr $now - [sct lastup]]
    if {[sctval [sct]] == 0} {
      set r 0
    }
    if {$r < 5} {
      hsetprop [sct]/switchupdelay label "switch up delay"
    } else {
      hsetprop [sct]/switchupdelay label "switch up d.($r)"
    }
  }
  if {$htr < $switchdown / 0.3 && $htrrng > $minr} {
    sct lastup $now
    if {$htr <= $switchdown} {
      set newpow [expr $pow * 0.1]
      if {$newpow < $minp * 0.9} {
        set newpow $minp
      }
      if {$htr == 0} {
        sct lastdown $now
      }
      if {$now > [sct lastdown] + [hvali [sct]/switchdowndelay]} {
        if {[sctval [sct]] == 0} {
          return idle
        }
        sct lastdown $now
        sctsync {
          hset $p/maxpower $newpow
        }
        return "auto370::complete $pow $newpow [hvali $p/power]"
      }
    } else {
      sct lastdown $now
    }
    set r [expr $now - [sct lastdown]]
    if {[sctval [sct]] == 0} {
      set r 0
    }
    if {$r < 5} {
      hsetprop [sct]/switchdowndelay label "switch down delay"
    } else {
      hsetprop [sct]/switchdowndelay label "switch down d.($r)"
    }
  }
  return idle
}

proc auto370::complete {oldpow newpow manpow} {
  set p [sct lsc370]
  sctsync {
    hset $p/manualpower $manpow
  }
  return "auto370::complete2 $oldpow"
}

proc auto370::complete2 {oldpow} {
  set p [sct lsc370]
  set pow [sctval $p/maxpower]
  sctsync {
    hset $p/mode 1
  }
#  return stdSct::complete
  return auto370::finish
}

proc auto370::finish {} {
  return idle
}

proc auto370::checkrange {} {
  sct @rangetarget [sct target]
  sct update [sct target]
}

proc auto370::checkmode {} {
  set code [silent {} sct mode[sct target]]
  if {$code eq ""} {
    error "illegal mode [sct target]"
  }
  sct update [sct target]
  set enum [split [hgetpropval [sct] enum] ,]
  clientput "set parameters for [lindex $enum [sct target]]"
  eval $code
}

proc auto370::mode {nr mode code} {
  set p $::stdConfig::path
  hsetprop $p/mode mode$nr $code
  set enum [split [hgetpropval $p/mode enum] ,]
  if {$nr > 10} {
    error "nr $nr too big"
  }
  while {[llength $enum] <= $nr} {
    lappend enum $nr
  }
  lset enum $nr $mode
  hsetprop $p/mode enum [join $enum ,]
}
