From ffef4e208f89cae40f761fadea834d5067b32d28 Mon Sep 17 00:00:00 2001 From: Douglas Clowes Date: Mon, 23 Jun 2014 17:40:10 +1000 Subject: [PATCH] SCT driver for Lakeshore 218S Temperature Monitor --- .../environment/temperature/lakeshore_218.sct | 43 + .../temperature/sct_lakeshore_218.tcl | 968 +++++++----------- 2 files changed, 397 insertions(+), 614 deletions(-) create mode 100644 site_ansto/instrument/config/environment/temperature/lakeshore_218.sct diff --git a/site_ansto/instrument/config/environment/temperature/lakeshore_218.sct b/site_ansto/instrument/config/environment/temperature/lakeshore_218.sct new file mode 100644 index 00000000..73ea7e81 --- /dev/null +++ b/site_ansto/instrument/config/environment/temperature/lakeshore_218.sct @@ -0,0 +1,43 @@ +driver lakeshore_218 = { + vendor = lakeshore; device = ls218; protocol = std; + class = environment; simulation_group = environment_simulation; + + group = { + data = false; + nxsave = false; + control = false; + readable = 1; + + var krdg = { + type = text; + read_command = "KRDG? 0"; + read_function = read_temps; + } + } + + group sensor = { + priv = user; + type = float; + mutable = true; + var ch1 = { type = float; units = 'K'; } + var ch2 = { type = float; units = 'K'; } + var ch3 = { type = float; units = 'K'; } + var ch4 = { type = float; units = 'K'; } + var ch5 = { type = float; units = 'K'; } + var ch6 = { type = float; units = 'K'; } + var ch7 = { type = float; units = 'K'; } + var ch8 = { type = float; units = 'K'; } + } + + code read_temps = {%% + set temps [split "${data}" ","] + hupdate ${tc_root}/sensor/ch1 [lindex ${temps} 0] + hupdate ${tc_root}/sensor/ch2 [lindex ${temps} 1] + hupdate ${tc_root}/sensor/ch3 [lindex ${temps} 2] + hupdate ${tc_root}/sensor/ch4 [lindex ${temps} 3] + hupdate ${tc_root}/sensor/ch5 [lindex ${temps} 4] + hupdate ${tc_root}/sensor/ch6 [lindex ${temps} 5] + hupdate ${tc_root}/sensor/ch7 [lindex ${temps} 6] + hupdate ${tc_root}/sensor/ch8 [lindex ${temps} 7] + %%} +} diff --git a/site_ansto/instrument/config/environment/temperature/sct_lakeshore_218.tcl b/site_ansto/instrument/config/environment/temperature/sct_lakeshore_218.tcl index a0ee4a12..5032f8be 100644 --- a/site_ansto/instrument/config/environment/temperature/sct_lakeshore_218.tcl +++ b/site_ansto/instrument/config/environment/temperature/sct_lakeshore_218.tcl @@ -1,635 +1,375 @@ -# Define procs in ::scobj::xxx namespace -# MakeSICSObj $obj SCT_ -# The MakeSICSObj cmd adds a /sics/$obj node. NOTE the /sics node is not browsable. +# Generated driver for lakeshore_218 +# vim: ft=tcl tabstop=8 softtabstop=2 shiftwidth=2 nocindent smartindent +# -## -# /*-------------------------------------------------------------------------- -# L A K E S H O R E 3 x x S E R I E S D R I V E R -# -# This file contains the implementation of a driver for the Lakeshore 218 -# controller implemented as a scriptcontext object in TCL. -# object in TCL. -# -# @author: Jing Chen, ANSTO, 2012-08-22 -# @brief: SICS driver for Lakeshore 218 Temperature Controller (in TCL) -# -# ----------------------------------------------------------------------------*/ - -proc debug_log {args} { - set d1 [clock format [clock seconds] -format %d%h%Y] - set fd [open "../log/ls218Temp$d1.log" a] - puts $fd "[clock format [clock seconds] -format "%D %T "] [string trim $args "{}"]" - close $fd +namespace eval ::scobj::lakeshore_218 { + set debug_threshold 5 } -# Default temperature controller parameters -namespace eval ::scobj::ls218 { - #variable logString "" +proc ::scobj::lakeshore_218::debug_log {tc_root debug_level debug_string} { + set catch_status [ catch { + set debug_threshold [hgetpropval ${tc_root} debug_threshold] + if {${debug_level} >= ${debug_threshold}} { + set fd [open "../log/lakeshore_218_[basename ${tc_root}].log" "a"] + set line "[clock format [clock seconds] -format "%T"] ${debug_string}" + puts ${fd} "${line}" + close ${fd} + } + } catch_message ] } -############# Reading polled nodes ################################### +proc ::scobj::lakeshore_218::sics_log {debug_level debug_string} { + set catch_status [ catch { + set debug_threshold ${::scobj::lakeshore_218::debug_threshold} + if {${debug_level} >= ${debug_threshold}} { + sicslog "::scobj::lakeshore_218::${debug_string}" + } + } catch_message ] +} -## -# @brief Sends a query command to the device via a read node formalism -# @param tc_root The path to the root of the node -# @param nextState The next function to call after this one (typically 'rdValue' -# to read the response from the device) -# @param cmd The query command to be send to the device (written to the -# node data value) -# @param idx indicates which control loop or which input channel -# the command belongs to -# @return nextState The next function to call after this one (typically 'rdValue') -proc ::scobj::ls218::getValue {idx cmd chID nextState} { - if {[ catch { - if {[hpropexists [sct] geterror]} { +# check function for hset change +proc ::scobj::lakeshore_218::checkrange {tc_root} { + set catch_status [ catch { + debug_log ${tc_root} 1 "checkrange tc_root=${tc_root} sct=[sct] target=[sct target]" + set setpoint [sct target] + if { [hpropexists [sct] lowerlimit] } { + set lolimit [sct lowerlimit] + } else { + # lowerlimit not set, use target + set lolimit [sct target] + } + if { [hpropexists [sct] upperlimit] } { + set hilimit [sct upperlimit] + } else { + # upperlimit not set, use target + set hilimit [sct target] + } +# checkrange hook code goes here + if { ${setpoint} < ${lolimit} || ${setpoint} > ${hilimit} } { + error "setpoint ${setpoint} violates limits (${lolimit}..${hilimit}) on [sct]" + } + return OK + } catch_message ] + handle_exception ${catch_status} ${catch_message} +} + +# function to request the read of a parameter on a device +proc ::scobj::lakeshore_218::getValue {tc_root nextState cmd_str} { + set catch_status [ catch { + debug_log ${tc_root} 1 "getValue tc_root=${tc_root} sct=[sct] cmd=${cmd_str}" + if { [hpropexists [sct] geterror] } { hdelprop [sct] geterror } + set cmd "${cmd_str}" +# getValue hook code goes here + debug_log ${tc_root} 1 "getValue sct send ${cmd}" + if {![string equal -nocase -length 10 ${cmd} "@@NOSEND@@"]} { + sct send "${cmd}" + } + return ${nextState} + } catch_message ] + handle_exception ${catch_status} ${catch_message} +} - if {$chID == 1} { - set comm "$cmd $idx" - } elseif {$chID == 0} { - set comm $cmd - } elseif {$chID == "G"} { - if {1 <= $idx <= 4} { - set comm "$cmd A" +# function to check the write parameter on a device +proc ::scobj::lakeshore_218::noResponse {tc_root} { + set catch_status [ catch { + debug_log ${tc_root} 1 "noResponse tc_root=${tc_root} sct=[sct] resp=[sct result]" +# noResponse hook code goes here + return "idle" + } catch_message ] + handle_exception ${catch_status} ${catch_message} +} + +# function to parse the read of a parameter on a device +proc ::scobj::lakeshore_218::rdValue {tc_root} { + set catch_status [ catch { + debug_log ${tc_root} 1 "rdValue tc_root=${tc_root} sct=[sct] result=[sct result]" + if { [hpropexists [sct] geterror] } { + hdelprop [sct] geterror + } + set data [sct result] + set nextState "idle" + if {[string equal -nocase -length 7 ${data} "ASCERR:"]} { + # the protocol driver has reported an error + sct geterror "${data}" + error "[sct geterror]" + } +# rdValue hook code goes here + if { ${data} != [sct oldval] } { + debug_log ${tc_root} 1 "[sct] changed to new:${data}, from old:[sct oldval]" + sct oldval ${data} + sct update ${data} + sct utime readtime + } + return ${nextState} + } catch_message ] + handle_exception ${catch_status} ${catch_message} +} + +# function to parse the read of a parameter on a device +proc ::scobj::lakeshore_218::read_temps {tc_root} { + set catch_status [ catch { + debug_log ${tc_root} 1 "read_temps tc_root=${tc_root} sct=[sct] result=[sct result]" + if { [hpropexists [sct] geterror] } { + hdelprop [sct] geterror + } + set data [sct result] + set nextState "idle" + if {[string equal -nocase -length 7 ${data} "ASCERR:"]} { + # the protocol driver has reported an error + sct geterror "${data}" + error "[sct geterror]" + } +# read_temps hook code starts + set temps [split "${data}" ","] + hupdate ${tc_root}/sensor/ch1 [lindex ${temps} 0] + hupdate ${tc_root}/sensor/ch2 [lindex ${temps} 1] + hupdate ${tc_root}/sensor/ch3 [lindex ${temps} 2] + hupdate ${tc_root}/sensor/ch4 [lindex ${temps} 3] + hupdate ${tc_root}/sensor/ch5 [lindex ${temps} 4] + hupdate ${tc_root}/sensor/ch6 [lindex ${temps} 5] + hupdate ${tc_root}/sensor/ch7 [lindex ${temps} 6] + hupdate ${tc_root}/sensor/ch8 [lindex ${temps} 7] +# read_temps hook code ends + if { [hpropexists [sct] geterror] } { + debug_log ${tc_root} 9 "[sct] error: [sct geterror]" + error "[sct geterror]" + } + if { ${data} != [sct oldval] } { + debug_log ${tc_root} 1 "[sct] changed to new:${data}, from old:[sct oldval]" + sct oldval ${data} + sct update ${data} + sct utime readtime + } + return ${nextState} + } catch_message ] + handle_exception ${catch_status} ${catch_message} +} + +# function to write a parameter value on a device +proc ::scobj::lakeshore_218::setValue {tc_root nextState cmd_str} { + set catch_status [ catch { + debug_log ${tc_root} 1 "setValue tc_root=${tc_root} sct=[sct] cmd=${cmd_str}" + if { [hpropexists [sct] geterror] } { + hdelprop [sct] geterror + } + set par [sct target] + set cmd "${cmd_str}${par}" +# setValue hook code goes here + if { [hpropexists [sct] driving] } { + if { [hpropexists [sct] writestatus] && [sct writestatus] == "start" } { + sct driving 1 + } + } + debug_log ${tc_root} 1 "setValue sct send ${cmd}" + if {![string equal -nocase -length 10 ${cmd} "@@NOSEND@@"]} { + sct send "${cmd}" + } + return ${nextState} + } catch_message ] + handle_exception ${catch_status} ${catch_message} +} + +proc ::scobj::lakeshore_218::mkDriver { sct_controller name } { + ::scobj::lakeshore_218::sics_log 9 "::scobj::lakeshore_218::mkDriver for ${name}" + set ns "[namespace current]" + set catch_status [ catch { + + MakeSICSObj ${name} SCT_OBJECT + + sicslist setatt ${name} klass environment + sicslist setatt ${name} long_name ${name} + + set scobj_hpath /sics/${name} + + hfactory ${scobj_hpath}/krdg plain user text + hsetprop ${scobj_hpath}/krdg read ${ns}::getValue ${scobj_hpath} read_temps {KRDG? 0} + hsetprop ${scobj_hpath}/krdg read_temps ${ns}::read_temps ${scobj_hpath} + hsetprop ${scobj_hpath}/krdg control false + hsetprop ${scobj_hpath}/krdg data false + hsetprop ${scobj_hpath}/krdg mutable false + hsetprop ${scobj_hpath}/krdg nxsave false + hsetprop ${scobj_hpath}/krdg oldval UNKNOWN + hsetprop ${scobj_hpath}/krdg sdsinfo "::nexus::scobj::sdsinfo" + hsetprop ${scobj_hpath}/krdg type "part" + hsetprop ${scobj_hpath}/krdg nxalias "${name}_krdg" + + if {[string equal -nocase [SplitReply [environment_simulation]] "false"]} { + ${sct_controller} poll ${scobj_hpath}/krdg 1 + } else { + ::scobj::lakeshore_218::sics_log 9 "[environment_simulation] => No poll/write for lakeshore_218" + } + + hfactory ${scobj_hpath}/sensor plain spy none + + hfactory ${scobj_hpath}/sensor/ch1 plain user float + hsetprop ${scobj_hpath}/sensor/ch1 control true + hsetprop ${scobj_hpath}/sensor/ch1 data true + hsetprop ${scobj_hpath}/sensor/ch1 mutable true + hsetprop ${scobj_hpath}/sensor/ch1 nxsave true + hsetprop ${scobj_hpath}/sensor/ch1 units K + hsetprop ${scobj_hpath}/sensor/ch1 oldval 0.0 + hsetprop ${scobj_hpath}/sensor/ch1 sdsinfo "::nexus::scobj::sdsinfo" + hsetprop ${scobj_hpath}/sensor/ch1 type "part" + hsetprop ${scobj_hpath}/sensor/ch1 nxalias "${name}_sensor_ch1" + + hfactory ${scobj_hpath}/sensor/ch2 plain user float + hsetprop ${scobj_hpath}/sensor/ch2 control true + hsetprop ${scobj_hpath}/sensor/ch2 data true + hsetprop ${scobj_hpath}/sensor/ch2 mutable true + hsetprop ${scobj_hpath}/sensor/ch2 nxsave true + hsetprop ${scobj_hpath}/sensor/ch2 units K + hsetprop ${scobj_hpath}/sensor/ch2 oldval 0.0 + hsetprop ${scobj_hpath}/sensor/ch2 sdsinfo "::nexus::scobj::sdsinfo" + hsetprop ${scobj_hpath}/sensor/ch2 type "part" + hsetprop ${scobj_hpath}/sensor/ch2 nxalias "${name}_sensor_ch2" + + hfactory ${scobj_hpath}/sensor/ch3 plain user float + hsetprop ${scobj_hpath}/sensor/ch3 control true + hsetprop ${scobj_hpath}/sensor/ch3 data true + hsetprop ${scobj_hpath}/sensor/ch3 mutable true + hsetprop ${scobj_hpath}/sensor/ch3 nxsave true + hsetprop ${scobj_hpath}/sensor/ch3 units K + hsetprop ${scobj_hpath}/sensor/ch3 oldval 0.0 + hsetprop ${scobj_hpath}/sensor/ch3 sdsinfo "::nexus::scobj::sdsinfo" + hsetprop ${scobj_hpath}/sensor/ch3 type "part" + hsetprop ${scobj_hpath}/sensor/ch3 nxalias "${name}_sensor_ch3" + + hfactory ${scobj_hpath}/sensor/ch4 plain user float + hsetprop ${scobj_hpath}/sensor/ch4 control true + hsetprop ${scobj_hpath}/sensor/ch4 data true + hsetprop ${scobj_hpath}/sensor/ch4 mutable true + hsetprop ${scobj_hpath}/sensor/ch4 nxsave true + hsetprop ${scobj_hpath}/sensor/ch4 units K + hsetprop ${scobj_hpath}/sensor/ch4 oldval 0.0 + hsetprop ${scobj_hpath}/sensor/ch4 sdsinfo "::nexus::scobj::sdsinfo" + hsetprop ${scobj_hpath}/sensor/ch4 type "part" + hsetprop ${scobj_hpath}/sensor/ch4 nxalias "${name}_sensor_ch4" + + hfactory ${scobj_hpath}/sensor/ch5 plain user float + hsetprop ${scobj_hpath}/sensor/ch5 control true + hsetprop ${scobj_hpath}/sensor/ch5 data true + hsetprop ${scobj_hpath}/sensor/ch5 mutable true + hsetprop ${scobj_hpath}/sensor/ch5 nxsave true + hsetprop ${scobj_hpath}/sensor/ch5 units K + hsetprop ${scobj_hpath}/sensor/ch5 oldval 0.0 + hsetprop ${scobj_hpath}/sensor/ch5 sdsinfo "::nexus::scobj::sdsinfo" + hsetprop ${scobj_hpath}/sensor/ch5 type "part" + hsetprop ${scobj_hpath}/sensor/ch5 nxalias "${name}_sensor_ch5" + + hfactory ${scobj_hpath}/sensor/ch6 plain user float + hsetprop ${scobj_hpath}/sensor/ch6 control true + hsetprop ${scobj_hpath}/sensor/ch6 data true + hsetprop ${scobj_hpath}/sensor/ch6 mutable true + hsetprop ${scobj_hpath}/sensor/ch6 nxsave true + hsetprop ${scobj_hpath}/sensor/ch6 units K + hsetprop ${scobj_hpath}/sensor/ch6 oldval 0.0 + hsetprop ${scobj_hpath}/sensor/ch6 sdsinfo "::nexus::scobj::sdsinfo" + hsetprop ${scobj_hpath}/sensor/ch6 type "part" + hsetprop ${scobj_hpath}/sensor/ch6 nxalias "${name}_sensor_ch6" + + hfactory ${scobj_hpath}/sensor/ch7 plain user float + hsetprop ${scobj_hpath}/sensor/ch7 control true + hsetprop ${scobj_hpath}/sensor/ch7 data true + hsetprop ${scobj_hpath}/sensor/ch7 mutable true + hsetprop ${scobj_hpath}/sensor/ch7 nxsave true + hsetprop ${scobj_hpath}/sensor/ch7 units K + hsetprop ${scobj_hpath}/sensor/ch7 oldval 0.0 + hsetprop ${scobj_hpath}/sensor/ch7 sdsinfo "::nexus::scobj::sdsinfo" + hsetprop ${scobj_hpath}/sensor/ch7 type "part" + hsetprop ${scobj_hpath}/sensor/ch7 nxalias "${name}_sensor_ch7" + + hfactory ${scobj_hpath}/sensor/ch8 plain user float + hsetprop ${scobj_hpath}/sensor/ch8 control true + hsetprop ${scobj_hpath}/sensor/ch8 data true + hsetprop ${scobj_hpath}/sensor/ch8 mutable true + hsetprop ${scobj_hpath}/sensor/ch8 nxsave true + hsetprop ${scobj_hpath}/sensor/ch8 units K + hsetprop ${scobj_hpath}/sensor/ch8 oldval 0.0 + hsetprop ${scobj_hpath}/sensor/ch8 sdsinfo "::nexus::scobj::sdsinfo" + hsetprop ${scobj_hpath}/sensor/ch8 type "part" + hsetprop ${scobj_hpath}/sensor/ch8 nxalias "${name}_sensor_ch8" + hsetprop ${scobj_hpath} klass environment + hsetprop ${scobj_hpath} debug_threshold 5 +# mkDriver hook code goes here + } catch_message ] + handle_exception ${catch_status} ${catch_message} +} + +namespace eval ::scobj::lakeshore_218 { + namespace export debug_threshold + namespace export debug_log + namespace export sics_log + namespace export mkDriver +} + +proc add_lakeshore_218 {name IP port} { + set catch_status [ catch { + ::scobj::lakeshore_218::sics_log 9 "add_lakeshore_218 ${name} ${IP} ${port}" + if {[string equal -nocase [SplitReply [environment_simulation]] "false"]} { + if {[string equal -nocase "aqadapter" "${IP}"]} { + ::scobj::lakeshore_218::sics_log 9 "makesctcontroller sct_${name} aqadapter ${port}" + makesctcontroller sct_${name} aqadapter ${port} } else { - set comm "$cmd B" + ::scobj::lakeshore_218::sics_log 9 "makesctcontroller sct_${name} std ${IP}:${port}" + makesctcontroller sct_${name} std ${IP}:${port} } } else { - return -code error "in getValue: error input Ch ID" + ::scobj::lakeshore_218::sics_log 9 "[environment_simulation] => No sctcontroller for lakeshore_218" } - sct send "$comm\r\n" - } message ]} { - return -code error "in getValue: $message" - } - after 1000 - return $nextState + ::scobj::lakeshore_218::sics_log 1 "::scobj::lakeshore_218::mkDriver sct_${name} ${name}" + ::scobj::lakeshore_218::mkDriver sct_${name} ${name} + } catch_message ] + handle_exception ${catch_status} ${catch_message} } -## -# @brief Reads the value of a read-node typically following a query command sent to the device -# rdValue is the default nextState for getValue() and setValue() -# @param idx indicates which control loop or which input channel the command belongs to -# @return idle Always returns system state idle - command sequence completed. -proc ::scobj::ls218::rdValue {nodeName varName idx} { - #variable logString +puts stdout "file evaluation of sct_lakeshore_218.tcl" +::scobj::lakeshore_218::sics_log 9 "file evaluation of sct_lakeshore_218.tcl" - if {[ catch { - set replyData [string trimright [sct result] " \r\n"] - - if {[string first "ASCERR" $replyData] != -1} { - sct geterror $replyData - return -code error "Error in LS218 Response: $replyData" - } elseif {[string length $replyData] < 1} { - return -code error "Error in LS218: no message returned from device" - } else { - set fields [split $replyData ,] - set curValue $replyData - } - - switch -exact $varName { - "alarm" { if {[lindex $fields 0] == 0} { - hset $nodeName/offOn off - } else { - hset $nodeName/offOn on - } - switch -exact [lindex $fields 1] { - 1 {hset $nodeName/source Kelvin} - 2 {hset $nodeName/source Celsius} - 3 {hset $nodeName/source "Sensor Units"} - 4 {hset $nodeName/source "Linear data"} - default {hset $nodeName/source UNKNOW} - } - hset $nodeName/highValue [lindex $fields 2] - hset $nodeName/lowValue [lindex $fields 3] - hset $nodeName/deadband [lindex $fields 4] - hset $nodeName/latchEnable [lindex $fields 5] - } - "aStatus" { if {[lindex $fields 0] == 0} { - hset $nodeName/highStatus Unactivated - } else { - hset $nodeName/highStatus Activated - } - if {[lindex $fields 1] == 0} { - hset $nodeName/lowStatus Unactivated - } else { - hset $nodeName/lowStatus Activated - } - } - "Celsius" { hset $nodeName [lindex $fields 0] - set curValue [lindex $fields 0] - } - "CurveHd" { hset $nodeName/curve [lindex $fields 0] - hset $nodeName/name [lindex $fields 1] - hset $nodeName/SN [lindex $fields 2] - switch -exact [lindex $fields 3] { - 2 {hset $nodeName/format V/K} - 3 {hset $nodeName/format Ohm/K} - 4 {hset $nodeName/format "log Ohm/K"} - default {} - } - hset $nodeName/limitValue [lindex $fields 4] - switch -exact [lindex $fields 5] { - 1 {hset $nodeName/coefficient negative} - 2 {hset $nodeName/coefficient positive} - default {hset $nodeName/coefficient UNKNOW} - } - } - "CurveID" { set ind [lindex $fields 0] - if {$ind == 0} { - hset $nodeName none - } elseif { 1 <= $ind <= 5} { - hset $nodeName "Standard Diode Curves" - } elseif { 6 <= $ind <= 9} { - hset $nodeName "Standard Platium Curves" - } elseif { 21 <= $ind <= 28 } { - hset $nodeName "User Curves" - } else { - } - } - "control" { set ind [lindex $fields 0] - if {$ind == 0} { - hset $nodeName off - } elseif {$ind == 1} { - hset $nodeName on - } else { - } - } - "inputType" { switch -exact [lindex $fields 0] { - 0 { hset $nodeName "2.5V Diode"} - 1 { hset $nodeName "7.5V Diode"} - 2 { hset $nodeName "250 Ohms Platinum"} - 3 { hset $nodeName "500 Ohms Platinum"} - 4 { hset $nodeName "5K Ohms Platinum"} - 5 { hset $nodeName "Cernox"} - default { hset $nodeName UNKNOW} - } - } - "Kelvin" { hset $nodeName $fields - #append $logString " Kelvin - $fields;" - set curValue $fields - } - "Linear" { hset $nodeName/varM [lindex $fields 0] - hset $nodeName/varB [lindex $fields 2] - switch -exact [lindex $fields 1] { - 1 { hset $nodeName/xSource Kelvin} - 2 { hset $nodeName/xSource Celsius} - 3 { hset $nodeName/xSource "sensor units"} - default { hset $nodeName/xSource UNKNOW} - } - } - "LinearEquData" { hset $nodeName $fields - set curValue $fields - } - "mnmxSource" { switch -exact [lindex $fields 0] { - 1 { hset $nodeName Kelvin} - 2 { hset $nodeName Celsius} - 3 { hset $nodeName "sensor units"} - 4 { hset $nodeName "linear data"} - default { hset $nodeName UNKNOW} - } - } - "mnmxValue" { hset $nodeName "[lindex $fields 0] , [lindex $fields 1]"} - "status" { - # RDGST? chID Reads input status returns an integer with the following meaning - # Bit Weighting StatusIndicator - # 4 16 temp underrange - # 5 32 temp overrange - # 6 64 units under range - # 7 128 untis over range - set field [string trimleft $fields 0] - if {[string length $field] == 0} { - set field 0 - } - - set str "" - set i [format %x $field] - set i [expr 0x$i >> 4] - set bitValue [expr 0x$i & 0x01] - if {$bitValue == 1} { append str "temp underrange, " } - set i [expr 0x$i >> 1] - set bitValue [expr 0x$i & 0x01] - if {$bitValue == 1} { append str "temp overrange, " } - set i [expr 0x$i >> 1] - set bitValue [expr 0x$i & 0x01] - if {$bitValue == 1} { append str "units under range, " } - set i [expr 0x$i >> 1] - set bitValue [expr 0x$i & 0x01] - if {$bitValue == 1} { append str "untis over range" } - - hset $nodeName $str - } - "SensorUnitValue" { hset $nodeName $fields - #append $logString " SensorUnitValue - $fields;" - set curValue $fields - } - "data" { hset $nodeName $fields - set curValue $fields - } - "aOutput" { switch -exact [lindex $fields 0] { - 0 { hset $nodeName/bipolarEnable "positive only" } - 1 { hset $nodeName/bipolarEnable "bipolar" } - default {} - } - switch -exact [lindex $fields 1] { - 0 { hset $nodeName/monitorMode off } - 1 { hset $nodeName/monitorMode input } - 2 { hset $nodeName/monitorMode manual } - default { hset $nodeName/monitorMode UNKNOW } - } - hset $nodeName/inputChID [lindex $fields 2] - switch -exact [lindex $fields 3] { - 1 { hset $nodeName/source Kelvin} - 2 { hset $nodeName/source Celsius} - 3 { hset $nodeName/source "sensor units"} - 4 { hset $nodeName/source "linear data"} - default { hset $nodeName/source UNKNOW} - } - hset $nodeName/highValue [lindex $fields 4] - hset $nodeName/lowValue [lindex $fields 5] - hset $nodeName/manualValue [lindex $fields 6] - } - "IDN" { hset $nodeName/manufacturer [lindex $fields 0] - hset $nodeName/model [lindex $fields 1] - hset $nodeName/serialNumber [lindex $fields 2] - hset $nodeName/firmwareDate [lindex $fields 3] - } - "logStatus" { switch -exact [lindex $fields 0] { - 0 { hset $nodeName off } - 1 { hset $nodeName on } - default { hset $nodeName UNKNOW } - } - } - "relay" { switch -exact [lindex $fields 0] { - 0 { hset $nodeName/mode off } - 1 { hset $nodeName/mode on } - default { hset $nodeName/mode UNKNOW } - } - hset $nodeName/input [lindex $fields 1] - switch -exact [lindex $fields 2] { - 0 { hset $nodeName/alarmType "low Alarm" } - 1 { hset $nodeName/alarmType "High Alarm" } - 2 { hset $nodeName/alarmType "Both Alarm" } - default { hset $nodeName/alarmType UNKNOW } - } - } - default { return -code error "in rdValue: wrong input parameter"} - } - if {$curValue != [sct oldval]} { - sct oldval $curValue - sct update $curValue - sct utime readtime - } - } message ]} { - return -code error "in rdValue: $message " - } - return idle -} - - -## -# @brief createNode() creates a node for the given nodename with the properties and virtual -# function names provided -# @param nodeType represent for sub-tree inputNode, outputNode and otherNode -# @param scobj_hpath string variable holding the path to the object's base node in sics (/sample/tc1) -# @param sct_controller name of the ls218 scriptcontext object (typically sct_ls218_tc1 or tc2) -# @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 pollEnabled set to 1 if the node property pollable is to be enabled (node gets read every 5 secs) -# @param idx indicates which control loop or which input channel the command corresponds to -# @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 hdbEnable set to 1 if need to create a HDB node, otherwise ignore all following parameters -# @param klasse Nexus class name (?) -# @param control attribute for HDB tree -# @param data attribute for HDB tree -# @param priv attribute for HDB tree -# @param type attribute for HDB tree -# @param nxsave attribute for HDB tree -# @param mutable attribute for HDB tree -# @param nxalias attribute for HDB tree -# @return OK - -proc createNode {scobj_hpath idx sct_controller cmdGroup varName readable pollEnabled dataType permission rdCmd chID rdFunc} { - # It is a command that is supported by the device - if {[ catch { - set ns ::scobj::ls218 - - if {$idx > 0} { - set channel ch$idx - set basePath $scobj_hpath/$channel - } else { - set basePath $scobj_hpath - } - - if {1 > [string length $cmdGroup]} { - set nodeName $basePath/$varName - } else { - set nodeName $basePath/$cmdGroup/$varName - } - - hfactory $nodeName plain $permission $dataType - switch -exact $dataType { - "none" {hset $nodeName none} - "text" {hset $nodeName UNKNOWN} - "int" {hset $nodeName 0} - "float" {hset $nodeName 0.0} - default {hset $nodeName UNKNOWN} - } - - if {$readable == 1} { - hsetprop $nodeName read ${ns}::getValue $idx $rdCmd $chID rdFuncState - hsetprop $nodeName rdFuncState ${ns}::rdValue $nodeName $varName $idx - hsetprop $nodeName oldval UNKNOW - } - if {$pollEnabled == 1} { - if {[SplitReply [environment_simulation]]=="false"} { - # puts "enabling polling for $nodeName" - $sct_controller poll $nodeName 3 - } - } - } message ]} { - return -code error "in createNode $message" - } - return OK -} - -# create parent HDB nods -proc createParentHDBNode {hPath klass priv type control data} { - - if {[ catch { - hsetprop $hPath klass $klass - hsetprop $hPath privilege $priv - hsetprop $hPath type $type - hsetprop $hPath control $control - hsetprop $hPath data $data - } message ]} { - return -code error "in createParentHDBNode $message" - } - - return OK -} - -## -# @brief mkLS218() creates a scriptcontext object for a Lakeshore 218 temperature controller -# @param sct_controller name of the ls218 scriptcontext object (typically sct_ls218_tc1 or tc2) -# @param klasse Nexus class name (?), typically 'environment' -# @param tempobj short name for the temperature controller scriptcontext object (typ. tc1 or tc2) -# @param tol temperature tolerance in Kelvin (typ. 1) -# @return nothing (well, the sct object) -proc ::scobj::ls218::mkLS218 {argList} { - #variable logString - - if {[ catch { - foreach {k v} $argList { - set KEY [string toupper $k] - set pa($KEY) $v - } - - MakeSICSObj $pa(NAME) SCT_OBJECT - sicslist setatt $pa(NAME) klass environment - sicslist setatt $pa(NAME) long_name $pa(NAME) - - set scobj_hpath /sics/$pa(NAME) - - makesctcontroller sct_$pa(NAME) std $pa(IP):$pa(PORT) - - # create parent nodes for input, output, and relay - foreach chanType {input output relay} { - switch -exact $chanType { - "input" {set chanList $pa(INPUTCHAN)} - "output" {set chanList $pa(OUTPUTCHAN)} - "relay" {set chanList $pa(RELAYCHAN)} - } - if {[string length $chanList] > 0} { - set hPath $scobj_hpath/$chanType - hfactory $hPath plain user none - hset $hPath UNKNOWN - foreach idx $chanList { - set hPath $scobj_hpath/$chanType/ch$idx - hfactory $hPath plain user none - hset $hPath UNKNOWN - } - } - } - - # create parent node for other - set hPath $scobj_hpath/other - hfactory $hPath plain user none - hset $hPath UNKNOWN - - # Create state machines for the following device commands (non-polled entries are place-holders - # for manually maintained nodes, like the selector for the input that provides the sample temperature - # 'sampleSensor', the sample tempreature reading 'Tsample', the input that provides for the controlLoop - # 'value', and the control loop parameters in human-rdadable form 'ctrl_Loop_x') - # Note that drivable nodes require the index of the control loop in their call to halt() - # Nodes appear in gumtree in the order in which they are created here. - # - # Initialise the model-dependent list of supported device commands - # RdWrPlDrIdx - # cmdGroup subdirectory (below /sample/tc*/) in which the node is to be created - # varName name of the actual node typically representing one device command - # - # readable set to 1 if the node represents a query command, 0 if it is not - # pollEnabled set to 1 if the node property pollable is to be enabled (node gets read every 5 secs) - # - # dataType data type of the node, must be one of none, int, float, text - # permission defines what user group may read/write to this node (is one of spy, user, manager) - # rdCmd actual device query command to be sent to the device - # chID set to 1 presenting a need to specify the ID of input (1-8) or output (1-2) channels - # rdFunc nextState Function to be called after the getValue function, typically rdValue() - # hdbTree set to 1 if a HDB needs to be created at the node - # klass type of klass of the HDB node - # control type of contrl of the HDB node - # data type of data of the HDB node - # priv type of previlige level of the HDB node - # type type of the HDB node - # nxsave save the node or not, values are true or false - # mutable mutable control or not, values are true or false - # nxalias alias name of the node in the HDB tree - ########################################################################################################### - - set inputNodes { - {} alarm 1 1 none user {ALARM?} 1 {rdValue} - alarm offOn 0 0 text user {} 0 {} - alarm source 0 0 text user {} 0 {} - alarm highValue 0 0 float user {} 0 {} - alarm lowValue 0 0 float user {} 0 {} - alarm deadband 0 0 float user {} 0 {} - alarm latchEnable 0 0 int user {} 0 {} - {} aStatus 1 1 none user {ALARMST?} 1 {rdValue} - aStatus highStatus 0 0 text user {} 0 {} - aStatus lowStatus 0 0 text user {} 0 {} - {} Celsius 1 1 float user {CRDG?} 1 {rdValue} - {} CurveHd 1 1 none user {CRVHDR?} 0 {rdValue} - CurveHd curve 0 0 text user {} 0 {} - CurveHd name 0 0 text user {} 0 {} - CurveHd SN 0 0 text user {} 0 {} - CurveHd format 0 0 text user {} 0 {} - CurveHd limitValue 0 0 float user {} 0 {} - CurveHd coefficient 0 0 text user {} 0 {} - {} CurveID 1 1 text user {INCRV?} 1 {rdValue} - {} control 1 1 text user {INPUT?} 1 {rdValue} - {} inputType 1 1 text user {INTYPE?} G {rdValue} - {} Kelvin 1 1 float user {KRDG?} 1 {rdValue} - {} Linear 1 1 none user {LINEAR?} 1 {rdValue} - Linear varM 0 0 float user {} 0 {} - Linear xSource 0 0 text user {} 0 {} - Linear varB 0 0 float user {} 0 {} - {} LinearEquData 1 1 float user {LRDG?} 1 {rdValue} - {} mnmxSource 1 1 text user {MNMX?} 1 {rdValue} - {} mnmxValue 1 1 text user {MNMXRDG?} 1 {rdValue} - {} status 1 1 text user {RDGST?} 1 {rdValue} - {} SensorUnitValue 1 1 float user {SRDG?} 1 {rdValue} - } - - set outputNodes { - {} data 1 1 float user {AOUT?} 1 {rdValue} - {} aOutput 1 1 none user {ANALOG?} 1 {rdValue} - aOutput bipolarEnable 0 0 text user {} 0 {} - aOutput monitorMode 0 0 text user {} 0 {} - aOutput inputChID 0 0 int user {} 0 {} - aOutput source 0 0 text user {} 0 {} - aOutput highValue 0 0 float user {} 0 {} - aOutput lowValue 0 0 float user {} 0 {} - aOutput manualValue 0 0 int user {} 0 {} - } - - set relayNodes { - {} relay 1 1 none user {RELAY?} 1 {rdValue} - relay mode 0 0 text user {} 0 {} - relay input 0 0 int user {} 0 {} - relay alarmType 0 0 text user {} 0 {} - } - - set otherNodes { - {} IDN 1 1 none user {*IDN?} 0 {rdValue} - IDN manufacturer 0 0 text user {} 0 {} - IDN model 0 0 text user {} 0 {} - IDN serialNumber 0 0 text user {} 0 {} - IDN firmwareDate 0 0 text user {} 0 {} - {} logStatus 1 1 text user {LOG?} 0 {rdValue} - } - - # create sub-tree for all 8 input channels - foreach idx $pa(INPUTCHAN) { - foreach {cmdGroup varName readable pollEnabled dataType permission rdCmd chID rdFunc} $inputNodes { - createNode $scobj_hpath/input $idx sct_$pa(NAME) $cmdGroup $varName $readable $pollEnabled $dataType $permission $rdCmd $chID $rdFunc - } - } - - # create sub-tree for all 2 output channels - foreach idx $pa(OUTPUTCHAN) { - foreach {cmdGroup varName readable pollEnabled dataType permission rdCmd chID rdFunc} $outputNodes { - createNode $scobj_hpath/output $idx sct_$pa(NAME) $cmdGroup $varName $readable $pollEnabled $dataType $permission $rdCmd $chID $rdFunc - } - } - - # create sub-tree for all 8 relay channels - foreach idx $pa(RELAYCHAN) { - foreach {cmdGroup varName readable pollEnabled dataType permission rdCmd chID rdFunc} $relayNodes { - createNode $scobj_hpath/relay $idx sct_$pa(NAME) $cmdGroup $varName $readable $pollEnabled $dataType $permission $rdCmd $chID $rdFunc - } - } - - # create "other" sub-tree - foreach {cmdGroup varName readable pollEnabled dataType permission rdCmd chID rdFunc} $otherNodes { - createNode $scobj_hpath/other 0 sct_$pa(NAME) $cmdGroup $varName $readable $pollEnabled $dataType $permission $rdCmd $chID $rdFunc - } - - # create HDB tree - - ::scobj::set_required_props $scobj_hpath - - foreach {hPath klass control data priv type} { - ls218 environment true true spy part - input NXsensor true true user NXsensor - output NXsensor true true user NXsensor - } { - switch -exact $hPath { - "ls218" { set curPath /sics/$hPath - createParentHDBNode $curPath $klass $priv $type $control $data - } - default { switch -exact $hPath { - "input" {set chanList $pa(INPUTCHAN)} - "output" {set chanList $pa(OUTPUTCHAN)} - } - if {[string length $chanList] > 0} { - set curPath /sics/ls218/$hPath - createParentHDBNode $curPath $klass $priv $type $control $data - foreach idx $chanList { - set curPath /sics/ls218/$hPath/ch$idx - createParentHDBNode $curPath $klass $priv $type $control $data - } - } - } +proc ::scobj::lakeshore_218::read_config {} { + set catch_status [ catch { + set ns "::scobj::lakeshore_218" + dict for {k v} $::config_dict { + if { [dict exists $v "driver"] } { + if { [dict get $v "driver"] == "lakeshore_218" } { + if { [dict get $v enabled] } { + set name [dict get $v name] + if { ![string equal -nocase [SplitReply [environment_simulation]] "false"] } { + set asyncqueue "null" + ${ns}::sics_log 9 "[environment_simulation] => using null asyncqueue" + } elseif { [dict exists $v "asyncqueue"] } { + set asyncqueue [dict get $v "asyncqueue"] + } else { + if { [dict exists $v "asyncprotocol"] } { + set asyncprotocol [dict get $v "asyncprotocol"] + } else { + set asyncprotocol ${name}_protocol + MakeAsyncProtocol ${asyncprotocol} + if { [dict exists $v "terminator"] } { + ${asyncprotocol} sendterminator "[dict get $v "terminator"]" + ${asyncprotocol} replyterminator "[dict get $v "terminator"]" + } + } + set asyncqueue ${name}_queue + set IP [dict get $v ip] + set PORT [dict get $v port] + MakeAsyncQueue ${asyncqueue} ${asyncprotocol} ${IP} ${PORT} + if { [dict exists $v "timeout"] } { + ${asyncqueue} timeout "[dict get $v "timeout"]" + } + } + add_lakeshore_218 ${name} "aqadapter" ${asyncqueue} + } } + } } - - if {[string length $pa(INPUTCHAN)] > 0} { - foreach idx $pa(INPUTCHAN) { - foreach {item klass control data nxsave mutable priv alias} { - Celsius sensor true true true true user Celsius - Kelvin sensor true true true true user Kelvin - SensorUnitValue sensor true true true true user SensorUnitValue - } { - set hpath /sics/ls218/input/ch$idx/$item - hsetprop $hpath nxalias ls218-input-ch$idx-$alias - hsetprop $hpath klass $klass - hsetprop $hpath privilege $priv - hsetprop $hpath control $control - hsetprop $hpath data $data - hsetprop $hpath nxsave $nxsave - hsetprop $hpath mutable $mutable - hsetprop $hpath sdsinfo ::nexus::scobj::sdsinfo - } - } - } - - if {[string length $pa(OUTPUTCHAN)] > 0} { - foreach idx $pa(OUTPUTCHAN) { - set hpath /sics/ls218/output/ch$idx/data - hsetprop $hpath nxalias ls218-output-ch$idx-data - hsetprop $hpath klass sensor - hsetprop $hpath privilege user - hsetprop $hpath control true - hsetprop $hpath data true - hsetprop $hpath nxsave true - hsetprop $hpath mutable true - hsetprop $hpath sdsinfo ::nexus::scobj::sdsinfo - } - } - - ::scobj::hinitprops $pa(NAME) - - } message ]} { - return -code error "in mkLS218 $message" - } + } catch_message ] + handle_exception ${catch_status} ${catch_message} } -# end of namespace ::scobj::ls218 - -# Main process call -# @param name short name for the Lakeshore Temp controller 218 -# @param IP IP address of the device (e.g. IP of moxabox that hooks up to the AG1010) -# @param port port number on the moxabox (typ. 4001, 4002, 4003, or 4004) -# @param turning if the parameter is turnable and can be set from the Gumtree -# @internal time internal in polling the nodes -# @return nothing (well, the sct object) - -MakeAsyncProtocol std -MakeAsyncQueue ls218 std 137.157.202.214 4002 -::scobj::ls218::mkLS218 { - name ls218 - IP aqadapter - PORT ls218 - tuning 1 - interval 5 - inputChan {1 2 3 4 5 6 7 8} - outputChan {1 2} - relayChan {1 2 3 4 5 6 7 8} +if { [info exists ::config_dict] } { + ::scobj::lakeshore_218::read_config +} else { + ::scobj::lakeshore_218::sics_log 5 "No config dict" } -