namespace eval bronkgen {} {
}

proc stdConfig::bronkgen {label scale {adr 128} {readonly 0} {ramp 0} {setoffset 0} {ctrlmode 0} {invalidvalue none}} {

# driver for Bronkhorst Flow or Pressure regulator  (i.e.P-602CV-21KA-AAD) 
# serial interface is set to:
# Char Size/Stop Bits:         8/1    Input Speed:                38400
# Flow Ctrl:                  None    Output Speed:               38400
# Parity:                     None    Modem Control:               None

# syntax (chaining not mentioned):
# read command:  :06Ad04CopyPrTp
# write command: :LnAd01PrTpData
# where:
#   Ln: number of bytes (hex digits pairs) following
#   Ad: node address (starting from 3)
#   Copy: values just to be copied by the reply (first and third digit < 8)
#         recommended practice: Use PrTp for Copy
#   Pr: Process number (<80)
#   Tp: Type + parameter number. Type: 00 byte, 20 int, 40 long/float, 60 string
#       for strings either 00 (for nul terminated) or the max. number of chars
#       has to be appended to the type
#   Data: length depending on type.

# the interface returns readings on a scale where 32000 is 100%

  set adr [format %02x $adr]

  controller std "\r\n"
  prop read bronkgen::read
  prop write bronkgen::write
  prop update bronkgen::update
  prop startcmd :07${adr}047163716300
  prop convert_idn bronkgen::result2string 

  clientput "BRONK $label $scale $adr"

  obj BRONKGEN rd
  prop label $label
  prop par "1 0 $scale"
  prop @adr $adr
  if {$invalidvalue ne "none"} {
    prop invalidvalue $invalidvalue
  }

  kids "$label" {
    if {$readonly == 0} {
      node set wr
      prop label setpoint
      prop par "1 1 $scale"
      prop setoffset $setoffset

      if {$ramp != 0} {
        prop write bronkgen::write_with_ramp
        prop update bronkgen::update_set

        node reg upd

        node ramptime rd
        prop par "1 2 i 0.1"

        node ramp par $ramp

        node rampstd par $ramp
      }

      if {$ctrlmode} {
        node ctrlmode wr
        prop label "control mode"
        prop enum manual=4,loop=11
        prop par "1 4 b"

        node output wr
        prop par "114 1 l 0.002"
      }
    }
      
    node fluid rd -text
    prop par "1 17 s"

    node par out -text
    default "113 6 s"
    prop write bronkgen::setpar /x

    node x wr -text
    prop par "113 6 s"
    prop width 32
  }
  
}

proc bronkgen::result2string {} {
  set string ""
  foreach {h l} [split [string range [sct result] 13 end-2] ""] {
    append string [format %c [scan $h$l %2x]]
  }
  return $string
}

proc bronkgen::update {{as_function 0}} {
  lassign [sct par] process par type scale
  if {$scale eq ""} {
    set scale 1
  }
  set value 0
  switch -- $type {
    b {
      set res [scan [string range [sct result] 11 12] %2x value]
    }
    i {
      set res [scan [string range [sct result] 11 14] %4x value]
      set value [expr $value * $scale]
    }
    l {
      set res [scan [string range [sct result] 11 18] %8x value]
      set value [expr $value * $scale]
    }
    s {
      set value ""
      foreach {h l} [split [string range [sct result] 13 end-2] ""] {
        append value [format %c [scan $h$l %2x]]
      }
      set res 1
    }
    default {
      set res [scan [string range [sct result] 11 14] %4x value]
      set value [format %.4g [expr $value * $type / 32000.]]
    }
  }
  if {$res != 1} {
    error "bad result format: '[sct result]'"
  } elseif {$as_function} {
    return $value
  }
  if {$value != [silent -0.123e-45 sct invalidvalue]} {
    sct update $value
  }
  return idle
}


proc bronkgen::read {} {
  lassign [sct par] process par type
  switch -- $type {
    b {
      set arg [format %2.2X%2.2X $process $par]
      set arg $arg$arg
    }
    s {
      set arg [format %2.2X%2.2X $process [expr $par + 0x60]]
      set arg $arg${arg}00
    }   
    l {
      set arg [format %2.2X%2.2X $process [expr $par + 0x40]]
      set arg $arg${arg}00
    }   
    default { # i and scaled
      set arg [format %2.2X%2.2X $process [expr $par + 0x20]]
      set arg $arg$arg
    }
  }
  set len [format %2.2X [expr 2 + [string length $arg] / 2]]
  sct send :$len[sct @adr]04$arg
  return update
}

proc bronkgen::write {{logtext ""}} {
  # clientput "write [sct] [sct target] $logtext"
  lassign [sct par] process par type scale
  if {$scale eq ""} {
    set scale 1
  }
  switch -- $type {
    b {
      set arg [format %2.2X%2.2X%2.2X $process $par [sct target]]
    }   
    s {
      set arg [format %2.2X%2.2X $process [expr $par + 0x60]]
      set arg $arg00
      foreach char [split [sct target] ""] {
        lappend arg [format %2.2X [scan $char %c]]
      }
      lappend "00"
    }   
    i {
      set arg [format %2.2X%2.2X%4.4X $process [expr $par + 0x20] [expr round([sct target]/$scale)]]
    }
    l {
      set arg [format %2.2X%2.2X%8.8X $process [expr $par + 0x40] [expr round([sct target]/$scale)]]
    }
    default {
      set val [expr round(([sct target] + [silent 0 sct setoffset]) * 32000. / $type)]
      if {$val > 32000} {
        if {$val < 32320} {
          set val 32000
        } else {
          error "[sct]: [sct target] must be <= $type"
        }
      } elseif {$val < 0} {
        error "[sct]: [sct target] must be >= 0"
      }
      set arg [format %2.2X%2.2X%4.4X $process [expr $par + 0x20] $val]
    }
  }
  set len [format %2.2X [expr 2 + [string length $arg] / 2]]
  sct send :$len[sct @adr]01$arg
  return bronkgen::acknowledge
}

proc bronkgen::acknowledge {} {
  if {[string range [sct result] 7 8] ne "00"} {
    error "[sct]: bad status '[sct result]'"
  }
  return read
}

proc bronkgen::write_with_ramp {} {
  lassign [sct par] process par scale
  set rpath [sct objectPath]/ramp
  set ramp [hval $rpath]
  set ramptime [format %.1f [expr min(3200.0, max(10., $scale * 60.0 / max($ramp, 0.1)))]]
  if {$ramptime != [silent -1 hval ${rpath}time]} {
    sct send :06[sct @adr]010122[format %4.4X [expr round(10*$ramptime)]]
    hupdate $rpath [hval ${rpath}std]
    return "bronkgen::write {with ramp $ramp}"
  }
  hupdate $rpath [hval ${rpath}std]
  return [bronkgen::write]
}

proc bronkgen::setpar {relpath} {
  hsetprop [sct objectPath]$relpath par [sct target]
  sct update [sct target]
  [sct controller] queue [sct objectPath]$relpath read read
  return stdSct::complete
}

proc bronkgen::update_set {} {
  set req [silent none sct requested]
  if {$req ne "none"} {
    sct update $req
    return idle
  }
  set value [expr max(0,[bronkgen::update 1] - [sct setoffset])]
  if {$value == [silent -9999 sct old]} {
    sct update $value
  }
  sct old $value
  updateval [sct parent]/reg $value
  return idle
}

