# Define procs in ::scobj::xxx namespace # MakeSICSObj $obj SCT_ # The MakeSICSObj cmd adds a /sics/$obj node. NOTE the /sics node is not browsable. namespace eval ::scobj::lh45 { # Temperature controllers must have at least the following nodes # /tempcont/setpoint # /tempcont/sensor/value proc getValue {nextState cmd} { sct send $cmd return $nextState } proc setValue {nextState cmd} { set par [sct target] sct send "$cmd $par" return $nextState } proc setPoint {tc_root nextState cmd} { set par [sct target] if [hval $tc_root/apply_tolerance] { set tol [hval $tc_root/tolerance] hset $tc_root/subtemp_warnlimit [expr $par - $tol] hset $tc_root/overtemp_warnlimit [expr $par + $tol] } hset $tc_root/power 1 hset $tc_root/status "busy" if {[sct writestatus] == "start"} { # Called by drive adapter hsetprop $tc_root/setpoint driving 1 } sct send "$cmd $par" return $nextState } proc rdValue {} { set data [sct result] switch -glob -- $data { "ASCERR:*" { sct geterror $data } default { if {$data != [sct oldval]} { sct oldval $data sct update $data sct utime readtime } } } return idle } ## # @brief Check if temperature controller is on or off # # Julabo LH45 Manual pg41 # If a time of 00:00 is set for a profile, the profile is continued with # the next section only after the setpoint temperature (±0.2 °C) is # reached. # That's good enough for me. # # When the power is off no temp is outside of any range, therefore we # report that the T.C. is in tolerance. This implies that counters # can continue running if the T.C. is off, also lets you acquire data # while allowing a sample to cool to ambient temperature. proc rdPower {tc_root} { set power [sct result] sct utime readtime switch -glob -- $power { "ASCERR:*" { sct geterror $power } default { set oldval [sct oldval] if {$oldval == "UNKNOWN"} { sct utime timecheck } if {$power != $oldval} { sct oldval $power sct update $power } if {$power==1} { set intol [::scobj::lh45::checktol $tc_root [sct readtime] [sct timecheck]] if {$intol==0} { sct utime timecheck } set setpt [hval $tc_root/setpoint] set temp [hval $tc_root/sensor/value] if {[expr abs($setpt - $temp)] > 0.2} { hset $tc_root/emon/monmode "drive" hset $tc_root/status "busy" } else { hsetprop $tc_root/setpoint driving 0 hset $tc_root/emon/monmode "monitor" hset $tc_root/status "idle" } } else { hset $tc_root/emon/monmode "idle" hset $tc_root/emon/isintol 1 hset $tc_root/status "idle" } } } return idle } proc getState {tc_root nextState cmd} { # Apply tolerance if [hval $tc_root/apply_tolerance] { set tol [hval $tc_root/tolerance] set setpt [hval $tc_root/setpoint] hset $tc_root/subtemp_warnlimit [expr $setpt - $tol] hset $tc_root/overtemp_warnlimit [expr $setpt + $tol] } sct send $cmd return $nextState } proc 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 {[expr $currtime - $timecheck] > $timeout} { hset $tc_root/emon/isintol 1 } return 1 } } ## # @brief Reads the current lh45 state and error messages. proc rdState {tc_root} { set data [sct result] if {[string first "ASCERR:" $data] >=0} { sct geterror $data } elseif {$data != [sct oldval]} { sct oldval $data sct update $data sct utime readtime switch -- [lindex $data 0] { "00" { hset $tc_root/remote_ctrl False } "01" { hset $tc_root/remote_ctrl False } "02" { hset $tc_root/remote_ctrl True } "03" { hset $tc_root/remote_ctrl True } default { hset $tc_root/remote_ctrl UNKNOWN hset $tc_root/lh45_lasterror $data } } } 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} { hset $tc_root/setpoint [hval $tc_root/sensor/value] hsetprop $tc_root/setpoint driving 0 return idle } proc mk_sct_julabo_lh45 {sct_controller klass tempobj tol} { if [ catch { set ns ::scobj::lh45 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 rdValue "in_sp_00" hsetprop $scobj_hpath/setpoint write ${ns}::setPoint $scobj_hpath noResponse "out_sp_00" 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 hfactory $scobj_hpath/overtemp_warnlimit plain user float hsetprop $scobj_hpath/overtemp_warnlimit read ${ns}::getValue rdValue "in_sp_03" hsetprop $scobj_hpath/overtemp_warnlimit write ${ns}::setValue noResponse "out_sp_03" 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 rdValue "in_sp_04" hsetprop $scobj_hpath/subtemp_warnlimit write ${ns}::setValue noResponse "out_sp_04" 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 rdValue "in_pv_00" hsetprop $scobj_hpath/sensor/value rdValue ${ns}::rdValue hsetprop $scobj_hpath/sensor/value oldval UNKNOWN hsetprop $scobj_hpath/sensor/value units "C" hfactory $scobj_hpath/heating_power_percent plain internal float hsetprop $scobj_hpath/heating_power_percent read ${ns}::getValue rdValue "in_pv_01" hsetprop $scobj_hpath/heating_power_percent rdValue ${ns}::rdValue hsetprop $scobj_hpath/heating_power_percent oldval UNKNOWN hfactory $scobj_hpath/power plain user int hsetprop $scobj_hpath/power read ${ns}::getValue rdValue "in_mode_05" hsetprop $scobj_hpath/power write ${ns}::setValue noResponse "out_mode_05" hsetprop $scobj_hpath/power rdValue ${ns}::rdPower $scobj_hpath hsetprop $scobj_hpath/power noResponse ${ns}::noResponse hsetprop $scobj_hpath/power oldval UNKNOWN hsetprop $scobj_hpath/power values 0,1 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/status plain spy text hset $scobj_hpath/status "idle" hsetprop $scobj_hpath/status values busy,idle hfactory $scobj_hpath/lh45_state plain spy text hsetprop $scobj_hpath/lh45_state read ${ns}::getState $scobj_hpath rdState "status" hsetprop $scobj_hpath/lh45_state rdState ${ns}::rdState $scobj_hpath hsetprop $scobj_hpath/lh45_state oldval UNKNOWN hfactory $scobj_hpath/remote_ctrl plain spy text hset $scobj_hpath/remote_ctrl UNKNOWN hfactory $scobj_hpath/lh45_lasterror plain user text hset $scobj_hpath/lh45_lasterror "" hfactory $scobj_hpath/lowerlimit plain mugger float hsetprop $scobj_hpath/lowerlimit units "C" hset $scobj_hpath/lowerlimit -45 hfactory $scobj_hpath/upperlimit plain mugger float hsetprop $scobj_hpath/upperlimit units "C" hset $scobj_hpath/upperlimit 130 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/power $sct_controller write $scobj_hpath/power $sct_controller poll $scobj_hpath/sensor/value $sct_controller poll $scobj_hpath/lh45_state 5 halt read } 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 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_julabo_lh45 } ## # @brief Create a Julabo lh45 temperature controller # # @param name, then name of the temperature controller (eg tc1) # @param IP, the IP address of the device, this can be a hostname, (eg ca5-quokka) # @param port, the IP protocol port number of the device # @param _tol (optional), this is the initial tolerance setting proc add_lh45 {name IP port {_tol 5.0}} { if {[SplitReply [environment_simulation]]=="false"} { makesctcontroller sct_lh45 std ${IP}:$port "\r" } mk_sct_julabo_lh45 sct_lh45 environment $name $_tol makesctemon $name /sics/$name/emon/monmode /sics/$name/emon/isintol /sics/$name/emon/errhandler } namespace import ::scobj::lh45::*