diff --git a/site_ansto/instrument/config/environment/temperature/sct_oxford_mercury.tcl b/site_ansto/instrument/config/environment/temperature/sct_oxford_mercury.tcl index 786496ae..0a58d88d 100644 --- a/site_ansto/instrument/config/environment/temperature/sct_oxford_mercury.tcl +++ b/site_ansto/instrument/config/environment/temperature/sct_oxford_mercury.tcl @@ -100,20 +100,25 @@ namespace eval ::scobj::[set vendor]_[set device] { return "[join [lrange [split $node '/'] 0 end-1] '/']" } - proc setValue {tc_root nextState cmd} { + proc setPoint {tc_root nextState cmd} { # send a command to set a value - debug_log 1 "setValue tc_root=$tc_root sct=[sct] $cmd [sct target]" + debug_log 1 "setPoint tc_root=$tc_root sct=[sct] $cmd [sct target]" if { [hpropexists [sct] geterror] } { hdelprop [sct] geterror } set par [sct target] - if { "$cmd" == "XXXX" } { - set command "$cmd $par;$cmd?" - } else { - set command "$cmd $par" - } + set command "$cmd:$par" + debug_log 1 "setPoint tc_root=$tc_root sct=[sct] Write $command" sct send "$command" + sct driving 1 + sct time_check [clock seconds] + if {$par != [sct oldval]} { + sct oldval $par + sct update $par + sct utime readtime + debug_log 1 "setPoint: new data for $tc_root [sct] result=$par" + } return $nextState } @@ -175,6 +180,57 @@ namespace eval ::scobj::[set vendor]_[set device] { if { [string equal -nocase [basename [sct]] "XXXX"] } { return "idle" } + scan [lindex [split "$data" ":"] end] "%g" data + if {$data != [sct oldval]} { + debug_log 1 "[sct] changed to new:$data, from old:[sct oldval]" + sct oldval $data + sct update $data + sct utime readtime + } + } + return "idle" + } + + proc rdText {tc_root} { + # process the received response to the read request + debug_log 1 "[ns]::rdText tc_root=$tc_root sct=[sct] result=[sct result]" + set data [sct result] + if {[string equal -nocase -length 7 $data "ASCERR:"]} { + # the protocol driver has reported an error + sct geterror "$data" + } elseif {[string equal -nocase -length 1 $data "?"]} { + # the device has reported an error + sct geterror "Error: $data" + } else { + if { [string equal -nocase [basename [sct]] "XXXX"] } { + return "idle" + } + scan [lindex [split "$data" ":"] end] "%s" data + if {$data != [sct oldval]} { + debug_log 1 "[sct] changed to new:$data, from old:[sct oldval]" + sct oldval $data + sct update $data + sct utime readtime + } + } + return "idle" + } + + proc rdTextAll {tc_root} { + # process the received response to the read request + debug_log 1 "[ns]::rdText tc_root=$tc_root sct=[sct] result=[sct result]" + set data [sct result] + if {[string equal -nocase -length 7 $data "ASCERR:"]} { + # the protocol driver has reported an error + sct geterror "$data" + } elseif {[string equal -nocase -length 1 $data "?"]} { + # the device has reported an error + sct geterror "Error: $data" + } else { + if { [string equal -nocase [basename [sct]] "XXXX"] } { + return "idle" + } + #scan [lindex [split "$data" ":"] end] "%s" data if {$data != [sct oldval]} { debug_log 1 "[sct] changed to new:$data, from old:[sct oldval]" sct oldval $data @@ -415,97 +471,95 @@ namespace eval ::scobj::[set vendor]_[set device] { return "$nextState" } - proc loadCurve_XXX {fname} { - sct print "loading curve [sct target] into [sct]" - set lines [list] - if {[file exists "[sct target]"] && [file readable "[sct target]"]} { - sct print "opening [file normalize "[sct target]"]" - set f [open "[sct target]"] - while {1} { - set line [gets $f] - if {[eof $f]} { - close $f - break - } - # TODO - parse lines and build database - } - close $f - } - } - - proc loadCurve_34A {fname} { - sct print "loading curve [sct target] into [sct]" - set profile_index 0 - set lines [list] - if {[file exists "[sct target]"] && [file readable "[sct target]"]} { - sct print "opening [file normalize "[sct target]"]" - set f [open "[sct target]"] - while {1} { - set line [gets $f] - if {[eof $f]} { -#close $f - break - } - if { [string equal -nocase -length 5 "$line" "Name:"] } { - sct profile_name "[string range "$line" 6 end]" - } elseif { [string equal -nocase -length 14 "$line" "Serial number:"] } { - sct profile_number "[string range "$line" 15 end]" - } elseif { [string equal -nocase -length 7 "$line" "Format:"] } { - sct profile_format [lindex [split "$line"] 1] - } elseif { [string equal -nocase -length 6 "$line" "Limit:"] } { - sct profile_limit [lindex [split "$line"] 1] - } elseif { [string equal -nocase -length 12 "$line" "Coefficient:"] } { - sct profile_coeff [lindex [split "$line"] 1] - } elseif { [string equal -nocase -length 6 "$line" "Point "] } { - set rslt [scan "$line" "Point %d: %f,%f" idx r t] - if { $rslt != 3 } { - debug_log 1 "Profile error ($rslt) for: $line" - } else { - incr profile_index 1 - sct profile_line_[format "%03d" $profile_index] "[list $idx $r $t]" - debug_log 1 "Profile line $profile_index: [sct profile_line_[format "%03d" $profile_index]]" + proc chkTarget {tc_root par} { + set target {} + if {[hpropexists [sct] values]} { + set values [split [SplitReply [sct values]] ","] + foreach value $values { + #sct print "Testing $par against $value" + set lo_hi [split $value ":"] + if {[llength $lo_hi] == 2} { + if {[lindex $lo_hi 0] <= $par && [lindex $lo_hi 1] >= $par} { + #sct print "Success $par between [lindex $lo_hi 0] and [lindex $lo_hi 1]" + set target "$par" + break } } else { - debug_log 1 "Profile error unknown for: $line" + if {[string toupper "$par"] == [string toupper "$value"]} { + #sct print "Success $par matches $value" + set target "$par" + break + } } } - close $f - } - } - - proc ldCurve { tc_root nextState cmd } { - if { ! [hpropexists [sct] index] } { - sct index 0 - } - if { [hpropexists [sct] geterror] } { - hdelprop [sct] geterror - } - if { "[sct index]" <= 0 } { - [ns]::loadCurve_34A "[sct target]" - # TODO send curve header - debug_log 1 "Curve header: " - sct index [expr {[sct index] + 1}] - return write - } else { - # write curve to device - if { [hpropexists [sct] profile_line_[format "%03d" [sct index]]] } { - set profile_line [sct profile_line_[format "%03d" [sct index]]] - debug_log 1 "Profile line $profile_line" + if {"$target" == ""} { + #sct print "Failure $par no matches in $values" + return -code error "Invalid value: \"$par\"" } - return "$nextState" + } else { + set target "$par" } - return "$nextState" + return $target } - proc ackCurve {tc_root} { - sct index [expr {[sct index] + 1}] - if { [hpropexists [sct] profile_line_[format "%03d" [sct index]]] } { - return "write" +## +# @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} { + #debug_log 1 "drivestatus tc_root=$tc_root, sct=[sct], setpoint=[hval [sct]], temp=[hval [pathname [sct]]/sensor], driving=[sct driving]" + if {[sct driving]} { + # tolerance + set temp [hval [pathname [sct]]/sensor] + set loval [expr {[hval [sct]] - [sct tolerance]}] + set hival [expr {[hval [sct]] + [sct tolerance]}] + if {$loval <= $temp && $temp <= $hival} { + # settle time + set timeout [sct settletime] + set timecheck [sct time_check] + set currtime [clock seconds] + #debug_log 1 "drivestatus tc_root=$tc_root, sct=[sct], setpoint=[hval [sct]], temp=[hval [pathname [sct]]/sensor], temp=($loval, $temp, $hival), time=($timecheck, $currtime, $timeout)" + if { ($currtime - $timecheck) >= $timeout } { + debug_log 1 "drivestatus tc_root=$tc_root, sct=[sct], finished driving" + sct driving 0 + } + } else { + sct time_check [clock seconds] + } + return busy + } else { + debug_log 1 "drivestatus tc_root=$tc_root, sct=[sct], now idle" + return idle } - sct index 0 + } + + proc halt {tc_root} { + debug_log 1 "halt $tc_root" + foreach loop {Loop1 Loop2 Loop3 Loop4} { + set catch_status [ catch { + hset $tc_root/$loop/setpoint [hval $tc_root/$loop/sensor] + hsetprop $tc_root/$loop/setpoint driving 0 + } catch_message ] + } + debug_log 1 "halt $tc_root done driving" return idle } + proc check {tc_root} { + debug_log 1 "check tc_root=$tc_root, sct=[sct], target=[sct target]" + set catch_status [ catch { + set target [chkTarget $tc_root [sct target]] + if { "$target" == "" } { + error "setpoint violates limits" + } + } catch_message ] + if {$catch_status != 0} { + return -code error $catch_message + } + return OK + } + proc wrNode {tc_root cmd level} { set space [string repeat " " $level] set val [hval $tc_root] @@ -660,55 +714,42 @@ namespace eval ::scobj::[set vendor]_[set device] { set scobj_hpath /sics/$the_name hfactory $scobj_hpath/Loop1 plain spy none - hfactory $scobj_hpath/Heater plain spy none - hfactory $scobj_hpath/Chan1 plain spy none - hfactory $scobj_hpath/Curve1 plain spy none - hfactory $scobj_hpath/Analog1 plain spy none - hfactory $scobj_hpath/Analog2 plain spy none - hfactory $scobj_hpath/Sensor plain spy none + hfactory $scobj_hpath/Loop2 plain spy none + hfactory $scobj_hpath/Loop3 plain spy none + hfactory $scobj_hpath/Loop4 plain spy none + hfactory $scobj_hpath/Level plain spy none set deviceCommand {\ - Sensor value 1 0 0 float user {RDGK?} {rdValue} {} {} {}\ - {} setpoint 1 1 0 float user {SETP?} {rdValue} {SETP} {setValue} {}\ - {} device_state 1 0 0 text user {NULL} {getState.rdState} {} {} {}\ - {} alarm_reset 0 1 0 text user {} {} {ALMRST} {setValue} {}\ - {} device_reset 0 1 0 text user {} {} {*RST} {setValue} {}\ - {} mnmx_reset 0 1 0 text user {} {} {MNMXRST} {setValue} {}\ - {} Tree 0 1 0 text user {} {} {} {wrTree} {}\ - Loop1 ramp 1 1 0 text user {RAMP?} {rdValue} {RAMP} {setValue} {}\ - Loop1 ramp_enable 0 0 0 float user {} {} {} {} {}\ - Loop1 ramp_rate 0 0 0 float user {} {} {} {} {}\ - Loop1 setpoint 1 1 0 float user {SETP?} {rdValue} {SETP} {setValue} {}\ - Loop1 cset 1 1 0 text user {CSET?} {rdValue} {CSET} {setValue} {}\ - Loop1 sensor_channel 0 0 0 int user {} {} {} {} {}\ - Loop1 filter_enable 0 0 0 int user {} {} {} {} {}\ - Loop1 units 0 0 0 int user {} {} {} {} {}\ - Loop1 delay 0 0 0 int user {} {} {} {} {}\ - Loop1 current_or_power 0 0 0 int user {} {} {} {} {}\ - Loop1 htr_limit 0 0 0 int user {} {} {} {} {}\ - Loop1 htr_resistance 0 0 0 float user {} {} {} {} {}\ - Analog1 analog 1 1 0 text user {ANALOG?} {rdValue} {ANALOG} {setValue} {}\ - Analog1 aout 1 0 0 float user {AOUT?} {rdValue} {} {} {}\ - Analog2 analog 1 1 0 text user {ANALOG?} {rdValue} {ANALOG} {setValue} {}\ - Analog2 aout 1 0 0 float user {AOUT?} {rdValue} {} {} {}\ - Heater htr 1 0 0 float user {HTR?} {rdValue} {} {} {}\ - Heater htrrng 1 1 0 int user {HTRRNG?} {rdValue} {HTRRNG} {setValue} {}\ - Heater htrst 1 0 0 int user {HTRST?} {rdValue} {} {} {}\ - Chan1 alarm 1 1 0 text user {ALARM?} {rdValue} {ALARM} {setValue} {}\ - Chan1 alarmst 1 0 0 text user {ALARMST?} {rdValue} {} {} {}\ - Chan1 filter 1 1 0 text user {FILTER?} {rdValue} {FILTER} {setValue} {}\ - Chan1 inset 1 1 0 text user {INSET?} {rdValue} {INSET} {setValue} {}\ - Chan1 ldat 1 0 0 float user {LDAT?} {rdValue} {} {} {}\ - Chan1 linear 1 1 0 text user {LINEAR?} {rdValue} {LINEAR} {setValue} {}\ - Chan1 mdat 1 0 0 text user {MDAT?} {rdValue} {} {} {}\ - Chan1 mnmx 1 1 0 text user {MNMX?} {rdValue} {MNMX} {setValue} {}\ - Chan1 rdgk 1 0 0 float user {RDGK?} {rdValue} {} {} {}\ - Chan1 rdgpwr 1 0 0 float user {RDGPWR?} {rdValue} {} {} {}\ - Chan1 rdgr 1 0 0 float user {RDGR?} {rdValue} {} {} {}\ - Chan1 rdgrng 1 1 0 text user {RDGRNG?} {rdValue} {RDGRNG} {setValue} {}\ - Chan1 rdgst 1 0 0 int user {RDGST?} {rdValue} {} {} {}\ - Curve1 header 1 0 0 text user {CRVHDR?} {rdValue} {} {} {}\ - Curve1 profile 0 1 0 text user {} {} {XX} {ldCurve.ackCurve} {}\ + Loop1 setpoint 5 1 1 float user {READ:DEV:MB1.T1:TEMP:LOOP:TSET} {rdValue} {SET:DEV:MB1.T1:TEMP:LOOP:TSET} {setPoint} {1:333}\ + Loop1 sensor 1 0 0 float user {READ:DEV:MB1.T1:TEMP:SIG:TEMP} {rdValue} {} {} {}\ + Loop1 nick 5 0 0 text user {READ:DEV:MB1.T1:TEMP:NICK} {rdText} {} {} {}\ + Loop1 temp 0 0 0 text user {READ:DEV:MB1.T1:TEMP} {rdTextAll} {} {} {}\ + Loop1 loop 0 0 0 text user {READ:DEV:MB1.T1:TEMP:LOOP} {rdTextAll} {} {} {}\ + Loop1 heater 0 0 0 text user {READ:DEV:MB0.H1:HTR} {rdTextAll} {} {} {}\ + Loop1 power 5 0 0 float user {READ:DEV:MB0.H1:HTR:SIG:POWR} {rdValue} {} {} {}\ + Loop2 setpoint 5 1 1 float user {READ:DEV:DB6.T1:TEMP:LOOP:TSET} {rdValue} {SET:DEV:DB6.T1:TEMP:LOOP:TSET} {setPoint} {1:333}\ + Loop2 sensor 1 0 0 float user {READ:DEV:DB6.T1:TEMP:SIG:TEMP} {rdValue} {} {} {}\ + Loop2 nick 5 0 0 text user {READ:DEV:DB6.T1:TEMP:NICK} {rdText} {} {} {}\ + Loop2 temp 0 0 0 text user {READ:DEV:DB6.T1:TEMP} {rdTextAll} {} {} {}\ + Loop2 loop 0 0 0 text user {READ:DEV:DB6.T1:TEMP:LOOP} {rdTextAll} {} {} {}\ + Loop2 heater 0 0 0 text user {READ:DEV:DB1.H1:HTR} {rdTextAll} {} {} {}\ + Loop2 power 5 0 0 float user {READ:DEV:DB1.H1:HTR:SIG:POWR} {rdValue} {} {} {}\ + Loop3 setpoint 5 1 1 float user {READ:DEV:DB7.T1:TEMP:LOOP:TSET} {rdValue} {SET:DEV:DB7.T1:TEMP:LOOP:TSET} {setPoint} {1:333}\ + Loop3 sensor 1 0 0 float user {READ:DEV:DB7.T1:TEMP:SIG:TEMP} {rdValue} {} {} {}\ + Loop3 nick 5 0 0 text user {READ:DEV:DB7.T1:TEMP:NICK} {rdText} {} {} {}\ + Loop3 temp 0 0 0 text user {READ:DEV:DB7.T1:TEMP} {rdTextAll} {} {} {}\ + Loop3 loop 0 0 0 text user {READ:DEV:DB7.T1:TEMP:LOOP} {rdTextAll} {} {} {}\ + Loop3 heater 0 0 0 text user {READ:DEV:DB2.H1:HTR} {rdTextAll} {} {} {}\ + Loop3 power 5 0 0 float user {READ:DEV:DB2.H1:HTR:SIG:POWR} {rdValue} {} {} {}\ + Loop4 setpoint 5 1 1 float user {READ:DEV:DB8.T1:TEMP:LOOP:TSET} {rdValue} {SET:DEV:DB8.T1:TEMP:LOOP:TSET} {setPoint} {1:333}\ + Loop4 sensor 1 0 0 float user {READ:DEV:DB8.T1:TEMP:SIG:TEMP} {rdValue} {} {} {}\ + Loop4 nick 5 0 0 text user {READ:DEV:DB8.T1:TEMP:NICK} {rdText} {} {} {}\ + Loop4 temp 0 0 0 text user {READ:DEV:DB8.T1:TEMP} {rdTextAll} {} {} {}\ + Loop4 loop 0 0 0 text user {READ:DEV:DB7.T1:TEMP:LOOP} {rdTextAll} {} {} {}\ + Loop4 heater 0 0 0 text user {READ:DEV:DB3.H1:HTR} {rdTextAll} {} {} {}\ + Loop4 power 5 0 0 float user {READ:DEV:DB3.H1:HTR:SIG:POWR} {rdValue} {} {} {}\ + Level Nitrogen 15 0 0 float user {READ:DEV:DB5.L1:LVL:SIG:NIT:LEV} {rdValue} {} {} {}\ + Level Helium 15 0 0 float user {READ:DEV:DB5.L1:LVL:SIG:HEL:LEV} {rdValue} {} {} {}\ } foreach {cmdGroup varName\ @@ -718,17 +759,98 @@ namespace eval ::scobj::[set vendor]_[set device] { wrCmd wrFunc\ allowedValues} $deviceCommand { [ns]::createNode $scobj_hpath $sct_controller\ - $cmdGroup $varName\ - $readable $writable $drivable\ - $dataType $permission\ - $rdCmd $rdFunc\ - $wrCmd $wrFunc\ - $allowedValues $the_klass + $cmdGroup $varName\ + $readable $writable $drivable\ + $dataType $permission\ + $rdCmd $rdFunc\ + $wrCmd $wrFunc\ + $allowedValues $the_klass } + foreach cmdGroup { Loop1 Loop2 Loop3 Loop4 } { + set pathName "$scobj_hpath/$cmdGroup" + hsetprop $pathName type part + hsetprop $pathName klass NXsensor + hsetprop $pathName privilege spy + hsetprop $pathName control true + hsetprop $pathName data true + hsetprop $pathName nxsave true + hsetprop $pathName/setpoint tolerance $tol + hsetprop $pathName/setpoint settletime 5 + foreach varName { setpoint sensor nick } { + set nodeName "$pathName/$varName" + hsetprop $nodeName nxalias ${the_name}_${cmdGroup}_${varName}_value + hsetprop $nodeName long_name $varName + hsetprop $nodeName klass sensor + hsetprop $nodeName priviledge user + hsetprop $nodeName control true + hsetprop $nodeName data true + hsetprop $nodeName nxsave true + hsetprop $nodeName mutable true + hsetprop $nodeName sdsinfo ::nexus::scobj::sdsinfo + } + } + + foreach cmdGroup { Level } { + set pathName "$scobj_hpath/$cmdGroup" + hsetprop $pathName type part + hsetprop $pathName klass NXsensor + hsetprop $pathName privilege spy + hsetprop $pathName control true + hsetprop $pathName data true + hsetprop $pathName nxsave true + foreach varName { Nitrogen Helium } { + set nodeName "$pathName/$varName" + hsetprop $nodeName long_name $varName + hsetprop $nodeName klass sensor + hsetprop $nodeName priviledge user + hsetprop $nodeName control true + hsetprop $nodeName data true + hsetprop $nodeName nxsave true + hsetprop $nodeName nxalias ${the_name}_${cmdGroup}_${varName}_value + hsetprop $nodeName mutable true + hsetprop $nodeName sdsinfo ::nexus::scobj::sdsinfo + } + } + + hfactory $scobj_hpath/device_state plain spy none hset $scobj_hpath/device_state "STATE_INIT" hsetprop $scobj_hpath/device_state substate 0 + set point 0 + set catch_status_2 [ catch { + hsetprop $scobj_hpath privilege spy + hsetprop $scobj_hpath long_name $the_name + hsetprop $scobj_hpath sicsdev $the_name + hsetprop $scobj_hpath nxalias $the_name + hsetprop $scobj_hpath control true + hsetprop $scobj_hpath data true + hsetprop $scobj_hpath nxsave true + hsetprop $scobj_hpath mutable true + hsetprop $scobj_hpath klass parameter + hsetprop $scobj_hpath sdsinfo ::nexus::scobj::sdsinfo + set point 1 +if {0} { + ::scobj::hinitprops $the_name Loop1 + set point 2 + ::scobj::hinitprops $the_name Loop2/setpoint + set point 3 + ::scobj::hinitprops $the_name Loop3/setpoint + set point 4 + ::scobj::hinitprops $the_name Loop4/setpoint + set point 5 +} + } catch_message_2 ] + if {$catch_status_2 != 0} { + debug_log 5 "error in [ns]::mk_sct_driver($point) $catch_message_2" + return -code error "in [ns]::mk_sct_driver($point) $catch_message_2" + } + if {[SplitReply [environment_simulation]]=="false"} { + ansto_makesctdrive ${the_name}_loop1 $scobj_hpath/Loop1/setpoint $scobj_hpath/Loop1/sensor $sct_controller + ansto_makesctdrive ${the_name}_loop2 $scobj_hpath/Loop2/setpoint $scobj_hpath/Loop2/sensor $sct_controller + ansto_makesctdrive ${the_name}_loop3 $scobj_hpath/Loop3/setpoint $scobj_hpath/Loop3/sensor $sct_controller + ansto_makesctdrive ${the_name}_loop4 $scobj_hpath/Loop4/setpoint $scobj_hpath/Loop4/sensor $sct_controller + } } catch_message ] if {$catch_status != 0} { debug_log 5 "error in [ns]::mk_sct_driver $catch_message" @@ -738,6 +860,8 @@ namespace eval ::scobj::[set vendor]_[set device] { proc add_[set vendor]_[set device] {the_name IP port {_tol 5.0}} { set [ns]::log_file "/tmp/[set [ns]::ven_dev]_[set the_name].log" + set fd [open [ns]::log_file "w"] + close $fd debug_log 1 "add_[set [ns]::vendor]_[set [ns]::device] ${the_name} ${IP} ${port} ${_tol}" puts "Namespace: [namespace current]" puts "::vendor $::vendor"