Files
sics/site_ansto/instrument/pelican/config/chopper/fermimot.tcl

699 lines
22 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 fcRPM fcPHASE
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
}
set fcRPM 0
set fcPHASE 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 $CW
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
}
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_setrefSpeedRPM {hpath node addr name {target ""}} {
if {$target == ""} {
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
} else {
set ref_period_50ns [expr {60.0 / ($target * 50e-9)} ]
hset $hpath/$node/control/set_ref_period $ref_period_50ns
return $target
}
}
##
# 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
set currspeed [hval $hpath/$node/rotation_speed]
set currdir [hval $hpath/$node/motdir]
if {$target >= 0} {
if {$currdir != $CW && $currspeed != 0} {
clientput "ERROR: Tried to change direction when speed != 0. Set motor to idle first."
return $SCode(HWFault)
}
set dir $CW
} else {
if {$currdir != $CCW && $currspeed != 0} {
clientput "ERROR: Tried to change direction when speed != 0. Set motor to idle first."
return $SCode(HWFault)
}
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_motor_dir $dir
hset $hpath/$node/control/set_vetowin50 [expr $vetowin/50.0]
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_setPeriod {hpath node addr name {target ""}} {
global SCode
if {$target == ""} {
set refPeriod [hval $hpath/$node/ref_period]
return $refPeriod
} else {
hset $hpath/$node/control/set_ref_period $target
return $target
}
}
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
set catch_status [ catch {
if {[hval $hpath/$node/control/device_error] != ""} {
hset $hpath/$node/control/device_error ""
}
# imot_status clears the abort flag, but this only happens if imot_status
# is being checked because of a "run" or "drive" when a stop is sent.
hsetprop $hpath/$node abort 0
set ret [setSpeed $hpath $node $addr $name $target]
if {$ret != $SCode(OKOK)} {
return $ret
}
hset $hpath/$node/control/start 1
# Disable idle if set
if {[hval $hpath/$node/idle_toggle] == 1} {
hset $hpath/$node/control/idle_toggle 1
}
hsetprop $hpath/$node/control timecheck -1
set readtime [hgetpropval $hpath read_time]
hsetprop $hpath lastupdate $readtime
return $SCode(OKOK)
} message ]
handle_exception $catch_status $message
}
proc imot_Status {hpath node addr name} {
global SCode
set catch_status [ catch {
if [hgetpropval $hpath/$node abort] {
hsetprop $hpath/$node abort 0
set errStr "ERROR: User requested stop. Aborting operation"
clientput $errStr
hsetprop $hpath/$node errmsg $errStr
return $SCode(HWFault)
}
set devErr [hval $hpath/$node/control/device_error]
if {$devErr != ""} {
set errStr "ERROR: Drive request failed. Aborting operation: device_error = $devErr"
clientput $errStr
hsetprop $hpath/$node errmsg $errStr
return $SCode(HWFault)
}
if [hpropexists $hpath geterror] {
clientput "WARNING: [hgetpropval $hpath geterror]"
}
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 $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 {
return $SCode(HWBusy)
}
} else {
if {$timecheck != -1} {
hsetprop $hpath/$node/control timecheck $readtime
}
return $SCode(HWBusy)
}
} message ]
handle_exception $catch_status $message
}
proc imot_Halt {hpath node addr name} {
global SCode
# Don't halt the choppers just because someone sent INT1712 to interrupt a scan.
if { [GetInt] == "continue" } {
hset $hpath/$node/control/stop 1
hsetprop $hpath/$node abort 1
}
return $SCode(OKOK)
}
proc imot_GetError {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} {
set delay [hval $hpath/$node/ref_delay]
return $delay
}
proc imot_PhRun {hpath node addr name target} {
global SCode
if {[hval $hpath/$node/control/device_error] != ""} {
hset $hpath/$node/control/device_error ""
}
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_PhFixit {hpath node addr name icode fVal} {
global SCode
return $SCode(MOTFAIL)
}
proc get_phase_acc {ch} {
variable chPath
set phase_acc [hval $chPath/$ch/phase_acc]
return $phase_acc
}
proc get_phase_rep {ch} {
variable chPath
set phase_rep [hval $chPath/$ch/phase_rep]
return $phase_rep
}
proc get_phase_ok {ch} {
variable chPath
set phase_ok [hval $chPath/$ch/phase_ok]
return $phase_ok
}
##
# @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)
}
# Speed motor stop
proc stop {hpath node addr name} {
global SCode
hset $hpath/$node/control/stop 1
hsetprop $hpath/$node abort 1
return $SCode(OKOK)
}
proc reset {hpath node addr name} {
global SCode
hset $hpath/$node/control/reset 1
return $SCode(OKOK)
}
proc idle {hpath node addr name} {
global SCode
hset $hpath/$node/control/idle_toggle 1
hsetprop $hpath/$node abort 1
return $SCode(OKOK)
}
}
proc mkFSpeedMot {fmot hdbroot chnode addr lowlim uplim} {
clientput [info level 0]
Motor $fmot tclmot [subst {\
getpos "::chopper::imot_SGetpos $hdbroot $chnode $addr"\
run "::chopper::imot_SRun $hdbroot $chnode $addr"\
status "::chopper::imot_Status $hdbroot $chnode $addr"\
halt "::chopper::imot_Halt $hdbroot $chnode $addr"\
geterror "::chopper::imot_GetError $hdbroot $chnode $addr"\
fixit "::chopper::imot_SFixit $hdbroot $chnode $addr"\
refspeed "::chopper::get_setrefSpeedRPM $hdbroot $chnode $addr"\
refperiod "::chopper::get_setPeriod $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"\
idle "::chopper::idle $hdbroot $chnode $addr"\
}]
sicslist setatt $fmot mtype tclmot
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
$fmot maxretry 0
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} {
clientput [info level 0]
Motor $fmot tclmot [subst {\
getpos "::chopper::imot_PhGetPos $hdbroot $chnode $addr"\
run "::chopper::imot_PhRun $hdbroot $chnode $addr"\
status "::chopper::imot_Status $hdbroot $chnode $addr"\
halt "::chopper::imot_Halt $hdbroot $chnode $addr"\
geterror "::chopper::imot_GetError $hdbroot $chnode $addr"\
fixit "::chopper::imot_PhFixit $hdbroot $chnode $addr"\
refspeed "::chopper::get_setrefSpeedRPM $hdbroot $chnode $addr"\
refperiod "::chopper::get_setPeriod $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"\
phase_acc "::chopper::get_phase_acc $hdbroot $chnode $addr"\
phase_rep "::chopper::get_phase_rep $hdbroot $chnode $addr"\
phase_ok "::chopper::get_phase_ok $hdbroot $chnode $addr"\
start "::chopper::start $hdbroot $chnode $addr"\
stop "::chopper::stop $hdbroot $chnode $addr"\
idle "::chopper::idle $hdbroot $chnode $addr"\
}]
sicslist setatt $fmot mtype tclmot
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 50
$fmot maxretry 0
sicslist setatt $fmot units ns
sicslist setatt $fmot klass fermi_chopper
sicslist setatt $fmot long_name $fmot
}
##
# @brief Make the fermichopper driver, and speed motors for the master and
# slave choppers. NOTE: You can make just one chopper motor.
#
# @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 { master maddr {slave "EMPTY"} {saddr "EMPTY"} } {
set mSpdmot ${master}s
set mPhmot ${master}p
set sSpdmot ${slave}s
set sPhmot ${slave}p
if {$slave != "EMPTY"} {
# Make master and slave choppers
set hdbPath [mkChoppers " $master $maddr $slave $saddr"]
mkFSpeedMot $sSpdmot $hdbPath $slave $saddr -24000 24000
mkFPhaseMot $sPhmot $hdbPath $slave $saddr 0 166.6667e6
} else {
set hdbPath [mkChoppers " $master $maddr"]
}
# Create Master Chopper Speed motor object
mkFSpeedMot $mSpdmot $hdbPath $master $maddr -24000 24000
mkFPhaseMot $mPhmot $hdbPath $master $maddr 0 166.6667e6
}
## Example for making a master and slave chopper motor
# mkFermiMotors mch 2 sch 3
# mchs mode $fcPHASE
# schs mode $fcPHASE
## Example which just makes one chopper motor
# mkFermiMotors mch 2
# mchs mode $fcPHASE