namespace eval sim921 {
}

# for curve reading
if {![namespace exists lsc]} {
  source drivers/lsc.tcl
}

proc sim921::sendto {chan cmd} {
  sct send "_\nconn $chan,\"_\n\"\n$cmd"
}

proc stdConfig::sim921 {args} {
  variable node

  controller std lineseparator=|

  pollperiod 1 1

  obj SIM921 -none upd
  kids "AC resistance bridge SIM921" {
    foreach {chan nam} $args {
      node $nam rd
      prop read "sim921::poll $chan"
      prop badcnt 0
      prop rngcnt 0
      prop snschk 0
      prop modcnt 0
      prop lastok 0
      prop last_range_change 0
      prop cnvcnt 0

      kids "$nam" {
        node r upd

        node auto -int par 1
        prop enum 1

        node excitation -int out
        prop write "sim921::wrexci $chan"
        prop width 2

        node range -int out
        prop write "sim921::wrrange $chan"
        prop width 2

        node rangemin -int par 3
        prop width 2

        node rangemax -int par 8
        prop width 2

        node filter out
        prop write "sim921::wrtcon $chan"

        node status -text upd
        prop width 32

        node current upd

        node voltage upd

        node mode -int out
        prop label "excit.mode"
        prop enum "passive,current,voltage,power"
        prop write "sim921::wrmode $chan"

        node rraw upd

        node sensorcheck -int par 0
        prop enum 1
        prop label "sensor check"

        node curve out -text
        prop chan $chan
        prop model 0
        prop width 32
        prop check lsc::read_curve
        prop write sim921::set_curve

        hfactory $node/points plain mugger floatvarar 1

        node reset out -int
        prop enum push
        prop newline 1
        prop label reset
        prop write "sim921::reset $chan"
      }
    }
  }
}

proc sim921::rohm {range} {
  if {$range < 0} {
    set range 0
  } elseif {$range > 9} {
    set range 9
  }
  return [lindex {20mOhm 200mOhm 2Ohm 20Ohm 200Ohm 2kOhm 20kOhm 200kOhm 2MOhm 20Mohm} $range]
}

proc sim921::rexci {exci} {
  if {$exci < -1} {
    set exci -1
  } elseif {$exci > 8} {
    set exci 9
  }
  incr exci
  return [lindex {0 3uV 10uV 30uV 100uV 300uV 1mV 3mV 10mV 30mV} $exci]
}

proc sim921::poll {chan} {
  sendto $chan "tval?;rval?;rang?;exci?;ovcr?;iexc?;vexc?;tcon?;agai?;mode?{10}"
  return sim921::upd
}

proc sim921::setstatus {msg} {
#  if {$msg ne "[hvali [sct]/status]"} {
#    if {$msg eq ""} {
#      clientput ok
#    }
#    clientput $msg
#  }
  hupdate [sct]/status $msg
}

proc sim921::upd {} {
  if {[scan [sct result] "%f|%f|%d|%d|%d|%f|%f|%d|%d|%d" t r rng exci status iex vex tcon agai mode] != 10} {
    error "bad result: [sct result]"
  }
  set now [DoubleTime]
  set rref [expr pow(10, $rng-2)]
  if {$exci % 2 == 1} {
    set exref [expr pow(10, ($exci - 11) / 2)]
  } else {
    set exref [expr 3 * pow(10, ($exci - 12) / 2)]
  }
  set oldrng $rng
  set bad 0
  set msg ""
  updateval [sct]/current $iex
  updateval [sct]/voltage $vex
  updateval [sct]/mode $mode 
  if {$agai == 1} {
    hupdate [sct]/status "adjust gain"
    return idle
  }
  if {1 & $status} {
    set msg "overload $status"
    set bad 2
  } elseif {2 & $status} {
    set msg "preamp-overload $status"
    set bad 2
  } elseif {4 & $status} {
    set msg "current-overload $status"
    set bad 2
  } elseif {$mode == 2 && (24 & $status)} {
#    set msg "adjusting excit."
#    set bad 1
  } elseif {$mode == 2 && abs(log10(abs($vex) / $exref)) > 0.08} {
    # 20 % error of excit, not detected by bridge
#    set msg "adjusting excit"
    set bad 1
  } elseif {32 & $status} {
# this is an R underflow, but we normally have a negative T coefficient
    set msg "T-overflow"
  } elseif {64 & $status} {
    set msg "T-underflow"
  }
  if {$mode == 0} {
    set rraw [expr $exref * 18.2 / $iex - 22 * $rref]
  } else {
    set rraw 0
  }
  updateval [sct]/rraw $rraw
  set badcnt [sct badcnt]
  set rngcnt [sct rngcnt]
  set cnvcnt [sct cnvcnt]
  set rmin [hvali [sct]/rangemin]
  set rmax [hvali [sct]/rangemax]
  set vref [expr $iex * $rref]
  set auto [hvali [sct]/auto]
  set sensorcheck [hvali [sct]/sensorcheck]
  if {$mode == 0 && $bad == 0} {
    set snschk [sct snschk]
    if {$rmax < 8} {
      set rlim [expr 20 * pow(10, $rmax -2)]
    } else {
      set rlim 2.0e7
    }
    if {$rraw > 20 * $rref || $rraw > $rlim} {
      # no sensor ?
      set bad 1
      set msg "no sensor"
      setstatus $msg
      if {$snschk < -3} {
        set rm [expr $rmax - 1]
        if {$rng != $rm && $rraw < $rlim} {
          set rm [expr $rmax - 1]
          hset [sct]/range $rm
          clientput "[sct] max range $rm (passive)"
          sct snschk 3
        } else {
          # error message immediately
          updateerror [sct]/r $msg 1
          updateerror [sct] $msg 1
        }
        return idle
      }
      incr snschk -1
    } elseif {$auto} {
      setstatus "sensor check"
      if {$snschk > 10} {
        set cnt 0
        while {$r > $rref * 5 && $cnt < 3} {
          incr rng
          incr cnt
          set rref [expr 10 * $rref]
        }
        while {$r < $rref * 0.2 && $cnt < 3} {
          incr rng -1
          incr cnt
          set rref [expr 0.1 * $rref]
        }
        if {$rng > $rmax} {
          set rng $rmax
        } elseif {$rng < $rmin} {
          set rng $rmin
        }
        if {$rng != $oldrng} {
          clientput "[sct] new range $rng voltage excit."
        } else {
          clientput "[sct] range $rng voltage excit."
        }
        hset [sct]/range $rng
        hset [sct]/mode 2
        sct snschk 0
        return idle
      } else {
        incr snschk
      }
    }
    sct snschk $snschk
  }
  set cnvlim 4
  if {$bad <= 1} {
    if {$cnvcnt < $cnvlim || ($cnvcnt <= $cnvlim && $bad == 0)} {
      incr cnvcnt
      sct cnvcnt $cnvcnt
    }
  }
  if {$cnvcnt > $cnvlim} {
    updateval [sct]/r $r
    if {[string match T-* $msg]} {
      updateerror [sct] $msg 1
    } else {
      sct update $t
    }
    sct lastok $now
  } elseif {$msg eq ""} {
    set msg "stabilize $cnvcnt"
  }
  setstatus $msg
  if {$now > [sct lastok] + 60} {
    updateerror [sct]/r $msg 1
    updateerror [sct] $msg 1
  }
  if {$mode == 2} {
    if {$sensorcheck} {
      set modcnt [sct modcnt]
      if {$bad > 0} {
        if {$modcnt > 20} {
          sct modcnt 0
          sct rngcnt 0
          clientput "[sct] passive excit."
          hset [sct]/mode 0
          return idle
        }
        incr modcnt
      } elseif {$modcnt > 15} {
        incr modcnt -1
      }
      sct modcnt $modcnt
    }
    if {abs($vex) > $vref * 5} {
      incr rngcnt
    } elseif {abs($vex) < $vref * 0.2} {
      incr rngcnt -1
    }
  }
  if {$bad > 1} {
    set rngcnt 0
    if {$badcnt > 5} {
      set bad 3
    }
    incr badcnt
  } elseif {$badcnt > -5} {
    incr badcnt -1
  }
#if {[sct] eq "/r/tb" && abs(log10(abs($vex) / $exref)) > 0.04} {
#clientput "[sct] [expr $vref / $exref] [expr $vex / $exref] bad $badcnt rngcnt $rngcnt $r $status"
#}
  sct badcnt $badcnt
  if {$auto && $bad == 0 && $mode == 2} {
    if {$rngcnt > 5} {
      if {$rng < $rmax} {
        incr rng
      }
      set rngcnt 0
    } elseif {$rngcnt < -5} {
      if {$rng > $rmin} {
        incr rng -1
      }
      set rngcnt 0
    }
  }
  sct rngcnt $rngcnt
  updateval [sct]/filter [format %.1g [expr 0.32 * pow(10, $tcon * 0.5)]]
  if {$exci != [hvali [sct]/excitation]} {
    hsetprop [sct]/excitation label "excit. ([rexci $exci])"
    updateval [sct]/excitation $exci
    sct rngcnt 0
    sct badcnt 0
    sct cnvcnt 0
  }
  if {$bad == 3 && ($mode == 0 || $sensorcheck == 0 || $now < [sct last_range_change] + 20)} {
    clientput "adjust gain on [sct] (rng=[rohm $oldrng] exci=[rexci $exci])"
    sct badcnt 0
    sct cnvcnt 0
    sct send "agai 1;agai?"
    return stdSct::complete
  }
  if {$rng < $rmin} {
    set rng $rmin
  } elseif {$rng > $rmax} {
    set rng $rmax
  }
  if {$rng != $oldrng} {
    clientput "[sct] new range $rng"
    hset [sct]/range $rng
  } elseif {$rng != [hvali [sct]/range]} {
    clientput "[sct] range changed to $rng"
    hsetprop [sct]/range label "range ([rohm $rng])"
    updateval [sct]/range $rng
    sct rngcnt 0
    sct badcnt 0
    sct modcnt 0
    sct cnvcnt 0
  }
  return idle
}

proc sim921::wrrange {chan} {
  sendto $chan "rang [sct target];rang?"
  sct update [sct target]
  hsetprop [sct parent] rngcnt 0
  hsetprop [sct parent] badcnt 0
  hsetprop [sct parent] modcnt 0
  hsetprop [sct parent] cnvcnt 0
  sct label "range ([rohm [sct target]])"
  return stdSct::complete
}

proc sim921::wrexci {chan} {
  sendto $chan "exci [sct target];exon 1;exci?"
  sct update [sct target]
  hsetprop [sct parent] rngcnt 0
  hsetprop [sct parent] badcnt 0
  hsetprop [sct parent] cnvcnt 0
  sct label "excit. ([rexci [sct target]])"
  return stdSct::complete
}

proc sim921::wrmode {chan} {
  sendto $chan "mode [sct target];mode?"
  sct update [sct target]
  hsetprop [sct parent] last_range_change [DoubleTime]
  return stdSct::complete
}

proc sim921::wrtcon {chan} {
  set filt [sct target]
  if {$filt < 0.1} {
    set filt 0.1
  }
  set tcon [expr round(log10($filt) * 2 + 1)]
  sct update [format %.1g [expr 0.32 * pow(10, $tcon * 0.5)]]  
  sendto $chan "tcon $tcon;tcon?"
  return stdSct::complete
}

proc sim921::set_curve {} {
  sendto [sct chan] "cini?1;cini?2;cini?3{3}"
  return sim921::set_curve2
}

proc sim921::set_curve2 {} {
# download always log log format 
  sct logT 1
  sct logR 1
  set z 3

  set points [hvali [sct]/points]
  set crc [lrange [lsc::crc_code $points] 0 15]
  set nam [string toupper [sct target]]
  set short $nam
  if {[string length $short] > 5} {
    if {[string index $short 0] eq "R"} {
      set short [string range $short 1 end]
    }
    if {[string index $short 0] eq "X"} {
      set short [string range $short 1 end]
    }
  }
  set n [expr [llength $points] / 2]
  set ini "$z,$short/$crc,$n"
  if {[sicsdescriptor sim921] eq "notfound"} {
    makeobject sim921 array
  }
  set id [sct parent]
  if {[sim921 exists ${id}_lastno] == 0} {
    sim921 makeitem ${id}_lastno 0
    sim921 makeitem ${id}_lastno2 0
  }
  set lastno [result sim921 ${id}_lastno]
  set lastno2 [result sim921 ${id}_lastno2]
  set no 1
  set emptyno 0
  set changedno 0
  foreach iniold [split [sct result] |] {
    sct curve$no $iniold
    if {$iniold eq $ini} {
      sct print "[sct parent]: activate curve $nam (already loaded as no $no)"
      sct update [sct target]
      if {$no != $lastno} {
        sim921 ${id}_lastno2 $lastno
        sim921 ${id}_lastno $no
      }
      sendto [sct chan] "curv $no;*OPC?"
      return stdSct::complete
    }
    if {[string match "*,$short/*" $iniold]} {
      set changedno $no
    } elseif {[lindex [split $iniold ","] 2] == 0} {
      set emptyno $no
    }
    incr no
  }
  if {$changedno != 0} {
    set no $changedno
    sct print "[sct parent]: curve $nam has changed - start loading"
  } elseif {$emptyno > 0} {
    set no $emptyno
    sct print "[sct parent]: start loading curve $nam to no $no"
  } else {
    set no $lastno
    incr no
    if {$no > 3} {
      set no 1
    }
    if {$no == $lastno2} {
      incr no
      if {$no > 3} {
        set no 1
      }
    }
    sct print "[sct parent]: start loading curve $nam overwriting no $no"
  }
  if {$no != $lastno} {
    sim921 ${id}_lastno2 $lastno
    sim921 ${id}_lastno $no
  }
  sct no $no
  sendto [sct chan] "cini $no,$z,$short/$crc;*OPC?" 
  return "sim921::load_pt 0"
}

proc sim921::load_pt {i} {
  set points [hvali [sct]/points]
  if {$i >= [llength $points]} {
    sct print "[sct parent]: loading finished"
    sct update [sct target]
    sendto [sct chan] "curv [sct no];*OPC?"
    return stdSct::complete
  }
  set r [lindex $points $i]
  if {[sct logR]} {
    set r [expr log10($r)]
  }
  incr i
  set t [lindex $points $i]
  if {[sct logR]} {
    set t [expr log10($t)]
  }
  incr i
  sendto [sct chan] "capt [sct no],[format %.7g $r],[format %.7g $t]\n*OPC?"
  return "sim921::load_pt $i"
}

proc sim921::reset {chan} {
  sendto $chan "*RST;*OPC?"
  return sim921::resetupdate
}

proc sim921::resetupdate {} {
  sct udpate 0
  return idle
}

