namespace eval nvstep {
}

proc stdConfig::nvstep {} {
  variable name

  controller syncedprot
  pollperiod 1 1

  obj NvStep wr
  prop write nvstep::setmode
  prop read nvstep::read
  prop enum fixed=0,controlled=1,automatic=2,close=3,open=4
  prop write nvstep::write
  prop lastpulse 0
  prop filter 0
  prop closetest 0
  default 0

  kids "needle value" {
    node motpos upd

    node flow upd

    node set out
    default 2.5
    prop check nvstep::checkset
    prop write stdSct::complete
    prop label "flow set"

    node flowmax par 20
    prop label "flow maximum"

    node ctrl -none
    kids "control parameters" {
      node prop par 50
      node int par 15
      node delay par 5
      node dif upd
      node lim upd
      node tol upd
    }
    node autoflow -none
    kids "autoflow control parameters" {
      flow::make flow::tmts {result tt set/reg}
    }
  }
}

proc nvstep::checkset {} {
  sct update [sct target]
  set s [hvali [sct parent]]
  if {[hvali /cc/fa] == 0 || ($s != 1 && $s != 2)} {
    hset [sct parent] 1
  }
}

proc nvstep::write {} {
  switch {[sct target]} {
    3 {
      nvmot [hvali /nvmot/posclosed]
      sct closetest 1
    }
    4 {
      nvmot [hvali /nvmot/posopen]
    }
  }
  sct update [sct target]
  return idle
}

proc nvstep::transf {flow} {
  if {$flow > 1} {
    set flow [expr 2 - 1.0 / $flow]
  }
  return $flow
}

proc nvstep::poll {} {
  hupdate [sct]/motpos [hvali /nvmot/pos]
  set now [DoubleTime]
  set delta [expr $now - [silent 0 sct lastpoll]]
  sct lastpoll $now
  if {$delta > 1} {
    set delta 1
  }
  set umsg "automatic needle valve not activated - set temperature undefined"
  switch [hvali [sct]] {
    1 - 2 {
      if {[hvali [sct]] == 1} {
        set soll [hvali [sct]/set]
        hupdate [sct]/autoflow/flowtarget $soll
      } else {
        flow::task [sct]/autoflow [hvali [sct]/set] [hvali [sct]/flowmax]
        set soll [hvali [sct]/autoflow/flowset]
        if {[hgetpropval [sct]/autoflow/getTset t_set_undefined]} {
          if {[hvali [sct]/status] ne $umsg} {
            clientput "ERROR: $umsg"
          }
          hupdate [sct]/status $umsg
        }
      }
      set ist [hvali [sct]/flow]
      set soll2 [expr $soll + [hvali [sct]/ctrl/tol]]
      set soll [nvstep::transf $soll]
      set tol [expr [nvstep::transf $soll2] - $soll]
      set ist [nvstep::transf $ist]
      set dif [expr $soll - $ist]
      set lim [hvali [sct]/ctrl/lim]
      set int [hvali [sct]/ctrl/int]
      set lim [expr $lim * exp(-$delta/$int)]
      if {abs($dif) > $tol + $lim} {
        set lim [expr abs($dif) * exp($delta*[hvali [sct]/ctrl/delay]/double($int)) - $tol]
        set step [expr $dif * [hvali [sct]/ctrl/prop]]
        run nvmot [expr [hvali /nvmot] + $step]
      }
      hupdate [sct]/ctrl/dif [expr log(abs($dif)/$tol + 1e-3)]
      hupdate [sct]/ctrl/tol [expr log($lim/$tol + 1.0)]
      hupdate [sct]/ctrl/lim $lim
      sct lastmode [hvali [sct]]
    }
    0 - 3 - 4 { # fixed, close, open
      if {[hvali [sct]] == 3 && [sct closetest]} {
        if {[hgetpropval /nvmot status] ne "run"} {
          if {[hvali /nvmot] < [hvali /nvmot/posopen] + [hvali /nvmot/precision]} {
            hupdate [sct]/status "needle valve not fully closed"
          }
          sct closetest 0
        }
      }
      switch -- [hvali /cc/fm] {
        2 - 4 { # closing or closed
          sct update 3
        }
        1 - 3 { # opening or opened
          sct update 4
        }
        default {
#          sct update 0
        }
      }
      logsetup [sct]/set clear
      logsetup [sct]/autoflow/flowtarget clear
      return idle
    }
  }
  if {[hvali [sct]/status] eq $umsg && \
      ([sctval [sct]] != 2 || [hgetpropval [sct]/autoflow/getTset t_set_undefined] == 0)} {
    hsetprop [sct]/autoflow/getTset t_set_undefined 0
    hupdate [sct]/status ""
  }
  return idle
}

proc nvstep::read {} {
  hsetprop /cc/fa nvpath [sct]
  _cc updatescript /cc/fa nvstep::updatemode
  hsetprop /cc/f nvpath [sct]
  hsetprop /cc/f nvctrl [sct controller]
  _cc updatescript /cc/f nvstep::updateflow
  return unpoll
}

proc nvstep::updateflow {value} {
  set filter [lrange "[silent $value sct filter] $value" end-9 10]
  sct filter $filter
  # filter out values which are within the 3 highest or 3 lowest values
  # out of 10 last values (i.e. within 5 seconds)
  set m 3
  set filter [lsort -real $filter]
  if {[llength $filter] < 10} {
    set flow $value
  } else {
    set min [lindex $filter $m]
    set max [lindex $filter end-$m]
    set flow [silent 0 hvali [sct nvpath]/flow]
    if {$max < $flow} {
      set flow $max
    } elseif {$min > $flow} {
      set flow $min
    }
  }
  if {$flow < -50} {
    set flow -62.5
  }
#  clientput "flow $value $flow"
  updateval_e [sct nvpath]/flow $flow -62.5 no_sensor
  [sct nvctrl] queue [sct nvpath] read nvstep::poll
}


