## # @file Scan functionality and common high level commands are defined here. # # namespace variables\n # ::scan::save_filetype data/scratch, controls if data will be saved to a # scratch file.\n # ::scan::reset_position true/false (default=true), drive motor back to start # position at end of scan\n # ::scan::force_scan true/false, (default=false), Will force a scan if the # instrument isn't ready, eg shutters closed. Note: The scan will still fail if # you are scanning against a motor and motion control is disabled.\n # If force_scan is true it is immediately reset to false after it is checked. #TODO Get rid of duplication in bmonscan and hmscan code namespace eval scan { variable ic_runscanpar variable ic_hmm_datatype variable save_filetype variable reset_position variable check_thread0 variable check_instrument_ready variable force_scan set check_thread0 true set check_instrument_ready true set force_scan false set save_filetype "data" set reset_position "false" if {[SplitReply [motor_simulation]] == "true"} { set check_thread0 false } } ## # @brief This is a convenience command which allows hmscans to be forced. # Call this before calling hmscan run to force a scan if the instrument isn't ready. proc force_scan {} { set ::scan::force_scan "true" } publish force_scan user proc ::scan::bm_scan_collect {sobj uobj point} { set vlist [split [$sobj getvarpar 0] = ]; set w(NP) $point set sv [string trim [lindex [split [lindex $vlist 0] . ] 1]] set header [format "%-4.4s %-9.9s %-14s %-7.7s" NP $sv Counts Time] set varval [SplitReply [$sv]] set counts [SplitReply [bm getcounts]] set time [SplitReply [bm gettime]] set data [format "%-4d %-9.3f %-14d %-7.2f" $point $varval $counts $time] clientput $header clientput $data for {set chn 0} {$chn < $::counter::isc_numchannels} {incr chn} { clientput "Channel $chn [SplitReply [bm getmonitor $chn]]" } } proc ::scan::hmm_scan_collect {sobj uobj point} { set vlist [split [$sobj getvarpar 0] = ]; set w(NP) $point set sv [string trim [lindex [split [lindex $vlist 0] . ] 1]] set header [format "%-4.4s %-9.9s %-14s %-7.7s" NP $sv Counts Time] set varval [SplitReply [$sv]] set counts [SplitReply [::histogram_memory::total_counts]] set time [SplitReply [::histogram_memory::time]] set data [format "%-4d %-9.3f %-14d %-7.2f" $point $varval $counts $time] clientput $header clientput $data for {set bmn 1} {$bmn <= $::counter::isc_numchannels} {incr bmn} { set bmon bm$bmn clientput "Monitor $bmn [SplitReply [$bmon getcounts]]" } } proc ::scan::ic_initialize {} { if [ catch { variable ic_runscanpar variable ic_hmm_datatype set ic_hmm_datatype HISTOGRAM_XYT MakeScanCommand hmscan bm $::cfPath(scan)/scan_common_1.hdd recover.bin MakeScanCommand bmonscan bm $::cfPath(scan)/scan_common_1.hdd recover.bin bmonscan configure script bmonscan function writeheader ::scan::donothing bmonscan function writepoint ::scan::bm_writepoint bmonscan function count ::scan::bm_count bmonscan function collect ::scan::bm_scan_collect bmonscan function prepare ::scan::bm_scan_prepare bmonscan function finish ::scan::bm_scan_finish hmscan configure script hmscan function writeheader ::scan::donothing hmscan function writepoint ::scan::hmm_writepoint hmscan function count ::scan::hmm_count hmscan function collect ::scan::hmm_scan_collect hmscan function prepare ::scan::hmm_scan_prepare hmscan function finish ::scan::hmm_scan_finish # TODO Use ic_runscanpar to create the ::scan::runscan command and # to validate the "runscan" proc parameters. array set ic_runscanpar [subst { scanvar text=drivable start float stop float numpoints int=0,inf mode text=[join [concat [list time unlimited period count frame] $::counter::isc_beam_monitor_list ] , ] preset float=0,inf datatype text=[join [array names ::nexus::histmem_filetype_spec] , ] savetype text=save,nosave force boolean }] } message ] { if {$::errorCode=="NONE"} {return $message} return -code error $message } } ## # @brief Returns an error if a scan variable target position exceeds the limits. proc ::scan::check_limit {scan_variable limit_name target} { switch $limit_name { "hardlowerlim" - "softlowerlim" { set limit [SplitReply [$scan_variable $limit_name]] if { $target < $limit} { return -code error "Final position of $target violates $limit_name $limit for $scan_variable" } } "hardupperlim" - "softupperlim" { set limit [SplitReply [$scan_variable $limit_name]] if { $target > $limit} { return -code error "Final position of $target violates $limit_name $limit for $scan_variable" } } default { return -code error "Invalid limit name $limit_name" } } } ## \brief check final position against scan variable limits # # NOTE: The sics scan object alread checks if a variable is drivable # so we don't have to. # TODO We can't check limits of virtual motors yet because the # configurablevirtualmotor doesn't set a checklimits function. proc ::scan::check_scanvar {sobj uobj} { variable check_thread0 set vlist [split [$sobj getvarpar 0] = ]; set NP [SplitReply [$sobj np]] set scan_variable [string trim [lindex [split [lindex $vlist 0] . ] 1]] set scan_start [lindex $vlist 1]; set scan_increment [lindex $vlist 2]; if {[getatt $scan_variable type] == "motor"} { if {[SplitReply [$scan_variable fixed]] >= 0} { return -code error "ERROR: Can't drive scan variable, $scan_variable position is set to 'fixed'" } elseif {$check_thread0 && [SplitReply [$scan_variable thread0]] == -1} { return -code error "ERROR: Can't scan ${scan_variable}. Thread zero has stopped running on the motion controller" } set scan_final [expr $scan_start + ($NP-1) * $scan_increment] if [catch { ::scan::check_limit $scan_variable softlowerlim $scan_final ::scan::check_limit $scan_variable softupperlim $scan_final ::scan::check_limit $scan_variable softlowerlim $scan_start ::scan::check_limit $scan_variable softupperlim $scan_start }] { return -code error $::errorInfo } } } ## # @brief Instrument specific scan configurations can override this procedure to perform some setup # before running a scan, eg setting hmm frame frequency. # # NOTES\n # Returning an error will cause the scan to abort before it starts\n # eg\n # return -code error "error message" proc ::scan::pre_hmm_scan_prepare {} {} ## # @brief Do some pre-scan checks and prime the DAE proc ::scan::hmm_scan_prepare {sobj uobj} { variable save_filetype variable ic_hmm_datatype variable check_instrument_ready variable force_scan if {$force_scan || $check_instrument_ready && [::plc::inst_ready]} { set force_scan false if [catch { ::scan::check_scanvar $sobj $uobj ::scan::pre_hmm_scan_prepare }] { return -code error "HMSCAN ABORTED: $::errorInfo" } if [catch { set numpoints [SplitReply [$sobj np]] set vlist [split [$sobj getvarpar 0] = ] set scanstart [lindex $vlist 1] set scanstep [lindex $vlist 2] ::scan::runscan_cmd -set feedback status BUSY run_mode "hmscan" set ::histogram_memory::histmem_axes(SVAR) [SplitReply [sicslist [::scan::runscan_cmd -set scan_variable] hdb_path] ] ::nexus::newfile $ic_hmm_datatype $save_filetype clientput "Scan start: $scanstart, Scan step: $scanstep, Number of points: $numpoints" clientput "Datatype: $ic_hmm_datatype" # Prime DAE hmm pause }] { run_mode "normal" return -code error $::errorInfo } } else { return -code error "HMSCAN ABORTED: Instrument not ready" } } proc ::scan::hmm_count {sobj uobj point mode preset} { ::scan::runscan_cmd -set feedback scanpoint $point ::scan::runscan_cmd -set feedback scan_variable_value [SplitReply [[::scan::runscan_cmd -set scan_variable]]] # Start histogram and block until count is complete ::histogram_memory::start block } #TODO rangescan: drive to original position for rangescans, not the start position. proc ::scan::hmm_scan_finish {sobj uobj} { variable save_filetype variable reset_position set $save_filetype "data" ::histogram_memory::stop ::scan::runscan_cmd -set feedback status IDLE run_mode "normal" set ::histogram_memory::histmem_axes(SVAR) "/instrument/run_number" # Make sure that the next save command doesn't overwrite our scan data. # and clear any data links ::nexus::newfile clear data if {$reset_position == "true"} { set reset_position "false" set svar [::scan::runscan_cmd -get scan_variable] set svtype [getatt $svar type] if {$svtype == "motor" || $svtype == "configurablevirtualmotor"} { drive $svar [::scan::runscan_cmd -get scan_start] } } # ::histogram_memory::configure_server Filler_defaults } proc ::scan::bm_scan_finish {sobj uobj} { variable reset_position ::scan::hdb_bmonscan -set feedback status IDLE run_mode "normal" set ::histogram_memory::histmem_axes(SVAR) "/instrument/run_number" # Make sure that the next save command doesn't overwrite our scan data. # and clear any data links ::nexus::newfile clear data if {$reset_position == "true"} { set reset_position "false" set svar [::scan::hdb_bmonscan -get scan_variable] set svtype [getatt $svar type] if {$svtype == "motor" || $svtype == "configurablevirtualmotor"} { drive $svar [::scan::hdb_bmonscan -get scan_start] } } } proc ::scan::bm_writepoint {sobj uobj pt} { ::nexus::save $pt ::scan::hdb_bmonscan -set feedback counts [SplitReply [bm getcounts]]; } #TODO Feedback for Histogram memory scan proc ::scan::hmm_writepoint {sobj uobj pt} { variable save_filetype # Write hdb tree ::nexus::save $pt } proc ::scan::donothing {args} {} proc ::scan::bm_count {sobj uobj point mode preset} { ::scan::hdb_bmonscan -set mode $mode ::scan::hdb_bmonscan -set preset $preset ::scan::hdb_bmonscan -set feedback scanpoint $point; ::scan::hdb_bmonscan -set feedback mode $mode; ::scan::hdb_bmonscan -set feedback preset $preset; ::scan::hdb_bmonscan -set feedback scan_variable_value [SplitReply [[::scan::hdb_bmonscan -set scan_variable]]] ::monitor::count $mode $preset } proc ::scan::bm_scan_prepare {sobj uobj} { variable save_filetype variable check_instrument_ready variable force_scan if {$force_scan || $check_instrument_ready && [::plc::inst_ready]} { set force_scan false if [catch { ::scan::check_scanvar $sobj $uobj ::scan::pre_hmm_scan_prepare }] { return -code error "BMONSCAN ABORTED: $::errorInfo" } if [catch { #TODO Parameterise varindex in some way set varindex 0; set numpoints [SplitReply [$sobj np]] set vlist [split [$sobj getvarpar $varindex] = ] set scanstart [lindex $vlist 1] set scanstep [lindex $vlist 2] ::scan::hdb_bmonscan -set NP $numpoints ::scan::hdb_bmonscan -set scan_variable [string trim [lindex [split [lindex $vlist 0] . ] 1]]; ::scan::hdb_bmonscan -set scan_start $scanstart ::scan::hdb_bmonscan -set scan_increment $scanstep set scanvar_pts [SplitReply [$sobj getvardata $varindex]] ::scan::hdb_bmonscan -set feedback status BUSY run_mode "bmonscan" array set bm_fb [::scan::hdb_bmonscan -list feedback] set ::histogram_memory::histmem_axes(SVAR) [SplitReply [sicslist [::scan::hdb_bmonscan -set scan_variable] hdb_path] ] ::nexus::newfile BEAM_MONITOR $save_filetype #stdscan prepare $sobj $uobj; clientput "Scan start: $scanstart, Scan step: $scanstep, Number of points: $numpoints" clientput "Datatype: BEAM_MONITOR" }] { run_mode "normal" return -code error $::errorInfo } } else { return -code error "BMONSCAN ABORTED: Instrument not ready" } } Publish ::scan::hmm_count user Publish ::scan::hmm_scan_prepare user Publish ::scan::hmm_scan_finish user Publish ::scan::hmm_scan_collect user Publish ::scan::hmm_writepoint user Publish ::scan::donothing user Publish ::scan::bm_scan_prepare user Publish ::scan::bm_scan_finish user Publish ::scan::bm_scan_collect user Publish ::scan::bm_writepoint user Publish ::scan::bm_count user namespace eval scan { namespace export runscan VarMake ::scan::runscan_reset_position Text internal ::scan::runscan_reset_position false #TODO Add counter (monitor_1 monitor_2 ... histmem) and filetype BEAM_MONITOR HISTMEM_? ## # @brief Run a histogram memory scan # @param filetype one of the histogram filetypes (default=HISTOGRAM_XYT) # @param savetype save/nosave (default=save) # @param force true/false (default=false) proc runscan {scanvar start stop numpoints mode preset args} { variable ic_hmm_datatype variable save_filetype variable reset_position variable force_scan if [ catch { set force_scan false set hm_ft_names [array names ::nexus::histmem_filetype_spec] # Default filetype for histogram memory scans set ic_hmm_datatype "HISTOGRAM_XYT" # Default save uniquely numbered files set savetype "save" set reset_position [SplitReply [::scan::runscan_reset_position]] if {[is_drivable $scanvar] == 0} { error "The scan variable <$scanvar> must be drivable" } if {[string is integer $numpoints] != 1} { error "Number of points <$numpoints> must be an integer" } if { $numpoints < 1 } { error "Number of points <$numpoints> must not be less than one" } if {$numpoints == 1} { set step 0 } else { set step [expr double($stop - $start)/($numpoints - 1.0)] } if {$step == 0 && $numpoints > 1} { clientput "WARNING:Scan step is zero and number of points > 1. Adjusting numpoints to one" set numpoints 1 } foreach {arg val} $args { switch $arg { "force" { if [string is boolean $val] { set force_scan $val } else { error "ERROR: force must be true or false" } } "datatype" { set ic_hmm_datatype $val } "savetype" { switch $val { "save" { set save_filetype data } "nosave" { set save_filetype scratch } default { error "ERROR: $arg $val, valid values for $arg are 'save' or 'nosave'" } } } default { error "ERROR: $arg should be 'datatype' 'savetype' or 'force'" } } } set det_type [::scan::runscan_cmd -get detector] if {$det_type == "histmem"} { # hmscan ignores mode and preset, we use FAT_COUNT_METHOD and FAT_COUNT_STOP ::histogram_memory::count_method $mode ::histogram_memory::count_size $preset hmscan clear hmscan add $scanvar $start $step } elseif {$det_type == "bmon"} { set mode [string tolower $mode] switch -glob $mode { "time" { set bms_mode "timer" } "monitor" { set bms_mode "monitor" } default {return -code error "ERROR: mode should be 'time' or 'monitor' not $mode"} } bmonscan clear bmonscan add $scanvar $start $step } else { return -code error "ERROR: detector type should be 'histmem' or 'bmon' not $det_type" } ::scan::runscan_cmd -set numpoints $numpoints ::scan::runscan_cmd -set scan_variable $scanvar ::scan::runscan_cmd -set scan_start $start ::scan::runscan_cmd -set scan_stop $stop ::scan::runscan_cmd -set feedback scan_step $step ::scan::runscan_cmd -set mode $mode ::scan::runscan_cmd -set preset $preset if {$det_type == "histmem"} { hmscan run $numpoints timer 0 } else { bmonscan run $numpoints $bms_mode $preset } } message ] { set force_scan false return -code error "ERROR [info level 0]\n$message" } else { set force_scan false return $message } } } namespace import ::scan::runscan publish runscan user sicslist setatt runscan privilege internal