Files
sics/site_ansto/instrument/lyrebird/config/beamline/sct_flipper.tcl
Jing Chen 28772f6ec2 added Lyrebird instrument config
r3038 | jgn | 2010-12-13 16:10:57 +1100 (Mon, 13 Dec 2010) | 1 line
2012-11-15 17:07:02 +11:00

419 lines
13 KiB
Tcl

##
# @file Spin flipper control for Quokka
#
# Author: Ferdi Franceschini (ffr@ansto.gov.au) May 2010
#
# The spin flipper can be installed with the following command,
# ::scobj::rfgen::mkFlipper {
# name "flipper"
# address 1
# opCurr 68
# opFreq 241
# IP localhost
# PORT 65123
# tuning 1
# currtol 1
# interval 2
# }
#
# NOTE:
# If tuning=1 this will generate flipper/set_current and flipper/set_frequency
# nodes for the instrument scientists.
# The tuning parameter should be set to 0 for the users.
#
# The operation_manual_Platypus_polarization_system.doc:Sec 3.1 states the following
# Attention
# a) Do not switch on the RF output with non-zero current setting (the current
# control becomes unstable)! If unsure, rotate the current setting
# potentiometer 10 turns counter-clockwise.
# b) In case of RF vacuum discharge (harmful for the system)
# " the main symptom is that the RF power source turns into CV mode, the
# voltage increases to 34 Vem and the current decreases;
# " switch off the RF output;
# " decrease current setting by rotating the potentiometer 10 turns counter-clockwise;
# " verify the vacuum level in the tank and restart the flipper operation only if it is below 0.01 mbar.
namespace eval ::scobj::rfgen {
# Control states
variable RAMPIDLE 0
variable RAMPSTOP 1
variable RAMPSTART 2
variable RAMPBUSY 3
variable RAMPTOZERO 4
variable FLIPOFF 5
variable MAXVOLTAGE 34
}
##
# @brief Utility for trimming zero padding from current and frequency readings.
# We do this to avoid misinterpreting numbers as octal
proc ::scobj::rfgen::mkStatArr {stateArrName stateReport} {
upvar $stateArrName stateArr
array set stateArr $stateReport
if {$stateArr(curr) != 0} {
set val [string trimleft $stateArr(curr) 0]
if {[string is integer $val]} {
set stateArr(curr) $val
} else {
set stateArr(curr) -1
}
}
if {$stateArr(freq) != 0} {
set val [string trimleft $stateArr(freq) 0]
if {[string is integer $val]} {
set stateArr(freq) $val
} else {
set stateArr(freq) -1
}
}
if {$stateArr(voltage) != 0} {
set val [string trimleft $stateArr(voltage) 0]
if {[string is integer $val]} {
set stateArr(voltage) $val
} else {
set stateArr(voltage) -1
}
}
}
##
# @brief Switch the spin flipper on or off
proc ::scobj::rfgen::set_flip_on {basePath} {
variable RAMPSTART
variable RAMPTOZERO
set flipState [sct target]
switch $flipState {
"0" {
hsetprop $basePath targetCurr 0
hsetprop $basePath OutputState 0
hsetprop $basePath ramping $RAMPSTART
sct update 0
sct utime readtime
}
"1" {
hsetprop $basePath targetCurr [hgetpropval $basePath opCurr]
hsetprop $basePath targetFreq [hgetpropval $basePath opFreq]
hsetprop $basePath OutputState 1
hsetprop $basePath ramping $RAMPSTART
sct update 1
sct utime readtime
}
default {
set ErrMsg "[sct] invalid input $flipState, Valid states for [sct] are 1 or 0"
sct seterror "ERROR: $ErrMsg"
return -code error $ErrMsg
}
}
return idle
}
##
# @brief Get the target current and scale it for the RF generator.
# Also updates the operating current for this session.
#
# @param basePath, The "spin-flipper" object path, this is where we keep our state variables.
proc ::scobj::rfgen::set_current {basePath} {
variable RAMPSTART
set newCurr [sct target]
set current [expr {round(10.0 * $newCurr)}]
hsetprop $basePath targetCurr $current
hsetprop $basePath opCurr $current
hsetprop $basePath ramping $RAMPSTART
hsetprop $basePath OutputState 1
return idle
}
##
# @brief Get the target frequency. Also updates the operating frequency for this session.
#
# @param basePath, The "spin-flipper" object path, this is where we keep our state variables.
proc ::scobj::rfgen::set_frequency {basePath} {
variable RAMPSTART
set newFreq [sct target]
hsetprop $basePath targetFreq $newFreq
hsetprop $basePath opFreq $newFreq
hsetprop $basePath ramping $RAMPSTART
hsetprop $basePath OutputState 1
return idle
}
##
# @brief Request a state report from the RF generator
proc ::scobj::rfgen::rqStatFunc {} {
sct send "L:[sct address]"
return rdState
}
##
# @brief Read and record the state report from the RF generator
proc ::scobj::rfgen::rdStatFunc {} {
variable RAMPBUSY
variable RAMPSTART
variable RAMPTOZERO
variable RAMPIDLE
variable FLIPOFF
variable MAXVOLTAGE
set basePath [sct]
set currSuperState [sct ramping]
set updateFlipper 0
set statStr [sct result]
if {[string match "ASCERR:*" $statStr]} {
sct geterror $statStr
sct ramping $RAMPIDLE
return stateChange
}
set statList [split $statStr "|="]
foreach {k v} $statList {
if {$k == "type"} {
lappend temp "$k $v"
continue
}
if {[string is integer $v]} {
lappend temp "$k $v"
} else {
lappend temp "$k -1"
}
}
set statList [join $temp]
mkStatArr stateArr $statList
if {$statList != [sct oldStateRep]} {
hset $basePath/flip_current [expr {$stateArr(curr) / 10.0}]
hset $basePath/flip_frequency $stateArr(freq)
hset $basePath/flip_voltage $stateArr(voltage)
hset $basePath/flip_on $stateArr(O)
hset $basePath/state_report $statList
sct update $statList
sct utime readtime
sct oldStateRep $statList
}
if {$currSuperState != $FLIPOFF && $stateArr(curr) > [sct currTol] && $stateArr(O) && $stateArr(CV)} {
broadcast "WARNING: spin flipper has switched to voltage control, voltage = $stateArr(voltage)"
if {$stateArr(voltage) >= $MAXVOLTAGE} {
sct ramping $FLIPOFF
}
}
return stateChange
}
##
# @brief State transition function
proc ::scobj::rfgen::stateFunc {} {
variable RAMPIDLE
variable RAMPSTOP
variable RAMPSTART
variable RAMPBUSY
variable RAMPTOZERO
variable FLIPOFF
variable MAXVOLTAGE
set basePath [sct]
set currSuperState [sct ramping]
mkStatArr stateArr [hval $basePath/state_report]
set currControlStatus [sct status]
switch $currSuperState [ subst -nocommands {
$RAMPSTART {
# broadcast RAMPSTART
if [string match $currControlStatus "IDLE"] {
statemon start flipper
sct status "BUSY"
sct ramping $RAMPBUSY
return ramp
} else {
# Flipper is off, set current to zero before switching on
sct origTargetCurr [sct targetCurr]
sct targetCurr 0
sct OutputState 0
sct ramping $RAMPTOZERO
return ramp
}
}
$RAMPTOZERO {
# broadcast RAMPTOZERO
if {$stateArr(curr) <= [sct currTol]} {
# We've reached a safe state so switch on and ramp to target current
sct targetCurr [sct origTargetCurr]
sct OutputState 1
sct ramping $RAMPBUSY
} else {
sct targetCurr 0
sct OutputState 0
}
return ramp
}
$RAMPBUSY {
# broadcast RAMPBUSY
if { [expr {abs($stateArr(curr) - [sct targetCurr])}] <= [sct currTol] } {
sct ramping $RAMPSTOP
return idle
}
return ramp
}
$FLIPOFF {
sct targetCurr 0
sct OutputState 0
if { $stateArr(curr) <= [sct currTol] } {
sct ramping $RAMPSTOP
broadcast "ERROR: Spin flipper switched off voltage exceeds $MAXVOLTAGE in voltage control state, check vacuum"
return idle
} else {
return ramp
}
}
$RAMPSTOP {
# broadcast RAMPSTOP
if [string match $currControlStatus "BUSY"] {
statemon stop flipper
sct status "IDLE"
}
sct ramping $RAMPIDLE
return idle
}
$RAMPIDLE {
# broadcast RAMPIDLE
return idle
}
}]
}
##
# @brief Ramps the current up or down in steps of 0.5A and/or sets the frequency
proc ::scobj::rfgen::rampFunc {} {
set basePath [sct]
set currSuperState [sct ramping]
mkStatArr stateArr [hval $basePath/state_report]
set targetCurr [sct targetCurr]
set targetFreq [sct targetFreq]
set output [sct OutputState]
if { [expr {abs($stateArr(curr) - [sct targetCurr])}] <= [sct currTol] } {
set curr $stateArr(curr)
} elseif {$targetCurr < $stateArr(curr)} {
set curr [expr $stateArr(curr)-5]
if {$curr < $targetCurr} {
set curr $targetCurr
}
} elseif {$targetCurr > $stateArr(curr)} {
set curr [expr $stateArr(curr)+5]
if {$curr > $targetCurr} {
set curr $targetCurr
}
}
set reply [sct_rfgen send "S:[sct address]:I=$curr:F=$targetFreq:K3=$stateArr(K3):K2=$stateArr(K2):K1=$stateArr(K1):O=$output"]
return idle
}
##
# @brief Make a spin flipper control object
#
# @param argList, {name "flipper" address "1" opCurr 68 opFreq 241 IP localhost PORT 65123 tuning 0 interval 1}
#
# name: name of spin flipper object
# address: address assigned to RF generator 1-9
# opCurr: the operating current, when you switch the spin flipper on it will ramp to this current
# opFreq: the operating frequency, when you switch on the spin flipper it will set this frequency
# IP: IP address of RF generator moxa box
# PORT: Port number assigned to the generator on the moxa-box
# tuning: boolean, set tuning=1 to allow instrument scientists to set the current and frequency
# interval: polling and ramping interval in seconds. One sets the ramp rate to 0.5A/s
proc ::scobj::rfgen::mkFlipper {argList} {
variable RAMPIDLE
# Generate parameter array from the argument list
foreach {k v} $argList {
set KEY [string toupper $k]
set pa($KEY) $v
}
MakeSICSObj $pa(NAME) SCT_OBJECT
sicslist setatt $pa(NAME) klass instrument
sicslist setatt $pa(NAME) long_name $pa(NAME)
# hfactory /sics/$pa(NAME)/status plain spy text
hsetprop /sics/$pa(NAME) status "IDLE"
hfactory /sics/$pa(NAME)/state_report plain internal text
hfactory /sics/$pa(NAME)/flip_current plain internal float
hfactory /sics/$pa(NAME)/flip_frequency plain internal int
hfactory /sics/$pa(NAME)/flip_voltage plain internal int
hfactory /sics/$pa(NAME)/flip_on plain internal int
hsetprop /sics/$pa(NAME) read ::scobj::rfgen::rqStatFunc
hsetprop /sics/$pa(NAME) rdState ::scobj::rfgen::rdStatFunc
hsetprop /sics/$pa(NAME) stateChange ::scobj::rfgen::stateFunc
hsetprop /sics/$pa(NAME) ramp ::scobj::rfgen::rampFunc
hsetprop /sics/$pa(NAME) address $pa(ADDRESS)
hsetprop /sics/$pa(NAME) tuning $pa(TUNING)
hsetprop /sics/$pa(NAME) ramping $RAMPIDLE
hsetprop /sics/$pa(NAME) opCurr $pa(OPCURR)
hsetprop /sics/$pa(NAME) opFreq $pa(OPFREQ)
hsetprop /sics/$pa(NAME) targetCurr 0
hsetprop /sics/$pa(NAME) origTargetCurr 0
hsetprop /sics/$pa(NAME) oldStateRep ""
hsetprop /sics/$pa(NAME) currTol $pa(CURRTOL)
hfactory /sics/$pa(NAME)/comp_current plain internal float
hsetprop /sics/$pa(NAME)/comp_current units "A"
hset /sics/$pa(NAME)/comp_current $pa(COMPCURR)
hfactory /sics/$pa(NAME)/guide_current plain internal float
hsetprop /sics/$pa(NAME)/guide_current units "A"
hset /sics/$pa(NAME)/guide_current $pa(GUIDECURR)
hfactory /sics/$pa(NAME)/thickness plain internal float
hsetprop /sics/$pa(NAME)/thickness units "mm"
hset /sics/$pa(NAME)/thickness $pa(THICKNESS)
hfactory /sics/$pa(NAME)/set_flip_on plain user int
hsetprop /sics/$pa(NAME)/set_flip_on write ::scobj::rfgen::set_flip_on /sics/$pa(NAME)
# Only create the set current and frequency nodes when commissioning
# Initialise properties required for generating the API for GumTree and to save data
::scobj::hinitprops $pa(NAME) flip_current flip_frequency flip_voltage flip_on comp_current guide_current thickness
hsetprop /sics/$pa(NAME)/comp_current mutable false
hsetprop /sics/$pa(NAME)/guide_current mutable false
hsetprop /sics/$pa(NAME)/thickness mutable false
if {[SplitReply [rfgen_simulation]] == "false"} {
makesctcontroller sct_rfgen rfamp $pa(IP):$pa(PORT)
mkStatArr stateArr [split [sct_rfgen transact "L:$pa(ADDRESS)"] "|="]
hset /sics/$pa(NAME)/flip_current [expr {$stateArr(curr) / 10.0}]
hset /sics/$pa(NAME)/flip_frequency $stateArr(freq)
hset /sics/$pa(NAME)/flip_voltage $stateArr(voltage)
hset /sics/$pa(NAME)/flip_on $stateArr(O)
hsetprop /sics/$pa(NAME) targetFreq $stateArr(freq)
hsetprop /sics/$pa(NAME) targetCurr [expr {$stateArr(curr) / 10.0}]
sct_rfgen poll /sics/$pa(NAME) $pa(INTERVAL)
sct_rfgen write /sics/$pa(NAME)/set_flip_on
}
if {$pa(TUNING)} {
hfactory /sics/$pa(NAME)/set_current plain user float
hfactory /sics/$pa(NAME)/set_frequency plain user int
hsetprop /sics/$pa(NAME)/set_current write ::scobj::rfgen::set_current /sics/$pa(NAME)
hsetprop /sics/$pa(NAME)/set_frequency write ::scobj::rfgen::set_frequency /sics/$pa(NAME)
if {[SplitReply [rfgen_simulation]] == "false"} {
sct_rfgen write /sics/$pa(NAME)/set_current
sct_rfgen write /sics/$pa(NAME)/set_frequency
}
}
}