# Define procs in ::scobj::xxx namespace # MakeSICSObj $obj SCT_ # The MakeSICSObj cmd adds a /sics/$obj node. NOTE the /sics node is not browsable. #include ## # /*-------------------------------------------------------------------------- # 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 , 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 & &#38; # < Left angle bracket < &#60; # > Right angle bracket > > # " Straight quotation mark " ' # ' Apostrophe ' " # 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::*