sct_julabo_lh45.tcl

Create object when in simulation mode

nxscripts_common_1.tcl
Set units attributes on script context objects data

sans aperture_configuration.tcl
Update rotary attenuator lookup table. Set parameters when motors positions are
within tolerance of the lookup table positions.

sans, parameters.tcl sct_velsel.tcl
Set units and update parameter names to be consistent

quokka_configuration.tcl
Add convenience command to load environment controllers.

server_config.tcl
Make sure that controllers are properly generated when loading them from the ext
raconfig.tcl.

r2881 | ffr | 2010-01-29 16:50:51 +1100 (Fri, 29 Jan 2010) | 20 lines
This commit is contained in:
Ferdi Franceschini
2010-01-29 16:50:51 +11:00
committed by Douglas Clowes
parent ce113f26fd
commit 4f3fb5e250
8 changed files with 176 additions and 140 deletions

View File

@@ -297,17 +297,19 @@ namespace eval ::scobj::lh45 {
hfactory $scobj_hpath/emon/errhandler plain user text
hset $scobj_hpath/emon/errhandler "pause"
$sct_controller poll $scobj_hpath/setpoint
$sct_controller write $scobj_hpath/setpoint
$sct_controller poll $scobj_hpath/subtemp_warnlimit
$sct_controller write $scobj_hpath/subtemp_warnlimit
$sct_controller poll $scobj_hpath/overtemp_warnlimit
$sct_controller write $scobj_hpath/overtemp_warnlimit
$sct_controller poll $scobj_hpath/heating_power_percent
$sct_controller poll $scobj_hpath/power
$sct_controller write $scobj_hpath/power
$sct_controller poll $scobj_hpath/sensor/value
$sct_controller poll $scobj_hpath/lh45_state 5 halt read
if {[SplitReply [environment_simulation]]=="false"} {
$sct_controller poll $scobj_hpath/setpoint
$sct_controller write $scobj_hpath/setpoint
$sct_controller poll $scobj_hpath/subtemp_warnlimit
$sct_controller write $scobj_hpath/subtemp_warnlimit
$sct_controller poll $scobj_hpath/overtemp_warnlimit
$sct_controller write $scobj_hpath/overtemp_warnlimit
$sct_controller poll $scobj_hpath/heating_power_percent
$sct_controller poll $scobj_hpath/power
$sct_controller write $scobj_hpath/power
$sct_controller poll $scobj_hpath/sensor/value
$sct_controller poll $scobj_hpath/lh45_state 5 halt read
}
::scobj::hinitprops $tempobj
hsetprop $scobj_hpath klass NXenvironment
@@ -330,16 +332,27 @@ namespace eval ::scobj::lh45 {
hsetprop $scobj_hpath privilege spy
::scobj::hinitprops $tempobj setpoint
ansto_makesctdrive ${tempobj}_driveable $scobj_hpath/setpoint $scobj_hpath/sensor/value $sct_controller
if {[SplitReply [environment_simulation]]=="false"} {
ansto_makesctdrive ${tempobj}_driveable $scobj_hpath/setpoint $scobj_hpath/sensor/value $sct_controller
}
} message ] {
return -code error $message
}
}
namespace export mk_sct_julabo_lh45
}
# Julabo 137.157.202.85:4003
##
# @brief Create a Julabo lh45 temperature controller
#
# @param name, then name of the temperature controller (eg tc1)
# @param IP, the IP address of the device, this can be a hostname, (eg ca5-quokka)
# @param port, the IP protocol port number of the device
# @param _tol (optional), this is the initial tolerance setting
proc add_lh45 {name IP port {_tol 5.0}} {
makesctcontroller sct_lh45 std ${IP}:$port "\r"
if {[SplitReply [environment_simulation]]=="false"} {
makesctcontroller sct_lh45 std ${IP}:$port "\r"
}
mk_sct_julabo_lh45 sct_lh45 environment $name $_tol
makesctemon $name /sics/$name/emon/monmode /sics/$name/emon/isintol /sics/$name/emon/errhandler
}

View File

@@ -1163,6 +1163,12 @@ proc ::nexus::scobj::sdsinfo {sdsName data_type args} {
if [ catch {
array set param $args
set dtype [::nexus::hdb2nx_type $data_type]
if [hpropexists $param(hpath) "units"] {
set unitsval [hgetpropval $param(hpath) units]
set units_att " -attr {units,$unitsval} "
} else {
set units_att " "
}
switch $data_type {
text {
set dimdef [subst {-dim {[string length [hval $param(hpath)]]}}]
@@ -1170,9 +1176,9 @@ proc ::nexus::scobj::sdsinfo {sdsName data_type args} {
}
default {
if {$param(mutable) == true} {
set sdsStr "$sdsName -type $dtype -rank 1 -dim {-1}"
set sdsStr "$sdsName -type $dtype -rank 1 -dim {-1} $units_att"
} else {
set sdsStr "$sdsName -type $dtype"
set sdsStr "$sdsName -type $dtype $units_att"
}
}
}
@@ -1338,7 +1344,7 @@ set nx_content_release_tag [lindex $tmpstr [expr [llength $tmpstr] - 1]]
sics_release $nx_content_release_tag
sics_release lock
set tmpstr [string map {"$" ""} {$Revision: 1.51.2.3 $}]
set tmpstr [string map {"$" ""} {$Revision: 1.51.2.4 $}]
set nx_content_revision_num [lindex $tmpstr [expr [llength $tmpstr] - 1]]
#namespace eval data {

View File

@@ -1,16 +1,16 @@
namespace eval optics {
array set AttRotLookupTable {
0 0.0
30 1.5
60 3.4
30 1.3
60 3.3
90 4.9
120 6.4
150 8.3
180 9.8
180 9.6
210 11.2
240 13.2
270 18.1
300 23.0
240 13.1
270 15.0
300 18.0
330 25.0
}
@@ -30,11 +30,11 @@ namespace eval optics {
}
}
proc ::optics::AttRotLookup {angle} {
proc ::optics::AttRotLookup {angle tol} {
variable AttRotLookupTable
set foundit false
foreach vangle [array names AttRotLookupTable] {
if {$vangle == [expr int($angle)]} {
if {$vangle >= [expr {$angle-$tol}] && $vangle <= [expr {$angle+$tol}]} {
set foundit true
break
}
@@ -46,7 +46,7 @@ proc ::optics::AttRotLookup {angle} {
}
}
proc ::optics::EApLookUp {angle param} {
proc ::optics::EApLookUp {angle param tol} {
variable EApLookupTable
set foundit false
@@ -65,7 +65,7 @@ proc ::optics::EApLookUp {angle param} {
}
}
foreach vangle [array names EApLookupTable] {
if {$vangle == [expr int($angle)]} {
if {$vangle >= [expr {$angle-$tol}] && $vangle <= [expr {$angle+$tol}]} {
set foundit true
break
}

View File

@@ -11,7 +11,7 @@ foreach {var lname type priv units klass} {
BeamCenterZ BeamCenterZ float user mm parameter
BeamStop BeamStop int user none parameter
BSdiam BSdiam float user mm parameter
DetPosYOffset DetPosYOffsetfloat float user mm parameter
DetPosYOffset DetPosYOffset float user mm parameter
EApPosY EApPosY float user mm parameter
EndFacePosY EndFacePosY float readonly mm parameter
GuideConfig GuideConfig text user none parameter
@@ -30,38 +30,38 @@ foreach {var lname type priv units klass} {
proc sicsmsgfmt {args} {return "[info level -1] = $args"}
::utility::macro::getset float SamplePosYmm {} {
::utility::macro::getset float SamplePosY {} {
set sy [SplitReply [samy]]
set syo [SplitReply [SamYOffset]]
return [sicsmsgfmt [expr {$sy+$syo}]]
}
sicslist setatt SamplePosYmm long_name SamplePosYmm
sicslist setatt SamplePosYmm klass parameter
sicslist setatt SamplePosYmm units mm
sicslist setatt SamplePosY long_name SamplePosY
sicslist setatt SamplePosY klass parameter
sicslist setatt SamplePosY units mm
::utility::macro::getset float Plex {} {
return [sicsmsgfmt [ ::optics::AttRotLookup [SplitReply [att]] ]]
return [sicsmsgfmt [ ::optics::AttRotLookup [SplitReply [att]] [SplitReply [att precision]] ]]
}
sicslist setatt Plex units mm
sicslist setatt Plex long_name Plex
sicslist setatt Plex klass parameter
::utility::macro::getset float EApX {} {
return [sicsmsgfmt [::optics::EApLookUp [SplitReply [srce]] "size"]]
return [sicsmsgfmt [::optics::EApLookUp [SplitReply [srce]] "size" [SplitReply [srce precision]] ]]
}
sicslist setatt EApX units mm
sicslist setatt EApX long_name EApX
sicslist setatt EApX klass parameter
::utility::macro::getset float EApZ {} {
return [sicsmsgfmt [::optics::EApLookUp [SplitReply [srce]] "size"]]
return [sicsmsgfmt [::optics::EApLookUp [SplitReply [srce]] "size" [SplitReply [srce precision]] ]]
}
sicslist setatt EApZ units mm
sicslist setatt EApZ long_name EApZ
sicslist setatt EApZ klass parameter
::utility::macro::getset text EApShape {} {
return [sicsmsgfmt [::optics::EApLookUp [SplitReply [srce]] "shape"]]
return [sicsmsgfmt [::optics::EApLookUp [SplitReply [srce]] "shape" [SplitReply [srce precision]] ]]
}
sicslist setatt EApShape long_name EApShape
sicslist setatt EApShape klass parameter
@@ -69,7 +69,7 @@ sicslist setatt EApShape mutable false
::utility::macro::getset float L1 {} {
set efpy [SplitReply [EndFacePosY]]
set samposy [SplitReply [SamplePosYmm]]
set samposy [SplitReply [SamplePosY]]
set eapy [SplitReply [EApPosY]]
return [sicsmsgfmt [expr {$efpy + $samposy - $eapy}]]
}
@@ -80,7 +80,7 @@ sicslist setatt L1 units mm
::utility::macro::getset float L2 {} {
set detpy [SplitReply [det]]
set detpyos [SplitReply [DetPosYOffset]]
set sapy [SplitReply [SamplePosYmm]]
set sapy [SplitReply [SamplePosY]]
return [sicsmsgfmt [expr {$detpy + $detpyos - $sapy}]]
}
sicslist setatt L2 long_name L2
@@ -91,7 +91,7 @@ sicslist setatt L2 units mm
################################################################################
# INITIALISE PARAMETERS
# The collimation system aperture positions
# Reference position is outer wall of velocity selector bunker, ie VelSelPosYmm
# Reference position is outer wall of velocity selector bunker, ie EndFacePosY
array set collapposmm {
inputguide 633
apwheel 675
@@ -124,7 +124,7 @@ namespace eval parameters {
L2
Plex
SamYOffset
SamplePosYmm
SamplePosY
Transmission
}
}

View File

@@ -92,44 +92,31 @@ namespace eval ::scobj::velocity_selector {
}
array set paramindex {
state 0
rspeed 1
aspeed 2
sspeed 3
aveto 4
ploss 5
splos 6
ttang 7
rtemp 8
wflow 9
winlt 10
woutt 11
vacum 12
wvalv 13
vvalv 14
vibrt 15
bcuun 16
}
array set paramtype {
state text
rspeed float
aspeed float
sspeed float
aveto text
ploss float
splos float
ttang float
rtemp float
wflow float
winlt float
woutt float
vacum float
wvalv text
vvalv text
vibrt float
bcuun float
}
foreach {
param index type units } {
state 0 text @none
rspeed 1 float rpm
aspeed 2 float rpm
sspeed 3 float Hz
aveto 4 text @none
ploss 5 float @none
splos 6 float @none
ttang 7 float degrees
rtemp 8 float degrees
wflow 9 float @none
winlt 10 float @none
woutt 11 float @none
vacum 12 float @none
wvalv 13 text @none
vvalv 14 text @none
vibrt 15 float @none
bcuun 16 float @none
} {
set paramindex($param) $index
set paramtype($param) $type
set paramunits($param) $units
}
MakeSICSObj velsel_poller SCT_OBJECT
MakeSICSObj velocity_selector SCT_OBJECT
sicslist setatt velocity_selector klass NXvelocity_selector
@@ -193,7 +180,7 @@ proc rdPwdAck {} {
statemon stop nvs_lambda
if [hgetpropval $root/setspeed driving] {
hsetprop $root/setspeed driving 0
hsetprop $root/setLambdaA driving 0
hsetprop $root/setLambda driving 0
}
}
}
@@ -208,7 +195,7 @@ proc rdPwdAck {} {
statemon stop nvs_lambda
if [hgetpropval $root/setspeed driving] {
hsetprop $root/setspeed driving 0
hsetprop $root/setLambdaA driving 0
hsetprop $root/setLambda driving 0
}
}
sct oldstate $state
@@ -260,14 +247,14 @@ proc rdPwdAck {} {
set angle [lindex [hval $statuspath] $paramindex(ttang) end]
set lambda [AngleSpeedToWavelength $angle $speed]
sct target $speed
hsetprop $vs_root/setLambdaA target $lambda
hsetprop $vs_root/setLambda target $lambda
hset $vs_root/status "busy"
statemon start nvs_speed
statemon start nvs_lambda
if {[sct writestatus] == "start"} {
# Called by drive adapter
hsetprop $vs_root/setspeed driving 1
hsetprop $vs_root/setLambdaA driving 1
hsetprop $vs_root/setLambda driving 1
}
return $nextState
}
@@ -407,7 +394,7 @@ proc checkBlockedWavelengths {statuspath} {
proc halt {root} {
hsetprop $root/setspeed driving 0
hsetprop $root/setLambdaA driving 0
hsetprop $root/setLambda driving 0
hset $root/status "idle"
statemon stop nvs_speed
statemon stop nvs_lambda
@@ -439,7 +426,7 @@ proc halt {root} {
statemon start nvs_lambda
if {[sct writestatus] == "start"} {
# Called by drive adapter
hsetprop $vs_root/setLambdaA driving 1
hsetprop $vs_root/setLambda driving 1
hsetprop $vs_root/setspeed driving 1
}
return $nextState
@@ -475,17 +462,23 @@ proc halt {root} {
hfactory $velselPath/LambdaResFWHM_percent plain user float
hfactory $velselPath/geometry plain spy none
hfactory $velselPath/geometry/position plain spy none
hfactory $velselPath/geometry/position/VelSelPosXmm plain user float
hfactory $velselPath/geometry/position/VelSelPosYmm plain user float
hfactory $velselPath/geometry/position/VelSelPosZmm plain user float
hfactory $velselPath/geometry/position/VelSelPosX plain user float
hsetprop $velselPath/geometry/position/VelSelPosX units "mm"
hfactory $velselPath/geometry/position/VelSelPosY plain user float
hsetprop $velselPath/geometry/position/VelSelPosY units "mm"
hfactory $velselPath/geometry/position/VelSelPosZ plain user float
hsetprop $velselPath/geometry/position/VelSelPosZ units "mm"
hfactory $velselPath/geometry/position/VelSelCoordScheme plain user text
# Get parameters from state report
# Setup nodes for state report parameters
foreach par [lsort [array names paramindex]] {
hfactory $velselPath/$par plain spy $paramtype($par)
hsetprop $velselPath/$par read ${scobjNS}::getpar rdpar
hsetprop $velselPath/$par rdpar ${scobjNS}::updatepar $statusPath $paramindex($par)
hsetprop $velselPath/$par oldval "UNKNOWN"
if {$paramunits($par) != "@none"} {
hsetprop $velselPath/$par units $paramunits($par)
}
}
# Initialise turntable command
hfactory $velselPath/ttinit plain spy none
@@ -500,24 +493,28 @@ proc halt {root} {
hsetprop $velselPath/set_ttang check ${scobjNS}::ttableCheck $statusPath ignore
hsetprop $velselPath/set_ttang write ${scobjNS}::setPar TTANGL ignore
hsetprop $velselPath/set_ttang ignore ${scobjNS}::noResponse
hsetprop $velselPath/set_ttang units "degrees"
# Get Lambda
hfactory $velselPath/LambdaA plain spy float
hsetprop $velselPath/LambdaA read ${scobjNS}::getpar rdpar
hsetprop $velselPath/LambdaA rdpar ${scobjNS}::readLambda $statusPath
hsetprop $velselPath/LambdaA oldval "UNKNOWN"
hfactory $velselPath/Lambda plain spy float
hsetprop $velselPath/Lambda read ${scobjNS}::getpar rdpar
hsetprop $velselPath/Lambda rdpar ${scobjNS}::readLambda $statusPath
hsetprop $velselPath/Lambda oldval "UNKNOWN"
hsetprop $velselPath/Lambda units "Angstrom"
# Set Lambda
hfactory $velselPath/setLambdaA plain spy float
hsetprop $velselPath/setLambdaA check ${scobjNS}::checkBlockedWavelengths $statusPath
hsetprop $velselPath/setLambdaA write ${scobjNS}::setLambda $velselPath $statusPath ignore
hsetprop $velselPath/setLambdaA ignore ${scobjNS}::noResponse
hsetprop $velselPath/setLambdaA driving 0
#TODO WARNING remove sicsdev and type if setLambdaA gets a drive addapter
# hsetprop $velselPath/setLambdaA sicsdev "nvs_lambda"
hsetprop $velselPath/setLambdaA type "drivable"
hsetprop $velselPath/setLambdaA target 0
hsetprop $velselPath/setLambdaA writestatus "UNKNOWN"
hfactory $velselPath/setLambda plain spy float
hsetprop $velselPath/setLambda check ${scobjNS}::checkBlockedWavelengths $statusPath
hsetprop $velselPath/setLambda write ${scobjNS}::setLambda $velselPath $statusPath ignore
hsetprop $velselPath/setLambda ignore ${scobjNS}::noResponse
hsetprop $velselPath/setLambda driving 0
#TODO WARNING remove sicsdev and type if setLambda gets a drive addapter
# hsetprop $velselPath/setLambda sicsdev "nvs_lambda"
hsetprop $velselPath/setLambda type "drivable"
hsetprop $velselPath/setLambda target 0
hsetprop $velselPath/setLambda writestatus "UNKNOWN"
hsetprop $velselPath/setLambda units "Angstrom"
# Set speed
hfactory $velselPath/setspeed plain spy int
@@ -528,6 +525,7 @@ proc halt {root} {
hsetprop $velselPath/setspeed type "drivable"
hsetprop $velselPath/setspeed target 0
hsetprop $velselPath/setspeed writestatus "UNKNOWN"
hsetprop $velselPath/setspeed units "rpm"
# Stop velocity selector (brake or idle)
hfactory $velselPath/cmd plain spy text
@@ -552,16 +550,18 @@ proc halt {root} {
hsetprop $velselPath/geometry/position type instrument
hsetprop $velselPath/geometry/position data true
hsetprop $velselPath/geometry/position control true
foreach {hpath klass control data nxsave mutable priv alias} {
LambdaA parameter true true true true user velsel_lambdaa
foreach {
hpath klass control data nxsave mutable priv alias
} {
Lambda parameter true true true true user velsel_lambdaa
LambdaResFWHM_percent parameter true true true true spy velsel_lambdaresfwhm_percent
rspeed parameter true true true true spy velsel_rspeed
aspeed parameter true true true true user velsel_aspeed
ttang parameter true true true true user velsel_ttang
ttinit parameter true false false true user velsel_ttang
geometry/position/VelSelPosXmm parameter true true true false user VelSelPosXmm
geometry/position/VelSelPosYmm parameter true true true false user VelSelPosYmm
geometry/position/VelSelPosZmm parameter true true true false user VelSelPosZmm
geometry/position/VelSelPosX parameter true true true false user VelSelPosX
geometry/position/VelSelPosY parameter true true true false user VelSelPosY
geometry/position/VelSelPosZ parameter true true true false user VelSelPosZ
geometry/position/VelSelCoordScheme parameter true true true false user VelSelCoordScheme
} {
hsetprop $velselPath/$hpath nxalias $alias
@@ -578,16 +578,16 @@ proc halt {root} {
hsetprop $velselPath/setspeed checkstatus ${scobjNS}::drivestatus
hsetprop $velselPath/setspeed halt ${scobjNS}::halt $velselPath
hsetprop $velselPath/setLambdaA checklimits ${scobjNS}::checkBlockedWavelengths $statusPath
hsetprop $velselPath/setLambdaA checkstatus ${scobjNS}::drivestatus
hsetprop $velselPath/setLambdaA halt ${scobjNS}::halt $velselPath
hsetprop $velselPath/setLambda checklimits ${scobjNS}::checkBlockedWavelengths $statusPath
hsetprop $velselPath/setLambda checkstatus ${scobjNS}::drivestatus
hsetprop $velselPath/setLambda halt ${scobjNS}::halt $velselPath
##
# @brief This is the position of the velocity selector bunker face. It is used
# as the reference for other positions. x=y=z=0.
hset $velselPath/geometry/position/VelSelPosXmm 0.0
hset $velselPath/geometry/position/VelSelPosYmm 0.0
hset $velselPath/geometry/position/VelSelPosZmm 0.0
hset $velselPath/geometry/position/VelSelPosX 0.0
hset $velselPath/geometry/position/VelSelPosY 0.0
hset $velselPath/geometry/position/VelSelPosZ 0.0
hset $velselPath/geometry/position/VelSelCoordScheme "Cartesian"
@@ -601,12 +601,12 @@ proc halt {root} {
}
sct_velsel write $velselPath/ttinit
sct_velsel write $velselPath/set_ttang
sct_velsel poll $velselPath/LambdaA $pollrate
sct_velsel write $velselPath/setLambdaA
sct_velsel poll $velselPath/Lambda $pollrate
sct_velsel write $velselPath/setLambda
sct_velsel write $velselPath/setspeed
sct_velsel write $velselPath/cmd
ansto_makesctdrive nvs_speed $velselPath/setspeed $velselPath/aspeed sct_velsel
ansto_makesctdrive nvs_lambda $velselPath/setLambdaA $velselPath/LambdaA sct_velsel
ansto_makesctdrive nvs_lambda $velselPath/setLambda $velselPath/Lambda sct_velsel
}
if {$sim_mode == "false"} {
makesctcontroller sct_velsel astvelsel $velsel_IP:$velsel_port "" 10

View File

@@ -38,14 +38,36 @@ source gumxml.tcl
::utility::mkVar ::anticollider::protect_detector text manager protect_detector false detector true false
::anticollider::protect_detector "true"
if {[SplitReply [environment_simulation]]=="false"} {
# catch { add_lh45 tc1 137.157.202.85 4003 1} message
# puts $message
#catch { add_ls340t tc1 127.0.0.1 4001 1} message
#puts $message
proc select_environment_controller {envtemp} {
if [ catch {
puts "selecting $envtemp for environment control"
switch $envtemp {
"lh45" {
add_lh45 tc1 ca5-quokka 4003 1
}
"rhqc" {
puts "Configuring RHQC"
::environment::temperature::add_ls340 tc1 1
tc1 controlsensor sensorB
puts "Added tc1 with [tc1 controlsensor]"
::environment::temperature::add_ls340 tc2 2
tc2 controlsensor sensorD
puts "Added tc2 with [tc2 controlsensor]"
}
"11TMagnet" {
puts "Configuring 11TMagnet"
::environment::temperature::add_ls340 tc1 1
tc1 controlsensor sensorA
}
default {
clientput "No temperature controller configured"
}
}
#::environment::temperature::add_ls340 tc1 1
} msg ] {
puts "Failed to configure $envtemp: $msg"
}
}
server_init
###########################################
# WARNING: Do not add any code below server_init, if you do SICS may fail to initialise properly.

View File

@@ -221,7 +221,7 @@ proc server_set_sobj_attributes {} {
}
proc server_init {} {
if [ catch {
set catch_status [ catch {
::source::isc_initialize
::counter::isc_initialize
::histogram_memory::isc_initialize
@@ -245,20 +245,12 @@ proc server_init {} {
sicslist setatt sics_suid nxsave true
sicslist setatt sics_suid long_name sics_suid
if [file exists ../extraconfig.tcl] {
fileeval ../extraconfig.tcl
}
server_set_sobj_attributes
buildHDB instrument_dictionary
} message ] {
return -code error $message
} else {
if [ catch {
if [file exists ../extraconfig.tcl] {
fileeval ../extraconfig.tcl
}
buildHDB instrument_dictionary
} message ] {
return -code error $message
}
return $message
}
} message ]
handle_exception $catch_status $message
}

View File

@@ -868,10 +868,13 @@ proc hsibPath {sibling} {
#
# @param status, the status returned by the 'catch' command.
# @param message, the message set by the 'catch' command.
# @param args, optional info which gets appended the error message.
# @param args, optional info which gets appended to the error message.
#
# Call this as the last command in the command block or
# Call this as the LAST COMMAND in the command block or
# for loop which encloses the 'catch'
# Eg,
# set catch_status [ catch { ... code ...} message ]
# handle_exception $catch_status $message
proc handle_exception {status message args} {
switch $status {
0 {