namespace eval jtccr {
}

proc stdConfig::jtccr {} {
  variable node
  
  controller syncedprot

  pollperiod 1 1
  obj JTCCR -int wr
  default 0
  prop check jtccr::set_mode
  prop write stdSct::complete
  prop read jtccr::read
  prop dir 0
  prop p1path /p1
  prop p2path /p2
  prop p3path /p3
  prop pressregpath /pressreg/setpoint
  prop compressorpath /epc/port1
  prop tpath /tt/main
  prop enum manual,high_pressure,circulating,warmup

  kids "jtccr" {
    #node p1min par 1.8
    #prop help "go to high_pressure mode when p1 below this value"

    node p1max par 2.2
    prop help "go to circulating mode when p1 above this value"

    node p2min par 0.12
    prop help "stop compressor when p2 is below this value"

    #node p2lim par 0.15
    #prop help "do not start compressor when p2 is below this value"

    node p2max par 0.8
    prop help "start compressor when p2 is above this value"

    node pdifmax par 5.0
    prop help "maximum pressure difference for compressor"

    node pdifmargin par 1
    prop help "safety margin for pressure difference"

    node p3margin par 0.01
    prop help "start compressor when p3 is below pressreg setpoint plus this value"

    node plow par 4
    prop help "pressure below 5 K"
   
    foreach i {1 2 3 4 5 6 7 8 9 10} {
      node v$i wr
      prop enum 1
      prop check jtccr::setvalve $i
      prop write stdSct::complete $i
      prop read jtccr::getvalve $i
    }

    node vm wr
    prop label vm
    prop enum closed,opened
    prop enum_ closed,opened,closing,opening,no_motor,undefined
    prop write jtccr::setmotvalve
    prop read jtccr::getmotvalve
    prop initstate init

    node verbose -int par 0
    prop enum 1
  }
}

proc jtccr::setvalve {i} {
  v vc$i [sct target]
  sct update [sct target]
  sct settime [DoubleTime]
}

proc jtccr::valve_state_text {i} {
  return [lindex [list off on no_valve timeout timeout1 boost] $i]
}
 
proc jtccr::getvalve {i} {
  if {[DoubleTime] < [silent 0 sct settime] + 2} {
    # do not update while write operation might be pending
    return idle
  }
  set vs [hvali /v/v$i]
  if {$vs > 1 && $vs < 5} {
    error "v$i error: [valve_state_text $vs]"
  }
  sct update $vs
  return idle
}

proc jtccr::setmotvalve {} {
  switch -- [sct target] {
    0 {
      v mp -[hval /v/mot]
      sct enum start_closing,open
    }
    1 {
      v mp [hval /v/mot]
      sct enum close,start_opening
    }
  }
  sct update [sct target]
  return idle
}

proc jtccr::getmotvalve {} {
  set fm [silent -1 hval /v/fm]
  switch -- $fm {
    1 {
      sct update 3
      sct enum close,open,opening=3
    }
    2 {
      sct update 2
      sct enum close,open,closing=2
    }
    3 {
      sct update 1
      sct enum close,opened
    }
    4 {
      sct update 0
      sct enum closed,open
    }
    5 {
      sct update 4
      sct enum close,open,no_motor=4
    }
    default {
      if {[sct initstate] eq "init"} {
        # try to close - if not done within short time, try to open
        sct initstate done
      }
      sct update 5
      sct enum close,open,undefined=5
    }
  }
  return idle
}

proc jtccr::set_mode {} {
  switch -- [sct target] {
    0 { # manual }
    1 { # high_pressure
      foreach i {3 4 5 6 7 8 10} {
        hset [sct]/v$i 0
      }
      foreach i {1 2 9 m} {
        hset [sct]/v$i 1
      }
      hset [sct pressregpath] 12    
    }
    2 { # circulate
      foreach i {3 4 5 6 7 9 10} {
        hset [sct]/v$i 0
      }
      foreach i {1 2 8 m} {
        hset [sct]/v$i 1
      }
      hset [sct pressregpath] [hvali [sct]/plow]
    }
    3 { # warmup
      foreach i {6 7 8 9 10} {
        hset [sct]/v$i 0
      }
      foreach i {1 2 3 4 5 m} {
        hset [sct]/v$i 1
      }
    }
    default {
      error "illegal mode"
    }
  }
  sct update [sct target]
}

proc jtccr::read {} {
  if {[hvali [sct]] == 0} {
    return idle
  }
  #if {![silent 0 sct lowt] && [hvali [sct tpath]] < 5} {
  #  hset [sct pressregpath] [hvali [sct]/plow]
  #  sct lowt 1
  #}
  set p1 [hvali [sct p1path]]
  set p2 [hvali [sct p2path]]
  set p3 [hvali [sct p3path]]
  set p3reg [expr [hvali [sct pressregpath]] + [hvali [sct]/p3margin]]
  set pdifmax [hvali [sct]/pdifmax]
  set p2min [hvali [sct]/p2min]
  set p2max [hvali [sct]/p2max]

  # security features:
  if {$p3 >= $p2 + $pdifmax + [hvali [sct]/pdifmargin]} {
    hset [sct compressorpath] 0
    hset [sct]/v9 0
    hset [sct]/v1 0
    hset [sct] 0  ;# manual
    hset [sct]/status "overpressure: He recovery output closed?"
    return idle
  }
  if {$p2 < $p2min && $p3 < $p3reg && [sctval [sct]] != 1} {
    hset [sct compressorpath] 0
    hset [sct]/v9 0
    hset [sct]/v1 0
    hset [sct] 0  ;# manual
    hset [sct]/status "underpressure: not enough He"
    return idle
  }
  hset [sct]/status ""

  set verbose [silent 0 hvali [sct]/verbose]

  if {[sctval [sct]/v8]} { # circulating
    if {$p3 < $p3reg && ![hvali [sct compressorpath]]} {
      if {$verbose} {
        clientput "switch compressor on (p3 < p3reg=$p3reg)"
      }
      hset [sct compressorpath] 1
    } elseif {$p3 - $p2 > $pdifmax && [hvali [sct compressorpath]]} {
      if {$verbose} {
        clientput "switch compressor off (p3 - p2 > pdifmax=$pdifmax)"
      }
      hset [sct compressorpath] 0
      # skip overpressure protection for one time
      # return idle
    }
  }
  if {[hvali [sct]] == 1 && $p1 > [hvali [sct]/p1max]} {
    # switch from high pressure mode to circulating when p1 > p1max
    hset [sct] 2
    return idle
  }
  if {$p2 > $p2max && ![hvali [sct compressorpath]]} {
    if {$verbose} {
      clientput "switch compressor on (p2 > p2max=$p2max)"
    }
    hset [sct compressorpath] 1
  } elseif {$p2 < $p2min && [hvali [sct compressorpath]]} {
    if {$verbose} {
      clientput "switch compressor off (p2 < p2min=$p2min)"
    }
    hset [sct compressorpath] 0
  }

  # overpressure protection
  if {$p3 - $p2 >= $pdifmax + 0.1} {
    # release to recovery
    if {![sctval [sct]/v10]} {
      hset [sct]/v10 1
      if {$verbose} {
        clientput "release to recovery (p3 - p2 > pdifmax=$pdifmax) p3-p2=[expr $p3 - $p2]"
      }
    }
  } elseif {[sctval [sct]/v10]} {
    # finish release to recovery
    hset [sct]/v10 0
    if {$verbose} {
      clientput "release finished (p3 - p2 < pdifmax=$pdifmax)"
    }
  }
  # clear error message
  sct update [hvali [sct]]
  return idle
}
