# secop driver 4 (v1.0): modules/accesibles are JSON objects, datatype is  flat JSON object, renamed to datainfo

namespace eval secop {} {
}

proc stdConfig::secop {{shownUnits ALL} {restart no} {sepUnits ""}} {
  # shown units: restrict units shown
  # restart: whether to restart on reconnect
  # sepUnits: append sepUnits to unis for different graphic window
  variable node
  variable name

  set addon _MAIN_

  controller secop3 timeout=60
  prop commerror secop::errorscript_
  prop connection_lost 0
  prop check secop::check
  prop read secop::read
  prop write secop::write
  prop node_built 0
  prop restart_on_reconnect [expr {$restart ne "no"}]

  prop startcmd *IDN?
  prop end_fast 0
  prop secopPath /$name
  prop active 0
  prop shownUnits $shownUnits
  prop sepUnits $sepUnits

  set node $node/tasks
  prop start secop::start

  pollperiod 0.001 0.001
  obj SECoP -text wr

  prop read secop::readmsg_
  prop test secop::test
  prop check secop::checkmsg
  prop write secop::writemsg
  prop cmd ""

  variable ctrl
  variable path
  hsetprop /sics/$ctrl ignore_no_response 1

}

proc secop::errorscript_ {} {
  if {[string match {ASCERR: no response*} [sct result]]} {
    sct send ping
    return secop::update_
  }
  sct connection_lost 1
  [sct controller] poll [sct secopPath] 5
  error [sct result]
}

proc secop::checkmsg {} {
#  variable MQ[sct]
#  upvar 0 MQ[sct] mq
#  if {![info exists mq]} {
#    # create message queue
#    set mq [list]
#  }
#  if {[llength $mq] > 0} {
#    set next [lindex $mq 0]
#    set mq [lrange $mq 1 end]
#    lappend mq [sct target]
#    sct target $next
#  }

  return ""
}

proc secop::writemsg {} {
  sct send [sct target]
  return secop::update_
}

proc secop::readmsg_ {} {  # ending with _: invisible on debug
  if {[sct connection_lost]} {
    [sct controller] poll [sct] 0.001
    sct connection_lost 0
    return [secop::start]
  }
  sct send ""
  return secop::update_
}

proc secop::test {} {
  clientput test
  return idle
}

proc secop::check {} {
  if {[silent "" sct secopar] eq ""} return
  set value [sct target]
  set validator [silent "" sct validator]
  if {$validator ne ""} {
    lappend validator $value
    set value [eval $validator]
  }
  lassign [split [hinfo [sct]] ","] type
  set msg "change [sct secopar] $value"
  hsetprop [sct] secoprequested [sct target]
  [sct controller] que [sct secopPath] write [list secop::queuedwrite $msg]
}

proc secop::queuedwrite {msg} {
  sct changed [DoubleTime]
  # send message on /secop node
  sct sent_message $msg
  sct send $msg
  return secop::update_
}

proc secop::read {} {
  # this should be polled and check for errors on _secop
  set status [silent 0 sct status]
  if {$status eq "run" && _secop_in_error} {
     clientput "[sct] FAULT"
     sct status posfault
  }
  # this should not be called
  clientput "WHY? secop::read [sct] [sct controller]"
  return idle
}

proc secop::write {} {
  # this does nothing, as the check script only is doing the work
  # clientput "secop::write [sct] [sct controller]"
  return idle
}

proc secop::get {} {
  error "secop::get is obsolete"
  sct send ""
  return secop::update_
}

proc secop::check_double {datainfo value} {
  set min [silent -inf dict get $datainfo min]
  set max [silent  inf dict get $datainfo max]
  set absolute_resolution [silent 0 dict get $datainfo absolute_resolution]
  set relative_resolution [silent 1.2e-7 dict get $datainfo relative_resolution]
  set prec [expr max($absolute_resolution, $relative_resolution * abs($value))]
  if {$value < $min} {
    if {$value >= $min - $prec} {
      return $min
    }
  } elseif {$value > $max} {
    if {$value <= $max + $prec} {
      return $max
    }
  } else {
    return $value
  }
  error "[sct]: value must be within \[$min, $max\]"
}

proc secop::check_int {datainfo value} {
  return [check_double $datainfo $value]
}

proc secop::check_string {datainfo value} {
  set len [string length $value]
  set min [silent -inf dict get $datainfo minlen]
  set max [silent  inf dict get $datainfo maxlen]
  if {$len < $min || $len > $max} {
      error "[sct]: string length must be within \[$min, $max\]"
  }
  return [format {"%s"} $value]
}


proc secop::check_bool {datainfo value} {
  switch -- [string tolower $value] {
    off - false - no - 0 {
      return 0
    }
    on - true - yes - 1 {
      return 1
    }
  }
  error "[sct]: illegal value for boolean: $value"
}

proc secop::check_tuple {datainfo value} {
  set items [list]
  foreach v $value dt [dict get $datainfo members] {
    set type [dict get $dt type]
    lappend items [check_$type $dt $v]
  }
  return [format {[%s]} [join $items ,]]
}

proc secop::check_array {datainfo value} {
  set items [list]
  set dt [dict get $datainfo members]
  set type [dict get $dt type]
  foreach v $value  {
    lappend items [check_$type $dt $v]
  }
  return [format {[%s]} [join $items ,]]
}

proc secop::check_struct {datainfo value} {
  set items [list]
  foreach {k dt} [dict get $datainfo members]  {
    set type [dict get $dt type]
    set v [check_$type $dt [dict get $value $k]]
    lappend items [format {"%s": %s} $k $v]
  }
  return [format {{%s}} [join $items ", "]]
}

proc secop::make_par {secopar desc {kind ""}} {
  set path [topath $secopar [silent "" dict get $desc group]]
  array set props $desc
  # lassign [silent "" set props(datainfo)] secoptype0 datadesc
  set datadesc [silent "" set props(datainfo)]
  set secoptype0 [dict get $datadesc type]
  if {$secoptype0 eq "tuple" && [string match *:status $secopar]} {
    set members [dict get $datadesc members]
    # lassign $validator_args members
    set text_path [regsub {status_code$} $path status_text]
    make_par0 text $text_path $secopar $desc
    hsetprop $text_path width 24
    set datadesc [lindex $members 0]
    set secoptype [dict get $datadesc type]
    #lassign [lindex $members 0] secoptype datadesc
    set status_node 1
  } else {
    set secoptype $secoptype0
    set status_node 0
  }
  switch -- $secoptype {
    double {set type float}
    int - enum {set type int}
    string {
      set type text
      # can not use SICS drivable for string
      set kind ""
    }
    bool - tuple - array - struct {set type text}
    none {set type none}
    default {
      clientput "unknown type for $secopar (use text): $secoptype ($datadesc)"
      set type text
    }
  }
  make_par0 $type $path $secopar $desc $kind
  hsetprop $path secoptype $secoptype0
  if {$status_node} {
    hsetprop $path nonewline 1
  }
  lassign [split $path /] nul obj par
  set fmtunit ""
  if {[lsearch [list enum int double] $secoptype0] >= 0} {
    set fmtunit ""
    if {[catch {set unit [dict get $datadesc unit]}]} {
      set unit 1
    } else {
      set fmtunit [format { [%s]} $unit]
    }
    if {$par eq "" || $par eq "target"} {
      set label [join [split $secopar :] .]
      set prevlabel [silent "" result graph_label [string tolower $label]]
      if {$prevlabel ne ""} {
        set label $prevlabel
      }
      if {[sct shownUnits] eq "ALL" || [lsearch [sct shownUnits] $unit] >= 0} {
        set sepUnits [silent "" sct sepUnits]
        if {$sepUnits ne ""} {
          set unit "${unit}_$sepUnits"
        }
        GraphAdd $path $unit $label
      }
    }
  }
  if {[info exists props(description)]} {
    set addlines [lassign [split $props(description) "\n"] firstline]
    hsetprop $path help [join [linsert $addlines 0 "$firstline$fmtunit"] "\n"]
    unset props(description)
  }
  switch -- $secoptype {
    enum {
      set enumprop [list]
      set wid 8
      set sorted [list]
      set members [dict get $datadesc members]

      foreach {name value} $members {
         lappend sorted [list $value $name]
      }
      foreach value_name [lsort -integer -index 0 $sorted] {
        lassign $value_name value name
        lappend enumprop "$name=$value"
        set wid [expr max($wid,[string length $name])]
      }
      hsetprop $path enum [join $enumprop ,]
      if {[silent "" hgetpropval $path type] eq "drivable"} {
        hsetprop $path visible readonly
      }
      if {$wid > 8} {
        hsetprop $path width $wid
      }
      hsetprop $path label $par
    }
    bool {
      hsetprop $path enum 1
      hsetprop $path validator [list secop::check_bool $datadesc]
    }
    double {
      hsetprop $path validator [list secop::check_double $datadesc]
    }
    int {
      hsetprop $path validator [list secop::check_int $datadesc]
    }
    string - tuple - struct - array {
      set type [dict get $datadesc type]
      hsetprop $path width 36
      hsetprop $path validator [list secop::check_$type $datadesc]
    }
  }
  return $path
}

proc secop::make_par0 {type path secopar desc {kind std}} {
  array set props $desc
  # clientput "$path $desc"
  set readonly [silent 0 set props(readonly)]
  if {$readonly} {
    set priv internal
  } else {
    set priv user
  }
  if {[silent "" hinfo $path] ne ""} {
    error "$path exists already!"
  }
  lassign [split $path /] nul obj par
  if {$par eq ""} {
    if {$kind eq "driv"} {
      dynsctdriveobj $obj float user SECoP [sct controller]
      hfactory $path link $obj
      hsetprop $obj checklimits secop::checklimits
      # hsetprop $obj checkstatus secop::checkstatus
      hsetprop $obj halt secop::halt
      # allow start without run:
      hsetprop $obj check secop::checklimits
      hsetprop $obj write secop::complete_run
      set readonly 0
      hsetprop $obj sicscommand "run $obj"
    } else {
      # clientput "OBJ $obj $type"
      dynsicsobj $obj SECoP $priv $type
      hfactory $path link $obj
    }
    hsetprop $path group $obj
    hsetprop $path s_group $obj
    hsetprop $path objectPath $path
    hsetprop /sics/[sct controller] p_$secopar:value $path
  } else {
    if {$par eq "status"} {
      set path /$obj/status_code
    }
    # clientput "PAR $path $type [array get props]"
    hfactory $path plain $priv $type
    if {[info exists props(visibility)]} {
      if {$props(visibility) >= 3} {
        hsetprop $path visible false
      }
    }
  }
  hsetprop $path secopar $secopar
  if {$par eq ""} {
    hsetprop $path secop_id $secopar:value
  } else {
    hsetprop $path secop_id $secopar
  }
  hsetprop /sics/[sct controller] p_$secopar $path
  if {!$readonly} {
    [sct controller] write $path
  } else {
    [sct controller] connect $path
  }
  # logsetup $path 1
  logsetup $path 5
  if {[info exists props(value)]} {
    if {[catch {hupdate /$path $props(value)} msg]} {
      clientput $msg
    }
    unset props(value)
  }
  if {![catch {set fmtstr [dict get $props(datainfo) fmtstr]}]} {
    hsetprop $path fmtstr $fmtstr
  }
  foreach {prop item} [array get props] {
    hsetprop $path s_$prop $item
  }
}

proc secop::make_cmd {secopar desc {first 0}} {
  array set props $desc
  set path [topath $secopar [silent "" dict get $desc group]]
  set cmd [join [lassign [split $path /] _ obj] /]
  set datadesc $props(datainfo)
  set argument None
  catch {
    set argument [dict get $datadesc argument]
  }
  if {$argument eq "None"} {
    $obj makescriptfunc $cmd "secop::check_cmd [sct secopPath] $secopar" user
    hsetprop $path newline $first
    hsetprop $path secopar $secopar
    hsetprop $path sicscommand "$obj $cmd"
    if {[info exists props(visibility)]} {
      if {$props(visibility) >= 3} {
        hsetprop $path visible false
      }
    }
  } else {
    dict set desc datainfo $argument
    make_par $secopar $desc
    set maintype [dict get $argument type]
    #lassign $argument maintype datadesc
    if {$maintype eq "double" || $maintype eq "int" || $maintype eq "bool"} {
      hsetprop $path check "secop::check_cmd_num [sct secopPath] $secopar"
    } else {
      hsetprop $path check "secop::check_cmd_text [sct secopPath] $secopar"
    }
  }
}

proc secop::check_cmd {secopPath secopar} {
  hset $secopPath "do $secopar"
}

proc secop::check_cmd_num {secopPath secopar} {
  hset $secopPath [format {do %s %.15g} $secopar [sct target]]
  sct update [sct target]
}

proc secop::check_cmd_text {secopPath secopar} {
  hset $secopPath [format {do %s "%s"} $secopar [sct target]]
  sct update [sct target]
}

proc secop::make_module {obj desc} {
  clientput "MAKE_MODULE $obj"
  set secopObj [string range [sct secopPath] 1 end]
  set addon _MAIN_
  foreach a [addon_list items] {
    if {[addon_$a exists $secopObj]} {
      set addon $a
    }
  }
  if {$addon ne "_MAIN_"} {
    addon_$addon makeitem $obj
  }
  if {[obj_list exists $obj]} {
    clientput "$obj exists already"
    return
  }
  obj_list makeitem $obj /$obj

  array unset modprop
  set parlist [list]
  set pardict [dict create]
  foreach {key item} $desc {
    switch $key {
      accessibles {
#        foreach acsitm $item {
#          lassign $acsitm parname pardesc
#          dict set pardict $parname $pardesc
#        }
        foreach {parname pardesc} $item {
          dict set pardict $parname $pardesc
        }
      }
      default {
        set modprop($key) $item
      }
    }
  }
  if {[dict exists $pardict value]} {
    set value [dict get $pardict value]
    dict unset pardict value
  } else {
    set value [dict create datainfo {type none}]
  }
  set classes [silent "" set modprop(interface_classes)]
  if {$classes eq ""} {
    set classes [silent "" set modprop(interface_class)]
  }
  if {[string match "* Drivable *" " $classes "]} {
    set path [make_par $obj $value driv]
  } else {
    set path [make_par $obj $value]
  }
  if {[info exists modprop(visibility)] && $modprop(visibility) >= 3} {
    hdelprop $path group
  }
  foreach {prop val} [array get modprop] {
    if {[string match _* $prop]} {
      set prop [string range $prop 1 end]
    }
    hsetprop $obj sm_$prop $val
  }
  device_layout makeitem /$obj [silent 0 set modprop(layoutpos)]

  set groups [dict create]
  foreach {parname pardesc} $pardict {
    if {[dict exists $pardesc group]} {
      dict set groups [dict get $pardesc group] 1
    }
  }
  foreach g [dict keys $groups] {
    clientput "GROUP $g"
    hfactory $obj/$g plain user none
    hsetprop $obj/$g group "group $g"
  }
  set shortcmds [list]
  foreach {parname pardesc} $pardict {
    set datadesc [dict get $pardesc datainfo]
    set secoptype [dict get $datadesc type]
    #lassign $datainfo secoptype datadesc
    if {$secoptype eq "command"} {
      if {[catch {set argument [dict get $datadesc argument]}]} {
        set argument None
      }
      if {$argument ne "None"} {
        # only commands with arguments
        make_cmd $obj:$parname $pardesc 1
      } else {
        lappend shortcmds $parname $pardesc
      }
    } else {
      make_par $obj:$parname $pardesc
    }
  }
  # then commands without arguments, on one line
  set first 1
  foreach {parname pardesc} $shortcmds {
    make_cmd $obj:$parname $pardesc $first
    set first 0
  }
}

proc secop::make_node {desc} {
  array unset nodeprop
  set modlist [list]
  foreach {key item} $desc {
    switch $key {
      modules {
        set modlist $item
      }
      default {
        set nodeprop($key) $item
      }
    }
  }
  foreach {modname moddesc} $modlist {
    make_module $modname $moddesc
  }
  foreach {prop val} [array get nodeprop] {
    sct sn_$prop $val
  }
  sort_layout
}

proc secop::topath {secopar {pargroup ""}} {
  lassign [split [string tolower $secopar] :] module parameter
  if {$parameter eq "value" || $parameter eq ""} {
    return "/$module"
  }
  if {$parameter eq "status"} {
    set parameter status_code
  }
  if {[string match {_*} $parameter]} {
    set parameter [string range $parameter 1 end]
  }
  if {$pargroup ne ""} {
    return "/$module/$pargroup/$parameter"
  }
  return "/$module/$parameter"
}

proc secop::update_ {} {
  if {[silent "" sct result] eq ""} {
    return idle
  }
  set sent_message [silent "" sct sent_message]
  set message_to_client ""

  lassign "[sct result]" messagetype par val
  set path [silent "" hgetpropval /sics/[sct controller] p_$par]
  lassign [split $par :] obj param
  #if {![sct active] && $messagetype ne "update"} {
  #  clientput "MSG: $messagetype $par"
  #}
  #if {[string match "f*" $par] && $messagetype eq "changed"} {
  #  clientput "MSG: [sct result]"
  #}
  switch -glob $messagetype {
    update - changed {
      # clientput "*** [DoubleTime]: [sct result]"
      #if {[sct] ne "/secop"} {
      #  clientput "[sct] is not /secop, why?"
      #}
      if {$messagetype eq "changed"} {
        if {[string match *:target $par]} {
          hsetprop /$obj writestatus done
          hsetprop /$obj changetime 0
        }
        hsetprop $path changed 0
        catch {hdelprop $path secoprequested}
        if {[lrange $sent_message 0 1] eq [list change $par]} {
          set message_to_client [sct result]
        }
        # clientput "CH $path [sct result]"
      } else {
        if {[DoubleTime] < [silent 0 hgetpropval $path changed] + 10} {
          # ignore updates of variables during change
          clientput "ignore [sct result]"
          return idle
        }
        if {[lrange $sent_message 0 1] eq [list read $par]} {
          set message_to_client [sct result]
        }
      }
      lassign $val value qual
      #if {$par eq "drv:value"} {
      #  clientput "drv $value"
      #}
      if {[silent 0 hgetpropval $path secoptype] eq "tuple" &&
          [string match *:status $par]} {
        if {[llength $value] > 2} {
          set text_value [lrange $value 1 end]
        } else {
          set text_value [lindex $value 1]
        }
        set objpath [sct parent $path]
        lassign $value value
        if {$value != 0} {
          hsetprop $objpath group [hgetpropval $objpath s_group]
          set shown 1
        } else { # disabled
          catch {hdelprop $objpath group}
          set shown 0
        }
        set writestatus [silent done hgetpropval $objpath writestatus]
        set changetime [silent 0 hgetpropval $objpath changetime]
        if {$value < 100 || $value >= 400} { # error
          # updateerror $objpath $text_value
          catch {
            updateerror $objpath/target $text_value
          }
          if {[silent 0 hgetpropval $objpath status] eq "run"} {
            hsetprop $objpath status posfault
          }
        } else {
          # if {[silent idle hgetpropval $objpath status] eq "run" && $writestatus ne "done"
          #     && ($value < 300 || $value >= 390)} {
          #   clientput "$objpath/target changed, but status not BUSY: $value $text_value ($writestatus)"
          #   set value 300
          #   set text_value "target changed ($text_value)"
          # }
          if {$value < 300 || $value >= 390} { # not busy or finalizing
            hsetprop $objpath status idle
          }
          catch {
            updateval $objpath/target [hvali $objpath/target]
          }
        }
        if {$text_value ne [hvali $objpath/status_text] } {
          if {$value == 200} {
            clientput "WARNING: $objpath $text_value"
          }
        }
        GraphItem shown $objpath $shown
        if {[silent "" hgetpropval $objpath/target logger_name] ne ""} {
          if {[string match *Drivable* [silent "" hgetpropval $objpath sm_interface_classes]]
              && [silent "enum" hgetpropval $objpath/target secoptype] ne "enum"} {
            GraphItem shown $objpath/target $shown
          } else {
            GraphItem shown $objpath/target 0
          }
        }
        set text_path [regsub {status_code$} $path status_text]
        if {[catch {updateval $text_path $text_value}]} {
          clientput "cannot update $text_path to $text_value"
          clientput "MSG([sct result])"
        }
      }
      if {[catch {updateval $path $value} msg]} {
        if {$value eq "None"} {
          hsetprop $path geterror None
        } else {
          clientput "cannot update $path to $value"
          clientput $msg
        }
      } elseif {[string match *:target $par]} {
        if {[string match 1* [silent 0 hval /$obj/status_code]]} {
          hsetprop /$obj target $value
        }
      }
      catch {
        hsetprop $path timestamp [dict get $qual t]
      }
    }
    pong {
      if {[lindex $sent_message 0] eq "ping"} {
        set message_to_client [sct result]
      }
    }
    done {
      hsetprop /$obj done_$param 1
      if {[lrange $sent_message 0 2] eq [list do $par]} {
        set message_to_client [sct result]
      } else {
        clientput "done $par $val"
      }
    }
    active {
      if {[lindex $sent_message 0] eq "activate"} {
        set message_to_client [sct result]
      } else {
        clientput ACTIVE
      }
      sct active 1
      sct end_fast 0
    }
    describing {
      set olddesc describing_[string map {/ _} [sct]]
      variable $olddesc
      if {[sct node_built]} {
        set restart [sct restart_on_reconnect]
        if {$val ne [silent 0 set $olddesc]} {
          clientput "description changed"
          set restart 1
        }
        if {$restart} {
          secop::restart
          return idle
        }
        clientput "description unchanged"
      }
      do_as_manager {
        make_node $val
      }
      sct send activate
      set $olddesc $val
      sct node_built 1
      [sct controller] poll [sct] 0.001
      return secop::update_
    }
    error_update {
      lassign $val etype erepr
      set etext erepr
      regexp {.*'(.*)'} $erepr -> etext
      hsetprop $path geterror "${etype}: $etext"
    }
    error_* {
      clientlog $val
      lassign $val etype etext
      set requesttype [string range $messagetype 6 end]
      if {$requesttype eq "change" && $path ne ""} {
        hsetprop $path changed 0
      }
      lassign $sent_message sent_type sent_par
      if {$requesttype eq $sent_message && $par eq $sent_par} {
        set message_to_client [sct result]
      } else {
        clientput "ERROR: $path $etext"
      }
    }
    default {
      if {[string match "*,*" $messagetype]} {
        clientput IDN=[sct result]
        clientput "send describe"
        sct send describe
        sct active 0
        return secop::update_
      }
      if {$sent_message ne ""} {
        set message_to_client [sct result]
      } else {
        # show untreated message
        clientput [sct result]
      }
    }
  }
  if {$message_to_client ne ""} {
    # clientput "[sct]:\n> $sent_message\n< $message_to_client"
    sct sent_message ""
    sct sent_time 1e20
  } elseif {[DoubleTime] > [silent 1e20 sct sent_time] + 10} {
    clientput "timeout waiting for response to $sent_message"
    sct sent_message ""
    sct sent_time 1e20
  }
  #if {[DoubleTime] < [sct end_fast]} {
  #  return secop::get
  #}
  # [sct controller] queue [sct] read secop::get
  return idle
}

proc secop::checklimits {} {
  # for whatever strange reason checklimits is called twice
  # in addition again as write script of the obj node
  # do this only once
  set ws [silent 0 sct writestatus]
  if {$ws ne "checked" && $ws ne "start" ||
      [sct target] != [silent "x" hgetpropval [sct]/target target]} {
    hset [sct]/target [sct target]
    sct writestatus checked
    sct changetime [DoubleTime]
    sct status run
  }
}

proc secop::checkstatus {} {
  # obsolete
  set ws [silent 0 sct writestatus]
  set status [hvali [sct]/status_code]
  if {[string match 3* $status]} {
    set result run
  } elseif {[string match 4* $status]} {
    set result posfault
  } else {
    if {$ws ne "done"} {
      set result run
    } else {
      set result idle
    }
  }
  sct status $result
  return $result
}

proc secop::complete_run {} {
  sct print "run [sct objectName] to [sct target]"
  return idle
}

proc secop::halt {} {
  if {[silent 1 sct sm_general_stop]} {
    sct done_stop 0
    [sct objectName] stop
    [sct controller] poll [sct] 0.1 read secop::finish_halt
    sct stop_issued [DoubleTime]
  } else {
    sct writestatus done
    sct changetime 0
    sct status idle
  }
  return idle
}

proc secop::finish_halt {} {
  if {[DoubleTime] > [sct stop_issued] + 10} {
    clientput "[sct] stop timeout"
  } elseif {[sct done_stop] == 0} {
    return idle
  }
  sct writestatus done
  sct changetime 0
  sct status idle
  return unpoll
}

proc secop::start {} {
  if {[sct restart_on_reconnect] && [sct node_built]} {
    secop::restart
    return idle
  }
  sct send *IDN?
  clientput "RECONNECT, send *IDN?"
  return secop::update_
}

proc secop::restart {} {
  # device newdevice [samenv name]
  device makeitem action rebuild
  sct rebuild_addon 1
}

proc secop::describe {} {
  sct send describe
  return secop::describing
}

proc secop::describing {} {
  #obsolete?
  lassign [sct result] messagetype par val
  switch $messagetype {
    describing {
      do_as_manager {
        make_node $val
      }
    }
    default {
      clientput "ignore $messagetype $par ..."
    }
  }
  sct send activate
  sct end_fast [expr [DoubleTime] + 5]
  return secop::update_
}

proc secop_send {args} {
  hset /secop $args
  hsetprop /secop sent_message $args
  hsetprop /secop sent_time [DoubleTime]
}

publishLazy secop_send
