# dil combi (2x LS370 + IGH)

namespace eval dil12 {
}

proc stdConfig::dil12 args {
  variable name
  variable path
  variable node
  variable ctrl
  
  scanargs $args var -reg370 /treg -mon370 /tmon -hasturbo 0

  variable hostport none
  controller syncedprot 30

  obj dilcombi -drive wr
  prop layoutpos -10
  prop @monpath $mon370
  prop @regpath $reg370
  prop label set
  if {[info proc stdConfig::tdrive] eq ""} {
    namespace eval :: {
      source drivers/trun.tcl
    }
  }
  tdrive $name -log 1
  hsetprop $path/set check dil12::check_set
  hsetprop $path/set visible false

  kids "main dilution control" {

    node ramp out
    prop help {ramp [K/min]}
    prop check dil12::check_ramp
    prop write stdSct::completeUpdate

    node heaterselect -text out
    prop label "heater on "
    prop enum sample,mix,mix(temporarely)
    default sample
    prop check dil12::check_heaterselect
    prop write stdSct::completeUpdate

    node control -text out
    prop label "control sensor "
    prop help "click off to reload list"
    prop enum "off"
    # for seadesc:
    prop enum_ off=0,mix=5,sample=6,samplehtr=8
    default off
    prop check dil12::check_control
    prop write stdSct::completeUpdate
#    prop regctrl _[string range $reg370 1 end]
    $ctrl queue $node read dil12::set_chanlist

    node heatermode -text out
    prop enum disabled=-1,off=0,on=1
    prop check named_enum
    prop write dil12::set_heatermode

    pollperiod 5 5

    node heaterrange -text wr
    prop enum 2uW=1,20uW=2,200uW=3,2mW=4,20mW=5
    prop check dil12::check_heaterrange
    prop write stdSct::complete
    prop read dil12::get_heater

    node autoheater -int wr
    default 1
    prop enum 1
    prop check stdSct::completeUpdate
    prop write stdSct::complete
    prop read dil12::autoheater
    prop help "automatic heater range"
    prop n_overflow 0
 
    kids "auto heater range parameters" {
      node wlp0 par 0.06
      prop label "base T" 
      prop help "weak link base temperature (used for auto heater)"
    
      node wlp1 par 0.12
      prop label "T for 1uW" 
      prop help "weak link temperature at 1 uW (used for auto heater)"
    
      node wlp2 par 0.35
      prop label "T for 10uW" 
      prop help "weak link temperature at 10 uW (used for auto heater)"
    
      node wlp3 par 1
      prop label "T for 100uW" 
      prop help "weak link temperature at 100 uW (used for auto heater)"
    
      node wlp4 par 2.8
      prop label "T for 1mW" 
      prop help "weak link temperature at 1 mW (used for auto heater)"
      
      node mxp0 par 0.02
      prop label "base T" 
      prop help "weak link base temperature (used for auto heater)"
    
      node mxp1 par 0.03
      prop label "T for 1uW" 
      prop help "mix.ch. temperature at 1 uW (used for auto heater)"
    
      node mxp2 par 0.07
      prop label "T for 10uW" 
      prop help "mix.ch. temperature at 10 uW (used for auto heater)"
    
      node mxp3 par 0.15
      prop label "T for 100uW" 
      prop help "mix.ch. temperature at 100 uW (used for auto heater)"
    
      node mxp4 par 0.4
      prop label "T for 1mW" 
      prop help "mix.ch. temperature at 1 mW (used for auto heater)"
    
      node switchdown par 5
      prop label "switch down" 
      prop help "percentage for auto heater switch down"
    
      node switchup par 50
      prop label "heater threshold" 
      prop help "when the calculated power is above this percentage, use higher range"
    }
    
    node autoprop -int out
    default 1
    prop check dil12::check_autoprop
    prop write stdSct::complete
    prop enum 1
    prop newline 1
    prop help "change prop value depending on actual power"

    kids "PI parameters" {
      node prop out
      default 1
      prop help "prop value (maximum prop value when autheater is on)"
      prop check dil12::check_prop
      prop label "max. prop"
      prop write stdSct::completeUpdate

      node integ out
      default 5
      prop help {integral value [sec], deriv is set to 0 on change}
      prop check dil12::check_integ
      prop write stdSct::completeUpdate
    }
    
    node instructions -text upd
    default ""
    prop label ""

    node auto -text wr
    if {$hasturbo} {
      set t_enum ",pipe_pump=11,pipes_ready=12"
    } else {
      set t_enum ""
    }
    prop enum off=0,cool_vti=1$t_enum,ready_for_sorbpump=2,sorb_pump=3,condense=4,circulate=5,remove_only=60,remove_and_warmup=62,remove_and_condense=61,remove_and_sorbpump=63,finished=7
    prop enumsplit 3,60,7
    default 0
    prop read dil12::pollauto
    prop write dil12::startauto
    prop check dil12::check_auto
    prop opentime 0
    prop closetime 0
    prop g3overg1 0.5
    prop time2halve 15

    kids "auto parameters" {
      node usetrap -text par 0
      prop enum trap_A,trap_B
      prop check named_enum

      if {!$hasturbo} {
        node hasturbo -int par $hasturbo
        prop enum 1
     } else {
        node hasturbo -int -internal par $hasturbo
        prop enum 1
      }
      
      node pipes_pumped -int par 0
      prop enum 1
 
      node cool_crit -text par {vti<4 && onek<4 && sorb<4 && still<4}
      prop help "criterium for transition to ready_for_sorbpump"
      prop width 40

      node pumptime1 par 1200
      prop help "pump time \[sec] while sorb at 40"
      node pumptime2 par 900
      prop help "pump time \[sec] while sorb cools down"

      node pumptime_remaining upd
      prop help "remeaining pump time \[min]"

      node condensep par 700
      prop label "condense pressure"

      node dumptarget par 0

      node condenseflow out
      default 8
      prop check dil12::check_condenseflow
      prop write stdSct::complete
      prop label "condense n.v. flow"
      prop help "VTI flow for condensing and min. flow for cooling"

      node circulateflow out
      default 4
      prop check dil12::check_circulateflow
      prop write stdSct::complete
      prop label "circulate n.v. flow"
      prop help "VTI flow for circulating (may be changed during circulation)"

      node onekstate -text out
      prop label "one K state"
      prop enum normal,fill,optimize
      default normal
      prop check dil12::check_onek
      prop write dil12::startauto
      
      node oneklimit par 2

      node minflow par 2
      prop help "minimum VTI flow (optimize stops at this value)"
      
      node p1low par 0.1
      prop help "p1 value indicating a negligible mixture amount"
      
      node p2low par 0.1
      prop help "p2 value indicating a good vacuum"
      
      node warmup_t par 80
      prop help "VTI temperature to reach when remove_and_warmup"
      
      node vti_t par 10
      prop help "VTI temperature to reach when remove_only"
      
      node xgas -text wr
      prop enum pump,add,filled,empty
      default empty
      prop label "x-gas state"
      prop check dil12::check_xgas
      prop write stdSct::complete
      prop read dil12::read_xgas
      
      node xgas_pulse par 60
      prop help {opening time for xgas capillary [sec]}
      
      node removephase -text out
      prop enum raw_remove,wait_p1_low,wait_v6_open,wait_a_little,wait_v6_closed,wait_t_warmup
      prop label "remove mixture status"
      prop check dil12::check_removephase
      prop write stdSct::completeUpdate
    }
  }
  hset $name/heatermode 0
  dolater 0 hset $name/autoprop/integ 5
}

proc dil12::check_set {} {
  hset [sct @regpath]/set [sct target]
  if {[sct target] == 0} {
    hset [sct parent]/heaterrange 0
  } elseif {[enum_val [sct parent]/heaterrange] == 0} {
    hset [sct parent]/heaterrange 1
  }
  hset [sct parent]/heaterselect [sctval [sct parent]/heaterselect]
  hset [sct parent]/control [sctval [sct parent]/control]
}

proc dil12::check_heaterselect {} {
  set req [named_enum]
  if {$req} {
    #mix
    hset [sct @regpath]/analog2 0
    hset [sct @monpath]/samplehtr/active 1
    if {$req ne "1"} {
      # mix (temporarely)
      sct requested 2
    }
    foreach hr {0 1 2 3 4} {
      hsetprop [sct parent]/autoheater/wlp$hr visible false
      hdelprop [sct parent]/autoheater/mxp$hr visible
    }
  } else {
    #sample
    hset [sct @regpath]/analog2 100
    hset [sct @monpath]/samplehtr/active 0
    foreach hr {0 1 2 3 4} {
      hsetprop [sct parent]/autoheater/mxp$hr visible false
      hdelprop [sct parent]/autoheater/wlp$hr visible
    }
  }
}

proc dil12::set_chanlist {{value -1}} {
  set enum off
  foreach kid [hlist [sct @regpath]] {
    set chan [silent -1 hgetpropval [sct @regpath]/$kid @channel]
    set monactive [silent -1 sctval [sct @monpath]/$kid/active]
    if {$chan > 0 && ![silent 0 hgetpropval [sct @regpath]/$kid manualactive]} {
      set regactive [sctval [sct @regpath]/$kid/active]
      if {[sct enum] eq "off"} {
        hsetprop [sct @regpath]/$kid UPDS [sct controller]
        [sct controller] updatescript [sct @regpath]/$kid "dil12::updateIfSelected [sct objectPath] [sct]"
      }
      if {$monactive > 0 || $regactive} {
        if {$chan == $value} {
          hset [sct @regpath]/set/channel $chan
          if {$monactive > 0} {
            hset [sct @monpath]/$kid/active 0
          }
          if {$regactive == 0} {
            hset [sct @regpath]/$kid/active 1
          }
        } elseif {$value > 0} {
          if {$monactive == 0} {
            hset [sct @monpath]/$kid/active 1
          }
          if {$regactive > 0} {
            hset [sct @regpath]/$kid/active 0
          }
        }
        append enum ",$kid=$chan"
      }
    }
  }
  if {$value == 0} {
    hset [sct @regpath]/set/mode 0
  }
  if {[sct enum] eq "off"} {
    [sct controller] updatescript [sct @regpath]/set "hsetprop [sct objectPath] target"
    [sct controller] updatescript [sct @regpath]/set/ramp "hupdate [sct objectPath]/ramp"
    [sct controller] updatescript [sct @regpath]/set/prop "dil12::updateIfOff [sct objectPath]/autoprop/prop [sct objectPath]/autoprop"
    [sct controller] updatescript [sct @regpath]/set/integ "hupdate [sct objectPath]/autoprop/integ"
  }
  sct enum $enum
  return idle
}

proc dil12::check_control {} {
  set req [named_enum]
  dil12::set_chanlist $req
}

proc dil12::updateIfSelected {objpath chanpath value} {
  if {[hvali [sct]/active] && [enum_val $chanpath] == [sct @channel]} {
    set e [silent 0 sct geterror]
    if {$e eq "0"} {
      updateval $objpath $value
      hsetprop $objpath updatetime [clock seconds]
    } else {
      updateerror $objpath $e 1
    }
  }
}

proc dil12::updateIfOff {path flagpath value} {
  if {[hvali $flagpath] == 0} {
    hupdate $path $value
  }
}

proc dil12::check_IGHpower {lp} {
  if {abs($lp - [sct target]) > $lp * 0.1} {
    error "linear power can not be changed directly, use ts heaterrange instead"
  }
  sct target $lp
}

proc dil12::set_heatermode {} {
  set enumval [lindex [sct requested] 0]
  if {$enumval <= 0} {
    dil htrrange 0
    if {[hvali [sct @regpath]/set/mode] > 0} {
      hset [sct @regpath]/set/mode $enumval
    }
    sct heater_range 0
    sct complete dil12::complete_heaterrange
  } else {
    sctsync {
      set old [result dil htrrange]
      # this is a fix, which might no longer be needed (special handling of enums)
      set r [lindex [split [sctval [sct parent]/heaterrange] ] 0]
      # original: set r [sctval [sct parent]/heaterrange]
      if {$r < 1} {
        set r 1
      } elseif {$r > 5} {
        set r 5
      }
      set lp [expr pow(10,$r) * 0.2e-6]
      if {[hvali [sct @regpath]/set/mode] < 1} {
        hset [sct @regpath]/set/mode 1
        hupdate [sct @regpath]/set/linearpower $lp
        hupdate [sct @regpath]/set/maxpower $lp
      } else {
        hsetprop [sct @regpath]/set/linearpower check "dil12::check_IGHpower $lp"
        hupdate [sct @regpath]/set/maxpower $lp
        hset [sct @regpath]/set/linearpower $lp
      }
      sct heater_fact [expr pow(10,$r-$old)]
      sct heater_range $r
      if {$r >= $old} {
        sct complete dil12::heaterrange_p
        sct complete_p dil12::heaterrange_h
        sct complete_h dil12::complete_heaterrange
      } else {
        sct complete dil12::heaterrange_h
        sct complete_h dil12::heaterrange_p
        sct complete_p dil12::complete_heaterrange
      }
    }
  }
  return complete
}

proc dil12::heaterrange_p {} {
  if {[sctval [sct parent]/autoprop]} {
    set htr [silent 0 hgetpropval [sct @regpath]/set/linearpower oldhtr]
    set prop [expr [sctval [sct parent]/autoprop/prop] * $htr * 0.01 / [sct heater_fact]]
  } else {
    set prop [hvali [sct @regpath]/set/prop]
  }
  # set prop and manual_output (manual_output calculated from lsc::wait370heaterchange)
  sctsync {
    hset [sct @regpath]/set/prop $prop
  }
  return complete_p
}

proc dil12::heaterrange_h {} {
  sctsync {
    dil htrrange [sct heater_range]
  }
  return complete_h
}

proc dil12::complete_heaterrange {} {
  sct update [sct target]
  hupdate [sct parent]/heaterrange [sct heater_range]
  return idle
}

proc dil12::check_heaterrange {} {
  set req [named_enum]
  if {$req == 0} {
    hset [sct parent]/heatermode 0
  } else {
    hset [sct parent]/heatermode 1
  }
}

proc dil12::get_heater {} {
  if {[silent "" hgetpropval [sct parent]/heaterrange requested] ne ""} {
    return idle
  }
  set r [result dil htrrange]
  if {$r eq ""} {
    set r 0
  }
  if {$r > 0 || [silent 0 sct geterror] ne "0"} {
    enum_update $r
  }
  set m [sctval [sct @regpath]/set/mode]
  if {$m > 1} {
    set m 1
  }
  hupdate [sct parent]/heatermode $m 
  return idle
}

proc dil12::check_autoprop {} {
  if {[sct target]} {
    hsetprop [sct]/prop label "max. prop"
  } else {
    hsetprop [sct]/prop label "prop"
  }
  sct update [sct target]
}

proc dil12::neededPower {T} {
  # needed power log10(p/uW) for T
  if {[enum_val [sct parent]/heaterselect] == 0} { # weak link
    set nam wl
    set Tbase [hvali [sct]/${nam}p0]
    set Tmix [hvali [sct @regpath]/mix]
    set Tsq [expr $T * $T - $Tmix * $Tmix]
  } else { # mix. ch.
    set nam mx
    set Tbase [hvali [sct]/${nam}p0]
    set Tsq [expr $T * $T - $Tbase * $Tbase]
  }
  if {$Tsq < 1e-6} {
    set Tsq 1e-6
  }
  set logp 0
  set Tsq0 [expr pow([hvali [sct]/${nam}p1],2) - $Tbase * $Tbase]
  if {$Tsq0 < 1e-6} {
    set Tsq0 1e-6
  }
  if {$Tsq < $Tsq0} {
    return [expr log10($Tsq) - log10($Tsq0) + $logp]
  }
  foreach hr {2 3 4} {
    set Tsqi [expr pow([hvali [sct]/${nam}p$hr],2) - $Tbase * $Tbase]
    if {$Tsqi < 1e-6} {
      set Tsqi 1e-6
    }
    if {$Tsq <= $Tsqi} {
      if {$Tsqi <= $Tsq0} {
        return $logp
      } else {
        return [expr (log10($Tsq) - log10($Tsq0)) / (log10($Tsqi) - log10($Tsq0)) + $logp]
      }
    }
    set Tsq0 $Tsqi
    incr logp
  }
  return [expr log10($Tsq) - log10($Tsq0) + $logp]
}

proc dil12::neededHr {htr T} {
  return [expr int([neededPower $T] - log10($htr * 0.01) + 1.7)]
}

proc dil12::autoheater {} {
  if {[silent 0 sct geterror] ne "0"} {
    sct update 1
  }
  set hr [enum_val [sct parent]/heaterrange]
  if {[hvali [sct]] == 0 && $hr == 0 || [enum_val [sct parent]/heatermode] < 1 \
      || [result dil pmix] > 0} {
    return idle
  }
  set htr [hgetpropval [sct @regpath]/set/power htr]

  set t [hvali [sct parent]]
  set regT [hvali [sct @regpath]/set/reg]
  if {[hvali [sct]]} {
    # auto heater on

    # switch up percentage (90 % used in comments below)
    set switchup [hvali [sct]/switchup]

    # min. heater range needed for regT with an estimated percentage of less than "switchup"
    set minhr [neededHr $switchup $regT]

    # switch down percentage (5 % used in comments below)
    set switchdown [hvali [sct]/switchdown]

    set n_overflow [sct n_overflow]
    if {$htr <= $switchdown} {
      if {$n_overflow > 0} {
        set n_overflow 0
      }
      incr n_overflow -1
    } elseif {$htr >= 100} {
      if {$n_overflow < 0} {
        set n_overflow 0
      }
      incr n_overflow
    } else {
      set n_overflow 0
    }
    sct n_overflow $n_overflow
    
    
    set minhr [neededHr 100 $regT]
#    clientput "regT $regT hr $hr minhr $minhr neededPower [neededPower $regT]"
    if {$hr < 5 && ($minhr > $hr || $n_overflow > 2)} {
      if {$minhr > $hr} {
        set hr [neededHr $switchup $regT]
        if {$hr > 5} {
          clientput "increase heaterrange to 5 (needed $hr for T = $regT)"
          set hr 5
        } else {
          clientput "increase heaterrange to $hr (needed for T = $regT)"
        }
      } else {
        incr hr
        clientput "increase heaterrange to $hr (heater at 100 %)"
      }
      hset [sct parent]/heaterrange $hr
      return "stdSct::complete"
    } elseif {$hr > 1 && $minhr < $hr - 1 && $n_overflow < -2} {
      if {$minhr < $hr - 1} {
        set hr [neededHr $switchup $regT]
        if {$hr < 1} {
          clientput "decrease heaterrange to 1 (needed $hr for T = $regT)"
          set hr 1
        } else {
          clientput "decrease heaterrange to $hr (needed for T = $regT)"
        }
      } else {
        incr hr -1
        clientput "decrease heaterrange to $hr ($htr < $switchdown %)"
      }
      hset [sct parent]/heaterrange $hr
      return "stdSct::complete"
    }
  }
  if {[hvali [sct parent]/autoprop]} {
    set prop [expr $htr * 0.01]
    set logpcalc [neededPower $regT]
    if {$logpcalc < -1.0} {
      set logpcalc -1.0
    }
    set p [expr 0.5 * pow(10,$logpcalc - 0.3 - $hr)]
    if {$p > $prop} {
      set prop $p
    }
    if {$prop < 0.01} {
      set prop 0.01
    } elseif {$prop > 1.0} {
      set prop 1.0
    }
    set old [hvali [sct @regpath]/set/prop]
    set prop [format "%.2g" [expr $prop * [hvali [sct parent]/autoprop/prop]]]
    if {abs($prop - $old) > $prop * 0.01} {
      hset [sct @regpath]/set/prop $prop
    }
  }
  return idle
}

proc dil12::check_prop {} {
  if {[sctval [sct]] == 0} {
    hset [sct @regpath]/set/prop [sct target]
  }
}

proc dil12::check_ramp {} {
  hset [sct @regpath]/set/ramp [sct target]
}

proc dil12::check_integ {} {
  hset [sct @regpath]/set/integ [sct target]
  hset [sct @regpath]/set/deriv 0
}

proc dil12::pollauto {} {
  set msg [silent 0 hgetpropval [sct] geterror]
  if {$msg ne "0"} {
    # no error messages here / must be on the instructions node
    dil12::msg $msg
    hdelprop [sct] geterror
  }
  if {[catch is_dil_cool msg]} {
    # clientput "is_dil_cool failed: $msg"
  }
  return [dil12::autostep [enum_txt [sct]] 0]
}

proc dil12::startcool {} {  
  nv set [silent 10 hvali [sct]/condenseflow]
  nv 2
  sorb mode 0
  if {[hvali [sct]/hasturbo]} {
    if {![hvali [sct]/pipes_pumped]} {
      dil12::set_VtiT 10.25
      return
    }
  }
  dil12::set_VtiT 1.25
  dil12::openvalves v1 v6 v3 v5
}

proc dil12::is_dil_cool {} {
  set vti [get_VtiT]
  set sorb [silentval 0 [sct @monpath]/sorb]
  set onek [silentval 0 [sct @monpath]/onek]
  set still [silentval 0 [sct @monpath]/stillt]
  set crit [hval [sct]/cool_crit]
  set crit [string map "sorb $sorb vti $vti onek $onek still $still" $crit]
  set failure [catch {set ret [expr $crit]}]
  set_or_clr_msg $failure "ERROR: illegal cool criterium"
  if {$failure} {
    return 0
  }
  return $ret
}

proc dil12::check_cool_button {} {
  if {[result dil extVersion] eq "0"} {
    if {[is_dil_cool] && [sctval [sct]] != 0} { # not off
      hset [sct] ready_for_sorbpump
    }
  } elseif {[result dil roots] > 0} { # "cool VTI" button
    if {[is_dil_cool]} {
      dil roots 0
    }
    set t [get_VtiSet]
    if {$t != 10.25 && $t != 1.25 || [result nv] != 2} {
      clientput "cooling VTI activated"
      dil12::startcool
    }
  }
}

proc dil12::check_trap {force} {
  if {[result dil lowerN2] > 90} {
    if {$force} {
      error "you must fill the trap before condensing"
    } else {
      dil12::msg "please fill trap!"
    }
    return 1
  }
  return 0
}

proc dil12::autostep {phase init} {
  if {$phase eq "6"} {
    set phase remove_only
  }
  if {$init} {
    set phase init_$phase
  }
  switch $phase {
    init_off {
      dil aux 0
      dil roots 0
    }
    off - finished - cool_vti {
      if {[result dil p2] > 1} {
        hset [sct]/pipes_pumped 0
      }
      if {[result dil aux] > 0} {
        if {[hvali [sct]/hasturbo]} {
          # will be set to pumped after the pipe_pump step!
          hset [sct]/pipes_pumped 0
          hset [sct] pipe_pump
        } else {
          dil aux 0
          hset [sct]/pipes_pumped 1
          hset [sct] pipes_ready
        }
      }
      dil12::check_cool_button
    }
    init_cool_vti {
      if {[result dil p2] > 1} {
        clientput "[sct]: p2 > 1 -> assume pipes are not pumped"
        hset [sct]/pipes_pumped 0
      }
      dil12::msg ""
      if {[enum_txt [sct]] eq "pipes_ready"} {
        hset [sct] pipes_ready
      }
      dil12::startcool
    }
    init_pipe_pump {
      if {[result dil aux] == 0} {
        hset [sct] off
        dil12::msg "please start the pipe_pump step from the IGH panel"
        return idle
      }
      if {![hvali [sct]/hasturbo]} {
        hset [sct] off
        dil12::msg "we have no turbo pump"
        hset [sct] off
        return idle
      }
      set closed_valves [list v1 v2 v3 v4 v5 v7 v8 v9 v10 v11a v11b v12a v12b v13a v13b v14 v1a v2a v4a v5a]
      set open_valves [list]
      foreach v $closed_valves {
        if {[result dil $v] > 0} {
          lappend open_valves $v
        }
      }
      if {[llength $open_valves]} {
          dil12::msg "valve(s) $open_valves must be closed before pipe_pump"
          hset [sct] off
          return idle
      }
      dil12::openvalves v5a v2a v7 v2 v1 v6 vb_open
      dil v4a 1
      sct pumptime 0
      dil12::msg "pumping, waiting for low pressures P1 and P2"
    }
    pipe_pump {
      if {[result dil aux] == 0} {
        dil v7 0
        dil v11a 0
        dil v11b 0
        dil v12a 0
        dil v12b 0
        dil v4a 0
        dil12::openvalves vm_open
        hset [sct] off
        return idle
      }
      dil12::check_cool_button
      if {[result dil p1] < [hvali [sct]/p1low] && [result dil g1] < 5 &&
          [result dil p2] < [hvali [sct]/p2low] && [result dil v6pos] > 50} {
        if {[sct pumptime] == 0} {
          dil12::openvalves v5a v2a v7 v2 v1 v6 v5 v3 vb_open
          dil v4a 1
          sct pumptime [expr [DoubleTime] + 105]
          dil12::msg "pump for 2 more minutes"
        } elseif {[DoubleTime] > [sct pumptime] && [result dil v6pos] > 99} {
          # do not check for vb/vm here, as the user already might manipulate the valves
          dil12::openvalves v1 v6 v3 v5
          dil v4a 0
          if {[DoubleTime] > [sct pumptime] + 15} {
            hset [sct]/pipes_pumped 1
            dil12::set_VtiT 1.25
            hset [sct] pipes_ready
            dil12::msg "close VB, open VM, open 3 valves on the stick, open dump valves and confirm on the IGH"
          } else {
            dil12::msg "pump for 15 more seconds"
          }
        }
      } elseif {[sct pumptime] != 0} {
        dil12::msg "there might be some mixure in TRAP A, please check"
      }
    }
    init_pipes_ready {
      if {![hvali [sct]/pipes_pumped]} {
        dil12::msg "pipes must be pumped first (see [sct]/pipes_pumped)"
        hset [sct] off
      }
      # do not check for vb/vm here, as the user already might manipulate the valves
      dil12::openvalves v1 v6 v3 v5
      sct wait4k 1
    }
    pipes_ready {
      if {[enum_txt [sct]/xgas] eq "empty"} {
        hset [sct]/xgas add
      }
      dil12::check_cool_button
      if {[hvali [sct]/hasturbo]} {
        if {[result dil aux] == 0} {
          if {[silent 1 sct wait4k]} {
            dil12::msg "wait for dilution unit to be < 4 K"
            sct wait4k 0
          }
        }
      } else {
        dil aux 0
      }
      if {[is_dil_cool]} {
        if {[result dil aux] == 0} {
          hset [sct] sorb_pump
        }
      }
    }
    init_ready_for_sorbpump {
      dil12::msg "please pump the IVC and switch to 'sorbpump'"
      nv set [silent 5 hvali [sct]/circulateflow]
      nv 2 ;# auto
    }
    ready_for_sorbpump {
    }
    init_sorb_pump {
      # Dec 2014: open valve 9 in order not to have overpressure
      dil12::clearButtons
      dil12::openvalves v1 v6 v3 v5 v9 vm_open

      if {[hvali [sct]/hasturbo]} {
        hset [sct]/xgas pump
      }

      nv set [silent 3 hvali [sct]/minflow] 
      nv 2 ;# auto
      run sorb 40
      sct sorbstart 0
      dil12::msg ""
      dil12::set_VtiT 5
    }
    sorb_pump {
      if {[sct sorbstart] == 0} {
        if {([hvali [sct @monpath]/onek] > 4) && \
            ([hvali [sct @monpath]/stillt] > 4) && \
            ([hvali [sct @regpath]/mix] > 4)} {
          sct sorbstart [clock seconds]
        }
      } elseif {[hvali /sorb/mode]} {
        set remt [expr [sct sorbstart] + [hvali [sct]/pumptime1] - [clock seconds]]
        if {$remt <= 0} {
          run sorb 0
          sorb mode 0
          sct sorbstart [clock seconds]
        } else {
          hupdate [sct]/pumptime_remaining [expr int(($remt + [hvali [sct]/pumptime2]) / 60)]
        }
      } elseif {[hvali /sorb/mode] == 0} {
        set remt [expr [sct sorbstart] + [hvali [sct]/pumptime2] - [clock seconds]]
        if {$remt < 0} {
          hset [sct] condense
          hupdate [sct]/pumptime_remaining 0
        } else {
          hupdate [sct]/pumptime_remaining [expr int($remt / 60)]
        }
      }
    }
    init_condense {
      dil12::check_trap 0
      dil12::clearButtons
      if {[enum_val [sct parent]/heaterselect] == 2} {
        # switch heating back to sample
        hset [sct parent]/heaterselect 0
      }
      dil12::openvalves v1 v6 v12 v13 vm_open

      if {[hvali [sct]/hasturbo]} {
        hset [sct]/xgas pump
      }
      sorb mode 0
      dil12::set_VtiT 1.25
      hset [sct parent]/set 0
      hset [sct parent]/heatermode 0
      nv set [silent 10 hvali [sct]/condenseflow]
      nv 2 ;# auto
      sct condensestart [clock seconds]
      dil12::msg ""
    }
    condense {
      dil12::check_trap 0
      if {[result dil aux] > 0} {dil aux 0}
      if {[result nv] == 4} {
        # if valve is open, go to automatic after 10 sec
        if {[clock seconds] > [silent 0 sct condensestart] + 10} {
          nv 2
        }
      }
      set cp [expr double([hvali [sct]/condensep])]
      set dt [expr double([hvali [sct]/dumptarget])]
      set g1 [result dil g1]
      set g2 [result dil g2]
      if {$g2 < $g1} {
# workaround for bad G1 sensor on DIL2
        set g1 $g2
      }
      set g3 [result dil g3]
      if {$cp > $g3 + 500} {
        set cp [expr $g3 + 500]
      }
      set g1dif [expr $cp - $g1]
      set p1 [result dil p1]
      if {[result dil v14] && [sct utime] > [sct opentime] + 9} {
        set g3 $p1
        if {$g3 < 0.5} {
          set g3 0.0
        }
      }
      if {$p1 < 10.0 && [result dil v6pos] > 30 && [sct utime] > [sct opentime] + 30} {
        set lim $dt
        if {$lim == 0.0} {
          set lim 1.0
        }
        if {$g3 <= $lim} {
          hset [sct] circulate
          return idle
        }
# calculate the amount of gas to remove in units of dump pressure
        set amount [expr $g1dif * [sct g3overg1]]
        if {$amount > 1.0} {
          if {$amount > 50} {
            set amount 50
          }
          if {$g3 < $dt + $amount} {
            set amount [expr $g3 - $dt]
            if {$dt > 1.0} {
              set amount [expr $amount + 1.0]
            }
          }
          set openperiod [expr log(($g3 + 0.1)/($g3 - $amount + 0.1)) * [sct time2halve]]
          if {$openperiod > 0.1} {
            if {[sct closetime] == 0} {
              # last pulse has finished
              [sct controller] poll [sct] 0.01 write "dil12::timerauto [expr [DoubleTime] + 5]"
            }
            set opentime [sct utime]
            if {![result dil v14]} {
              sct opentime $opentime
            }
            sct closetime [expr $opentime + $openperiod]
            dil v14 1
          }
        }
      }
    }
    init_circulate {
      dil12::clearButtons
      dil12::openvalves v1 v6 v12 v13 vm_open

      sorb mode 0
      dil12::set_VtiT 1.25
      hset [sct]/onekstate 0
      nv set [silent 5 hvali [sct]/circulateflow]
      nv 2 ;# auto
      dil12::msg ""
    }
    circulate {
      if {[result dil aux] > 0} {dil aux 0}
      switch [hvali [sct]/onekstate] {
        0 { # normal
          set tlim [expr [hvali [sct]/oneklimit] + 0.2]
          if {[silent 0 hvali [sct @monpath]/onek] > $tlim} {
            dil12::msg "oneK_T > $tlim -> increased VTI flow for refilling VTI"
            hset [sct]/onekstate 1
          }
        } 
        1 { # increased flow for filling pumping space
          set tlim [hvali [sct]/oneklimit]
          if {[silent 0 hvali [sct @monpath]/onek] < $tlim} {
            set circulateflow [expr [hvali [sct]/circulateflow] + 0.2]
            hupdate [sct]/circulateflow $circulateflow
            hset [sct]/onekstate 0
            clientput "oneK_T < $tlim -> VTI full enough -> normal flow slighly increased to [hvali [sct]/circulateflow]"
          }
        }
        2 { # optimize
          set tlim [expr [hvali [sct]/oneklimit] + 0.2]
          if {[silent 0 hvali [sct @monpath]/onek] > $tlim} {
            dil12::msg "oneK_T > $tlim -> increased flow for refilling VTI"
            hset [sct]/onekstate 1
          } elseif {[clock seconds] - [sct optimize] > 1800} {
            set circulateflow [expr [hvali [sct]/circulateflow] - 0.1]
            set minflow [hvali [sct]/minflow]
            if {$circulateflow <= $minflow} {
              hupdate [sct]/circulateflow $minflow
              hset [sct]/onekstate 1
              clientput "end optimizing VTI flow  - minimum $minflow reached"
            } else {
              hupdate [sct]/circulateflow $circulateflow
              sct optimize [clock seconds]
              clientput "reduced VTI flow to [hvali [sct]/circulateflow]"
              nv set $circulateflow
              nv 2 ;# auto
            }
          } 
        }
      }
    }
    init_finished {
      # close all valves and zero all powers
      dil12::clearButtons
      dil12::openvalves

      # close v4a
      hset [sct]/xgas empty

      sorb mode 0
      if {[result dil v6pos] == 0} {
        dil he3 0
      }
      nv set [silent 3 hvali [sct]/minflow]
      nv 2
      if {[enum_val [sct parent]/heaterselect] == 2} {
        # switch heating back to sample
        hset [sct parent]/heaterselect 0
      }
      if {[catch {vm 0}]} { # try to close VM
        dil12::msg "please close VM"
      }
    }
    default { # treat remove phases
      if {[string match init_remove* $phase]} {
        dil12::clearButtons
        # init remove
        dil12::openvalves v1 v6 v3 v5 v9 v12 v11 vm_open
        hset [sct parent]/heatermode 0

        if {$phase ne "remove_and_condense"} {
          # heat sorb only when not condensing afterwards
          run sorb 40
        } else {
          sorb mode 0
        }
        dil pstill 20
        dil pmix 20

        # pump trap on second path
        if {[result dil v7] == 0} {
          dil v8 1
          dil v2 1
        }

        # fix needle valve
        nv 0
        if {[enum_val [sct parent]/heaterselect] != 1} {
          # heating temporarely on mix. chamber
          hset [sct parent]/heaterselect 2
        } else {
          # make sure heating on mix. chamber
          hset [sct parent]/heaterselect 1
        }
        dil12::set_VtiT 5.0
        hset [sct]/removephase raw_remove

      } elseif {[string match remove* $phase]} {
      
        # remove phases: raw_remove,wait_p1_low,wait_v6_open,wait_a_little,wait_v6_closed,wait_t_warmup
        set removephase [enum_txt [sct]/removephase]
        set phaseinit [sct removephaseinit]
        sct removephaseinit 0
        switch $removephase {
          raw_remove {
            if {[result dil pmix] == 0} {
              dil pmix 20
            }
            if {[result dil p1] < 0.5 && [result dil g3] > 350} {
              hset [sct]/removephase wait_p1_low
            }
          }
          wait_p1_low {
            if {$phaseinit} {
              if {$phase eq "remove_and_condense"} {
                hset [sct] condense
              } elseif {$phase eq "remove_and_sorbpump"} {
                hset [sct] sorb_pump
              } elseif {$phase eq "remove_and_warmup"} {
                dil12::set_VtiT [silent 80 hval [sct]/warmup_t]
                nv set [silent 3 hvali [sct]/minflow]
                nv 2
                dil12::msg "wait for P1 < [hvali [sct]/p1low]"
              } else {
                dil12::set_VtiT 10.0
              }
              return idle
            }
            if {[result dil p1] <= [hvali [sct]/p1low]} {
              hset [sct]/removephase wait_v6_open
            }
          }
          wait_v6_open {
            if {$phaseinit} {
              # just to be sure that this phase eventually finishes
              dil v6 99.9
            }
            if {[result dil v6pos] > 99 && [result dil p1] <= [hvali [sct]/p1low]} {
              if {$phase ne "remove_and_warmup"} {
                dil12::msg "you might now safely heat the VTI to 80 K"
              }
              hset [sct]/removephase wait_a_little
            }
          }
          wait_a_little {
            if {$phaseinit} {
              nv set [silent 3 hvali [sct]/minflow]
              nv 2
              dil v6 90
              return idle
            }
            if {[result dil p1] <= [hvali [sct]/p1low] && [result dil v6pos] <= 90
                && [hvali /nv/flow] < [hvali /nv/autoflow/flowtarget] + 1} {
              hset [sct]/removephase wait_v6_closed
            }
          }
          wait_v6_closed {
            if {$phaseinit} {
              dil v6 0
              dil pstill 0
              dil pmix 0
              dil psorb 0
              sorb mode 0
              nv 2
              return idle
            }
            if {[result dil v6pos] == 0} {
              hset [sct]/removephase wait_t_warmup
            }
          }
          wait_t_warmup {
            if {$phaseinit} {
              dil12::msg "removing mixture finished, you might switch off the gas handling system"
              return idle
            }
            if {$phase eq "remove_and_warmup"} {
              if {[dil12::get_VtiT] < [silent 80 hval [sct]/warmup_t]} {
                return idle
              }
              dil12::msg "you might remove the dil insert and switch off the gas handling system"
            }
            hset [sct] finished
          }
        }
      }
    }
  }
  return idle
}

proc dil12::msg {txt} {
  set old [hvali [sct parent]/instructions]
  hupdate [sct parent]/instructions $txt
  if {$txt eq ""} {
    hdelprop [sct parent]/instructions geterror
  } else {
    if {$old != $txt} {
      clientput $txt
    }
    hsetprop [sct parent]/instructions geterror $txt
  }
}

proc dil12::set_or_clr_msg {flag txt} {
  if {$flag} {
    msg $txt
  } else {
    set old [hvali [sct parent]/instructions]
    if {$old eq $txt} {
      hupdate [sct parent]/instructions ""
      hdelprop [sct parent]/instructions geterror
    }
  }
}

proc dil12::check_removephase {} {
  named_enum
  hsetprop [sct parent] removephaseinit 1
}

proc dil12::check_onek {} {
  named_enum req
  sct update [sct target]
  switch $req {
    normal {
      nv set [hvali [sct parent]/circulateflow]
    }
    fill {
      nv set [expr [hvali [sct parent]/circulateflow] + 5]
    }
    optimize {
      hsetprop [sct parent] optimize [clock seconds]
      nv set [hvali [sct parent]/circulateflow]
    }
  }
}

proc dil12::set_VtiT {value} {
  if {[catch {
    tt set/ramp 0
    run tt $value
  }]} {
    tt set $value
  }
}

proc dil12::get_VtiT {} {
  return [result tt tm]
}

proc dil12::get_VtiSet {} {
  return [result tt set]
}

proc dil12::clearButtons {} {
  if {[result dil aux] != 0} {
    dil aux 0
  }
  if {[result dil roots] != 0} {
    dil roots 0
  }
}

proc dil12::openvalves args {
# open valves given in the args, close all others
# start 3he pump, set all powers to 0
# v7 is not set, but used to test if some other valves are allowed to open
# use v11, v12, v13 instead of v11A, v12A, v13B, as the trap choice is
# taken into account
# if vm_open is present, vm must be open, vb closed
# if vb_open is present, vb must be open, vm closed
# if vm_vb_closed is present, both vm and vb must be closed

  array set dil {
    v1 0
    v2 0
    v3 0
    v4 0
    v5 0
    v6 0
    v8 0
    v9 0
    v10 0
    v11 0
    v12 0
    v13 0
    v14 0
  }
  if {[hvali [sct]/hasturbo]} {
    set dil(v7) 0
    set dil(v2a) 0
    set dil(v5a) 0
  }
  set v_pos ""
  foreach {var} $args {
    if {[info exists dil($var)]} {
      set dil($var) 1
    } elseif {$var eq "vb_open"} {
      set v_pos 0,3
      set v_text "VB must be open, VM must be closed"
    } elseif {$var eq "vm_open"} {
      set v_pos 3,0
      set v_text "VM must be open, VB must be closed"
    } elseif {$var eq "vm_vb_closed"} {
      set v_pos 0,0
      set v_text "both VM and VB must be closed"
    } else {
      error "$var is not a valid argument for dil12::openvalves"
    }
  }
  if {$v_pos ne "" && [sicsdescriptor vb] eq "MotorValve"} {
    if {"[result vm],[result vb]" ne $v_pos} {
      error $v_text
    }
  }
  if {[enum_val [sct]/usetrap] == 0} {
    set trap A
  } else {
    set trap B
  }
  
  if {[result dil v7] && [result dil v2a] == 0} {
    # there might be a pump on V7
    if {[result dil v11$trap]} {
      # used trap is pumping
      if {$dil(v12) > 0 || $dil(v13)} {
        error "do not open V12$trap or V13$trap when pumping on trap $trap"
      }
    }
    # do not change V11
    unset dil(v11)
    # keep V8 and V2 closed (do not complain)
    set dil(v8) 0
    set dil(v2) 0
  } else {
    # close valves on unused trap
    if {$trap eq "A"} {
      set dil(v11B) 0
      set dil(v12B) 0
      set dil(v13B) 0
    } else {
      set dil(v11A) 0
      set dil(v12A) 0
      set dil(v13A) 0
    }
  }

  dil he3 1
  dil pstill 0
  dil pmix 0
  dil psorb 0
  foreach var [array names dil] {
    set val $dil($var)
    switch $var {
      v11 {
        set var v11$trap
      }
      v12 {
        set var v12$trap
        if {$var eq "v12A"} {
          if {$val} {
            set val 99.9
          }
        }
      }
      v13 {
        set var v13$trap
      }
      v6 {
        if {$val} {
          set val 99.9
        }
      } 
    }
    dil $var $val
  }
}

proc dil12::check_auto {} {
  if {[result dil status] ne ""} {
    clientput "ERROR: in IGH: [result dil status]"
  }
  named_enum req
  dil12::autostep $req 1
  sct update [sct target]
}

proc dil12::startauto {} {
  sct print ok
  return idle
}

proc dil12::timerauto {{reduce_prio 1e20}} {
  if {[result dil p1] > 20} {
    # close because p1 is high
    dil v14 0
    sct closetime 0
    # allow next opening soon
    sct opentime 0
    return unpoll
  }
  set now [DoubleTime]
  if {$now >= [sct closetime]} {
    dil v14 0
    sct closetime 0
    return unpoll
  }
  if {$now > $reduce_prio} {
    [sct controller] poll [sct] 0.5 read dil12::timerauto
    return unpoll
  }
  return idle
}

proc dil12::check_xgas {} {
  named_enum req
  set enum pump,add,empty,filled
  switch -- $req {
    pump {
      dil v4a 1
      set enum pump,add,empty
    }
    add {
      if {[enum_txt [sct]] eq "add"} {
        sct print "adding x-gas in progress"
        return
      }
      dil v4a 0
      dil closedelay [hvali [sct parent]/xgas_pulse]]
      dil v1a 1
#      [sct controller] poll [sct] 1 start dil12::xgas_off $close_time
      sct print "add x-gas (opened v1a for [hvali [sct parent]/xgas_pulse] sec)"
    }
    empty {
      if {[enum_txt [sct]] eq "pump"} {
        sct print "closed valve to x-gas pump"
        dil v4a 0
      } elseif {[enum_txt [sct]] ne "empty"} {
        sct print "set xgas to 'empty' (if this is not true set xgas back to 'filled')"
      }
    }
    filled {
      if {[enum_txt [sct]] eq "pump"} {
        error "ERROR: use '[sct sicscommand] add' to add x-gas"
      } elseif {[enum_txt [sct]] ne "empty"} {
        sct print "set xgas to 'filled' (if this is not true set xgas back to 'empty')"
      }
    }
  }
  sct enum $enum
  enum_update $req
}

proc dil12::read_xgas {} {
  set old [enum_txt [sct]]
  if {[result dil v4a] eq "1"} {
    sct enum pump,add,empty
    if {$old ne "pump"} {
      enum_update pump
    }
  } else {
    sct enum pump,add,empty,filled
    if {[result dil v1a] eq "1"} {
      enum_update add
    } elseif {$old eq "add"} {
      enum_update filled
    } elseif {$old eq "pump"} {
      enum_update empty
    }
  }
  return idle
}

proc dil12::check_condenseflow {} {
  switch [sctval [sct parent]] {
    1 - 4 {
      # cool or condense
      nv set [sct target]
    }
  }
  sct update [sct target]
}

proc dil12::check_circulateflow {} {
  if {[sctval [sct parent]] == 5} {
    # circulate
    if {[hvali [sct parent]/onekstate] == 1} {
      nv set [expr [sct target] + 5]
    } else {
      nv set [sct target]
    }
  }
  sct update [sct target]
}
