Files
sics/site_ansto/instrument/reflectometer/config/beamline/sct_RFGen.tcl
Ferdi Franceschini 6b1c629a43 Guard against misinterpreting readings that have a leading zero as octal.
Set the current to the target value when we're within one step rather than within tolerance to avoid undershooting the target.
Do not try and get the state when initialising the driver because it will fail to create the driver properly if the RF generator is unavailable on SICS startup.  The current is zero when the RF gen is power cycled so it is safe to set the current reading and target current as zero in initialisation so we just set the operating frequency and everything else is left at zero.
r3228 | ffr | 2011-06-26 12:18:27 +1000 (Sun, 26 Jun 2011) | 3 lines
2012-11-15 17:15:19 +11:00

432 lines
13 KiB
Tcl

##
# @file Mirrotron RF Generator control
#
# Author: Ferdi Franceschini (ffr@ansto.gov.au) May 2010
#
# The controller can be installed with the following command,
# ::scobj::rfgen::mkRFGen {
# name "anal"
# address 1
# opCurr 68
# opFreq 241
# IP localhost
# PORT 65123
# tuning 1
# currtol 1
# interval 2
# }
#
# NOTE:
# If tuning=1 this will generate xxx/set_current and xxx/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 generator on or off
proc ::scobj::rfgen::switch_on {basePath} {
variable RAMPSTART
variable RAMPTOZERO
set genState [sct target]
switch $genState {
"0" {
hsetprop $basePath targetCurr 0
hsetprop $basePath OutputState 0
hsetprop $basePath ramping $RAMPSTART
sct update 0
sct utime updatetime
}
"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 updatetime
}
default {
set ErrMsg "[sct] invalid input $genState, 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 object base-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 object base-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
}
# trim leading zeroes to guard against interpreting as octal
if {[string is integer [string trimleft $v 0]]} {
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 updatetime
sct oldStateRep $statList
}
if {$currSuperState != $FLIPOFF && $stateArr(curr) > [sct currTol] && $stateArr(O) && $stateArr(CV)} {
broadcast "WARNING: RF generator 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]
if {$stateArr(curr) == -1} {
# Got invalid current reading try again
return idle
}
set targetCurr [sct targetCurr]
set SCT_RFGEN [sct contname]
set K1 [sct K1]
set K2 [sct K2]
set K3 [sct K3]
set targetFreq [sct targetFreq]
set output [sct OutputState]
if { [expr {abs($stateArr(curr) - $targetCurr)}] <= 5 } {
set curr $targetCurr
} 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=$K3:K2=$K2:K1=$K1:O=$output"]
return idle
}
##
# @brief Make an RF generator control object
#
# @param argList, {name "analyser" address "1" opCurr 68 opFreq 241 IP localhost PORT 65123 tuning 0 interval 1}
#
# name: name of RF generator object
# address: address assigned to RF generator 1-9
# opCurr: the operating current, when you switch it on it will ramp to this current
# opFreq: the operating frequency, when you switch it on 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::mkRFGen {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) K1 $pa(K1)
hsetprop /sics/$pa(NAME) K2 $pa(K2)
hsetprop /sics/$pa(NAME) K3 $pa(K3)
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)/switch_on plain user int
hsetprop /sics/$pa(NAME)/switch_on write ::scobj::rfgen::switch_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"} {
set SCT_RFGEN sct_rfgen_$pa(NAME)
makesctcontroller $SCT_RFGEN rfamp $pa(IP):$pa(PORT)
hsetprop /sics/$pa(NAME) contname $SCT_RFGEN
# mkStatArr stateArr [split [$SCT_RFGEN transact "L:$pa(ADDRESS)"] "|="]
hset /sics/$pa(NAME)/flip_current 0
hset /sics/$pa(NAME)/flip_frequency $pa(OPFREQ)
hset /sics/$pa(NAME)/flip_voltage 0
hset /sics/$pa(NAME)/flip_on 0
hsetprop /sics/$pa(NAME) targetFreq $pa(OPFREQ)
hsetprop /sics/$pa(NAME) targetCurr 0
$SCT_RFGEN poll /sics/$pa(NAME) $pa(INTERVAL)
$SCT_RFGEN write /sics/$pa(NAME)/switch_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
}
}
}