Files
sics/site_ansto/instrument/pelican/config/chopper/fermimot.tcl
Ferdi Franceschini b15014bfd5 The mchs and schs motors can now be driven via run and drive command
wrappers.
Running mchs and schs sets the motor gain parameters.
Uses new ANSTO tclmotor implementation.
2013-04-23 10:26:28 +10:00

615 lines
18 KiB
Tcl

# Table Source Document: 998-0234-004 Rev A.doc
# Speed (RPM) {veto {KpCW KpCCW} {KiCW KiCCW} {KthCW KthCCW}
# NOTE: You can use the motor direction register value to lookup the gains because motdir=0 is CW and motdir=1 is CCW
# TEST
global SCode Ecode
array set SCode {
OKOK 1
HWIdle 2
HWBusy 3
HWFault 4
HWPosFault 5
HWCrash 6
NOMEMORY 7
HWNoBeam 8
HWPause 9
HWWarn 10
HWRedo 11
}
array set ECode {
MOTREDO -1
MOTFAIL 0
MOTOK 1
}
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"
}
}
}
namespace eval ::chopper {
variable ch1_gains
variable ch2_gains
variable ch3_gains
variable speedMult
variable CW 0 CCW 1
variable PROPGAIN 1 INTGAIN 2 PHGAIN 3
variable POSDIR
set POSDIR $CCW
array set ch1_gains {
24000 { 350 { 10 10 } { 10 10 } { 2.33 2.33 } }
21000 { 400 { 10 15 } { 10 13 } { 2.33 13 } }
18000 { 450 { 10 10 } { 10 10 } { 2.33 2.33 } }
15000 { 550 { 10 10 } { 10 10 } { 2.33 2.33 } }
12000 { 650 { 12 12 } { 10 10 } { 10 10 } }
9000 { 950 { 10 10 } { 10 10 } { 10 10 } }
6000 { 1350 { 10 10 } { 10 10 } { 10 10 } }
3600 { 2310 { 5 5 } { 5 5 } { 5 5 } }
3000 { 2750 { 5 5 } { 5 5 } { 2.33 2.33 } }
}
array set ch2_gains {
24000 { 350 { 15 15 } { 13 13 } { 13 13 } }
21000 { 400 { 15 15 } { 13 13 } { 13 13 } }
18000 { 450 { 15 15 } { 13 13 } { 13 13 } }
15000 { 550 { 15 15 } { 13 13 } { 13 13 } }
12000 { 650 { 10 10 } { 10 10 } { 2.33 2.33 } }
9000 { 950 { 10 10 } { 10 10 } { 2.33 2.33 } }
6000 { 1350 { 10 10 } { 10 10 } { 2.33 2.33 } }
3000 { 2750 { 5 5 } { 5 5 } { 2 2 } }
}
array set ch3_gains {
24000 { 350 { 15 10 } { 13 10 } { 13 2.33 } }
21000 { 400 { 15 10 } { 13 10 } { 13 2.33 } }
18000 { 450 { 15 10 } { 13 10 } { 13 2.33 } }
15000 { 550 { 10 10 } { 10 10 } { 2.33 2.33 } }
12000 { 650 { 10 10 } { 10 10 } { 2.33 2.33 } }
9000 { 950 { 10 10 } { 10 10 } { 2.33 2.33 } }
6000 { 1350 { 10 10 } { 10 10 } { 2.33 2.33 } }
3000 { 2750 { 5 5 } { 5 5 } { 2 2 } }
}
set i 0
foreach mult {
1.0/12 1.0/11 1.0/10 1.0/9 1.0/8
1.0/7 1.0/6 1.0/5 1.0/4 1.0/3
1.0/2 1 1.5 2 2.5
3 3.5 4 4.5 5
5.5 6 6.5 7 7.5
8 8.5 9 9.5 10
10.5 11 11.5 12 12.5
13 13.5 14 14.5 15
16 17 18 19 20
21 22 23 24 25
26 27 28 29 30
} {
set speedMult($i) [expr double($mult)]
incr i
}
##
# Echeck_SpeedMult: Checks that speed is an allowed multiple of the reference signal
# refFreq: Reference signal frequency (Hz)
# speed: Requested chopper speed (RPM)
proc Echeck_SpeedMult {refFreq speed} {
variable speedMult
set refRPM [expr 60 * $refFreq]
foreach mult $speedMult {
if { [expr $mult * $refRPM] == $speed} {return}
}
return -code error "Speed ($speed) is not an allowed multiple of the reference signal"
}
proc Echeck_ChSpeed {ch speed} {
set minSpeed 3000
array set vetoRange {min 9000 max 11000}
if {$ch != 1 && $ch != 2 && $ch != 3} {
return -code error "Chopper number ($ch) must be 1, 2, or 3"
}
if { ![string is double $speed] } {
return -code error "Speed ($speed) is not a valid number"
} elseif {$speed < $minSpeed} {
return -code error "Speed ($speed) should be >= $minSpeed"
} elseif {$speed >= $vetoRange(min) && $speed <= $vetoRange(max)} {
return -code error "Speed ($speed) is in the vetoed range: $vetoRange(min), $vetoRange(max)"
}
}
proc Echeck_gainDir {gain dir} {
variable CW
variable CCW
variable PROPGAIN
variable INTGAIN
variable PHGAIN
if {$gain != $INTGAIN && $gain != $PROPGAIN && $gain != $PHGAIN} {
return -code error "gain identifier ($gain) should be $PROPGAIN (PROPGAIN), $INTGAIN (INTGAIN), or $PHGAIN (PHGAIN)"
}
if {$dir != $CW && $dir != $CCW} {
return -code error "direction identifier ($dir) should be $CW {CW} or $CCW {CCW}"
}
}
proc findGainIndex {ch speed} {
variable ch1_gains
variable ch2_gains
variable ch3_gains
# Check argument and return error otherwise return veto window
set catch_status [ catch {
Echeck_ChSpeed $ch $speed
set speed [expr int($speed)]
set ch [expr int($ch)]
switch $ch {
1 { set gainTable ch1_gains }
2 { set gainTable ch2_gains }
3 { set gainTable ch3_gains }
}
set speeds [lsort -integer [array names $gainTable]]
set lower 0
set upper [expr [llength $speeds] - 1]
while {1} {
if {[expr $upper - $lower] == 1} {
if { $speed < [lindex $speeds $upper] } {
return "$gainTable [lindex $speeds $lower]"
} else {
return "$gainTable [lindex $speeds $upper]"
}
}
set mid [expr int(($lower + $upper)/2.0)]
if {$speed < [lindex $speeds $mid]} {
set upper $mid
} else {
set lower $mid
}
}
} message ]
handle_exception $catch_status $message
}
proc getGain {gain ch speed dir} {
variable ch1_gains
variable ch2_gains
variable ch3_gains
variable CW
variable CCW
variable INTGAIN
variable PROPGAIN
variable PHGAIN
set catch_status [ catch {
Echeck_ChSpeed $ch $speed
Echeck_gainDir $gain $dir
set gain [expr int($gain)]
set dir [expr int($dir)]
set speed [expr int($speed)]
set ch [expr int($ch)]
switch $ch {
1 { set gainTable ch1_gains }
2 { set gainTable ch2_gains }
3 { set gainTable ch3_gains }
}
foreach sp [lsort -integer -decreasing [array names $gainTable]] {
if {$sp <= $speed} {
return [lindex [set ${gainTable}($sp)] $gain $dir]
}
}
error "Failed when looking up integral gain for speed $speed"
} message ]
handle_exception $catch_status $message
}
#TODO Define a run function which looks up parameters from hdb tree and calls the setSpeed function
# pDriv->GetPosition = GetTclPos;
# pDriv->RunTo = TclRun;
# pDriv->GetStatus = TclStat;
# pDriv->GetError = TclError;
# pDriv->TryAndFixIt = TclFix;
# pDriv->SetDriverPar = TclSetPar;
# pDriv->GetDriverPar = TclGetPar;
# pDriv->Halt = TclHalt;
# pDriv->KillPrivate = KillTCL;
variable chPath "/instrument/fermi_chopper"
proc getSpeed {ch} {
variable chPath
set rSpeed [hval $chPath/$ch/rotation_speed]
return $rSpeed
}
proc findSpeedMult {sprat} {
variable speedMult
set lower 0
set upper [expr [array size speedMult] - 1]
while {1} {
if {[expr ($upper - $lower)] == 1} {
if { [ expr ($sprat - $speedMult($lower)) ] <= [expr ($speedMult($upper) - $sprat)] } {
return $speedMult($lower)
} else {
return $speedMult($upper)
}
}
set mid [expr int( ($upper + $lower) / 2.0)]
if { $sprat < $speedMult($mid) } {
set upper $mid
} else {
set lower $mid
}
}
}
proc listAllowedSpeeds {ref_period_50ns} {
variable speedMult
set ref_speed_rpm [ expr { 60.0 / ($ref_period_50ns * 50e-9) } ]
set len [array size speedMult]
for {set i 0} {$i < $len} {incr i} {
lappend speeds [format "%.2f" [expr $ref_speed_rpm * $speedMult($i)]]
}
return $speeds
}
proc get_refSpeedRPM {hpath node addr name} {
set ref_period_50ns [ hval $hpath/$node/ref_period ]
set ref_speed_rpm [ expr { 60.0 / ($ref_period_50ns * 50e-9) } ]
return $ref_speed_rpm
}
##
# Return nearest allowed speed to target speed
proc abspermittedSpeed {hpath node addr name target} {
set ref_period_50ns [ hval $hpath/$node/ref_period ]
# set ref_period_50ns 333333.33333334
set ref_speed_rpm [ expr { 60.0 / ($ref_period_50ns * 50e-9) } ]
set speed [expr abs($target)]
set sprat [expr {$speed / $ref_speed_rpm}]
set mult [findSpeedMult $sprat]
set allowed_speed [expr $mult * $ref_speed_rpm]
return $allowed_speed
}
proc setSpeed {hpath node addr name target} {
global SCode
variable CW
variable CCW
variable ch1_gains
variable ch2_gains
variable ch3_gains
if {$target >= 0} {
set dir $CW
} else {
set dir $CCW
}
set allowed_speed [abspermittedSpeed $hpath $node $addr $name $target]
foreach {gainTable index} [findGainIndex $addr $allowed_speed] {}
set row [array get $gainTable $index]
set vetowin [lindex $row 1 0]
set propGain [lindex $row 1 1 $dir]
set intGain [lindex $row 1 2 $dir]
set phGain [lindex $row 1 3 $dir]
hset $hpath/$node/control/set_vetowin50 $vetowin
hset $hpath/$node/control/set_prop_gain $propGain
hset $hpath/$node/control/set_int_gain $intGain
hset $hpath/$node/control/set_phase_gain $phGain
hset $hpath/$node/control/set_rotspeed $allowed_speed
return $SCode(OKOK)
}
proc get_setMode {hpath node addr name {target ""}} {
global SCode
if {$target == ""} {
set mode [hval $hpath/$node/mode]
return $mode
} else {
hset $hpath/$node/control/set_mode $target
return $target
}
}
proc get_setRefDelay {hpath node addr name {target_ ""}} {
global SCode
if {$target == ""} {
set delay [hval $hpath/$node/ref_delay]
return $delay
} else {
hset $hpath/$node/control/set_ref_delay $target
return $target
}
}
proc get_setSettleTime {hpath node addr name {settle ""}} {
global SCode
if {$settle == ""} {
return [hgetpropval $hpath/$node settletime]
} else {
hsetprop $hpath/$node settletime $settle
return $settle
}
}
proc imot_SGetpos {hpath node addr name} {
global SCode
variable CW
variable CCW
variable POSDIR
set chpath ${hpath}/$node/rotation_speed
set speed [hval $chpath]
if {[hval $hpath/$node/motdir] == $POSDIR} {
return $speed
} else {
return -$speed
}
}
proc imot_SRun {hpath node addr name target} {
global SCode
setSpeed $hpath $node $addr $name $target
hset $hpath/$node/control/start 1
hsetprop $hpath/$node/control timecheck -1
set readtime [hgetpropval $hpath read_time]
hsetprop $hpath lastupdate $readtime
return $SCode(OKOK)
}
proc imot_SStatus {hpath node addr name} {
global SCode
set catch_status [ catch {
if [hgetpropval $hpath/$node abort] {
hsetprop $hpath/$node abort 0
error "User requested stop. Aborting operation"
}
set readtime [hgetpropval $hpath read_time]
set lastupdate [hgetpropval $hpath lastupdate]
if {$readtime <= $lastupdate} {
return $SCode(HWBusy)
}
hsetprop $hpath lastupdate $readtime
set up_to_speed [hval $hpath/$node/system_status/up_to_speed]
set phase_locked [hval $hpath/$node/system_status/phase_locked]
set timecheck [hgetpropval $hpath/$node/control timecheck]
set timeout [hgetpropval $hpath/$node settletime]
if {[hval $hpath/$node/mode] == 0} {
# RPM mode
set locked $up_to_speed
} else {
# PHASE mode
set locked [expr $up_to_speed && $phase_locked]
}
if {$locked} {
if {$timecheck == -1} {
hsetprop $hpath/$node/control timecheck $readtime
return $SCode(HWBusy)
} elseif {[expr $readtime - $timecheck] > $timeout} {
return $SCode(HWIdle)
}
} else {
if {$timecheck != -1} {
hsetprop $hpath/$node/control timecheck $readtime
}
return $SCode(HWBusy)
}
} message ]
handle_exception $catch_status $message
}
proc imot_SHalt {hpath node addr name} {
global SCode
hset $hpath/$node/control/stop 1
return $SCode(OKOK)
}
proc imot_SGetError {hpath node addr name} {
global SCode
return [hgetpropval $hpath/$node errmsg]
}
proc imot_SFixit {hpath node addr name icode fVal} {
global ECode
return $ECode(MOTFAIL)
}
# Implementation of phase motor interface
proc imot_PhGetPos {hpath node addr name} {
global SCode
set phacc [hval $hpath/$node/phase_acc]
return $phacc
}
proc imot_PhRun {hpath node addr name target} {
global SCode
hset $hpath/$node/control/set_ref_delay $target
hsetprop $hpath/$node/control timecheck -1
set readtime [hgetpropval $hpath read_time]
hsetprop $hpath lastupdate $readtime
return $SCode(OKOK)
}
proc imot_PhStatus {hpath node addr name} {
global SCode
if [hgetpropval $hpath/$node abort] {
hsetprop $hpath/$node abort 0
error "User requested stop. Aborting operation"
}
set readtime [hgetpropval $hpath read_time]
set lastupdate [hgetpropval $hpath lastupdate]
if {$readtime <= $lastupdate} {
return $SCode(HWBusy)
}
hsetprop $hpath lastupdate $readtime
set phase_locked [hval $hpath/$node/system_status/phase_locked]
set timecheck [hgetpropval $hpath/$node/control timecheck]
set timeout [hgetpropval $hpath/$node settletime]
if {$phase_locked} {
if {$timecheck == -1} {
hsetprop $hpath/$node/control timecheck $readtime
return $SCode(HWBusy)
} elseif {[expr $readtime - $timecheck] > $timeout} {
return $SCode(HWIdle)
}
} else {
if {$timecheck != -1} {
hsetprop $hpath/$node/control timecheck $readtime
}
return $SCode(HWBusy)
}
}
proc imot_PhHalt {hpath node addr name} {
global SCode
hsetprop $hpath/$node abort 1
return $SCode(OKOK)
}
proc imot_PhGetError {hpath node addr name} {
global SCode
return "[info level 0]: TODO error message"
}
proc imot_PhFixit {hpath node addr name icode fVal} {
global SCode
return $SCode(MOTOK)
}
##
# @brief Returns the permitted speed as a multiple of the reference period
# @target 0, Lists permitted speeds as multiples of the reference speed.
# speed, Returns nearest permitted speed to the given speed.
proc permSpd {hpath node addr name target} {
global SCode
set ref_period_50ns [ hval $hpath/$node/ref_period ]
if {$target == 0} {
return "[listAllowedSpeeds $ref_period_50ns]"
}
set allowed_speed [abspermittedSpeed $hpath $node $addr $name $target]
if {$target > 0} {
return $allowed_speed
} else {
return -$allowed_speed
}
}
proc start {hpath node addr name} {
global SCode
hset $hpath/$node/control/start 1
return $SCode(OKOK)
}
proc stop {hpath node addr name} {
global SCode
hset $hpath/$node/control/stop 1
hsetprop $hpath/$node abort 1
return $SCode(OKOK)
}
}
proc mkFSpeedMot {fmot hdbroot chnode addr lowlim uplim} {
Motor $fmot tclmot [subst {\
getpos "::chopper::imot_SGetpos $hdbroot $chnode $addr"\
run "::chopper::imot_SRun $hdbroot $chnode $addr"\
status "::chopper::imot_SStatus $hdbroot $chnode $addr"\
halt "::chopper::imot_SHalt $hdbroot $chnode $addr"\
geterror "::chopper::imot_SGetError $hdbroot $chnode $addr"\
fixit "::chopper::imot_SFixit $hdbroot $chnode $addr"\
refspeed "::chopper::get_refSpeedRPM $hdbroot $chnode $addr"\
mode "::chopper::get_setMode $hdbroot $chnode $addr"\
refdelay "::chopper::get_setRefDelay $hdbroot $chnode $addr"\
settle "::chopper::get_setSettleTime $hdbroot $chnode $addr"\
setspeed "::chopper::setSpeed $hdbroot $chnode $addr"\
permspd "::chopper::permSpd $hdbroot $chnode $addr"\
start "::chopper::start $hdbroot $chnode $addr"\
stop "::chopper::stop $hdbroot $chnode $addr"\
}]
hsetprop $hdbroot/$chnode abort 0
$fmot movecount 5000
$fmot hardlowerlim $lowlim
$fmot hardupperlim $uplim
$fmot softlowerlim $lowlim
$fmot softupperlim $uplim
$fmot settle 30
$fmot precision 1
sicslist setatt $fmot units rpm
sicslist setatt $fmot klass fermi_chopper
sicslist setatt $fmot long_name $fmot
}
proc mkFPhaseMot {fmot hdbroot chnode addr lowlim uplim} {
Motor $fmot tclmot [subst {\
getpos "::chopper::imot_PhGetPos $hdbroot $chnode $addr"\
run "::chopper::imot_PhRun $hdbroot $chnode $addr"\
status "::chopper::imot_PhStatus $hdbroot $chnode $addr"\
halt "::chopper::imot_PhHalt $hdbroot $chnode $addr"\
geterror "::chopper::imot_PhGetError $hdbroot $chnode $addr"\
fixit "::chopper::imot_PhFixit $hdbroot $chnode $addr"\
}]
$fmot hardlowerlim $lowlim
$fmot hardupperlim $uplim
$fmot softlowerlim $lowlim
$fmot softupperlim $uplim
sicslist setatt $fmot units degrees
sicslist setatt $fmot klass fermi_chopper
sicslist setatt $fmot long_name $fmot
}
##
# @brief Make the fermichopper driver and speed and phase motors for the master and
# slave choppers.
#
# @param mSpdmot, Name of master chopper speed motor
# @param sSpdmot, Name of slave chopper speed motor
# @param sPhmot, Name of slave chopper phase motor
# @param master, Master chopper hdb node name
# @param maddr, Master chopper modbus address
# @param slave, Slave chopper hdb node name
# @param saddr, Slave chopper modbus address
#proc mkFermiMotors {mSpdmot sSpdmot master maddr slave saddr} {
proc mkFermiMotors {mSpdmot sSpdmot sPhmot master maddr slave saddr} {
set hdbPath [mkChoppers " $master $maddr $slave $saddr"]
# Create Master Chopper Speed motor object
mkFSpeedMot $mSpdmot $hdbPath $master $maddr -24000 24000
mkFSpeedMot $sSpdmot $hdbPath $slave $saddr -24000 24000
mkFPhaseMot $sPhmot $hdbPath $slave $saddr 0 180
}
mkFermiMotors mchs schs schp mch 1 sch 2