# rs485, 9600 baud / 8 bit / no parity / 1 stop
# modbus protocol, float order: words swapped (byte 0 is at pos. 2)

namespace eval jumo {} {
}

proc stdConfig::jumo {adr upperlimit} {
  # timeout 5, try again 2
  controller bin modbus-crc 5 2
  prop read jumo::readfloat
  prop write jumo::writefloat
  prop startcmd "int1 4 3 int2 0 6 crc / skip code skip str12 crc"

  obj JUMO rd
  prop @adr $adr
  prop adr 0x002a

  kids "Jumo $adr" {

    node set wr
    prop adr 0x3100
    prop upperlimit $upperlimit

    node reg rd
    prop adr 0x0035

    node htr rd
    prop scale 2
    prop adr 0x0039

#    node cool rd
#    prop adr 0x003b

    node pb wr
    prop label "prop. band"
    prop adr 0x3000

    node rt wr
    prop label "int t"
    prop adr 0x3006

    node td wr
    prop label "deriv t"
    prop adr 0x3004

    #node y0 wr -int
    #prop read jumo::readint
    #prop write jumo::writeint
    #prop adr 0x3017

    node ramp wr
    prop adr 0x004e

    #node state rd -int
    #prop read jumo::readint
    #prop adr 0x0020

    #node limitstate rd
    #prop read jumo::readint
    #prop adr 0x0024

    node manual wr -int
    prop write jumo::writemanual
    prop read jumo::readmanual
    prop update jumo::updatemanual
    prop enum 1

    node manual_output wr -int
    prop read jumo::readint
    prop write jumo::writeint
    prop scale 2
    prop adr 0x0041

    node dump out -text
    prop write jumo::dump
  }
}

proc jumo::readfloat {} {
  set cmd [format {int1 %d 3 int2 %d 2 crc} [sct @adr] [sct adr]]
  sct send "$cmd / skip code skip float2 crc"
  return jumo::update
}

proc jumo::readint {} {
  set cmd [format {int1 %d 3 int2 %d 1 crc} [sct @adr] [sct adr]]
  sct send "$cmd / skip code skip int2 crc"
  return jumo::update
}

proc jumo::update {} {
  sct update [expr [sct result] * [silent 1 sct scale]]
  return idle
}

proc jumo::writefloat {} {
  if {[sct target] > [silent [sct target] sct upperlimit]} {
    error "[sct] [sct target] is above upper limit [sct upperlimit]"
  }
  set tg [expr [sct target] / double([silent 1 sct scale])]
  set cmd [format {int1 %d 16 int2 %d 2 int1 4 float2 %g crc} [sct @adr] [sct adr] $tg]
  sct send "$cmd / skip code skip4 crc"
  return jumo::readfloat
}

proc jumo::writeint {} {
  set tg [expr [sct target] / [silent 1 sct scale]]
  set cmd [format {int1 %d 16 int2 %d 1 int1 2 int2 %d crc} [sct @adr] [sct adr] $tg]
  sct send "$cmd / skip code skip4 crc"
  return jumo::readint
}

proc jumo::writemanual {} {
  if {[sct target]} {
    set bitmap 4
  } else {
    set bitmap 8
  }
  set cmd [format {int1 %d 16 int2 %d 1 int1 2 int2 %d crc} [sct @adr] 0x0047 $bitmap]
  sct send "$cmd / skip code skip4 crc"
  return stdSct::completeUpdate
}

proc jumo::readmanual {} {
  set cmd [format {int1 %d 3 int2 %d 1 crc} [sct @adr] 0x0020]
  sct send "$cmd / skip code skip int2 crc"
  return jumo::updatemanual
}

proc jumo::updatemanual {} {
  sct update [expr ([sct result] >> 12) & 1]
  return idle
}

proc jumo::result2string {} {
  set string ""
  foreach ch [sct result] {
    if {$ch == 0} {
      break
    }
    append string [format %c $ch]
  }
  return $string
}


proc jumo::dump {{adr 0} {wadr -1}} {
  global jumo_fp
  if {$wadr >= 0} {
    if {$wadr == 0} {
      set jumo_fp [open jumo_[sct target]_[sct @adr].dump w]
      sct update [sct target]
    }
    set code [lindex [sct result] 0]
    if {$code > 128} {
      clientput "$wadr: error [expr $code - 128]"
    } else {
      for {set i 0} {$i < 16} {incr i} {
        puts $jumo_fp "[format %4.4x [expr $wadr + $i]]: [lrange [sct result] [expr $i * 2 + 1] [expr $i * 2 + 2]]"
      }
    }
  }
  if {$wadr >= 0x3000} {
    close $jumo_fp
    return idle
  }
  set cmd [format {int1 %d 3 int2 %d 16 crc / skip int1 skip dump} [sct @adr] $adr]
  clientput $cmd
  sct send $cmd
  set nextadr [expr $adr + 16]
  if {$nextadr == 0x60} {
    set nextadr 0x3000
  }
  return "jumo::dump $nextadr $adr"
}
