namespace eval n2fill {} {
}

proc stdConfig::n2fillCommon {} {

  obj N2Fill wr 1 read -int
  default -1
  prop write n2fill::write
  prop read n2fill::handler
  prop off n2fill::off
  prop on n2fill::on

  prop enum initializing=-1
  prop label "LN2 filling state"

  kids "LN2 fill settings" {

    node upper upd
    node lower upd
    node threshold par 90
    node limit par 200
    node tmo1 par 300
    node tmo2 par 1800
  }
}

proc stdConfig::n2fill {} {
  if {[silent 0 result n2_new] == 0} {
    controller std "" 2 "\r" ";"
    prop readfmt "%x+ ;%x+ "
    prop resolution 1.0
    prop readCmd "a0a1{2}"
    prop offCmd "O00"
    prop onCmd "O03"
    prop pulseCmd "O02"
    n2fillCommon
    return "LN2 fill prototype with old taskit RS232-ADC (12bit)"
  }

  controller std "\r" 2
  prop readfmt ":0404%4x%4x%*2x"
  prop resolution 26.214
  prop readCmd ":0400000002.."
  if {[result n2_new] == 2} {
    prop offCmd   ":10000000030600FF00FF0006.."
    prop onCmd    ":10000000030600FF00FF0005.."
    prop pulseCmd ":10000000030600FF00FF0004.."
  } else {
    prop offCmd   ":100000000306000700070000.."
    prop onCmd    ":100000000306000700070003.."
    prop pulseCmd ":100000000306000700070002.."
  }
  n2fillCommon

  return "LN2 fill prototype with taskit RS232-ADC16"
}

proc n2fill::handler {} {
  sct send [sct readCmd]
  return n2fill::update
}

proc n2fill::convert {sensor raw} {
  set millivolt [expr $raw / [sct resolution]]
  if {$millivolt < 30} {
    hupdate [sct]/status "LN2 sensor unplugged"
    hsetprop [sct]/$sensor geterror "LN2 sensor unplugged"
    return 0
  }
  if {$millivolt < 150 || $millivolt > 1500} {
    hupdate [sct]/status "illegal LN2 sensor reading: $millivolt mV"
    hsetprop [sct]/$sensor geterror "illegal LN2 sensor reading: $millivolt mV"
    return 0
  }
  set t [expr 68 * (5000.0 / $millivolt - 1.0) * 0.25 + 26.8]
  hupdate [sct]/$sensor $t
  hdelprop [sct]/$sensor geterror
  return $t
}

proc n2fill::update {} {
  set res [scan "[sct result]|0" "[sct readfmt]|%d" upper lower null]
  if {$res != 3} {
    error "bad response to '[sct send]': '[sct result]'"
  }
  set upper [convert upper $upper]
  set lower [convert lower $lower]
  set lim [hvali [sct]/limit]
  set thr [hvali [sct]/threshold]
  set state [hvali [sct]]
  if {$upper > $lim || $lower > $lim} {
    if {$state < 3} {
      hupdate [sct]/status "sensor warm"
      sct update 4
      sct enum fill=3,inactive (sensor warm)=4
      return off
    } elseif {$state == 4} {
      hupdate [sct]/status "sensor warm"
      sct update $state
      return idle
    }
  } elseif {[string equal "sensor warm" [result hvali [sct]/status]]} {
    hupdate [sct]/status ""
  }
  if {$upper < $thr && $lower > $thr} {
    if {$state < 4} {
      hupdate [sct]/status "sensor upside down"
      sct update 4
      sct enum fill=3,inactive (sensor upside down)=4
      return off
    } else {
      sct update $state
      return idle
    }
  } elseif {[string equal "sensor upside down" [result hvali [sct]/status]]} {
    hupdate [sct]/status ""
  }
  switch -- $state {
    -1 { # initializing
      sct update 4
      sct enum watching=0,fill=1,inactive=4
      [sct controllerName] poll [sct] 10
      hupdate [sct]/status "automatic LN2 filling off"
      return off
    }
    0 { # watching
      if {$lower > $thr} {
        clientput "start N2 fill"
        sct update 1
        sct enum watching=0,fill (starting)=1,inactive=4
        sct starttime [clock seconds]
        [sct controllerName] poll [sct] 2
        return on
      }
    }
    1 { # filling until lower or upper is cold
      if {[clock seconds] > [sct starttime] + [hvali [sct]/tmo1]} {
        if {$lower < $thr} {
          sct update 2
          sct enum watching=0,fill=2,inactive=4
          return on
        }
        clientput "stop N2 fill (lower sensor not cold quick enough)"
        hupdate [sct]/status "lower sensor not cold quick enough"
        sct update 4
        sct enum fill=3,inactive (lower sensor not cold quick enough)=4
        return off
      } else {
        if {$upper < $thr} {
          clientput "finished N2 fill"
          sct update 0
          sct enum watching=0,fill=1,inactive=4
          return off
        }
      }
      sct update $state
      return on
    }
    2 - 3 { # filling until upper is cold
      if {[clock seconds] > [sct starttime] + [hvali [sct]/tmo2]} {
        clientput "stop N2 fill (fill timeout)"
        hupdate [sct]/status "fill timeout"
        sct update 4
        sct enum filling=2,inactive (fill timeout)=4
        return off
      } elseif {$upper < $thr} {
        clientput "finished N2 fill"
        sct update 0
        sct enum watching=0,fill=1,inactive=4
        return off
      }
      sct update $state
      return on
    }
  }
  sct update $state
  return idle
}

proc n2fill::off {} {
  [sct controllerName] poll [sct] 10
  sct send [sct offCmd]
  return stdSct::complete
}

proc n2fill::on {} {
  sct send [sct onCmd]
  return n2fill::pulse
}

proc n2fill::pulse {} {
  sct send [sct pulseCmd]
  return stdSct::complete
}

proc n2fill::write {} {
  hupdate [sct]/status ""
  switch [sct target] {
# watching
    0 {
      sct update [sct target]
      sct enum watching=0,fill=3,inactive=4
      [sct controllerName] poll [sct] 10
      return off
    }
# inactive
    4 {
      sct update 4
      sct enum watching=0,fill=3,inactive=4
      hupdate [sct]/status "automatic LN2 filling off"
      return off
    }
    1 - 2 - 3 {
      sct starttime [clock seconds]
      [sct controllerName] poll [sct] 2
      if {[hvali [sct]] < 3} {
        sct update 1
        sct enum watching=0,fill=1,inactive=4
      } else {
        sct update 3
        sct enum watching=0,fill=3,inactive=4
      }
      return read
    }
  }
  return idle
}
