Files
sics/site_ansto/instrument/config/environment/temperature/sct_eurotherm_2000.tcl
Ferdi Franceschini d7acb7c16c Load drivers which have been enabled in the SICS config ini files.
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.
2014-05-05 12:25:00 +10:00

394 lines
15 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::et2000 {
# Temperature controllers must have at least the following nodes
# /tempcont/setpoint
# /tempcont/sensor/value
proc debug_log {args} {
set fd [open "/tmp/et2000.log" a]
puts $fd $args
close $fd
}
proc getValue {tc_root nextState cmd} {
set dev "[hval $tc_root/dev_id]"
sct send "$dev:3:$cmd"
return $nextState
}
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 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
}
sct send "$dev:16:$cmd $par"
debug_log "setPoint $dev:16:$cmd $par"
return $nextState
}
proc rdValue {} {
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
}
proc getState {tc_root nextState cmd} {
set dev "[hval $tc_root/dev_id]"
sct send "$dev:3:$cmd"
return $nextState
}
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 Reads the current et2000 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 [SplitReply [hgetprop $tc_root/setpoint target]]
debug_log "rdState $tc_root: target=$tgt"
} else {
hsetprop $tc_root/setpoint target $val
set tgt [SplitReply [hgetprop $tc_root/setpoint target]]
debug_log "rdState $tc_root: initialised target to: target=$tgt"
}
set data [sct result]
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
}
if {$my_driving > 0} {
set temp [hval $tc_root/sensor/value]
set tol [hval $tc_root/tolerance]
set lotemp [expr {$tgt - $tol}]
set hitemp [expr {$tgt + $tol}]
debug_log "rdState driving $tc_root until $data in ($lotemp, $hitemp)"
if {$data < $lotemp} {
} elseif {$data > $hitemp} {
} else {
hset $tc_root/status "idle"
hsetprop $tc_root/setpoint driving 0
}
} else {
if {[SplitReply [hget $tc_root/status]] != "idle"} {
hset $tc_root/status "idle"
}
}
return idle
}
proc noResponse {} {
return idle
}
proc wrtValue {wcmd args} {
}
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} {
error "setpoint violates limits"
}
return OK
}
##
# @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 {
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
}
proc mk_sct_eurotherm_et2000 {sct_controller klass tempobj dev_id tol CID CTYPE} {
if {[ catch {
set ns ::scobj::et2000
MakeSICSObj $tempobj SCT_OBJECT
sicslist setatt $tempobj klass $klass
sicslist setatt $tempobj long_name $tempobj
set scobj_hpath /sics/$tempobj
hfactory $scobj_hpath/setpoint plain user float
hsetprop $scobj_hpath/setpoint read ${ns}::getValue $scobj_hpath rdValue "2"
hsetprop $scobj_hpath/setpoint write ${ns}::setPoint $scobj_hpath noResponse "2"
hsetprop $scobj_hpath/setpoint check ${ns}::check $scobj_hpath
hsetprop $scobj_hpath/setpoint rdValue ${ns}::rdValue
hsetprop $scobj_hpath/setpoint noResponse ${ns}::noResponse
hsetprop $scobj_hpath/setpoint oldval UNKNOWN
hsetprop $scobj_hpath/setpoint driving 0
hsetprop $scobj_hpath/setpoint writestatus UNKNOWN
# Drive adapter interface
hsetprop $scobj_hpath/setpoint checklimits ${ns}::check $scobj_hpath
hsetprop $scobj_hpath/setpoint checkstatus ${ns}::drivestatus $scobj_hpath
hsetprop $scobj_hpath/setpoint halt ${ns}::halt $scobj_hpath
hsetprop $scobj_hpath/setpoint permlink data_set ${CTYPE}${CID}SP1
hfactory $scobj_hpath/overtemp_warnlimit plain user float
hsetprop $scobj_hpath/overtemp_warnlimit read ${ns}::getValue $scobj_hpath rdValue "14"
hsetprop $scobj_hpath/overtemp_warnlimit write ${ns}::setValue $scobj_hpath noResponse "14"
hsetprop $scobj_hpath/overtemp_warnlimit rdValue ${ns}::rdValue
hsetprop $scobj_hpath/overtemp_warnlimit noResponse ${ns}::noResponse
hsetprop $scobj_hpath/overtemp_warnlimit oldval UNKNOWN
hfactory $scobj_hpath/subtemp_warnlimit plain user float
hsetprop $scobj_hpath/subtemp_warnlimit read ${ns}::getValue $scobj_hpath rdValue "13"
hsetprop $scobj_hpath/subtemp_warnlimit write ${ns}::setValue $scobj_hpath noResponse "13"
hsetprop $scobj_hpath/subtemp_warnlimit rdValue ${ns}::rdValue
hsetprop $scobj_hpath/subtemp_warnlimit noResponse ${ns}::noResponse
hsetprop $scobj_hpath/subtemp_warnlimit oldval UNKNOWN
hfactory $scobj_hpath/sensor plain spy none
hfactory $scobj_hpath/sensor/value plain internal float
hsetprop $scobj_hpath/sensor/value read ${ns}::getValue $scobj_hpath rdValue "1"
hsetprop $scobj_hpath/sensor/value rdValue ${ns}::rdValue
hsetprop $scobj_hpath/sensor/value oldval UNKNOWN
hsetprop $scobj_hpath/sensor/value units "C"
hsetprop $scobj_hpath/sensor/value permlink data_set ${CTYPE}${CID}S1
hfactory $scobj_hpath/setpoint_slew_rate plain user float
hsetprop $scobj_hpath/setpoint_slew_rate read ${ns}::getValue $scobj_hpath rdValue "35"
hsetprop $scobj_hpath/setpoint_slew_rate write ${ns}::setValue $scobj_hpath noResponse "35"
hsetprop $scobj_hpath/setpoint_slew_rate rdValue ${ns}::rdValue
hsetprop $scobj_hpath/setpoint_slew_rate noResponse ${ns}::noResponse
hsetprop $scobj_hpath/setpoint_slew_rate oldval UNKNOWN
hfactory $scobj_hpath/power_limit_low plain user float
hsetprop $scobj_hpath/power_limit_low read ${ns}::getValue $scobj_hpath rdValue "31"
hsetprop $scobj_hpath/power_limit_low write ${ns}::setValue $scobj_hpath noResponse "31"
hsetprop $scobj_hpath/power_limit_low rdValue ${ns}::rdValue
hsetprop $scobj_hpath/power_limit_low noResponse ${ns}::noResponse
hsetprop $scobj_hpath/power_limit_low oldval UNKNOWN
hfactory $scobj_hpath/power_limit_high plain user float
hsetprop $scobj_hpath/power_limit_high read ${ns}::getValue $scobj_hpath rdValue "30"
hsetprop $scobj_hpath/power_limit_high write ${ns}::setValue $scobj_hpath noResponse "30"
hsetprop $scobj_hpath/power_limit_high rdValue ${ns}::rdValue
hsetprop $scobj_hpath/power_limit_high noResponse ${ns}::noResponse
hsetprop $scobj_hpath/power_limit_high oldval UNKNOWN
hfactory $scobj_hpath/power_slew_rate plain user float
hsetprop $scobj_hpath/power_slew_rate read ${ns}::getValue $scobj_hpath rdValue "37"
hsetprop $scobj_hpath/power_slew_rate write ${ns}::setValue $scobj_hpath noResponse "37"
hsetprop $scobj_hpath/power_slew_rate rdValue ${ns}::rdValue
hsetprop $scobj_hpath/power_slew_rate noResponse ${ns}::noResponse
hsetprop $scobj_hpath/power_slew_rate oldval UNKNOWN
hfactory $scobj_hpath/heating_power_percent plain internal float
hsetprop $scobj_hpath/heating_power_percent read ${ns}::getValue $scobj_hpath rdValue "3"
hsetprop $scobj_hpath/heating_power_percent rdValue ${ns}::rdValue
hsetprop $scobj_hpath/heating_power_percent oldval UNKNOWN
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/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/dev_id plain user int
hsetprop $scobj_hpath/dev_id values 1,2,3,4,5,6,7,8,9,10,11
hset $scobj_hpath/dev_id $dev_id
hfactory $scobj_hpath/status plain spy text
hset $scobj_hpath/status "idle"
hsetprop $scobj_hpath/status values busy,idle
hfactory $scobj_hpath/et2000_state plain spy text
hsetprop $scobj_hpath/et2000_state read ${ns}::getState $scobj_hpath rdState "1"
hsetprop $scobj_hpath/et2000_state rdState ${ns}::rdState $scobj_hpath
hsetprop $scobj_hpath/et2000_state oldval UNKNOWN
hfactory $scobj_hpath/remote_ctrl plain spy text
hset $scobj_hpath/remote_ctrl UNKNOWN
hfactory $scobj_hpath/et2000_lasterror plain user text
hset $scobj_hpath/et2000_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/setpoint
$sct_controller write $scobj_hpath/setpoint
$sct_controller poll $scobj_hpath/subtemp_warnlimit
$sct_controller write $scobj_hpath/subtemp_warnlimit
$sct_controller poll $scobj_hpath/overtemp_warnlimit
$sct_controller write $scobj_hpath/overtemp_warnlimit
$sct_controller poll $scobj_hpath/heating_power_percent
$sct_controller poll $scobj_hpath/setpoint_slew_rate
$sct_controller write $scobj_hpath/setpoint_slew_rate
$sct_controller poll $scobj_hpath/power_slew_rate
$sct_controller write $scobj_hpath/power_slew_rate
$sct_controller poll $scobj_hpath/power_limit_low
$sct_controller write $scobj_hpath/power_limit_low
$sct_controller poll $scobj_hpath/power_limit_high
$sct_controller write $scobj_hpath/power_limit_high
$sct_controller poll $scobj_hpath/sensor/value
$sct_controller poll $scobj_hpath/et2000_state 5 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
}
} message ]} {
return -code error $message
}
}
namespace export mk_sct_eurotherm_et2000
}
##
# @brief Create a Eurotherm et2000 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_et2000 {name IP port dev_id {_tol 5.0} {CID 1} {CTYPE T}} {
set fd [open "/tmp/et2000.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_eurotherm_et2000 sct_${name} environment $name $dev_id $_tol"
mk_sct_eurotherm_et2000 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_eurotherm_2000.tcl"
set fd [open "/tmp/et2000.log" w]
puts $fd "file evaluation of sct_eurotherm_2000.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"] == "et2000") } {
if { [ dict get $::secinfo enabled ] } {
set name [dict get $::secinfo name]
set IP [dict get $::secinfo ip]
set PORT [dict get $::secinfo port]
set devid [dict get $::secinfo devid]
set tol [dict get $::secinfo tol]
set cid [dict get $::secinfo id]
set ctype [dict get $::secinfo type]
add_et2000 $name $IP $PORT $devid $tol $cid $ctype
}
}
}
}
} message ]} {
puts "ERROR: $message"
}
namespace import ::scobj::et2000::*
#add_et2000 et2000 137.157.201.213 502 1 5
#add_et2000 et2000 localhost 30502 1 5