namespace eval heliox {
}

source drivers/trun.tcl

proc stdConfig::heliox {} {
  variable name
  controller syncedprot

  obj Heliox -drive out
  prop layoutpos -10
  prop check heliox::check_set
  prop write stdSct::complete
  # prop label set

  tdrive $name -log 1
  kids heliox {
    node mode -int wr
    prop enum undefined=0,hiT=1,midT=2,lowT=3,condense=4
    prop read heliox::read
    prop check heliox::check_mode
    prop write heliox::complete

    node lasts_until -text upd
    prop visible False
    default 0

    node pot_state -text upd

    node target_hours par 48
    prop help "how many hours the pot should last (< 75)"

    node holdflow out
    default 1.5
    prop check heliox::check_holdflow
    prop help "flow for holding pressure"
    prop write stdSct::complete

    node condenseflow out
    default 3
    prop check heliox::check_condenseflow
    prop help "flow for condensing"
    prop write stdSct::complete
  }
  catch {
      hsetprop /tt getsample hvali /th/pot
  } msg
  clientput $msg
}

proc heliox::check_set {} {
  # save for later
  sct goto [sct target]
  if {[sct target] < 1.6} {
    set condense_text ""
    if {[hvali [sct]/lasts_until] > [DoubleTime]} {
      if {[hvali [sct]/mode] < 3} {
        hupdate [sct]/mode 3
      }
    } elseif {[hvali [sct]/mode] == 4} {
      set condense_text "running"
    } else {
      hupdate [sct]/mode 4
      set condense_text "started"
    }
    # sct goto [sct target]
    if {$condense_text ne ""} {
      sct print "going to [sct target] K might take a while - condense process is $condense_text"
    }
  } else {
    if {[sct target] > 25} {
      hupdate [sct]/mode 1
    } elseif {[sct target] < 22} {
      hupdate [sct]/mode 2
    } elseif {[hval [sct]/mode] < 1 || [hval [sct]/mode] > 2} {
      hupdate [sct]/mode 1
    }
  }
  set mainsensor /th/pot
  switch [sctval [sct]/mode] {
    1 {
      nv set [hvali [sct]/holdflow]
      th setsorb/channel C
      th setsorb 20
      th mainloop set
      tt dblctrl 1
      run tt [sct target]
    }
    2 {
      nv set [hvali [sct]/holdflow]
      th setsorb/channel C
      th setsorb 20
      th mainloop set
      tt dblctrl 0
      tt set 1.25
      th set [sct target]
    }
    3 {
      nv set [hvali [sct]/holdflow]
      th setsorb/channel B
      th mainloop setsorb
      th set 0
      if {[sct target] != [hvali [sct]]} {
        th setsorb [sct target]
      }
      set mainsensor /th/low
      tt set 1.25
      set state "low-T mode"
    }
    4 {
      th setsorb/channel C
      th setsorb 40
      th set 0
      th mainloop setsorb
      tt set 1.25
    }
    default {
      error "bad mode"
    }
  }
  setmainsensor $mainsensor
  sct update [sct target]
  hsetprop /ts target [silent 0 sct goto]
  hsetprop /th target [silent 0 sct goto]
}

proc heliox::setmainsensor {mainsensor} {
  if {$mainsensor eq "/th/low"} {
    set old /th/pot
  } else {
    set old /th/low
  }
  catch {[sct controller] killupdatescript $old "updateval [sct objectPath]"}
  [sct controller] updatescript $mainsensor "updateval [sct objectPath]"
  hsetprop [sct objectPath] mainsensor $mainsensor
}

proc heliox::check_mode {} {
  set mainsensor /th/pot
  switch [sct target] {
    0 {
    }
    1 {
      nv set [hvali [sct parent]/holdflow]
      if {[sct target] != [hvali [sct]]} {
        th set [hvali /th/target]
      }
      th setsorb/channel C
      th setsorb 20
      th mainloop set
      set state "hi-T mode"
    }
    2 {
      nv set [hvali [sct parent]/holdflow]
      if {[sct target] != [hvali [sct]]} {
        th set [hvali /th/target]
      }
      th setsorb/channel C
      th setsorb 20
      th mainloop set
      set state "mid-T mode"
    }
    3 {
      nv set [hvali [sct parent]/holdflow]
      if {[hvali [sct parent]/lasts_until] > [DoubleTime]} {
        th set 0
        th setsorb/channel B
        th mainloop setsorb
        if {[sct target] != [hvali [sct]]} {
          th setsorb [expr min(1.8, [hvali /th/target])]
        }
        tt set 1.25
      } else {
        clientput "the 3He pot is probably empty"
        error "condense needed before lowT mode"
      }
      set mainsensor /th/low
      set state "low-T mode"
    }
    4 {
      th set 0
      th setsorb/channel C
      th mainloop set
      th setsorb 40
      tt set 1.25
      set state "-> condense"
      hsetprop /th target 0.25
      hsetprop /ts target 0.25
    }
  }
  setmainsensor $mainsensor
  clientput "the 3He pot [hvali [sct parent]/pot_state]"
  sct update [sct target]
}

proc heliox::check_condenseflow {} {
  if {[sctval [sct parent]/mode] == 4} {
    nv set [sct target]
  }
  sct update [sct target]
}

proc heliox::check_holdflow {} {
  if {[sctval [sct parent]/mode] != 4} {
    nv set [sct target]
  }
  sct update [sct target]
}

proc heliox::complete {} {
  sct print [lindex {undefined "hi-T mode" "mid-T mode" "low-T mode" "-> condense"} [sct target]]
  return idle
}

proc heliox::read {} {
  set now [DoubleTime]
  set lasts_until [hvali [sct parent]/lasts_until]

  # calculate additional losses
  set lasttime [silent 0 sct lasttime]
  if {$lasttime == 0} {
    set delay 5
  } else {
    set delay [expr min(30, $now - $lasttime)]
  }
  set ts [result th pot]
  set tsorb [result th sorb]
  sct lasttime $now
  if {$ts > 3 && $tsorb < 10} {
    if {$lasts_until} {
      clientput "3He pot went empty"
    }
    set lasts_until 0
  } else {  # check losses through heating
    # factor 30000 is a rough guess (measured 25000 for regulation th pot at 1 K)
    set loss [expr $delay * ([silent 0 result th set/power] + [silent 0 result tt set/power]) * 30000]
    set lasts_until [expr $lasts_until - $loss]
    if {$loss > 0 && $lasts_until < $now} {
      if {$lasts_until > 0} {
        clientput "guess 3He pot went empty"
      }
      set lasts_until 0
    }
  }
  if {[hvali [sct]] == 4} {
      if {$ts > 3} {
        nv set [hvali [sct parent]/condenseflow] 
        set lasts_until 0
        sct condense_deadline [expr $now + 36000]  # timeout: should be 3600
      } elseif {[result th sorb] > 39} {
        nv set [hvali [sct parent]/condenseflow] 
        # 3He vapor pressure: 200 mbar at 2 K, power law
        set p [expr 200 * ($ts / 2.0) ** 3.5]
        # dump pressure at condensation start
        set p_max 410
        # the amount of 3He corresponding to 1 mbar in the dump lasts that many hours
        set hours_per_mbar 0.3
        # the condensation does not stop immediately -> add 8 hours
        set hours [expr max(0, $hours_per_mbar * ($p_max - $p)) + 8]
        set target_hours [hvali [sct parent]/target_hours]
        set lasts_until [expr int($now + $hours * 3600)]
        calc_pot $lasts_until
        sct condense_deadline [silent [expr $now + 3600] sct condense_deadline]
        if {$now > [sct condense_deadline]} {
          clientput "condense timeout - cannot fill pot for lasting $target_hours hours"
          set target_hours $hours
        }
        if {$hours >= $target_hours} {
          th setsorb 0
          th setsorb/channel B
          th mainloop setsorb
          th setsorb [silent 0.25 hgetpropval [sct parent] goto]
          # switch to lowT and trigger check_mode
          hset [sct] 3
          nv set [hvali [sct parent]/holdflow]
          clientput "the 3He pot [hvali [sct parent]/pot_state]"
        }
      }
  }
  calc_pot $lasts_until
  sct update [hvali [sct]]
  return idle
}

proc heliox::calc_pot {lasts_until} {
  set now [clock seconds]
  set lasts_until [expr int($lasts_until)]
  hupdate [sct parent]/lasts_until $lasts_until
  if {$lasts_until == 0} {
    hupdate [sct parent]/pot_state "is empty"
  } elseif {$lasts_until > 0} {
    set dynow [clock format $now -format "%j"]
    set dayofyear [clock format $lasts_until -format "%j"]
    if {$dayofyear == $dynow} {
      set day today
    } elseif {$dayofyear == $dynow + 1} {
      set day tomorrow
    } else {
      set day [clock format $lasts_until -format %Y-%m-%d]
    }
    hupdate [sct parent]/pot_state "lasts until $day [clock format $lasts_until -format %H:%M]"
  }
}
