# CCU 4
namespace eval ccu4 {} {
# 1=remote
  variable deviceaction 1
  variable newdevice ""
}

proc stdConfig::ccu4 {{title CCU4} args} {
  variable name
  variable node

  set ins [string toupper [result instrument]]
  if {$ins eq "SEAMAN"} {
    set ins free
  }
  controller std "\n" 5
  prop startcmd "cid csf cir cic=$ins"
  hsetprop $node/tasks complete "ccu4::completeStart $name"
  prop write ccu4::write
  prop commerror ccu4::errorScript
  prop freelist 0
  prop obj /$name
  
  pollperiod 0.5 0.5 5
  obj ccu4 rd -int
  prop enum 1
  prop label on
  prop read ccu4::read
  prop cfgtablefile config/ccu4device.list
  prop layoutpos 99
#  if {[info exists ::rack_in_use]} {
    prop instrument $ins
#  }
  default 1
  kids $title {
    node autodevice par -int [expr [llength $args] == 0]
    prop enum 1
    
    node fav out -int
    prop newline 1
    prop enum 1
    prop label "flow available"
  
    node f upd
    prop label flow
    prop avnode fav
    
    node fs out -int
    prop enum ok,no_sens
    prop avnode fav
  
    node mav out -int
    prop newline 1
    prop enum 1
    prop label "n.v. motor available"
  
    node fm upd -int
    prop enum idle,opening,closing,opened,closed,no_motor
    prop avnode mav
  
    node fa out -int
    prop enum fixed,controlled,automatic,offline
    prop avnode mav
    
    node mp out
    prop label "motor pulse"
    prop avnode mav
  
    node msp upd
    prop label "motor speed"
    prop avnode mav
  
    node mmp upd
    prop label "measured pulse"
    prop avnode mav
  
    node mc out
    prop label "motor current"
    prop avnode mav
  
    node mfc out
    prop label "free current"
    prop avnode mav
  
#obsolete
    node moc out
    prop label "overrun const"
    prop avnode mav
  
    node mtc out
    prop label "torque const"
    prop avnode mav
  
    node mtl upd
    prop label "torque limit"
    prop avnode mav
  
    node mft out
    prop label "flow target"
    prop avnode mav
  
    node mt upd
    prop label "torque"
    prop avnode mav
  
#obsolete
    node mo upd
    prop label "overrun"
    prop avnode mav
  
    node mcr upd
    prop label "read current"
    prop avnode mav

    node mot upd
    prop label "open time"
    prop avnode mav

    node mw out
    prop label "wiggle"
    prop help "correction pulse after automatic open"
    prop avnode mav

    node hav out
    prop newline 1
    prop enum 1
    prop label "He lev. available"
  
    node h upd -secop=helev
    prop label "he level"
    prop avnode hav
    prop errvar hsf
    
    node hr upd
    prop label "he sensor mm"
    prop avnode hav
    
    node hc upd
    prop label "He meas. current"
    prop avnode hav
    
    node hu upd
    prop label "He meas. volt"
    prop avnode hav
    
    node hh out
    prop label "He full level"
    prop avnode hav
    
    node hl out
    prop label "He fill level"
    prop avnode hav
    
    node htf out
    prop label "fast period"
    prop help "meas. period in fast mode"
    prop avnode hav
    
    node hts out
    prop label "slow period"
    prop help "meas. period in slow mode"
    prop avnode hav
    
    node hd out
    prop label "He driv. current"
    prop avnode hav
    
    node hwr out
    prop label "He wire res."
    prop avnode hav
    
    node hem out
    prop label "empty"
    prop help "sensor length in mm from top to empty pos."
    prop avnode hav
    
    node hfu out
    prop label "full"
    prop help "sensor length in mm from top to full pos."
    prop avnode hav
    
    node hcd out -int
    prop enum stop,fill,off,auto,manual=7
    prop avnode hav
        
    node hv upd -int
    prop enum fill_valve_off,filling,no_fill_valve,timeout,timeout1
    prop avnode hav
    
    node hsf upd -int
    prop enum sens_ok,sens_warm,no_sens,timeout,not_yet_read,disabled
    prop avnode hav
    
    node ha out -int
    prop enum 1
    prop label "He auto fill"
    prop avnode hav
    
    node hm upd -int
    prop enum 1
    prop label measure
    prop avnode hav
    
    node hf out -int
    prop enum slow,fast
    prop avnode hav
    
    node hbe out -int
    prop enum 1
    prop label LHe 48V enabled
    prop avnode hav
    
    node hmf upd
    prop label "meas first"
    prop avnode hav
    
    node hms upd
    prop label "meas speed"
    prop avnode hav

    node hit out
    prop label "increase tolerance"
    prop avnode hav

    node hft out -int
    prop label "fast/fill timeout"
    prop avnode hav
    
    node hea out
    prop label "add. He channels"
    prop enum 0,1,6=6

    node hch out -int
    prop label "LHe channel"  
    prop avnode hea
  
    node hwr0 out
    prop label "ext. He wire res."
    prop avnode hea
    
    node hem0 out
    prop label "ext. empty"
    prop help "sensor length in mm from top to empty pos."
    prop avnode hea
    
    node hfu0 out
    prop label "ext. full"
    prop help "sensor length in mm from top to full pos."
    prop avnode hea
    
    node hd0 out
    prop label "ext. drive"
    prop help "external sensor drive current (mA)"
    prop avnode hea
    
    for {set ch 0} {$ch < 6} {incr ch} {
      node h$ch upd
      prop errvar hs$ch
      prop newline 1
      prop avnode hea

      node hs$ch upd -int
      prop enum sens_ok,sens_warm,no_sens,timeout,not_yet_read,disabled
      prop nonewline 1
      prop avnode hea

    }

    node hfb upd
    prop label He v. feedb.
    prop avnode hav

    node nav out -int
    prop newline 1
    prop enum 1
    prop label "N2 available"
    
    node nu upd -secop=n2upper
    prop label "N2 upper"
    prop avnode nav
    
    node nl upd -secop=n2lower
    prop label "N2 lower"
    prop avnode nav
    
    node nth out
    prop label "N2 threshold"
    prop avnode nav
    
    node ntc out
    prop label "N2 tube cool delay"
    prop avnode nav
    
    node ntm out
    prop label "N2 fill timeout"
    prop avnode nav
    
    node ns upd -int
    prop enum sens_ok,no_sens,short_circuit,upside_down,sens_warm,empty
    prop avnode nav
    
    node na out -int
    prop enum 1
    prop label "N2 auto fill"
    prop avnode nav
    
    node nv upd -int
    prop enum fill_valve_off,filling,no_fill_valve,timeout,timeout1,boost
    prop avnode nav
    
    node nc out -int
    prop enum stop,fill,off,auto
    prop avnode nav
        
    node nfb upd
    prop label LN2 v. feedb.
    prop avnode nav

    node cda upd
    prop newline 1
    prop label "code res. a"
  
    node cdb upd
    prop label "code res. b"
    
    node cba upd
    prop label "code a invalid"
  
    node cbb upd
    prop label "code b invalid"
  
    node cvs -int upd
    prop label "config version"
  
    node csp -int upd
    prop label "hardware type"

    node cdv out -text
    prop label "device"
  
    node cic out -text
    prop label "connected to"
  
    node cin upd -text
    prop label "connect to"
  
    node cds out
    prop label "device state"
    prop enum local,remote,loading,by_code,by_touch
  
    node timing par 0
    prop newline 1
    prop enum 1

    node tc upd
    prop avnode timing

    node tn upd
    prop avnode timing

    node th upd
    prop avnode timing

    node tf upd
    prop avnode timing

    node tm upd
    prop avnode timing

    node tv upd
    prop avnode timing

    node tq upd
    prop avnode timing
    
    node bdl out
    prop label "valve boost delay"

    foreach a $args {
      if {$a eq "aux"} {
        node axa upd -int
        node ax upd
        node axs upd
        node axd upd -int
      } elseif {$a eq "valve"} {
        for {set ch 1} {$ch <= 12} {incr ch} {
          node v$ch upd
          prop enum valve_off,valve_on,no_valve,timeout,timeout1,boost
          node vc$ch out -int
          prop enum valve_off,valve_on
        }
      } elseif {$a ne "multiHe"} {
        error "ERROR: unknown argument '$a' to ccu4"
      }
    }
  }
  return "CCU4"
} 

proc ccu4::completeStart {name} {
  set resval(cid) 0
  set resval(cir) ""
  set resval(csf) ""
  foreach res [split [sct result]] {
    lassign [split $res =] cmd val
    set resval($cmd) $val
  }
  sct result $resval(cid)
  if {$name eq "cc"} {
    hsetprop [sct parent] freelist ""
    set old [silent 0 result device ccu4version]
    if {$old ne $resval(csf)} {
      catch {device makeitem ccu4version $resval(csf)}
      error "WARNING: CCU4 version has changed from $old to $resval(csf), please make 'samenv reload'"
    }
    set rack [silent other result device rack]
    if {$rack ne "other" && $resval(cir) ne $rack && $resval(cir) ne "undef"} {
      # force config download (including rack name 'cir')
      hupdate [hgetpropval [sct parent] obj]/cvs 1
    }
  }
  return [stdSct::completeStart]
}

proc ccu4::errorScript {} {
  if {[sct result] eq "ASCERR: disconnected"} {
    hupdate [sct obj]/status disconnected
    return idle
  } else {
    hupdate [sct obj]/status [sct result]
    stdSct::errorScript
  }
}

proc ccu4::read {} {
  if {![silent 1 hvali [sct]]} {
    sct update 0
    return idle
  }
  sct update 1
  sct send "?"
  return ccu4::update 
}

proc ccu4::loadcodes {} {
  if {[sct codeidx] >= [sct codelen]} {
# set version and save
    sct send "cir=[silent {} result device rack] cvs=[sct codeversion] c=s"
    clientput "CCU4: updated cfgtable to version from [clock format [sct codeversion] -format {%Y-%m-%d %H:%M}]"
    if {[hvali [sct]/hea]} {
      set hv a
    } else {
      set hv no
    }
    clientput "CCU4: this CCU has $hv He vessel channel"
    return ccu4::loadcomplete
  }
# add item to device list
  sct send "c=[lindex [sct codelist] [sct codeidx]]"
  sct codeidx [expr [sct codeidx] + 1]
  return stdSct::complete
}

proc ccu4::loadcomplete {} {
  hupdate [sct]/cvs [sct codeversion]
  return unpoll
}

proc ccu4::update {} {
  variable deviceaction
  variable newdevice
  
  hupdate [sct]/status ""
  set fdone 0
  set hidecmd ""
  foreach item [sct result] {
    set item [split $item "="]
    
    set name [lindex $item 0]
    set value [lindex $item 1]
    hsetprop [sct]/$name done 1
    switch $name {
      f {
        set fdone 1
      }
      cds {
        if {$value >= 3 || $value == 0} { # device changed (local, by_code, by_touch)
          set deviceaction $value
        }
        set newdevice ""
      }
      cdv {
        if {$deviceaction >= 3} { # by_code, by_touch
          set newdevice $value
        }
      }
      cin {
        if {[info exists ::rack_in_use] && $value eq "free"} {
          clientput "disconnect requested from CCU touch display"
          sct send "cic="
          return "ccu4::completeDisconnect"
        }
      }
      csp {
        # check for not updated nodes
        foreach node [hlist [sct]] {
          set avnode [silent 0 hgetpropval [sct]/$node avnode]
          if {$avnode ne "0"} {
            set vis [hvali [sct]/$avnode]
            if {$vis} {
              set vistxt true
            } else {
              set vistxt false
            }
            if {[silent true hgetpropval [sct]/$node visible] ne $vistxt} {
              hsetprop [sct]/$node visible $vistxt
              if {! $vis} {
                logsetup [sct]/$node clear
              }
            }
            if {$vis && [silent 1 hgetpropval [sct]/$node done] == 0} {
              append hidecmd "$node "
            }
            hsetprop [sct]/$node done 0
          }
        }
      }
    }
    set visible [silent true hgetpropval [sct]/$name visible]
    if {[silent notFound hlist [sct]/$name] eq "notFound"} {
      if {[silent notFound sct notFound_$name] eq "notFound"} {
        clientput "ERROR: [sct]/$name not found"
        sct notFound_$name 1
      }
    } elseif {$visible eq "false"} {
      append hidecmd "$name:"
      logsetup [sct]/$name clear
    } else {
      set errvar [silent "" hgetpropval [sct]/$name errvar]
      if {$errvar ne ""} {
        set errval [hvali [sct]/$errvar]
        if {$errval == 1} {
          # sens warm
          updateval [sct]/$name -11
        } elseif {$errval == 3} {
          # timeout
          updateval [sct]/$name 111
        } elseif {$errval > 0} {
          hsetprop [sct]/$name geterror [lindex [split [hgetpropval [sct]/$errvar enum] ,] $errval]
          hupdate [sct]/$name $value
        } else {
          updateval [sct]/$name $value
        }
      } else {
        updateval [sct]/$name $value
      }
    }
  }
  # update flow anyway, as this triggers control
  if {!$fdone && [silent true hgetpropval [sct]/f visible] ne "false"} {
    hupdate [sct]/f [hvali [sct]/f]
  }
  hdelprop [sct] geterror
  set cvs [hvali [sct]/cvs]
  if {$cvs != 0 && [file exists [sct cfgtablefile]]} {
    sct codeversion [file mtime [sct cfgtablefile]]
    
    if {$cvs != [sct codeversion]} {
      clientput "CCU4: replace cfgtable version from [clock format $cvs -format {%Y-%m-%d %H:%M}]"
      hupdate [sct]/cvs 0
      set cfgtbl [split [read_file [sct cfgtablefile]] \n]
      set codelist [list ]
      foreach line $cfgtbl {
        if {[string index $line 0] ne "#" && [string length $line] > 1} {
          set lline [split $line "\t"]
          while {[llength $lline] < 6} {
            lappend lline {}
          }
          lappend codelist [join $lline ","]
        }
      }
      sct codelist $codelist
      sct codeidx 0
      sct codelen [llength [sct codelist]]
      sct send "cvs=0 c=c"
      [sct controller] poll [sct] 0.1 slow ccu4::loadcodes
      return stdSct::complete
    }
  }
  if {$deviceaction == 0} {
    # CCU4 was restarted
#    if {[info exists instrument]} {
#      set ins [string toupper [sct instrument]]
#      if {$instrument eq $ins} {
#        clientput "this rack is ours"
#      } elseif {$instrument eq ""} {
#        clientput "assume this rack is ours"
#      }
clientlog RENEW
      device makeitem action renew
      set deviceaction 1
      sct send cds=1
      return stdSct::complete
    }
  }
  if {$deviceaction >= 3} {
    if {$newdevice eq ""} {
      # get new device name
      sct send cdv
      return ccu4::update
    }
    if {[sctval [sct]/autodevice]} {
      set newdevice [string tolower $newdevice]
      if {![file exists ${newdevice}.config]} {
        set guess [glob ${newdevice}*.config]
        if {[llength $guess] == 1} {
          set newdevice [lindex [split $guess .] 0]
        }
      } 
      device makeitem newdevice $newdevice
      if {$deviceaction == 3} {
clientlog "PLUGGED $newdevice"
        device makeitem action "plugged"
      } else {
clientlog "SELECTED $newdevice"
        device makeitem action "selected on touch display"
      }
      # set device state to "loading"
      clientput loading
      sct send cds=2
    } elseif {$deviceaction == 3} {
      # clientput "plugged [string tolower $newdevice] but autodevice off"
      # set device state to "remote"
      sct send cds=1
    } else {
      # clientput "[string tolower $newdevice] selected on CCU4, but autodevice off"
      # set device state to "remote"
      sct send cds=1
    }
    set deviceaction 1
    return stdSct::complete
  }
  if {$hidecmd ne ""} {
    sct send [lrange $hidecmd 0 10]
    return stdSct::complete
  }
  # on seaman:
  set fi [string toupper [silent 0 free_instruments]]
  if {$fi ne "0"} {
    if {[silent 0 sct freelist] ne $fi} {
      sct freelist $fi
      sct send cil=$fi
      return stdSct::complete
    }
  }
  return idle
}

proc ccu4::getdevice {old} {
  variable deviceaction
  catch {
    set cdv [string toupper [hval /cc/cdv]]
    set old [string toupper $old]
    if {$cdv ne "NONE" && $cdv ne $old} {
      set deviceaction 3
    }
  }
}

proc ccu4::write {} {
  if {![hvali [sct parent]]} {
    sct update [sct target]
    return idle
  }
  set name [lindex [split [sct] "/"] 2]
#clientput "[expr [clock seconds] % 60] $name=[sct target]"
  sct send "$name=[sct target]"
  return ccu4::complete
}

proc ccu4::complete {} {
  set res [split [sct result] "="]
  sct update [lindex $res 1]
  return idle
}

proc ccu4::completeDisconnect args {
  dolater 0 rack_disconnect
  return idle
}
