Files
sics/site_ansto/instrument/config/environment/magneticField/sct_bruker_BEC1.tcl

1371 lines
61 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.
#include <tcl.h>
##
# /*--------------------------------------------------------------------------
# B R U K E R B - E C 1 D R I V E R
# Power supply for 1-Tesla magnet
#
# @file: This file contains the implementation of a driver for SICS for the
# Bruker B-EC1 power supply for the 1-Tesla magnet implemented as a
# scriptcontext object in TCL.
#
# @author: Arndt Meier, ANSTO, 2009-08-31
# @brief: driver for Bruker 1-Tesla magnet power supply (in TCL)
# @version: 20091009 for sics2_4
#
# ----------------------------------------------------------------------------*/
# Notes
# Uses the astvelsel protocol handler (from ./site_ansto/hardsup/sct_velselprot.c)
# instead of the standard script context handler. This is necessary because replies
# from the device have no terminator while queries to the device need a line terminator
# (CR 0x0D). am2010-04-07
#
# Hdb nodes which report data readings should have a "get" script attached to the
# "read" property of the node. This ensures that we can update the reading on demand
# which is necessary for logging data.
#
# Hdb nodes which report parameters which don't change after the driver has
# connected/reconnected to the device should be initialised with a call to the queue
# subcommand of the sct controller, Eg.
# sct queue /sics/path progress read
#
# If there are a large number of settings which need to be reported, and the device
# lets you get the read these setting with a single transaction then you can create
# a status polling object which fetches the settings and updates a given list of hdb
# nodes.
# Bugs, limitations, changes:
# - checks whether the reply from the device matches the query - if not, it discards the reply.
# - added lsb_err byte 1 interpretation (water failure). Added help notes
# for the user for the status byte text nodes.
# - MAJOR PROBLEM: Although the driver seems to be doing just about everything
# right, the BEC1 device shuts itself off (changes to DCpower=0) and goes
# into standby after between 30 secs to 3 minutes. There is nothing in the
# TCP/IP communication log that would suggest a fault on the driver's side.
# The only cause the manufacturer could think of is that we are sending
# commands to the device too quickly.
# - We introduced a new global variable bruker_BEC1_MIN_TIME_BETWEEN_COMMANDS
# that helps us to insure this time in milliseconds is the minimum time
# between sending 2 queries or commands to the device following a Bruker
# recommendation. Supposedly a value between 50 to 200milliseconds should
# suffice to prevent a collapse of communication as described above, alas it
# did not have the expected effect so far.
# - Fixed the StateMachineStatusByteTxt bug - now decodes the
# status byte correctly into its corresponding error text.
# - Removed terminator variable - obsolete as this is done when the scriptcontext
# object is created.
# - proc drivestatus now sets a retval variable that is returned. Default
# action for drivable when paused is lazy instead of pause to avoid
# stopping the histogram server.
# - source code syntax checked with nagelfar - removed a couple of potential
# problems, 20100121
##
# @brief Handle exceptions caught by a 'catch' command.
# Note: You must use 'error' not 'return -code error' to
# raise errors from within a catch block.
#
# @param status, the status returned by the 'catch' command.
# @param message, the message set by the 'catch' command.
#
# Call this as the last command in the command block or
# for loop which encloses the 'catch'
proc handle_exception {status message args} {
switch $status {
0 {
# TCL_OK, This is raised when you just drop out of the
# bottom of a 'catch' command.
return -code ok
}
1 {
# TCL_ERROR
return -code error "([info level -1]) $message $args"
}
2 {
# TCL_RETURN
return -code return "$message"
}
3 {
# TCL_BREAK
return -code break
}
4 {
# TCL_CONTINUE
return -code continue
}
default {
# Propogate user defined return codes with message
return -code $status "$message"
}
}
}
# Default parameters for the device
namespace eval ::scobj::bruker_BEC1 {
# The BEC1 cannot take commands faster than 1 per 50 milliseconds - increase to 100 or 200
# if necessary. If commands come in too fast, the DCpower may switch itself off.
set bruker_BEC1_MIN_TIME_BETWEEN_COMMANDS 50
# set a start value for the time of the last command send to or read from the device
#set bruker_BEC1_timeLastCommand 0
#set bruker_BEC1_timeLastCommand [clock clicks -milliseconds]
# Some global variables that are useful as default initilisation values or for tracking
# Name of the scriptcontext object created from calling this driver - an instrument may use more than one
set bruker_BEC1_sct_obj_name "UNKNOWN"
# Last query and write command sent to the device - for debugging and other tasks
set bruker_BEC1_lastQueryCmd " "
set bruker_BEC1_lastWriteCmd " "
# provide a global variable holding the path to the nodes
set bruker_BEC1_path2nodes "/sample/ma1"
# terminator string for serial communication
#set bruker_BEC1_term "" !obsolete. Done in the call to the scriptcontext constructor
# variables that are identical to node names but are needed internally as well
# temperature tolerance in Ampere
set bruker_BEC1_tolerance 0.1
# magnetic field strength difference between set and actual field strength at
# which the power supply changes from idle to driving - is driveTolerance*tolerance
set bruker_BEC1_driveTolerance 0.2
# Last 2 error messages
set bruker_BEC1_errMsg "none"
set bruker_BEC1_errMsg2 "none"
# Polarity switching unit installed or absent?
set bruker_BEC1_polarityUnitAbsent false
# value indicating an error or invalid value
set bruker_BEC1_errValue -9999.99
# upper and lower limit in mT
set bruker_BEC1_upperlimit 1000.0
set bruker_BEC1_lowerlimit 0.0
# magnetic field units are milliTesla
set bruker_BEC1_magneticFiledUnits "T"
# bruker_BEC1 status byte
set bruker_BEC1_statusByte -1
# set device ID to unknown
set this_sDeviceID "Unknown_Device"
# set self-test result to unknown
set this_selfTestResult -1
#set tc_dfltURL ca5-[instname]
array set moxaPortMap {1 4001 2 4002 3 4003 4 4004}
########### Initialisation #############################
##
# Initialise the bruker_BEC1:
# Checks the device ID, resets the device, checks the self-test, sets the communication
# protocol and tolerance.
# @param sct_controller the controller object created for this driver
# @param tc_root string variable holding the path to the object's base node in sics
# @return 0, always
proc bruker_BEC1_init {sct_controller tc_root} {
set catch_status [ catch {
# set the communication protocol: terminator <CR>, bps 9600 baud, 7 data bits +
# 1 stop bit + odd parity bit
# clientput "setting serial communication parameters"
# hset $tc_root/other/cfgProtocol_comm "COMM 1,5,1"
# Query the device ID
# clientput "sending: $sct_controller queue $tc_root/other/deviceID_idn progress read"
# $sct_controller queue $tc_root/other/deviceID_idn progress read
# !! Not working properly yet - needs fixing
#sct send "*IDN?"
#sct data [$sct_controller result]
#clientput "rdValDrct(): result is $data"
#set data [hget $tc_root/other/deviceID_idn]
# clientput "set deviceID_idn (hval $tc_root/other/deviceID_idn)"
#set data [hval $tc_root/other/deviceID_idn]
#set ::scobj::bruker_BEC1::this_sDeviceID data
# clientput "sct_bruker_BEC1.tcl: connected to device $::scobj::bruker_BEC1::this_sDeviceID"
# reset the device to have it in a defined state
# hset $tc_root/other/reset_rst {*RST}
# Queue the Read Device Status-byte command so we can access the result in the
# corresponding node later
# clientput "sending: $sct_controller queue $tc_root/other/statusByte progress read"
# $sct_controller queue $tc_root/other/statusByte progress read
# hset $tc_root/other/cfgProtocol_comm "COMM 1,5,1"
# Was the self-test successful?
# $sct_controller queue $tc_root/other/selftest progress read
# set ::scobj::bruker_BEC1::this_selfTestResult [hval $tc_root/other/selftest]
# if {$::scobj::bruker_BEC1::this_selfTestResult == 0} {
# clientput "sct_bruker_BEC1.tcl: Lakeshore $::scobj::bruker_BEC1::bruker_BEC1_LSmodel self-test ok."
# } else {
# clientput "sct_bruker_BEC1.tcl: The Lakeshore $::scobj::bruker_BEC1::bruker_BEC1_LSmodel failed its self-test."
# }
# Set the default tolerances for the setpoint magentic field strength
hset $tc_root/emon/tolerance $::scobj::bruker_BEC1::bruker_BEC1_tolerance
} message ]
handle_exception $catch_status $message
}
############# Reading polled nodes ###################################
##
# @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 getValue {tc_root nextState cmd expectedLen} {
set catch_status [ catch {
set ::scobj::bruker_BEC1::bruker_BEC1_lastQueryCmd "$cmd"
if [hpropexists [sct] geterror] {
hdelprop [sct] geterror
}
after $::scobj::bruker_BEC1::bruker_BEC1_MIN_TIME_BETWEEN_COMMANDS
sct send $cmd
# clientput "sct send !$cmd!"
return $nextState
} message ]
handle_exception $catch_status $message "in getValue(). Last query command: $::scobj::bruker_BEC1::bruker_BEC1_lastQueryCmd"
}
##
# @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 bXtract if set to 'X' the value/number is extratced from the response and shown
# as the node values - allows float and int values as opposed to text. This
# is important if this value shall be logged via Nexus.
# @return idle Always returns system state idle - command sequence completed.
proc rdValue {expectedLength} {
set data [sct result]
# clientput "rdValue(): result is $data"
# broadcast rdValue "rdValue(): result is $data"
#clientput "sct result !$data!"
set rData $data
# Do we get the answer to the question we asked?! Occasionally the BEC1 is sending info on its own.
if { 0 != [string compare -length 4 $data $::scobj::bruker_BEC1::bruker_BEC1_lastQueryCmd]} {
# Discard if it is not the reply to our query
return idle
}
set catch_status [ catch {
# Continue as normal
switch -glob -- $data {
"ASCERR:*" {
clientput "ASCERR in rdValue: Last query command: $::scobj::bruker_BEC1::bruker_BEC1_lastQueryCmd"
sct geterror $data
}
default {
if { [string length $data] > $expectedLength } {
# Discard - this it is not the reply to our query because this is what happened:
#ERROR: in rdValue: in analyseStatusByte(): in decodeErrByte(): syntax error in expression "0xHF": extra tokens at end of expression. errByte: HF, errList: 08 {Inrush procedure error} 01 {Current limit exceeded}
# . statusByteString: STA/0CHF/-0.0002T. Last query command: STA/
return idle
#clientput "Rejected !$data! as reply to $::scobj::bruker_BEC1::bruker_BEC1_lastQueryCmd"
}
set orgdata $data
set data [ExtractValue $data $::scobj::bruker_BEC1::bruker_BEC1_lastQueryCmd]
if {$data != [sct oldval]} {
sct oldval $data
sct update $data
sct utime readtime
if {1==0} {
# Not in use - always results in error - we may not have the polarity reversal unit
# We are done here - below are some special cases
# if polarity unit is not installed, don't show repetetive error messages
if { 0 == [string compare -length 4 $::scobj::bruker_BEC1::bruker_BEC1_lastQueryCmd "POL/"]} {
if {$data == 0} {
set ::scobj::bruker_BEC1::bruker_BEC1_polarityUnitAbsent true
} else {
set ::scobj::bruker_BEC1::bruker_BEC1_polarityUnitAbsent false
}
}
}
# When reading the status byte, provide a human readable interpretation
if { 0 == [string compare -length 4 $rData "STA/"]} {
# we have to extract again without stripping trailing characters that
# normally represent a physical unit like Ampere but could here be part
# of a hexadecimal value
#clientput "rdValue: Interpreting status byte information: $orgdata $::scobj::bruker_BEC1::bruker_BEC1_lastQueryCmd"
# Ananlyse the status bytes string containing the 4 status bytes and present the
# result in human readable form (Text instead of hex number)
analyseStatusByte $rData
}
if {1==0} {
# Not in use - we are using serial connection via a moxabox so we don't need the
# ethernet address of the Bruker device
# When reading the ethernet address in revers hex notation, provide a normal decimal interpretation
if { 0 == [string compare -length 4 $::scobj::bruker_BEC1::bruker_BEC1_lastQueryCmd "ETH/"]} {
# we have to extract again without stripping trailing characters that
# normally represent a physical unit like Ampere but could here be part
# of a hexadecimal value
# Beware that Bruker uses reverse notation.
set data [ExtractValue $orgdata $::scobj::bruker_BEC1::bruker_BEC1_lastQueryCmd]
#clientput "rdValue: Interpreting status byte information: $::scobj::bruker_BEC1::bruker_BEC1_lastQueryCmd"
#pwrCtrl EthernetAddrHex 1 0 1 0 text spy {ETH/} {rdValue} {} {setValue} {}
hset $::scobj::bruker_BEC1::bruker_BEC1_path2nodes/pwrctrl/EthernetAddrDec "UNKNOWN"
}
}
}
}
}
return idle
} message ]
handle_exception $catch_status $message "in rdValue(). Last query command: $::scobj::bruker_BEC1::bruker_BEC1_lastQueryCmd"
}
##
# @brief Does what rdValue() does plus it checks if the current is in tolerance.
# inTolerance is the default nextState after getValue() for read node pwrctrl/dc_power.
# If the device is switched off, current is reported to be in tolerance so that
# slow return to ambient conditions can be carried out
# @return idle Always returns system state idle - command sequence completed.
proc inTolerance {expectedLength} {
set tc_root $::scobj::bruker_BEC1::bruker_BEC1_path2nodes
set data [sct result]
# clientput "inT result !$data!"
# Do we get the answer to the question we asked?! Occasionally the BEC1 is sending info on its own.
if { 0 != [string compare -length 4 $data $::scobj::bruker_BEC1::bruker_BEC1_lastQueryCmd]} {
# Discard if it is not the reply to our query
return idle
}
set catch_status [ catch {
set oldvalue [sct oldval]
# clientput "inTolerance(): data=$data oldval=$oldvalue"
switch -glob -- $data {
"ASCERR:*" {
clientput "ASCERR in inTolerance: Last query command: $::scobj::bruker_BEC1::bruker_BEC1_lastQueryCmd"
sct geterror $data
}
default {
# DCP/ 0
if { [string length $data] > $expectedLength } {
# Discard - this it is not the reply to our query because this is what happened:
#ERROR: in rdValue: in analyseStatusByte(): in decodeErrByte(): syntax error in expression "0xHF": extra tokens at end of expression. errByte: HF, errList: 08 {Inrush procedure error} 01 {Current limit exceeded}
# . statusByteString: STA/0CHF/-0.0002T. Last query command: STA/
#clientput "Rejected !$data! as reply to $::scobj::bruker_BEC1::bruker_BEC1_lastQueryCmd"
return idle
}
set data [ExtractValue $data $::scobj::bruker_BEC1::bruker_BEC1_lastQueryCmd]
if {$data != $oldvalue} {
if {$oldvalue == "UNKNOWN"} {
sct utime timecheck
}
sct oldval $data
sct update $data
sct utime readtime
}
}
}
# clientput "inTolerance $::scobj::bruker_BEC1::bruker_BEC1_sct_obj_name data:$data"
# now update the manual nodes reporting whether the actual field strength
# is within tolerance of the corresponding setpoint
if {$data == 0 } {
# DC power switched off
hset $tc_root/emon/mon_mode "idle"
hset $tc_root/emon/is_in_tolerance "inTolerance"
hset $tc_root/status "idle"
} else {
set intol [checktol $tc_root]
if {$intol==0} { sct utime timecheck }
set nodename $tc_root/sensor/nominal_outp_current
set setpt [hval $nodename]
set nodename $tc_root/sensor/desired_current
set NominalOutpCurrent [hval $nodename]
# clientput "inTolerance(): comparing sensor/setpoint=$setpt with actual sensorValue=$temp"
set diff [expr {abs($setpt - $NominalOutpCurrent)}]
if {$diff > $::scobj::bruker_BEC1::bruker_BEC1_driveTolerance} {
# ERROR: node /sics/ma1/emon/mon_mode not found. Last query command: DCP/
set nodename $tc_root/emon/mon_mode
hset $nodename "drive"
hset $tc_root/status "busy"
} else {
set nodename $tc_root/sensor/nominal_outp_current
hsetprop $nodename driving 0
set nodename $tc_root/emon/mon_mode
hset $nodename "monitor"
hset $tc_root/status "idle"
}
}
# clientput "inTolerance 4 $::scobj::bruker_BEC1::bruker_BEC1_sct_obj_name data:$data"
return idle
} message ]
handle_exception $catch_status $message "in inTolerance(). Last query command: $::scobj::bruker_BEC1::bruker_BEC1_lastQueryCmd"
# clientput "Leaving inTolerance idx:$CtrlLoopIdx"
}
################## Writing to nodes ###########################################
##
# @brief Writes a new value to a node and sends the corresponding command to the device.
# @param tc_root string variable holding the path to the object's base node in sics
# @param nextState the next function to call after this one
# @param cmd the command to be send to the device (written to the node data value)
# @return nextState Is typically noResponse as the device does not acknowledge the write request
proc setValue {tc_root nextState cmd } {
# tc_root is not being used - however, don't remove so we can use the
# same calling mask as for setPoint() or other $wrFunc
set catch_status [ catch {
set par [sct target]
set ::scobj::bruker_BEC1::bruker_BEC1_lastWriteCmd "$cmd$par"
after $::scobj::bruker_BEC1::bruker_BEC1_MIN_TIME_BETWEEN_COMMANDS
sct send "$cmd$par"
if { 0 == [string compare -length 4 $cmd "RST=0"] } {
# Reset error messages - also update the node displaying the last error
set ::scobj::bruker_BEC1::bruker_BEC1_errMsg "none"
set ::scobj::bruker_BEC1::bruker_BEC1_errMsg2 "none"
hset $::scobj::bruker_BEC1::bruker_BEC1_path2nodes/emon/last_error_msg $::scobj::bruker_BEC1::bruker_BEC1_errMsg
hset $::scobj::bruker_BEC1::bruker_BEC1_path2nodes/emon/last_error_msg2 $::scobj::bruker_BEC1::bruker_BEC1_errMsg2
}
return $nextState
} message ]
handle_exception $catch_status $message "in setValue(). While sending command: $::scobj::bruker_BEC1::bruker_BEC1_lastWriteCmd"
}
##
# @brief Is an empty function just so we can define a next state function after a write operation.
# @return idle Returns the default system state indicating that the device is ready for the next command
proc noResponse {} {
return idle
}
##
# @brief Sets the desired magnetic field strength.
# @param tc_root string variable holding the path to the object's base node in sics
# @param nextState next state of the device, typically that is 'noResponse'
# @param cmd string variable containing the device command to change the setpoint
# @return nextState
proc setDesiredField {tc_root nextState cmd} {
set catch_status [ catch {
#clientput "executing setDesiredField ($tc_root $nextState $cmd)"
set ns ::scobj::lh45
set par [sct target]
#hset $tc_root/status "busy"
set wrStatus [sct writestatus]
if {$wrStatus == "start"} {
# Called by drive adapter
# clientput "setDesiredField(): driving set to 1"
set nodename $tc_root/sensor/setpoint
hsetprop $nodename driving 1
}
#clientput "setDesiredField(wrStatus=$wrStatus): sct send $cmd$par"
after $::scobj::bruker_BEC1::bruker_BEC1_MIN_TIME_BETWEEN_COMMANDS
sct send "$cmd$par"
return $nextState
} message ]
handle_exception $catch_status $message "in setDesiredField(). Last write command: $::scobj::bruker_BEC1::bruker_BEC1_lastWriteCmd"
}
##
# @brief Sets the desired nominal current.
# @param tc_root string variable holding the path to the object's base node in sics
# @param nextState next state of the device, typically that is 'noResponse'
# @param cmd string variable containing the device command to change the setpoint
# @return nextState
proc setDesiredCurrent {tc_root nextState cmd} {
set catch_status [ catch {
# clientput "executing setDesiredCurrent ($tc_root $nextState $cmd)"
set ns ::scobj::lh45
set par [sct target]
#hset $tc_root/status "busy"
set wrStatus [sct writestatus]
if {$wrStatus == "start"} {
# Called by drive adapter
# clientput "setDesiredCurrent(): driving set to 1"
set nodename $tc_root/sensor/nominal_outp_current
hsetprop $nodename driving 1
}
#clientput "setDesiredCurrent(wrStatus=$wrStatus): sct send $cmd$par"
after $::scobj::bruker_BEC1::bruker_BEC1_MIN_TIME_BETWEEN_COMMANDS
sct send "$cmd$par"
return $nextState
} message ]
handle_exception $catch_status $message "in setDesiredCurrent(). Last write command: $::scobj::bruker_BEC1::bruker_BEC1_lastWriteCmd"
}
############# functions used with drivable ##########################################
##
# @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} {
# broadcast "DEBUG: in drivestatus. Last write command: $::scobj::bruker_BEC1::bruker_BEC1_lastWriteCmd"
set catch_status [ catch {
if {[sct driving]} {
set retval busy
} else {
set retval idle
}
return $retval
} message ]
handle_exception $catch_status $message "in drivestatus(). Last write command: $::scobj::bruker_BEC1::bruker_BEC1_lastWriteCmd"
}
##
# @brief Stops driving at current magnetic field strenght.
# Sets the setpoint to the current magnetic field strength
# @param tc_root string variable holding the path to the object's base node in sics
# @return idle Indicates that the device is ready for the next command
proc halt {tc_root} {
# stop driving at current field strength or current
set sensorValue $tc_root/sensor/desired_current
set nodename $tc_root/sensor/nominal_outp_current
hset $nodename [hval $sensorValue]
hsetprop $nodename driving 0
return idle
}
############# Auxiliary functions ##########################################
##
# @brief Extracts the actual value or number from the response message of the device. The
# Bruker BEC1 repeats the query command send followed by its response and often as
# well a physical unit like A for Ampere, T for Tesla etc. This subroutine extracts
# the number from the string.
proc ExtractValue {response lastQueryCmd} {
# A typical argument may look like this: "CHF/-0.0002T" or "FLD/ 0.0000Tref"
# However, with STA/0000C100 and ETH/95EC3E29 we have to be careful not to
# strip a letter that is part of a hex number.
set catch_status [ catch {
set extractedValue 0
set queryStringLength [string length $lastQueryCmd]
set responseStringLength [string length $response]
incr responseStringLength -1
set units [string range $response $responseStringLength $responseStringLength]
set max 4
# The length of trailing units is max 4 characters long, like "Tref"
while {[string is alpha -strict $units] && $max > 0} {
# We have to strip off the physical unit at the end of the string like 'T' or 'Tref' for Tesla "CHF/-0.0002T"
incr responseStringLength -1
incr max -1
set units [string range $response $responseStringLength $responseStringLength]
}
set extractedValue [string range $response $queryStringLength $responseStringLength]
# Check whether there is an error flag instead of a value
if { 0 == [string compare -length 1 $extractedValue "E"] } {
# we can ignore a polarity query error if no polarity unit is installed - else show the error message
set ::scobj::bruker_BEC1::bruker_BEC1_errMsg2 $::scobj::bruker_BEC1::bruker_BEC1_errMsg
set ::scobj::bruker_BEC1::bruker_BEC1_errMsg "Unknown error. The device replied $response"
switch -glob $extractedValue {
"E01*" {set ::scobj::bruker_BEC1::bruker_BEC1_errMsg "FUNCTION ERROR - Function not supported, $response"}
"E02*" {set ::scobj::bruker_BEC1::bruker_BEC1_errMsg "ARGUMENT ERROR - command argument contains unexpected characters, $response"}
"E03*" {set ::scobj::bruker_BEC1::bruker_BEC1_errMsg "PORT NOT AVAILABLE, $response"}
"E04*" {set ::scobj::bruker_BEC1::bruker_BEC1_errMsg "LOCAL ERROR - access denied, check local_remote_state setting, $response"}
"E05*" {set ::scobj::bruker_BEC1::bruker_BEC1_errMsg "RANGE ERROR - the arguments are out of the allowed range, $response"}
"E06*" {set ::scobj::bruker_BEC1::bruker_BEC1_errMsg "MODE ERROR - access denied, external reference active, $response"}
"E07*" {set ::scobj::bruker_BEC1::bruker_BEC1_errMsg "ERROR PENDING, $response"}
"E09*" {set ::scobj::bruker_BEC1::bruker_BEC1_errMsg "DC ERROR, $response"}
"E99*" {set ::scobj::bruker_BEC1::bruker_BEC1_errMsg "NO TCP/IP COMMUNICATION, $response"}
}
set extractedValue $::scobj::bruker_BEC1::bruker_BEC1_errValue
# update the node last_error_msg
hset $::scobj::bruker_BEC1::bruker_BEC1_path2nodes/emon/last_error_msg $::scobj::bruker_BEC1::bruker_BEC1_errMsg
hset $::scobj::bruker_BEC1::bruker_BEC1_path2nodes/emon/last_error_msg2 $::scobj::bruker_BEC1::bruker_BEC1_errMsg2
#if { 0 != [string compare -length 4 $lastQueryCmd "POL/"]
# || $::scobj::bruker_BEC1::bruker_BEC1_polarityUnitAbsent == false} {
#}
}
#clientput "ExtractValue(): response:$response, extractedValue:$extractedValue"
return $extractedValue
} message ]
handle_exception $catch_status $message "in ExtractValue(). Last device response: $response"
}
# error messages according to Oak Ridge version of the manual
# I suspect that the difference are that we do not have a polarity reversal unit.
# State machine; hex Value; State
# 00 Neutral state
# 05 Reset external and field reference
# 06 Ramp DAC to 0
# 07 Test DAC=0
# 08 Test ADC=0
# 09 Set DC off
# 0A Set time 1 second
# 0B Wait time
# 0C Return to neutral state
# 0F Reset external and field reference
# 10 Set DAC to 0
# 11 Test ADC=0
# 12 Set Inrush on
# 13 Set time 1 second
# 14 Wait time
# 15 Set DC ON
# 16 Set time 1 second
# 17 Wait time
# 18 Set Inrush OFF
# 19 Return to neutral state
# #Set polarity positive
# 19 Ramp DAC to 0
# 1A Reset ext / BH-15 ref.
# 1B Test DAC=0
# 1C Test ADC=0
# 1D Set time=2 seconds
# 1E Wait time
# 1F Reset SEM
# 20 Set time=2 seconds
# 21 Wait time
# 22 Set polarity positive, set timeout
# 23 Wait pol. read back, check timeout
# 24 Set SEM
# 25 Set time=1 second
# 26 Wait time
# 27 Set ext / BH-15 ref?
# 28 Return to neutral state
# 2D Ramp DAC to 0
# 2E Reset ext / BH-15 ref
# 2F Test DAC=0
# 30 Test ADC=0
# 31 Set time=2 seconds
# 32 Wait time
# 33 Reset SEM
# 34 Set time=2 seconds
# 35 Wait time
# 36 Set polarity negative, set timeout
# 37 Wait polarity read back, check timeout
# 38 Set SEM
# 39 Set time=1 second
# 3A Wait time
# 3B Set ext / BH-15 ref?
# 3C Return to neutral state
##
# @brief Matches the error byte hexadecimal value to its error message text
# Multiple conditioins may apply.
# @param errByte the error byte as a hexadecimal value
# @param errList 2-column list of error hex value and corresponding message text
# @return returnval String holding the corresponding error message
proc decodeErrByte {errByte errList} {
set errorText ""
set delim ", "
#clientput "decodeErrByte: errByte:$errByte"
set catch_status [ catch {
# convert to decimal for calculations
set hexvar "0x$errByte"
set decErrByte [expr {$hexvar}]
# clientput "errByte=$errByte, hexvar=$hexvar, decErrByte=$decErrByte"
foreach {hexVal errText} $errList {
#clientput "errByte:$errByte hexVal:$hexVal errText:$errText"
# convert to decimal for calculations
set hexvar "0x$hexVal"
set decVal [expr { $hexvar }]
#clientput "decErrByte:$decErrByte decVal:$decVal errText:$errText"
if { $decErrByte >= $decVal } {
if {[string length $errorText] > 1} {
set errorText "$errorText$delim$errText"
} else {
set errorText $errText
}
set decErrByte [expr {$decErrByte - $decVal}]
}
}
if {2 > [string length $errText]} {
set errorText "Unknown error"
}
return $errorText
} message ]
handle_exception $catch_status $message "in decodeErrByte(). errByte: $errByte, errList: $errList"
}
##
# @brief extracts one byte from the 4-byte status string, each byte is
# represented by a two digit hexadecimal number.
# @param statusByteString The string holding the 4 status bytes
# @return returnval String holding the analysed status information
proc extractStatusByte {statusByteString whichByte} {
# A typical argument may look like this:
# "STA/0000C100"
set catch_status [ catch {
set statusByte "UNKNOWN"
set statusByteStringLength [string length $statusByteString]
set offset [expr {$whichByte*2}]
# allow for an initial offset of 4 char for 'STA/'
incr offset 4
set statusByte [string range $statusByteString $offset [expr {$offset+1}]]
#clientput "ExtractValue(): response:$response, extractedValue:$extractedValue"
return $statusByte
} message ]
handle_exception $catch_status $message "in extractStatusByte(). statusByteString: $statusByteString, whichByte: $whichByte"
}
proc analyseStatusByte {statusByteString} {
# A typical argument may look like this:
# "STA/0000C100"
set catch_status [ catch {
#clientput "statusByteString:$statusByteString"
set LSB_ErrByteTxt "Ok"
set MSB_ErrByteTxt "Ok"
set PwrSupplyStatusByteTxt "Ok"
set StateMachineStatusByteTxt "Ok"
set tmp_LSB_ErrByte [extractStatusByte $statusByteString 0]
set tmp_MSB_ErrByte [extractStatusByte $statusByteString 1]
set tmp_PwrSupplyStatusByte [extractStatusByte $statusByteString 2]
set tmp_StateMachineStatusByte [extractStatusByte $statusByteString 3]
if { 0 != [string compare -length 2 $tmp_LSB_ErrByte "00"]} {
# Error byte, LSB
# Value, hex, Explanation
set errList {
08 {External Security}
04 {Temperature transformer, rectifier or PWM}
02 {Line Phase missing}
01 {Cooling water failure}
}
set LSB_ErrByteTxt [decodeErrByte $tmp_LSB_ErrByte $errList]
}
if { 0 != [string compare -length 2 $tmp_MSB_ErrByte "00"]} {
# status byte hex Value, Explanation
set errList {
08 {Inrush procedure error}
01 {Current limit exceeded}
}
set MSB_ErrByteTxt [decodeErrByte $tmp_MSB_ErrByte $errList]
}
if { 0 != [string compare -length 2 $tmp_PwrSupplyStatusByte "00"]} {
# status byte hex Value, Explanation
set errList {
80 {Remote enabled}
40 {DC power on}
20 {Normal polarity}
10 {Reverse polarity}
04 {External reference enabled}
02 {Field regulation enabled}
01 {Internal reference set}
}
set PwrSupplyStatusByteTxt [decodeErrByte $tmp_PwrSupplyStatusByte $errList]
}
if { 0 != [string compare -length 2 $tmp_StateMachineStatusByte "00"]} {
# status byte hex Value; State
set errList {
1C {Return to neutral state}
1B {Control DC interlock}
1A {Wait time}
19 {Set time 1 second}
18 {Set Inrush OFF}
17 {Wait time}
16 {Set time 1 second}
15 {Set DC ON}
14 {Wait time}
13 {Set time 1 second}
12 {Set Inrush ON}
11 {Test ADC:0}
10 {Set DAC to 0}
0F {Set reference to intern}
0C {Return to neutral state}
0B {Wait time}
0A {Set 2 seconds}
09 {Set reference to intern}
08 {Set DC off}
07 {Test ADC:0}
06 {Test DAC:0}
05 {Ramp DAC to 0}
}
#00 {Neutral state}
set StateMachineStatusByteTxt [decodeErrByte $tmp_StateMachineStatusByte $errList]
}
#clientput "LSB_ErrByte :x$tmp_LSB_ErrByte: $LSB_ErrByteTxt"
#clientput "MSB_ErrByte :x$tmp_MSB_ErrByte: $MSB_ErrByteTxt"
#clientput "pwr_supply_status:x$tmp_PwrSupplyStatusByte: $PwrSupplyStatusByteTxt"
#clientput "StateMachine :x$tmp_StateMachineStatusByte: $StateMachineStatusTxt"
hset $::scobj::bruker_BEC1::bruker_BEC1_path2nodes/pwrctrl/lsb_err "x$tmp_LSB_ErrByte: $LSB_ErrByteTxt"
hset $::scobj::bruker_BEC1::bruker_BEC1_path2nodes/pwrctrl/msb_err "x$tmp_MSB_ErrByte: $MSB_ErrByteTxt"
hset $::scobj::bruker_BEC1::bruker_BEC1_path2nodes/pwrctrl/pwr_supply_status "x$tmp_PwrSupplyStatusByte: $PwrSupplyStatusByteTxt"
hset $::scobj::bruker_BEC1::bruker_BEC1_path2nodes/pwrctrl/state_machine_status "x$tmp_StateMachineStatusByte: $StateMachineStatusByteTxt"
return "Ok"
} message ]
handle_exception $catch_status $message "in analyseStatusByte(). statusByteString: $statusByteString"
}
##
# @brief Extracts elements from a coma (or other symbol) separated list of values provided in a string
# @param s The string holding a list of values separated by comas
# @param element Index of the element to extract (starts from zero)
# @param separator String holding the separator used in $s, e.g. a coma
# @return returnval String holding the extracted element. String is empty if operation failed.
proc getValFromString {s element separator} {
#clientput "getValFromString $s $element $separator"
set startIdx 0
set endIdx 0
set eIdx $element
set idx 0
while {$eIdx > 0} {
set idx [string first $separator $s $startIdx]
if {$idx > 0} {
if { " " == [string range $s $idx $idx]} {
incr idx 1
}
set startIdx $idx
if {$startIdx > 0} {
incr startIdx 1
}
}
incr eIdx -1
}
# startIdx points to the first non-blank character of the value we are interested in
set returnval ""
if {$startIdx < 0} {
return $returnval
}
set endIdx [string first $separator $s $startIdx]
incr endIdx -1
#clientput "startIdx=$startIdx endIdx=$endIdx"
# endIdx points to one character before the next separator or is -1 if it is the
# last element in the string $s
if {$endIdx >= 0} {
set returnval [string range $s $startIdx $endIdx]
} else {
set returnval [string range $s $startIdx 555]
}
#clientput "getValFromString $s, $element, $separator,\n returns: $returnval"
return $returnval
}
##
# @brief Checktol() checks whether the current field strength or current is within tolerance.
# @param tc_root string variable holding the path to the object's base node in sics
# @return retVal returns 1 if in tolerance, 0 else.
proc checktol {tc_root} {
set catch_status [ catch {
set retVal 0
set sensorValue $tc_root/sensor/desired_current
set NominalOutpCurrent [hval $sensorValue]
set isetp $tc_root/sensor/nominal_outp_current
set setpt [hval $isetp]
set tol [hval $tc_root/emon/tolerance]
set loField [expr {$setpt - $tol}]
set hiField [expr {$setpt + $tol}]
if { $NominalOutpCurrent < $loField || $NominalOutpCurrent > $hiField} {
hset $tc_root/emon/is_in_tolerance "outsideTolerance"
set retVal 0
} else {
hset $tc_root/emon/is_in_tolerance "inTolerance"
set retVal 1
}
return $retVal
} message ]
handle_exception $catch_status $message "in checktol(). Last query command: $::scobj::bruker_BEC1::bruker_BEC1_lastQueryCmd"
}
##
# @brief check() checks whether the the newly chosen Setpoint is inside the alarm levels
# @param tc_root string variable holding the path to the object's base node in sics
# @return 'OK' if the new setpoint is within the alarm levels; else an error is reported
proc check {tc_root whichParameter} {
# Unlike the Lakeshore temperature controllers, the BEC1 does not manage alarm levels.
# If the chosen setpoint is outside what is permitted, an error will be set.
# Nonetheless the drivable interface requires this check() routine - so let's
# give it one that is always happy...
return OK
}
##
# Provides online help for the driver commands available and the meaning of their
# options and parameters. Note that the help text is limited to
# - 500 bytes in total length.
# - 128 characters per line (if not, may get truncated on the display)
# - The following 5 special characters must be escape-coded for xml-compliance:
# Character Name Entity Reference Numeric Reference
# & Ampersand &amp; &#38;#38;
# < Left angle bracket &lt; &#38;#60;
# > Right angle bracket &gt; &#62;
# " Straight quotation mark &quot; &#39;
# ' Apostrophe &apos; &#34;
# Note that the xml file will be parsed more than once, meaning that escaping
# special characters may be insufficient and it may be necessary to avoid all
# xml special characters to avoid problems further downstream. It appears also
# that at present gumtree is truncating the help text when it comes across an
# equal sign '=' within the string. Multiple lines are not a problem.
# The help text provided is taken from the user manual - it is as good or bad
# as the manual
proc helpNotes4user {scobj_hpath cmdGroup varName} {
set catch_status [ catch {
set nodeName "$scobj_hpath/$cmdGroup/$varName"
# We presume that SICServer is running on Linux but Gumtree on Windows.
# Hence multi-line help texts should use CRLF instead of only LF
# Note that gumxml.tcl processes the node properties including this help text
# and may do strange things to it...
#set CR 0x0D
#set LF 0x0A
set CR "\r"
set LF "\n"
set CRLF $CR$LF
if {1 > [string length $cmdGroup]} {
set nodeName "$scobj_hpath/$varName"
}
set helptext "No help available"
#clientput "helpNotes4user $scobj_hpath/$cmdGroup varName"
switch -glob $varName {
"local_remote_state*" {
set h1 {REM: «n» Query or Set local/remote state.}
set h2 {«n» 0:local (local and remote comands allowed).}
set h3 {«n» 1:remote state (only remote commands allowed).}
set helptext $h1$CRLF$h2$CRLF$h3
}
"dc_power*" {
set h1 {DCP «off/on» DC power }
set h2 { 0:off, 1:on}
set helptext $h1$CRLF$h2
}
"desired_current*" {
set h1 {CUR «nn.nn» Read and set DAC current}
set h2 {«nn.nn» DAC current in Amperes}
set helptext $h1$CRLF$h2
}
"pwr_ctrl_from*" {
set h1 {EXT: «n» Query / Set source of reference for power supply control}
set h2 {«EXT:0» internal reference, DAC}
set h3 {«EXT:1» external reference voltage}
set h4 {«EXT:2» external reference field}
set helptext $h1$CRLF$h2$CRLF$h3$CRLF$h4
}
"QueryPolarity*" {
set h1 {POL/ Query polarity}
set h2 {«POL:0» no polarity reversal unit available}
set h3 {«POL:1» positive polarity}
set h4 {«POL:2» negative polarity}
set h5 {«POL:3» polarity reversal unit busy - please wait}
set helptext $h1$CRLF$h2$CRLF$h3$CRLF$h4$CRLF$h5
}
"SetPolarity*" {
set h1 {POL:«n» Set polarity}
set h2 {«POL:0» Set positive polarity}
set h3 {«POL:1» Set negative polarity}
set helptext $h1$CRLF$h2$CRLF$h3
}
"desired_field*" {
set h1 {FLD «nn.nn» Query/Set magnetic field strength }
set h2 {«nn.nn» magnetic field strength in Teslas (setpoint)}
set helptext $h1$CRLF$h2
}
"nominal_outp_current*" {
set h1 {CHN «nn.nn» Query nominal output current.}
set h2 {«nn.nn» Nominal output current in Amperes}
set helptext $h1$CRLF$h2
}
"output_voltage*" {
set h1 {CHV «nn.nn» Query output voltage.}
set h2 { «nn.nn» Output voltage in Volts.}
set helptext $h1$CRLF$h2
}
"LoadResistance*" {
set h1 {RES/ Query load resistance }
set h2 {Returned: Load resistance in Ohms.}
set helptext $h1$CRLF$h2
}
"measured_field*" {
set h1 {CHF/ Query measured magnetic field. }
set h2 {Returned: Measured magnetic field in Teslas.}
set h3 {}
set helptext $h1$CRLF$h2$CRLF$h3
}
"reset_error_msg*" {
set h1 {RST: «0» Reset error messages. }
set h2 { «0» Send zero to reset error messages.}
set helptext $h1$CRLF$h2
}
"statusByte*" {
set h1 {STA/ Query status information about the power supply or STA:0 Reset the command flow. }
set h2 {Returned: status byte in hexadecimal format. Note: DC power must be ON}
set helptext $h1$CRLF$h2
}
"lsb_err*" {
set h1 {Interpretation of the first 2 hexadecimal characters of the StatusByte string.}
set h2 {LSB (lower status byte) contains interlock status conditions}
set h3 {such as water or partial power failure.}
set helptext $h1$CRLF$h2$CRLF$h3
}
"msb_err*" {
set h1 {Interpretation of the hexadecimal characters 3 and 4 of the StatusByte string.}
set h2 {MSB (machine status byte) contains interlock status conditions}
set h3 {such as an overcurrent condition.}
set helptext $h1$CRLF$h2$CRLF$h3
}
"pwr_supply_status*" {
set h1 {Interpretation of the hexadecimal characters 5 and 6 of the StatusByte string.}
set h2 {Contains information about the power supply status}
set h3 {such as DC power on or off.}
set helptext $h1$CRLF$h2$CRLF$h3
}
"state_machine_status*" {
set h1 {Interpretation of the hexadecimal characters 7 and 8 of the StatusByte string.}
set h2 {Contains information about the power supply State Machines}
set h3 {such as the Inrush or Test DAC states.}
set helptext $h1$CRLF$h2$CRLF$h3
}
"status*" {
set helptext {Device status. Is 'idle' while in tolerance or 'busy' while driving or resetting}
}
"EthernetAddr*" {
set h1 {ETH/ Query ethernet address of the power supply }
set h2 {Returned: ethernet address in hexadecimal numbers.}
set helptext $h1$CRLF$h2
}
"is_in_tolerance*" {
set h1 {A flag that indicates whether the actual magnetic field strength is within tolerance}
set h2 {of the setpoint field strength.}
set helptext $h1$CRLF$h2
}
"apply_tolerance*" {
set h1 {A flag that indicates whether the power control should actively try}
set h2 {to keep the magnetic field strength within the set tolerance of the setpoint.}
set helptext $h1$CRLF$h2
}
"tolerance*" {
set h1 {Tolerance specifies the magnetic field strength tolerance in Tesla applicable to the setpoint.}
set helptext $h1
}
"mon_mode*" {
set h1 {A flag that indicates whether the magnet's current is being actively changed to}
set h2 {drive the magnetic field towards the setpoint or whether the current is stable}
set h3 {with the magnetic field in tolerance with the setpoint.}
set helptext $h1$CRLF$h2$CRLF$h3
}
"errhandler*" {
set h1 {Specifies the default action in case of a serious error.}
set h2 {The default action is always 'pause'.}
set helptext $h1$CRLF$h2
}
"last_error_msg*" {
set helptext {last_error_msg and last_error_msg2 display the last 2 error messages received from the device.}
}
default {
set helptext {Sorry mate. No help available.}
clientput "No help info available for node $varName"
}
}
#set sLen [string bytelength $helptext]
#clientput "helptext ($sLen bytes) $helptext"
hsetprop $nodeName help $helptext
} message ]
handle_exception $catch_status $message "in helpNotes4user(). varName=$varName."
}
##
# @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 bruker_BEC1 scriptcontext object (typically sct_bruker_BEC1_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 writable set to 1 if the node represents a request for a change in settings sent to the device
# @param pollEnabled set to 1 if the node property pollable is to be enabled (node gets read every 5 secs)
# @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 klasse Nexus class name (?)
# @return OK
proc createNode {scobj_hpath sct_controller cmdGroup varName readable writable pollEnabled drivable replyLen dataType permission rdCmd rdFunc wrCmd wrFunc allowedValues klasse} {
#clientput "createing node for: $scobj_hpath $cmdGroup $varName $readable $writable $pollEnabled $drivable $dataType $permission $rdCmd $rdFunc $wrCmd $wrFunc"
set catch_status [ catch {
set ns ::scobj::bruker_BEC1
set nodeName "$scobj_hpath/$cmdGroup/$varName"
if {1 > [string length $cmdGroup]} {
set nodeName "$scobj_hpath/$varName"
}
hfactory $nodeName plain $permission $dataType
if {$readable == 1} {
hsetprop $nodeName read ${ns}::getValue $scobj_hpath $rdFunc $rdCmd $replyLen
}
if {$pollEnabled == 1} {
# clientput "enabling polling for $nodeName"
$sct_controller poll $nodeName
}
hsetprop $nodeName $rdFunc ${ns}::$rdFunc $replyLen
if {$writable == 1} {
hsetprop $nodeName write ${ns}::$wrFunc $scobj_hpath noResponse $wrCmd
hsetprop $nodeName writestatus UNKNOWN
hsetprop $nodeName noResponse ${ns}::noResponse
if {$pollEnabled == 1} {
$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 1
hsetprop $nodeName driving 0
hsetprop $nodeName checklimits ${ns}::check $scobj_hpath 1
hsetprop $nodeName checkstatus ${ns}::drivestatus $scobj_hpath
hsetprop $nodeName halt ${ns}::halt $scobj_hpath
}
helpNotes4user $scobj_hpath $cmdGroup $varName
return OK
} message ]
handle_exception $catch_status $message "in createNode(). varName=$varName"
}
##
# @brief mk_sct_bruker_BEC1() creates a scriptcontext object for a Bruker BEC1 power supply (for 1T magnet)
# @param sct_controller name of the bruker_BEC1 scriptcontext object (typically sct_bruker_BEC1_tc1 or tc2)
# @param klasse Nexus class name (?), typically 'environment'
# @param tempobj short name for the magnet power supply scriptcontext object (typ. ma1 or ma2)
# @param tol magentic field strength tolerance in Tesla (typ. 1)
# @return nothing (well, the sct object)
proc mk_sct_bruker_BEC1 {sct_controller klasse tempobj tol CID CTYPE} {
set catch_status [ catch {
set ns ::scobj::bruker_BEC1
set ::scobj::bruker_BEC1::bruker_BEC1_sct_obj_name $tempobj
# terminator string for serial communication
#set CR "\r"
#set LF "\n"
set ::scobj::bruker_BEC1::bruker_BEC1_tolerance $tol
set ::scobj::bruker_BEC1::bruker_BEC1_driveTolerance [expr {$tol * $::scobj::bruker_BEC1::bruker_BEC1_tolerance}]
MakeSICSObj $tempobj SCT_OBJECT
sicslist setatt $tempobj klass $klasse
sicslist setatt $tempobj long_name $tempobj
# Create a base node for all the state machines of this sics object
set scobj_hpath /sics/$tempobj
set ::scobj::bruker_BEC1::bruker_BEC1_path2nodes $scobj_hpath
# Create state machines for the following device commands (non-polled entries are place-holders
# for manually maintained nodes like parameters in human-readable form)
# 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
# writable set to 1 if the node represents a request for a change in settings sent to the device
# pollEnabled set to 1 if the node property pollable is to be enabled (node gets read every 5 secs)
# drivable if set to 1 it prepares the node to provide a drivable interface
# replyLen
# 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
# rdFunc nextState Function to be called after the getValue function, typically rdValue()
# wrCmd actual device write command to be sent to the device
# wrFunc Function to be called to send the wrCmd to the device, typically setValue()
# allowedValues allowed values for the node data - does not permit other
set deviceCommandToplevel {
sensor desired_field 1 1 1 0 16 float user {FLD/} {rdValue} {FLD=} {setValue} {}
sensor measured_field 1 0 1 0 13 float spy {CHF/} {rdValue} {} {setValue} {}
sensor desired_current 1 1 1 1 15 float user {CUR/} {rdValue} {CUR=} {setDesiredCurrent} {}
sensor nominal_outp_current 1 0 1 0 13 float spy {CHN/} {rdValue} {} {setDesiredCurrent} {}
}
set deviceCommand {
pwrctrl dc_power 1 1 1 0 7 int user {DCP/} {inTolerance} {DCP=} {setValue} {0,1}
pwrctrl pwr_ctrl_from 1 1 1 0 7 int user {EXT/} {rdValue} {EXT=} {setValue} {0,1,2}
pwrctrl local_remote_state 1 1 1 0 7 int user {REM/} {rdValue} {REM=} {setValue} {0,1}
pwrctrl output_voltage 1 0 1 0 12 float spy {CHV/} {rdValue} {} {setValue} {}
pwrctrl reset_error_msg 0 1 0 0 7 int user {} {rdValue} {RST=} {setValue} {0}
pwrctrl statusByte 1 1 1 0 13 text user {STA/} {rdValue} {STA=} {setValue} {0}
}
# it seems these functions are not supported by our device
#pwrCtrl EthernetAddrHex 1 0 1 0 text spy {ETH/} {rdValue} {} {setValue} {}
#pwrCtrl LoadResistance 1 0 1 0 float spy {RES/} {rdValue} {} {setValue} {}
#pwrCtrl QueryPolarity 1 0 1 0 int spy {POL/} {rdValue} {} {setValue} {}
#pwrCtrl SetPolarity 0 1 0 0 int user {} {rdValue} {POL=} {setValue} {0,1}
hfactory $scobj_hpath/sensor plain spy none
foreach {cmdGroup varName readable writable pollEnabled drivable replyLen dataType permission rdCmd rdFunc wrCmd wrFunc allowedValues} $deviceCommandToplevel {
createNode $scobj_hpath $sct_controller $cmdGroup $varName $readable $writable $pollEnabled $drivable $replyLen $dataType $permission $rdCmd $rdFunc $wrCmd $wrFunc $allowedValues $klasse
}
# create a base node for each commandGroup element - these are all polled
hfactory $scobj_hpath/emon plain spy none
hfactory $scobj_hpath/pwrctrl plain spy none
foreach {cmdGroup varName readable writable pollEnabled drivable replyLen dataType permission rdCmd rdFunc wrCmd wrFunc allowedValues} $deviceCommand {
createNode $scobj_hpath $sct_controller $cmdGroup $varName $readable $writable $pollEnabled $drivable $replyLen $dataType $permission $rdCmd $rdFunc $wrCmd $wrFunc $allowedValues $klasse
}
# Create state machines for the following required nodes that do not correspond
# to device commands.
hfactory $scobj_hpath/emon/last_error_msg plain user text
#hsetprop $scobj_hpath/emon/last_error_msg values *any*
hset $scobj_hpath/emon/last_error_msg $::scobj::bruker_BEC1::bruker_BEC1_errMsg
helpNotes4user $scobj_hpath "emon" "last_error_msg"
hfactory $scobj_hpath/emon/last_error_msg2 plain user text
hset $scobj_hpath/emon/last_error_msg2 $::scobj::bruker_BEC1::bruker_BEC1_errMsg2
helpNotes4user $scobj_hpath "emon" "last_error_msg2"
#hfactory $scobj_hpath/emon/statusText plain user text
#hset $scobj_hpath/emon/statusText $::scobj::bruker_BEC1::bruker_BEC1_statusText
#helpNotes4user $scobj_hpath "emon" "statusText"
hfactory $scobj_hpath/emon/apply_tolerance plain user int
hsetprop $scobj_hpath/emon/apply_tolerance values 0,1
hset $scobj_hpath/emon/apply_tolerance 1
helpNotes4user $scobj_hpath "emon" "apply_tolerance"
hfactory $scobj_hpath/emon/is_in_tolerance plain spy text
hsetprop $scobj_hpath/emon/is_in_tolerance values idle,drive,monitor,error
hset $scobj_hpath/emon/is_in_tolerance "inTolerance"
helpNotes4user $scobj_hpath "emon" "is_in_tolerance"
hfactory $scobj_hpath/emon/tolerance plain user float
hsetprop $scobj_hpath/emon/tolerance units $::scobj::bruker_BEC1::bruker_BEC1_magneticFiledUnits
# hsetprop $scobj_hpath/emon/tolerance units "T"
hset $scobj_hpath/emon/tolerance $tol
helpNotes4user $scobj_hpath "emon" "tolerance"
# environment monitoring flags: shows if setpoints (field, current) are in tolerance
hfactory $scobj_hpath/emon/mon_mode plain user text
hsetprop $scobj_hpath/emon/mon_mode values idle,drive,monitor,error
hset $scobj_hpath/emon/mon_mode "idle"
helpNotes4user $scobj_hpath "emon" "mon_mode"
hfactory $scobj_hpath/emon/errhandler plain spy text
hset $scobj_hpath/emon/errhandler "lazy"
helpNotes4user $scobj_hpath "emon" "errhandler"
hfactory $scobj_hpath/pwrctrl/lsb_err plain spy text
hset $scobj_hpath/pwrctrl/lsb_err "UNKNOWN"
helpNotes4user $scobj_hpath "pwrctrl" "lsb_err"
hfactory $scobj_hpath/pwrctrl/msb_err plain spy text
hset $scobj_hpath/pwrctrl/msb_err "UNKNOWN"
helpNotes4user $scobj_hpath "pwrctrl" "msb_err"
hfactory $scobj_hpath/pwrctrl/pwr_supply_status plain spy text
hset $scobj_hpath/pwrctrl/pwr_supply_status "UNKNOWN"
helpNotes4user $scobj_hpath "pwrctrl" "pwr_supply_status"
hfactory $scobj_hpath/pwrctrl/state_machine_status plain spy text
hset $scobj_hpath/pwrctrl/state_machine_status "UNKNOWN"
helpNotes4user $scobj_hpath "pwrctrl" "state_machine_status"
#hfactory $scobj_hpath/pwrctrl/EthernetAddrDec plain user text
#hset $scobj_hpath/pwrctrl/EthernetAddrDec "UNKNOWN"
#helpNotes4user $scobj_hpath "pwrctrl" "EthernetAddrDec"
hfactory $scobj_hpath/status plain spy text
hsetprop $scobj_hpath/status values busy,idle
hset $scobj_hpath/status "idle"
helpNotes4user $scobj_hpath "" "status"
::scobj::hinitprops $tempobj
hsetprop $scobj_hpath klass NXenvironment
::scobj::set_required_props $scobj_hpath
# These are loggable parameters that need additional nexus properties
# Note: node names longer than 8 characters cause eror messages - avoid
set nxProperties "
$scobj_hpath sensor NXsensor spy
$scobj_hpath sensor/measured_field sensor user
$scobj_hpath sensor/desired_field sensor user
$scobj_hpath sensor/desired_current sensor user
$scobj_hpath sensor/nominal_outp_current sensor user
"
set aliasProperties "
$scobj_hpath measured_field sensor _sensor_MeasuredField
$scobj_hpath desired_field sensor _sensor_ActualDAC_Field
$scobj_hpath desired_current sensor _sensor_DesiredCurrent
$scobj_hpath nominal_outp_current sensor _sensor_NominalOutpCurrent
"
foreach {rootpath hpath klasse priv} $nxProperties {
hsetprop $rootpath/$hpath klass $klasse
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
foreach {rootpath node groupy myalias} $aliasProperties {
hsetprop $scobj_hpath/$groupy/$node nxalias $tempobj$myalias
hsetprop $scobj_hpath/$groupy/$node mutable true
hsetprop $scobj_hpath/$groupy/$node sdsinfo ::nexus::scobj::sdsinfo
}
hsetprop $scobj_hpath privilege spy
# call hinitprops from script_context_util.tcl which initialises the hdb properties required
# for generating the GumTree interface and saving data for script context objects (hdf file)
# @param scobj, name of script context object (path to a node)
# @param par, optional parameter (name of the node variable)
# Changed ffr 20100625: do not call hinitprops to avoid a conflict with a change in SICServer vers. 2_5
#::scobj::hinitprops $tempobj/sensor nominal_outp_current
hsetprop $scobj_hpath/pwrctrl/statusByte control false
hsetprop $scobj_hpath/sensor/desired_current type drivable
hsetprop $scobj_hpath/sensor/desired_current permlink data_set ${CTYPE}${CID}SP1
hsetprop $scobj_hpath/sensor/measured_field permlink data_set ${CTYPE}${CID}S1
ansto_makesctdrive ${tempobj}_driveable $scobj_hpath/sensor/desired_current $scobj_hpath/sensor/nominal_outp_current $sct_controller
# initialise the device
bruker_BEC1_init $sct_controller $scobj_hpath
clientput "Bruker BEC1 power supply for 1-Tesla magnet ready at /sample/$tempobj (Driver 2010-06-25)"
} message ]
handle_exception $catch_status $message "In subroutine mk_sct_bruker_BEC1()."
}
namespace export mk_sct_bruker_BEC1
# endproc mk_sct_bruker_BEC1 sct_controller klasse tempobj tol bruker_BEC1_LSmodel
}
# end of namespace mk_sct_bruker_BEC1
##
# @brief add_bruker_bec1() adds a scriptcontext object for a Bruker BEC1 power supply (1T magnet)
# and makes it available to SICServer
# @param name short name for the temperature controller scriptcontext object (typ. ma1 or ma2)
# @param IP IP address of the device (e.g. IP of moxabox that hooks up to the BEC1)
# @param port port number on the moxabox (typ. 4001, 4002, 4003, or 4004)
# @param tol magnetic field strength tolerance in Tesla (default 0.1T)
# @return nothing (well, the sct object)
proc add_bruker_bec1 {name IP port {_tol 0.1} {CID 1} {CTYPE B} } {
# Don't create a magnet controller for the script validator, this may cause the
# the BEC1 to lock up.
# NOTE: This is placed outside the catch block because "return" raises an exception
if {[SplitReply [environment_simulation]]} {
return
}
set catch_status [ catch {
clientput "\nadd_bruker_BEC1: makesctcontroller $name astvelsel ${IP}:$port for Bruker BEC1 1-Tesla magnet power supply"
# Command terminator for Bruker unit is always carriage return without linefeed '\r'
makesctcontroller sct_bruker_BEC1_$name astvelsel ${IP}:$port "\r"
# proc mk_sct_bruker_BEC1 (sct_controller klasse tempobj tol)
mk_sct_bruker_BEC1 sct_bruker_BEC1_$name environment $name $_tol $CID $CTYPE
makesctemon $name /sics/$name/emon/mon_mode /sics/$name/emon/is_in_tolerance /sics/$name/emon/errhandler
} message ]
handle_exception $catch_status $message "In subroutine add_bruker_bec1()."
}
clientput "file evaluation of sct_bruker_bec1.tcl"
namespace import ::scobj::bruker_BEC1::*