Lakeshore 336 drivers with known IP addresses have been added to the ini files with unique IDs. All entries in the ini files now have unique IDs The wombat ini now has radio buttons to select sample stage motor configurations.
806 lines
31 KiB
Tcl
806 lines
31 KiB
Tcl
# Define procs in ::scobj::xxx namespace
|
|
# MakeSICSObj $obj SCT_<class>
|
|
# The MakeSICSObj cmd adds a /sics/$obj node. NOTE the /sics node is not browsable.
|
|
|
|
|
|
namespace eval ::scobj::watlow_st {
|
|
# Temperature controllers must have at least the following nodes
|
|
# /tempcont/setpoint
|
|
# /tempcont/sensor/value
|
|
proc debug_log {args} {
|
|
set fd [open "/tmp/watlow_st.log" a]
|
|
puts $fd "[clock format [clock seconds] -format "%T"] $args"
|
|
close $fd
|
|
}
|
|
|
|
proc f_to_c { f_temp } {
|
|
return [expr ($f_temp - 32.0) * (5.0 / 9.0)]
|
|
}
|
|
|
|
proc c_to_f { c_temp } {
|
|
return [expr $c_temp * (9.0 / 5.0) + 32.0]
|
|
}
|
|
|
|
# issue a command to read a register and expect a value response
|
|
proc getValue {tc_root nextState cmd} {
|
|
set dev "[hval $tc_root/dev_id]"
|
|
sct send "$dev:3:$cmd"
|
|
return $nextState
|
|
}
|
|
|
|
# issue a command with a value in the target property of the variable
|
|
proc setValue {tc_root nextState cmd} {
|
|
set dev "[hval $tc_root/dev_id]"
|
|
set par [sct target]
|
|
sct send "$dev:16:$cmd $par"
|
|
debug_log "setValue $dev:16:$cmd $par"
|
|
return $nextState
|
|
}
|
|
|
|
proc rdValue {tc_root} {
|
|
set data [sct result]
|
|
switch -glob -- $data {
|
|
"ASCERR:*" {
|
|
sct geterror $data
|
|
}
|
|
default {
|
|
if { [hpropexists [sct] geterror] } {
|
|
hdelprop [sct] geterror
|
|
}
|
|
if {$data != [sct oldval]} {
|
|
sct oldval $data
|
|
sct update $data
|
|
sct utime readtime
|
|
}
|
|
}
|
|
}
|
|
return idle
|
|
}
|
|
|
|
# write a floating point value
|
|
proc setFloat {tc_root nextState cmd} {
|
|
set dev "[hval $tc_root/dev_id]"
|
|
set par [sct target]
|
|
sct send "$dev:1016:$cmd $par"
|
|
debug_log "setFloat $dev:1016:$cmd $par"
|
|
return $nextState
|
|
}
|
|
|
|
# request a floating point value
|
|
proc getFloat {tc_root nextState cmd} {
|
|
set dev "[hval $tc_root/dev_id]"
|
|
sct send "$dev:1003:$cmd"
|
|
return $nextState
|
|
}
|
|
|
|
# read a floating point value
|
|
proc rdFloat {tc_root} {
|
|
set data [sct result]
|
|
switch -glob -- $data {
|
|
"ASCERR:*" {
|
|
sct geterror $data
|
|
}
|
|
default {
|
|
if { [hpropexists [sct] geterror] } {
|
|
hdelprop [sct] geterror
|
|
}
|
|
if {$data != [sct oldval]} {
|
|
sct oldval $data
|
|
sct update $data
|
|
sct utime readtime
|
|
}
|
|
}
|
|
}
|
|
return idle
|
|
}
|
|
|
|
# write a floating point value as a temperature
|
|
proc setTemp {tc_root nextState cmd} {
|
|
set dev "[hval $tc_root/dev_id]"
|
|
set par [sct target]
|
|
set par [c_to_f $par]
|
|
sct send "$dev:1016:$cmd $par"
|
|
debug_log "setTemp $dev:1016:$cmd $par"
|
|
return $nextState
|
|
}
|
|
|
|
# request a floating point value as a temperature
|
|
proc getTemp {tc_root nextState cmd} {
|
|
set dev "[hval $tc_root/dev_id]"
|
|
sct send "$dev:1003:$cmd"
|
|
return $nextState
|
|
}
|
|
|
|
# read a floating point value as a temperature
|
|
proc rdTemp {tc_root} {
|
|
set data [sct result]
|
|
set data [f_to_c $data]
|
|
switch -glob -- $data {
|
|
"ASCERR:*" {
|
|
sct geterror $data
|
|
}
|
|
default {
|
|
if { [hpropexists [sct] geterror] } {
|
|
hdelprop [sct] geterror
|
|
}
|
|
if {$data != [sct oldval]} {
|
|
sct oldval $data
|
|
sct update $data
|
|
sct utime readtime
|
|
}
|
|
}
|
|
}
|
|
return idle
|
|
}
|
|
|
|
proc adjust_power {tc_root} {
|
|
set now_time [clock seconds]
|
|
set was_time [hval $tc_root/device_control/was_time]
|
|
set elapsed [expr $now_time - $was_time]
|
|
if {$elapsed < 1} {
|
|
return
|
|
}
|
|
set Heater_Kp 0.85
|
|
set Heater_Tp 600
|
|
set Heater_Td 30
|
|
|
|
if {[hval $tc_root/device_control/power] >= 0} {
|
|
set power [hval $tc_root/device_control/power]
|
|
} else {
|
|
hset $tc_root/device_control/power 20.0
|
|
set power [hval $tc_root/device_control/power]
|
|
debug_log "initialised $tc_root/device_control/power to $power"
|
|
}
|
|
set previous_error [hval $tc_root/device_control/previous_error]
|
|
|
|
set working_setpoint [hval $tc_root/device_control/target]
|
|
set heater_temp [hval $tc_root/samplesensor]
|
|
|
|
if {$Heater_Tp > 8.0*$Heater_Td} {
|
|
set Tc [expr {1.0 * $Heater_Tp}]
|
|
} else {
|
|
set Tc [expr {8.0 * $Heater_Td}]
|
|
}
|
|
# if aggressive divide Tc by ten
|
|
#set Tc [expr {$Tc / 10.0}]
|
|
#set Tc [expr {$Tc / 10.0}]
|
|
|
|
|
|
set Kc [expr {(1.0 / $Heater_Kp) * ((1.0 * $Heater_Tp)/($Tc + $Heater_Td))}]
|
|
set Kc [expr {1.0 * $Kc}]
|
|
|
|
set Ti [expr {1.0 * $Heater_Tp}]
|
|
|
|
set Kc [expr 1.00 * [hval $tc_root/device_control/Kc]]
|
|
set Ti [expr 1.00 * [hval $tc_root/device_control/Ti]]
|
|
|
|
set new_power $power
|
|
set current_error [expr {$working_setpoint - $heater_temp}]
|
|
set delta_power [expr {$Kc * ((1 + ((1.0 * $elapsed) / $Ti)) * $current_error - $previous_error)}]
|
|
debug_log {adjust_power: $delta_power = $Kc * ((1 + ((1.0 * $elapsed) / $Ti)) * $current_error - $previous_error)}
|
|
debug_log "adjust_power: $delta_power = $Kc * ((1 + ((1.0 * $elapsed) / $Ti)) * $current_error - $previous_error)"
|
|
debug_log "adjust_power: delta = $delta_power, elapsed = $elapsed, current_error = $current_error, previous_error = $previous_error"
|
|
|
|
set new_power [expr {$power + $delta_power}]
|
|
set min $working_setpoint
|
|
set max 400
|
|
if {$new_power < $min} {
|
|
set new_power $min
|
|
} elseif {$new_power > $max} {
|
|
set new_power $max
|
|
}
|
|
set test_value [expr {abs($new_power - $power)}]
|
|
|
|
set do_it ""
|
|
if {$test_value >= 50} {
|
|
set do_it "$test_value >= 50"
|
|
} elseif {$elapsed == 10 && $test_value >= 40} {
|
|
set do_it "$elapsed == 10 && $test_value >= 40"
|
|
} elseif {$elapsed == 20 && $test_value >= 30} {
|
|
set do_it "$elapsed == 20 && $test_value >= 30"
|
|
} elseif {$elapsed >= 30 && (abs($current_error) >= 20 || $test_value >= 2)} {
|
|
set do_it "$elapsed >= 30 && (abs($current_error) >= 20 || $test_value >= 2)"
|
|
} elseif {$elapsed >= 60 && (abs($current_error) >= 1 || $test_value >= 1)} {
|
|
set do_it "$elapsed >= 60 && (abs($current_error) >= 1 || $test_value >= 1)"
|
|
set new_power [expr {$power + $delta_power / 4.0}]
|
|
if {$new_power < $min} {
|
|
set new_power $min
|
|
} elseif {$new_power > $max} {
|
|
set new_power $max
|
|
}
|
|
}
|
|
|
|
if {$do_it != ""} {
|
|
debug_log "adjust_power: new_power = $new_power ($do_it)"
|
|
hset $tc_root/device_control/power $new_power
|
|
hset $tc_root/device_control/previous_error $current_error
|
|
hset $tc_root/device_control/was_time $now_time
|
|
hset $tc_root/Loop1/setpoint $new_power
|
|
hset $tc_root/Loop2/setpoint $new_power
|
|
hset $tc_root/Loop3/setpoint $new_power
|
|
hset $tc_root/Loop4/setpoint $new_power
|
|
}
|
|
}
|
|
|
|
proc getState {tc_root nextState cmd} {
|
|
set dev "[hval $tc_root/dev_id]"
|
|
sct send "$dev:3:$cmd"
|
|
return $nextState
|
|
}
|
|
|
|
##
|
|
# @brief Reads the current watlow state and error messages.
|
|
proc rdState {tc_root} {
|
|
set my_driving [SplitReply [hgetprop $tc_root/setpoint driving]]
|
|
debug_log "rdState $tc_root: driving=$my_driving"
|
|
set val [hval $tc_root/setpoint]
|
|
debug_log "rdState $tc_root: setpoint=$val"
|
|
if {![hpropexists $tc_root/setpoint target]} {
|
|
set tgt 20.0
|
|
hset $tc_root/setpoint $tgt
|
|
debug_log "rdState $tc_root: initialised target to: target=$tgt"
|
|
}
|
|
set tgt [SplitReply [hgetprop $tc_root/setpoint target]]
|
|
debug_log "rdState $tc_root: target=$tgt"
|
|
if {![hpropexists $tc_root/limit_hi target]} {
|
|
hset $tc_root/limit_hi 25.0
|
|
}
|
|
if {$my_driving > 0} {
|
|
set temp [hval $tc_root/samplesensor]
|
|
set tol [hval $tc_root/tolerance]
|
|
set lotemp [expr {$tgt - $tol}]
|
|
set hitemp [expr {$tgt + $tol}]
|
|
debug_log "rdState driving $tc_root until $temp in ($lotemp, $hitemp)"
|
|
if {$temp < $lotemp} {
|
|
} elseif {$temp > $hitemp} {
|
|
} else {
|
|
hset $tc_root/status "idle"
|
|
hsetprop $tc_root/setpoint driving 0
|
|
}
|
|
} else {
|
|
if {[hval $tc_root/status] != "idle"} {
|
|
hset $tc_root/status "idle"
|
|
}
|
|
}
|
|
set data [SplitReply [hgetprop $tc_root/setpoint driving]]
|
|
debug_log "rdState $tc_root: result=$data"
|
|
if {[string first "ASCERR:" $data] >=0} {
|
|
sct geterror $data
|
|
} elseif {$data != [sct oldval]} {
|
|
sct oldval $data
|
|
sct update $data
|
|
sct utime readtime
|
|
}
|
|
set cur [hval $tc_root/samplesensor]
|
|
debug_log "rdState: target = $tgt, current = $cur"
|
|
if {[hval $tc_root/device_control/Auto] != 0} {
|
|
adjust_power $tc_root
|
|
}
|
|
return idle
|
|
}
|
|
|
|
# Get the Sample Sensor
|
|
proc getSS {tc_root nextState cmd} {
|
|
set d1 [hval $tc_root/Loop1/sensor2]
|
|
set d2 [hval $tc_root/Loop2/sensor2]
|
|
set d3 [hval $tc_root/Loop3/sensor2]
|
|
set d4 [hval $tc_root/Loop4/sensor2]
|
|
set data [expr ($d1 + $d2 + $d3 + $d4) / 4.0]
|
|
# KLUDGE TODO remove
|
|
set data [expr 1.0 * $d1]
|
|
if {$data != [sct oldval]} {
|
|
sct oldval $data
|
|
sct update $data
|
|
sct utime readtime
|
|
}
|
|
if {![hpropexists $tc_root/setpoint target]} {
|
|
hsetprop $tc_root/setpoint target 20.0
|
|
debug_log "getSS initialised $tc_root/setpoint target to: target=$data"
|
|
hset $tc_root/device_control/target 20.0
|
|
debug_log "getSS initialised $tc_root/device_control/target to $data"
|
|
}
|
|
debug_log "getSS $tc_root $nextState $cmd [sct] = $data ($d1 $d2 $d3 $d4)"
|
|
for {set i 1} {$i <= 4} {incr i} {
|
|
debug_log "getSS Loop $i limit test: [hval $tc_root/Loop$i/limit_state] == 51 && [hval $tc_root/Loop$i/sensor2] < [hval $tc_root/Loop$i/limit_hi]"
|
|
if {[hval $tc_root/Loop$i/limit_state] == 51 && [hval $tc_root/Loop$i/sensor2] < ([hval $tc_root/Loop$i/limit_hi] - 2.0)} {
|
|
debug_log "getSS Loop $i limit reset)"
|
|
hset $tc_root/Loop$i/limit_clear 0
|
|
}
|
|
}
|
|
return idle
|
|
}
|
|
|
|
# Get the Heater Power
|
|
proc getHP {tc_root nextState cmd} {
|
|
set d1 [hval $tc_root/Loop1/power]
|
|
set d2 [hval $tc_root/Loop2/power]
|
|
set d3 [hval $tc_root/Loop3/power]
|
|
set d4 [hval $tc_root/Loop4/power]
|
|
set data [expr ($d1 + $d2 + $d3 + $d4) / 4.0]
|
|
if {$data != [sct oldval]} {
|
|
sct oldval $data
|
|
sct update $data
|
|
sct utime readtime
|
|
}
|
|
debug_log "getHP $tc_root $nextState $cmd [sct] = $data ($d1 $d2 $d3 $d4)"
|
|
return idle
|
|
}
|
|
|
|
# Get the Process Variable
|
|
proc getPV {tc_root nextState cmd} {
|
|
set d1 [hval $tc_root/Loop1/sensor]
|
|
set d2 [hval $tc_root/Loop2/sensor]
|
|
set d3 [hval $tc_root/Loop3/sensor]
|
|
set d4 [hval $tc_root/Loop4/sensor]
|
|
set data [expr ($d1 + $d2 + $d3 + $d4) / 4.0]
|
|
if {$data != [sct oldval]} {
|
|
sct oldval $data
|
|
sct update $data
|
|
sct utime readtime
|
|
}
|
|
if {[hval $tc_root/device_control/power] < 0} {
|
|
hset $tc_root/device_control/power $data
|
|
debug_log "getPV initialised $tc_root/device_control/power to $data"
|
|
}
|
|
debug_log "getPV $tc_root $nextState $cmd [sct] = $data ($d1 $d2 $d3 $d4)"
|
|
return idle
|
|
}
|
|
|
|
# Get the Set Point
|
|
proc getSP {tc_root nextState cmd} {
|
|
set d1 [hval $tc_root/Loop1/setpoint]
|
|
set d2 [hval $tc_root/Loop2/setpoint]
|
|
set d3 [hval $tc_root/Loop3/setpoint]
|
|
set d4 [hval $tc_root/Loop4/setpoint]
|
|
set data [expr {($d1 + $d2 + $d3 + $d4) / 4.0}]
|
|
if {$data != [sct oldval]} {
|
|
sct oldval $data
|
|
sct update $data
|
|
sct utime readtime
|
|
}
|
|
debug_log "getSP $tc_root $nextState $cmd [sct] = $data ($d1 $d2 $d3 $d4)"
|
|
return idle
|
|
}
|
|
|
|
# Set the Set Point
|
|
proc setSP {tc_root nextState cmd} {
|
|
debug_log "setSP $tc_root $nextState $cmd [sct]=[sct target] [hget [sct]]"
|
|
if {[hval $tc_root/device_control/Auto] == 0} {
|
|
hset $tc_root/Loop1/setpoint [sct target]
|
|
hset $tc_root/Loop2/setpoint [sct target]
|
|
hset $tc_root/Loop3/setpoint [sct target]
|
|
hset $tc_root/Loop4/setpoint [sct target]
|
|
}
|
|
hset $tc_root/device_control/target [sct target]
|
|
hset $tc_root/device_control/previous_error [expr [sct target] - [hval $tc_root/samplesensor]]
|
|
hset $tc_root/status "busy"
|
|
return idle
|
|
}
|
|
|
|
# Get the High Limit Point
|
|
proc getLimHi {tc_root nextState cmd} {
|
|
set d1 [hval $tc_root/Loop1/limit_hi]
|
|
set d2 [hval $tc_root/Loop2/limit_hi]
|
|
set d3 [hval $tc_root/Loop3/limit_hi]
|
|
set d4 [hval $tc_root/Loop4/limit_hi]
|
|
set data [expr {($d1 + $d2 + $d3 + $d4) / 4.0}]
|
|
if {$data != [sct oldval]} {
|
|
sct oldval $data
|
|
sct update $data
|
|
sct utime readtime
|
|
}
|
|
debug_log "getLimHi $tc_root $nextState $cmd [sct] = $data ($d1 $d2 $d3 $d4)"
|
|
return idle
|
|
}
|
|
|
|
# Set the High Limit Point
|
|
proc setLimHi {tc_root nextState cmd} {
|
|
debug_log "setLimHi $tc_root $nextState $cmd [sct]=[sct target] [hget [sct]]"
|
|
hset $tc_root/Loop1/limit_hi [sct target]
|
|
hset $tc_root/Loop2/limit_hi [sct target]
|
|
hset $tc_root/Loop3/limit_hi [sct target]
|
|
hset $tc_root/Loop4/limit_hi [sct target]
|
|
return idle
|
|
}
|
|
|
|
# Set the All Set Points
|
|
proc setAll {tc_root nextState cmd} {
|
|
debug_log "setAll $tc_root $nextState $cmd [sct]=[sct target] [hget [sct]]"
|
|
hset $tc_root/Loop1/setpoint [sct target]
|
|
hset $tc_root/Loop2/setpoint [sct target]
|
|
hset $tc_root/Loop3/setpoint [sct target]
|
|
hset $tc_root/Loop4/setpoint [sct target]
|
|
hset $tc_root/device_control/previous_error [expr [sct target] - [hval $tc_root/samplesensor]]
|
|
return idle
|
|
}
|
|
|
|
proc setPoint {tc_root nextState cmd} {
|
|
set dev "[hval $tc_root/dev_id]"
|
|
set par [sct target]
|
|
|
|
if {[sct writestatus] == "start"} {
|
|
# Called by drive adapter
|
|
hset $tc_root/status "busy"
|
|
hsetprop $tc_root/setpoint driving 1
|
|
}
|
|
set par [c_to_f $par]
|
|
sct send "$dev:1016:$cmd $par"
|
|
debug_log "setPoint $dev:1016:$cmd $par"
|
|
return $nextState
|
|
}
|
|
|
|
proc noResponse {} {
|
|
return idle
|
|
}
|
|
proc wrtValue {wcmd args} {
|
|
}
|
|
|
|
# check that a target is within allowable limits
|
|
proc check {tc_root} {
|
|
set setpoint [sct target]
|
|
set lolimit [hval $tc_root/lowerlimit]
|
|
set hilimit [hval $tc_root/upperlimit]
|
|
if {$setpoint < $lolimit || $setpoint > $hilimit} {
|
|
sct driving 0
|
|
error "setpoint violates limits"
|
|
}
|
|
return OK
|
|
}
|
|
|
|
# Check that the sensor is reading within tolerance of the setpoint.
|
|
# Return 1 or 0 if it is or is not, respectively.
|
|
proc checktol {tc_root currtime timecheck} {
|
|
debug_log "checktol $tc_root $currtime $timecheck"
|
|
set temp [hval $tc_root/sensor/value]
|
|
set lotemp [hval $tc_root/subtemp_warnlimit]
|
|
set hitemp [hval $tc_root/overtemp_warnlimit]
|
|
if { $temp < $lotemp || $temp > $hitemp} {
|
|
hset $tc_root/emon/isintol 0
|
|
return 0
|
|
} else {
|
|
set timeout [hval $tc_root/tolerance/settletime]
|
|
if { ($currtime - $timecheck) > $timeout } {
|
|
hset $tc_root/emon/isintol 1
|
|
}
|
|
return 1
|
|
}
|
|
}
|
|
|
|
##
|
|
# @brief Implement the checkstatus command for the drivable interface
|
|
#
|
|
# NOTE: The drive adapter initially sets the writestatus to "start" and will
|
|
# only call this when writestatus!="start"
|
|
proc drivestatus {tc_root} {
|
|
if {[sct driving]} {
|
|
return busy
|
|
} else {
|
|
sct print "drivestatus: idle"
|
|
return idle
|
|
}
|
|
}
|
|
|
|
proc halt {tc_root} {
|
|
debug_log "halt $tc_root"
|
|
hset $tc_root/setpoint [hval $tc_root/sensor/value]
|
|
hsetprop $tc_root/setpoint driving 0
|
|
return idle
|
|
}
|
|
|
|
##
|
|
# @brief createNode() creates a node for the given nodename with the properties and virtual
|
|
# function names provided
|
|
# @param scobj_hpath string variable holding the path to the object's base node in sics (/sample/tc1)
|
|
# @param sct_controller name of the scriptcontext object (typically sct_xxx_yyy)
|
|
# @param cmdGroup subdirectory (below /sample/tc*/) in which the node is to be created
|
|
# @param varName name of the actual node typically representing one device command
|
|
# @param readable set to 1 if the node represents a query command, 0 if it is not
|
|
# @param writable set to 1 if the node represents a request for a change in settings sent to the device
|
|
# @param drivable if set to 1 it prepares the node to provide a drivable interface
|
|
# @param dataType data type of the node, must be one of none, int, float, text
|
|
# @param permission defines what user group may read/write to this node (is one of spy, user, manager)
|
|
# @param rdCmd actual device query command to be sent to the device
|
|
# @param rdFunc nextState Function to be called after the getValue function, typically rdValue()
|
|
# @param wrCmd actual device write command to be sent to the device
|
|
# @param wrFunc Function to be called to send the wrCmd to the device, typically setValue()
|
|
# @param allowedValues allowed values for the node data - does not permit other
|
|
# @param klass Nexus class name (?)
|
|
# @return OK
|
|
proc createNode {scobj_hpath sct_controller cmdGroup varName readable writable\
|
|
drivable dataType permission rdCmd rdFunc wrCmd\
|
|
wrFunc allowedValues klass} {
|
|
|
|
set catch_status [ catch {
|
|
# set ns ::scobj::ls460
|
|
set ns "[namespace current]"
|
|
set nodeName "$scobj_hpath/$cmdGroup/$varName"
|
|
if {1 > [string length $cmdGroup]} {
|
|
set nodeName "$scobj_hpath/$varName"
|
|
}
|
|
debug_log "Creating node $nodeName"
|
|
hfactory $nodeName plain $permission $dataType
|
|
if {$readable > 0} {
|
|
set parts [split "$rdFunc" "."]
|
|
if { [llength $parts] == 2 } {
|
|
set func_name [lindex $parts 0]
|
|
set next_state [lindex $parts 1]
|
|
hsetprop $nodeName read ${ns}::$func_name $scobj_hpath $next_state $rdCmd
|
|
hsetprop $nodeName $next_state ${ns}::$next_state $scobj_hpath
|
|
} else {
|
|
if {$rdFunc == "getPV"} {
|
|
set func_name "$rdFunc"
|
|
} elseif {$rdFunc == "getSP"} {
|
|
set func_name "$rdFunc"
|
|
} elseif {$rdFunc == "getHP"} {
|
|
set func_name "$rdFunc"
|
|
} elseif {$rdFunc == "getSS"} {
|
|
set func_name "$rdFunc"
|
|
} elseif {$rdFunc == "getLimHi"} {
|
|
set func_name "$rdFunc"
|
|
} elseif {$rdFunc == "rdFloat"} {
|
|
set func_name "getFloat"
|
|
} elseif {$rdFunc == "rdTemp"} {
|
|
set func_name "getTemp"
|
|
} else {
|
|
set func_name "getValue"
|
|
}
|
|
hsetprop $nodeName read ${ns}::$func_name $scobj_hpath $rdFunc $rdCmd
|
|
hsetprop $nodeName $rdFunc ${ns}::$rdFunc $scobj_hpath
|
|
}
|
|
set poll_period 30
|
|
if { $readable >= 0 && $readable <= 9 } {
|
|
set poll_period [lindex [list 0 1 2 3 4 5 10 15 20 30] $readable]
|
|
}
|
|
debug_log "Registering node $nodeName for poll at $poll_period seconds"
|
|
$sct_controller poll $nodeName $poll_period
|
|
}
|
|
if {$writable == 1} {
|
|
set parts [split "$wrFunc" "."]
|
|
if { [llength $parts] == 2 } {
|
|
set func_name [lindex $parts 0]
|
|
set next_state [lindex $parts 1]
|
|
hsetprop $nodeName write ${ns}::$func_name $scobj_hpath $next_state $wrCmd
|
|
hsetprop $nodeName $next_state ${ns}::$next_state $scobj_hpath
|
|
} else {
|
|
hsetprop $nodeName write ${ns}::$wrFunc $scobj_hpath noResponse $wrCmd
|
|
hsetprop $nodeName noResponse ${ns}::noResponse
|
|
}
|
|
hsetprop $nodeName writestatus UNKNOWN
|
|
debug_log "Registering node $nodeName for write callback"
|
|
$sct_controller write $nodeName
|
|
}
|
|
switch -exact $dataType {
|
|
"none" { }
|
|
"int" { hsetprop $nodeName oldval -1 }
|
|
"float" { hsetprop $nodeName oldval -1.0 }
|
|
default { hsetprop $nodeName oldval UNKNOWN }
|
|
}
|
|
if {1 < [string length $allowedValues]} {
|
|
hsetprop $nodeName values $allowedValues
|
|
}
|
|
# Drive adapter interface
|
|
if {$drivable == 1} {
|
|
hsetprop $nodeName check ${ns}::check $scobj_hpath
|
|
hsetprop $nodeName driving 0
|
|
hsetprop $nodeName checklimits ${ns}::check $scobj_hpath
|
|
hsetprop $nodeName checkstatus ${ns}::drivestatus $scobj_hpath
|
|
hsetprop $nodeName halt ${ns}::halt $scobj_hpath
|
|
}
|
|
} message ]
|
|
if {$catch_status != 0} {
|
|
return -code error "in createNode $message"
|
|
}
|
|
return OK
|
|
}
|
|
|
|
proc mk_sct_watlow_st {sct_controller klass tempobj dev_id tol CID CTYPE} {
|
|
set catch_status [ catch {
|
|
set ns "[namespace current]"
|
|
|
|
MakeSICSObj $tempobj SCT_OBJECT
|
|
sicslist setatt $tempobj klass $klass
|
|
sicslist setatt $tempobj long_name $tempobj
|
|
|
|
set scobj_hpath /sics/$tempobj
|
|
|
|
set deviceCommand {\
|
|
{} all 0 1 0 float user {0} {} {0} {setAll} {}\
|
|
{} setpoint 1 1 1 float user {0} {getSP} {0} {setSP} {}\
|
|
sensor value 1 0 0 float internal {0} {getPV} {0} {} {}\
|
|
{} power 1 0 0 float internal {0} {getHP} {0} {} {}\
|
|
{} samplesensor 1 0 0 float internal {0} {getSS} {0} {} {}\
|
|
{} limit_hi 1 1 0 float user {0} {getLimHi} {0} {setLimHi} {}\
|
|
Loop1 setpoint 1 1 0 float user {2160} {rdTemp} {2160} {setTemp} {}\
|
|
Loop1 sensor 1 0 0 float internal {1940} {rdTemp} {1940} {} {}\
|
|
Loop1 sensor2 1 0 0 float internal {562} {rdTemp} {562} {} {}\
|
|
Loop1 limit_hi 1 1 0 float user {686} {rdTemp} {686} {setTemp} {}\
|
|
Loop1 limit_state 1 0 0 int internal {690} {rdValue} {690} {} {}\
|
|
Loop1 limit_clear 0 1 0 int user {680} {} {680} {setValue} {}\
|
|
Loop1 power 1 0 0 float internal {1900} {rdFloat} {1900} {} {}\
|
|
Loop2 setpoint 1 1 0 float user {5160} {rdTemp} {5160} {setTemp} {}\
|
|
Loop2 sensor 1 0 0 float internal {4940} {rdTemp} {4940} {} {}\
|
|
Loop2 sensor2 1 0 0 float internal {3562} {rdTemp} {3562} {} {}\
|
|
Loop2 limit_hi 1 1 0 float user {3686} {rdTemp} {3686} {setTemp} {}\
|
|
Loop2 limit_state 1 0 0 int internal {3690} {rdValue} {3690} {} {}\
|
|
Loop2 limit_clear 0 1 0 int user {3680} {} {3680} {setValue} {}\
|
|
Loop2 power 1 0 0 float internal {4900} {rdFloat} {4900} {} {}\
|
|
Loop3 setpoint 1 1 0 float user {8160} {rdTemp} {8160} {setTemp} {}\
|
|
Loop3 sensor 1 0 0 float internal {7940} {rdTemp} {7940} {} {}\
|
|
Loop3 sensor2 1 0 0 float internal {6562} {rdTemp} {6562} {} {}\
|
|
Loop3 limit_hi 1 1 0 float user {6686} {rdTemp} {6686} {setTemp} {}\
|
|
Loop3 limit_state 1 0 0 int internal {6690} {rdValue} {6690} {} {}\
|
|
Loop3 limit_clear 0 1 0 int user {6680} {} {6680} {setValue} {}\
|
|
Loop3 power 1 0 0 float internal {7900} {rdFloat} {7900} {} {}\
|
|
Loop4 setpoint 1 1 0 float user {11160} {rdTemp} {11160} {setTemp} {}\
|
|
Loop4 sensor 1 0 0 float internal {10940} {rdTemp} {10940} {} {}\
|
|
Loop4 sensor2 1 0 0 float internal {9562} {rdTemp} {9562} {} {}\
|
|
Loop4 limit_hi 1 1 0 float user {9686} {rdTemp} {9686} {setTemp} {}\
|
|
Loop4 limit_state 1 0 0 int internal {9690} {rdValue} {9690} {} {}\
|
|
Loop4 limit_clear 0 1 0 int user {9680} {} {9680} {setValue} {}\
|
|
Loop4 power 1 0 0 float internal {10900} {rdFloat} {10900} {} {}\
|
|
}
|
|
|
|
hfactory $scobj_hpath/sensor plain spy none
|
|
hfactory $scobj_hpath/Loop1 plain spy none
|
|
hfactory $scobj_hpath/Loop2 plain spy none
|
|
hfactory $scobj_hpath/Loop3 plain spy none
|
|
hfactory $scobj_hpath/Loop4 plain spy none
|
|
|
|
foreach {cmdGroup varName readable writable drivable dataType permission rdCmd rdFunc wrCmd wrFunc allowedValues} $deviceCommand {
|
|
createNode $scobj_hpath $sct_controller $cmdGroup $varName $readable $writable $drivable $dataType $permission $rdCmd $rdFunc $wrCmd $wrFunc $allowedValues $klass
|
|
}
|
|
|
|
hsetprop $scobj_hpath/sensor/value lowerlimit 0
|
|
hsetprop $scobj_hpath/sensor/value upperlimit 500
|
|
hsetprop $scobj_hpath/sensor/value units "C"
|
|
hsetprop $scobj_hpath/sensor/value permlink data_set ${CTYPE}${CID}S1
|
|
hsetprop $scobj_hpath/setpoint permlink data_set ${CTYPE}${CID}SP1
|
|
|
|
hfactory $scobj_hpath/apply_tolerance plain user int
|
|
hsetprop $scobj_hpath/apply_tolerance values 0,1
|
|
hset $scobj_hpath/apply_tolerance 1
|
|
|
|
hfactory $scobj_hpath/dev_id plain user int
|
|
hsetprop $scobj_hpath/dev_id values 0,1,2,3,4,5,6,7,8,9
|
|
hset $scobj_hpath/dev_id $dev_id
|
|
|
|
hfactory $scobj_hpath/tolerance plain user float
|
|
hsetprop $scobj_hpath/tolerance units "C"
|
|
hfactory $scobj_hpath/tolerance/settletime plain user float
|
|
hset $scobj_hpath/tolerance/settletime 5.0
|
|
hsetprop $scobj_hpath/tolerance/settletime units "s"
|
|
hset $scobj_hpath/tolerance $tol
|
|
|
|
hfactory $scobj_hpath/status plain spy text
|
|
hset $scobj_hpath/status "idle"
|
|
hsetprop $scobj_hpath/status values busy,idle
|
|
|
|
hfactory $scobj_hpath/device_state plain spy text
|
|
hsetprop $scobj_hpath/device_state read ${ns}::getState $scobj_hpath rdState "2160"
|
|
hsetprop $scobj_hpath/device_state rdState ${ns}::rdState $scobj_hpath
|
|
hsetprop $scobj_hpath/device_state oldval UNKNOWN
|
|
|
|
hfactory $scobj_hpath/device_control plain spy none
|
|
hfactory $scobj_hpath/device_control/target plain user float
|
|
hset $scobj_hpath/device_control/target 20.0
|
|
hfactory $scobj_hpath/device_control/power plain user float
|
|
hset $scobj_hpath/device_control/power 0.0
|
|
hfactory $scobj_hpath/device_control/previous_error plain user float
|
|
hset $scobj_hpath/device_control/previous_error 0.0
|
|
hfactory $scobj_hpath/device_control/was_time plain internal int
|
|
hset $scobj_hpath/device_control/was_time [clock seconds]
|
|
hfactory $scobj_hpath/device_control/Kc plain user float
|
|
hset $scobj_hpath/device_control/Kc [expr 6.0]
|
|
hfactory $scobj_hpath/device_control/Ti plain user float
|
|
hset $scobj_hpath/device_control/Ti [expr 250.0]
|
|
hfactory $scobj_hpath/device_control/Auto plain user int
|
|
hset $scobj_hpath/device_control/Auto 1
|
|
|
|
hfactory $scobj_hpath/remote_ctrl plain spy text
|
|
hset $scobj_hpath/remote_ctrl UNKNOWN
|
|
|
|
hfactory $scobj_hpath/device_lasterror plain user text
|
|
hset $scobj_hpath/device_lasterror ""
|
|
|
|
hfactory $scobj_hpath/lowerlimit plain mugger float
|
|
hsetprop $scobj_hpath/lowerlimit units "C"
|
|
hset $scobj_hpath/lowerlimit 0
|
|
|
|
hfactory $scobj_hpath/upperlimit plain mugger float
|
|
hsetprop $scobj_hpath/upperlimit units "C"
|
|
hset $scobj_hpath/upperlimit 500
|
|
|
|
hfactory $scobj_hpath/emon plain spy none
|
|
hfactory $scobj_hpath/emon/monmode plain user text
|
|
hsetprop $scobj_hpath/emon/monmode values idle,drive,monitor,error
|
|
hset $scobj_hpath/emon/monmode "idle"
|
|
hfactory $scobj_hpath/emon/isintol plain user int
|
|
hset $scobj_hpath/emon/isintol 1
|
|
hfactory $scobj_hpath/emon/errhandler plain user text
|
|
hset $scobj_hpath/emon/errhandler "pause"
|
|
|
|
if {[SplitReply [environment_simulation]]=="false"} {
|
|
$sct_controller poll $scobj_hpath/device_state 1 halt read
|
|
}
|
|
|
|
::scobj::hinitprops $tempobj
|
|
hsetprop $scobj_hpath klass NXenvironment
|
|
::scobj::set_required_props $scobj_hpath
|
|
foreach {rootpath hpath klass priv} "
|
|
$scobj_hpath sensor NXsensor spy
|
|
$scobj_hpath sensor/value sensor user
|
|
" {
|
|
hsetprop $rootpath/$hpath klass $klass
|
|
hsetprop $rootpath/$hpath privilege $priv
|
|
hsetprop $rootpath/$hpath control true
|
|
hsetprop $rootpath/$hpath data true
|
|
hsetprop $rootpath/$hpath nxsave true
|
|
}
|
|
hsetprop $scobj_hpath type part
|
|
hsetprop $scobj_hpath/sensor type part
|
|
hsetprop $scobj_hpath/sensor/value nxalias tc1_sensor_value
|
|
hsetprop $scobj_hpath/sensor/value mutable true
|
|
hsetprop $scobj_hpath/sensor/value sdsinfo ::nexus::scobj::sdsinfo
|
|
|
|
hsetprop $scobj_hpath privilege spy
|
|
::scobj::hinitprops $tempobj setpoint
|
|
hsetprop $scobj_hpath/setpoint data true
|
|
if {[SplitReply [environment_simulation]]=="false"} {
|
|
ansto_makesctdrive ${tempobj}_driveable $scobj_hpath/setpoint $scobj_hpath/sensor/value $sct_controller
|
|
}
|
|
} catch_message ]
|
|
if {$catch_status != 0} {
|
|
return -code error $catch_message
|
|
}
|
|
}
|
|
namespace export mk_sct_watlow_st
|
|
}
|
|
|
|
##
|
|
# @brief Create a Watlow ST temperature controller
|
|
#
|
|
# @param name, the name of the temperature controller (eg tc1)
|
|
# @param IP, the IP address of the device, this can be a hostname, (eg ca5-kowari)
|
|
# @param port, the IP protocol port number of the device (502 for modbus)
|
|
# @param _tol (optional), this is the initial tolerance setting
|
|
proc add_watlow_st {name IP port dev_id {_tol 5.0} {CID 1} {CTYPE T}} {
|
|
set fd [open "/tmp/watlow_st.log" a]
|
|
if {[SplitReply [environment_simulation]]=="false"} {
|
|
puts $fd "makesctcontroller sct_${name} modbus ${IP}:$port"
|
|
makesctcontroller sct_${name} modbus ${IP}:$port
|
|
}
|
|
puts $fd "mk_sct_watlow_st sct_${name} environment $name $dev_id $_tol"
|
|
mk_sct_watlow_st sct_${name} environment $name $dev_id $_tol $CID $CTYPE
|
|
puts $fd "makesctemon $name /sics/$name/emon/monmode /sics/$name/emon/isintol /sics/$name/emon/errhandler"
|
|
makesctemon $name /sics/$name/emon/monmode /sics/$name/emon/isintol /sics/$name/emon/errhandler
|
|
close $fd
|
|
}
|
|
|
|
puts stdout "file evaluation of sct_watlow_st4.tcl"
|
|
set fd [open "/tmp/watlow_st.log" w]
|
|
puts $fd "file evaluation of sct_watlow_st4.tcl"
|
|
close $fd
|
|
|
|
if {[ catch {
|
|
if { [ info exists ::config_dict ] } {
|
|
dict for {secname secinfo} $::config_dict {
|
|
if { [dict exists $secinfo "driver"] && ([dict get $secinfo "driver"] == "watlow_st") } {
|
|
if { [ dict get $::secinfo enabled ] } {
|
|
set name [dict get $::secinfo name]
|
|
set IP [dict get $::secinfo ip]
|
|
set PORT [dict get $::secinfo port]
|
|
set sensor [dict get $::secinfo sensor]
|
|
set tol [dict get $::secinfo tol]
|
|
set cid [dict get $::secinfo id]
|
|
set ctype [dict get $::secinfo type]
|
|
add_watlow_st $name $IP $PORT $devid $tol $cid $ctype
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} message ]} {
|
|
puts "ERROR: $message"
|
|
}
|
|
|
|
namespace import ::scobj::watlow_st::*
|