Files
sics/site_ansto/instrument/config/environment/magneticField/sct_bruker_BEC1.tcl
Ferdi Franceschini d7acb7c16c Load drivers which have been enabled in the SICS config ini files.
Lakeshore 336 drivers with known IP addresses have been added to the ini files with unique IDs.
All entries in the ini files now have unique IDs
The wombat ini now has radio buttons to select sample stage motor configurations.
2014-05-05 12:25:00 +10:00

1390 lines
62 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
# puts "setting serial communication parameters"
# hset $tc_root/other/cfgProtocol_comm "COMM 1,5,1"
# Query the device ID
# puts "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]
#puts "rdValDrct(): result is $data"
#set data [hget $tc_root/other/deviceID_idn]
# puts "set deviceID_idn (hval $tc_root/other/deviceID_idn)"
#set data [hval $tc_root/other/deviceID_idn]
#set ::scobj::bruker_BEC1::this_sDeviceID data
# puts "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
# puts "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} {
# puts "sct_bruker_BEC1.tcl: Lakeshore $::scobj::bruker_BEC1::bruker_BEC1_LSmodel self-test ok."
# } else {
# puts "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
# puts "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]
# puts "rdValue(): result is $data"
# broadcast rdValue "rdValue(): result is $data"
#puts "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:*" {
puts "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
#puts "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
#puts "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]
#puts "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]
# puts "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]
# puts "inTolerance(): data=$data oldval=$oldvalue"
switch -glob -- $data {
"ASCERR:*" {
puts "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/
#puts "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
}
}
}
# puts "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 nominal_outp_current [hval $nodename]
# puts "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"
}
}
# puts "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"
# puts "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 {
#puts "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
# puts "setDesiredField(): driving set to 1"
set nodename $tc_root/sensor/setpoint
hsetprop $nodename driving 1
}
#puts "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 {
# puts "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
# puts "setDesiredCurrent(): driving set to 1"
set nodename $tc_root/sensor/nominal_outp_current
hsetprop $nodename driving 1
}
#puts "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} {
#}
}
#puts "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 ", "
#puts "decodeErrByte: errByte:$errByte"
set catch_status [ catch {
# convert to decimal for calculations
set hexvar "0x$errByte"
set decErrByte [expr {$hexvar}]
# puts "errByte=$errByte, hexvar=$hexvar, decErrByte=$decErrByte"
foreach {hexVal errText} $errList {
#puts "errByte:$errByte hexVal:$hexVal errText:$errText"
# convert to decimal for calculations
set hexvar "0x$hexVal"
set decVal [expr { $hexvar }]
#puts "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}]]
#puts "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 {
#puts "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]
}
#puts "LSB_ErrByte :x$tmp_LSB_ErrByte: $LSB_ErrByteTxt"
#puts "MSB_ErrByte :x$tmp_MSB_ErrByte: $MSB_ErrByteTxt"
#puts "pwr_supply_status:x$tmp_PwrSupplyStatusByte: $PwrSupplyStatusByteTxt"
#puts "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} {
#puts "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
#puts "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]
}
#puts "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 nominal_outp_current [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"
#puts "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.}
puts "No help info available for node $varName"
}
}
#set sLen [string bytelength $helptext]
#puts "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} {
#puts "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} {
# puts "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
puts "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 {
puts "\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()."
}
if {[ catch {
if { [ info exists ::config_dict ] } {
dict for {secname secinfo} $::config_dict {
if { [dict exists $secinfo "driver"] && ([dict get $secinfo "driver"] == "bruker_bec1") } {
if { [ dict get $::secinfo enabled ] } {
set name [dict get $::secinfo name]
set IP [dict get $::secinfo ip]
set PORT [dict get $::secinfo port]
set tol [dict get $::secinfo tol]
set cid [dict get $::secinfo id]
set ctype [dict get $::secinfo type]
add_bruker_BEC1 $name $IP $PORT $tol $cid $ctype
}
}
}
}
} message ]} {
puts "ERROR: $message"
}
namespace import ::scobj::bruker_BEC1::*