namespace eval hepump {
}

proc stdConfig::hepump {{motname hemot}} {
  variable name

  controller syncedprot
  prop write stdSct::complete
  pollperiod 1 1

  obj HePump -int wr
  default 0
  prop enum neodry=8,xds35_auto=0,xds35_manual=1,sv65=2,other=3,no=-1
  prop help "xds35: scroll pump, sv65: leybold"
  prop read hepump::read
  prop write hepump::set_type
  prop @motname $motname
  prop health_cnt 0

  kids "He pump ([silent xds35_auto result instconfig he_pump_type])" {
    node running -int wr
    prop enum 1
    prop check hepump::chk_running
    prop read hepump::get_running

    node eco -int wr
    prop enum 1
    prop check hepump::set_eco
    prop read hepump::get_eco
    prop nonewline 1

    node auto -int wr
    default 1
    prop enum 1
    prop check hepump::set_auto
    prop read hepump::get_auto
    prop nonewline 1

    node valve -int wr
    prop enum closed,closing,opening,opened,undefined
    prop check hepump::check_valve
    prop write hepump::set_valve
    prop read hepump::get_valve
    prop buttontitle "operate valve"
    prop buttons open/2/2,3:close/1/0,1
    prop try 0

    node eco_t_lim par 3
    prop help "switch off eco mode when T_set < eco_t_lim and T < eco_t_lim * 2"

    node calib par 1

    node health upd
  }
}

proc hepump::set_type {} {
  sct update [sct target]
  return hepump::set_calib
}

proc hepump::visible {pumpcode} {
  if {$pumpcode == 0} {
    set v true
    if {[silent 0 result instconfig he_pump_type] eq "other_pump_with_valve"} {
      set rv false
    } else {
      set rv true
    }
    set runv true
  } elseif {$pumpcode == 8} {
    set rv false
    set runv true
    set v true
  } else {
    set v false
    set rv false
    set runv false
  }

  foreach node "valve [sct @motname]" {
    if {[silent true hgetpropval [sct]/$node visible] ne $v} {
      hsetprop [sct]/$node visible $v
    }
  }
  if {[silent true hgetpropval [sct]/running visible] ne $runv} {
    hsetprop [sct]/running visible $runv
  }
  foreach node "eco auto eco_t_lim" {
    if {[silent true hgetpropval [sct]/$node visible] ne $rv} {
      hsetprop [sct]/$node visible $rv
    }
  }
  if {$pumpcode == 3} {
    set v true
  } else {
    set v false
  }
  if {[silent true hgetpropval [sct]/calib visible] ne $v} {
    hsetprop [sct]/calib visible $v
  }
}

proc hepump::set_calib {} {
  set pumpcode [hvali [sct]]
  sct update $pumpcode
  if {$pumpcode < 0} { # no pump
    _[sct @motname] disconnect
    ignore_msg _[sct @motname] disconnected
    hepump::visible $pumpcode
    hsetprop [sct]/[sct @motname] group 0
    return idle
  } elseif {$pumpcode == 2} { # sv65
    set f 0.9
    hsetprop [sct]/[sct @motname] group 0
  } elseif {$pumpcode == 1} { # xds35_manual
    set f 0.6
    hsetprop [sct]/[sct @motname] group 0
  } elseif {$pumpcode == 0} { # xds35_auto
    set status [string trim [_[sct @motname] status]]
    if {$status eq "disconnected" || $status eq "offline"} {
      # clientput [_[sct @motname] hostport]
      _[sct @motname] reconnect
    }
    if {$status eq "no response"} {
      set pumpcode 1 ;# treat as manual
      hsetprop [sct]/[sct @motname] group "hepump valve motor (not connected)"
    } else {
      hsetprop [sct]/[sct @motname] group "hepump valve motor"
    }
    set eco [sctval [sct]/[sct @motname]/output1]
    if {$eco == 1} {
      set f 0.6 ;# this was 0.4
    } else {
      set f 0.6 ;# 0.95 at 30 mbar
    }
  } elseif {$pumpcode == 8} { # neodry
    set status [string trim [_[sct @motname] status]]
    if {$status eq "disconnected" || $status eq "offline"} {
      # clientput [_[sct @motname] hostport]
      _[sct @motname] reconnect
    }
    if {$status eq "no response"} {
      set pumpcode 1 ;# treat as manual
      hsetprop [sct]/[sct @motname] group "hepump valve motor (not connected)"
    } else {
      hsetprop [sct]/[sct @motname] group "hepump valve motor"
    }
    set f 0.55
  } elseif {$pumpcode == 3} { # other
    hsetprop [sct]/[sct @motname] group "hepump valve motor"
    set f [hval [sct]/calib]
  } else { # other: should not happen
    error "illegal pump code"
  }
  hepump::visible $pumpcode
  if {$f != [silent $f hval /nv/calib/ln_per_min_per_mbar]} {
    nv calib/ln_per_min_per_mbar $f
    hsetprop /nv off_until [expr [DoubleTime] + 20]
    hsetprop /nv off_reason "pump mode changed"
    sct print "pump mode change: suspend needle valve control for 20 sec."
  }
  return idle  
}

proc hepump::read {} {
  set p [expr [silent 0 result cc f] * [silent 0.6 hvali /nv/calib/ln_per_min_per_mbar]]
  set f [silent 0 result nvflow]
  if {[hvali [sct]/eco] == 0 && $p > 1  && $f > 0.5 && $f < 25} {
    set health [expr 100 * $f / ($p / 1.2 - 0.5)]
    set health [expr [silent $health sct health] * 0.95 + 0.05 * $health]
    sct health $health
    if {abs($health - [silent 0 hval [sct]/health]) > 1} {
      sct health_cnt 0
      hupdate [sct]/health [format %.1f $health]
    } else {
      sct health_cnt [expr [sct health_cnt] + 1]
      if {[sct health_cnt] == 10} {
        if {$health > 100} {set health 100}
        catch {prep0 / "pump_health_update [result instrument] [format %.0f $health]"} msg
      }
    }
  } else {
    # reset filter
    hdelprop [sct] health
  }
  return [hepump::set_calib]
}

proc hepump::set_valve_status {new} {
  set old [silent "" sct valvestatus]
  set path [sct objectPath]/status
  set status [hvali $path]
  if {$new ne "" || $old eq $status} {
    hupdate $path $new
  }
  sct valvestatus $new
}

proc hepump::check_valve {} {
  set_valve_status ""
  sct try 5
  sct slowspeed 50
}

proc hepump::set_valve {} {
  if {[sctval [sct parent]] != 0} return
  set val [hvali [sct]]
  if {[sct target] < 2} {
    if {[hvali [sct parent]/[sct @motname]/encoder] > 20} {
      [sct @motname] maxspeed 200
      sctsync {
        run [sct @motname] 15
      }
    }
    set val 1
    catch {
    # fix n.v.
      hsetprop /nv off_until [expr [DoubleTime] + 10]
      hsetprop /nv off_reason "pump valve closed"
    }
    sct update 1
    return hepump::slow_close
  } elseif {[sct target] < 4} {
    [sct @motname] maxspeed 200
    hset [sct parent]/[sct @motname] [hval [sct parent]/[sct @motname]/upperlimit]
    set val 2
  }
  sct update $val
  return idle
}

proc hepump::slow_close {} {
  [sct @motname] maxspeed [sct slowspeed]
  sct slowspeed [expr max(10,[sct slowspeed] - 10)]
  run [sct @motname] [hval [sct parent]/[sct @motname]/lowerlimit]
  return idle
}

proc hepump::get_valve {} {
  if {[sctval [sct parent]] != 0} {return idle}
  set p [sct parent]/[sct @motname]
  set stat [hgetpropval $p status]
  set pos [hvali $p/encoder]
  if {$pos < [hval $p/lowerlimit] - 15 ||
      $pos > [hval $p/upperlimit] + 15} {
    hsetprop $p/encoder adjust_zero 1
    return idle
  }
  set dir [string map {0 close 1 close 2 open 3 open 4 undefined} [silent 4 sct target]]
  if {[sct try] == 0} {
    set dir undefined
  }
  if {$stat eq "run"} {
    if {[hgetpropval $p target] > $pos} {
      sct update 2
    } else {
      sct update 1
    }
  } elseif {$pos < 2 && $dir ne "open"} {
    sct update 0 ;# closed
    sct try 0
    set_valve_status ""
  } elseif {$pos > 30 && $dir ne "close"} {
    sct update 3 ;# opened
    sct try 0
    set_valve_status ""
  } else {
    if {[sct try] > 1 && $dir ne "undefined"} {
      set try [sct try]
      incr try -1
      if {$try <= 3} {
        clientput "[sct] try again to $dir ($try)"
      }
      hset [sct] [sct target]
      sct try $try
    } else {
      sct try 0
      if {$dir ne "undefined"} {
        set txt "cannot $dir valve at He pump. Please do it manually"
        clientput "ERROR: $txt"
        set_valve_status $txt
        sct target 4
      }
      sct update 4 ;# undefined
    }
  }
  if {[hvali [sct]] != 3} {
    # not open - fix n.v.
    catch {
      hsetprop /nv off_until [expr [DoubleTime] + 10]
      hsetprop /nv off_reason "pump valve closed"
    }
  }
  return idle
}

proc hepump::chk_running {} {
  if {[sctval [sct parent]] != 0} return
  if {[sct target]} {
    hset [sct parent]/[sct @motname]/output0 1
  } else { # off
    if {[silent 0 result instconfig he_pump_type] ne "other_pump_with_valve"} {
      # fix n.v.
      catch {
        hsetprop /nv off_until [expr [DoubleTime] + 10]
        hsetprop /nv off_reason "He pump not running"
      }
      hset [sct parent]/[sct @motname]/output0 0
    }
  }
  hsetprop [sct parent]/[sct @motname]/output0 trigger_path [sct parent]/[sct @motname]/input3
  sct update [sct target]
}

proc hepump::get_running {} {
  # only for xds35
  if {[sctval [sct parent]] != 0} {return idle}

  set running [sctval [sct parent]/[sct @motname]/output0]
  set pumpoff [silent 1 hval [sct parent]/[sct @motname]/input3]
  set now [DoubleTime]
  if {$running != [silent 0 sct last_running]} {
    sct last_running $running
    # leave early (status should be checked later, when input3 is read again)
    return idle
  }
  sct update $running
  if {$now < [silent 0 sct last_ok] + 30} {
    return idle
  }
  set old [hvali [sct parent]/status]
  set new ""
  if {$running} {
    if {$pumpoff} {
      if {[silent 0 result instconfig he_pump_type] ne "other_pump_with_valve"} {
        set new "He pump remote cable is not connected"
        catch {hsetprop /nv off_until [expr $now + 10]}
      }
    } else {
      sct last_ok [DoubleTime]
    }
  } else {
    if {!$pumpoff} {
      set new "He pump is still running - manual power must be switched off"
    } else {
      sct last_ok [DoubleTime]
    }
    catch {
      hsetprop /nv off_until [expr $now + 10]
      hsetprop /nv off_reason "He pump not running"
    }
  }
  if {$new ne $old} {
    if {$new ne ""} {
      clientput "ERROR: $new"
    }
    hupdate [sct parent]/status $new
  }
  return idle
}

proc hepump::set_eco {} {
  if {[sctval [sct parent]] != 0} {return idle}
  if {[sct target] != [hvali [sct]]} {
    hset [sct parent]/auto 0
  }
  if {[sct target]} {
    hset [sct parent]/[sct @motname]/output1 1
  } else { # eco off
    hset [sct parent]/[sct @motname]/output1 0
  }
  hepump::get_eco
}

proc hepump::get_eco {} {
  if {[sctval [sct parent]] != 0} {
    sct update 0
    return idle
  }
  set eco [sctval [sct parent]/[sct @motname]/output1]
  sct update $eco
  [sct controller] queue [sct parent] write hepump::set_calib
  return idle
}

proc hepump::set_auto {} {
  if {[sctval [sct parent]] != 0} {return idle}
  sct update [sct target]
}

proc hepump::get_auto {} {
  if {[sctval [sct parent]] != 0} {return idle}
  set auto [hvali [sct]]
  set oldeco [sctval [sct parent]/eco]
  if {$auto && $oldeco != 2} {
    set eco [sctval [sct parent]/eco]
    set lim [sctval [sct parent]/eco_t_lim 7]
    if {[silent 10 hval /tt/target] < $lim && [silent 10 hval /tt] < 2 * $lim} {
      set ecoset 0
    } elseif {[silent 10 hval /tt/target] > $lim || [silent 10 hval /tt] > 3 * $lim} {
      set ecoset 1
    } else {
      set ecoset $eco
    }
    if {$oldeco != $ecoset} {
      hset [sct parent]/eco $ecoset
    }
  }
  sct update $auto
  return idle
}

