#---------------------------------------------------------------------- # Support functions for table processing in SICS # # This includes a CSV processing module from someone else. See below. # # copyright: see file COPYRIGHT # # Mark Koennecke, November 2008 #---------------------------------------------------------------------- if { [info exists __tableheader] == 0 } { set __tableheader NULL Publish tableexe User Publish loop User } #===================================================================== # Csv tcl package version 2.0 # A tcl library to deal with CSV (comma separated value) # files, generated and readable by some DOS/Windows programs # Contain two functions: # csv2list string ?separator? # and # list2csv list ?separator? # which converts line from CSV file to list and vice versa. # # Both functions have optional "separator argument" becouse some silly # Windows # program might use semicomon as delimiter in COMMA separated values # file. # # Copyright (c) SoftWeyr, 1997-99 # Many thanks to Robert Seeger # for beta-testing and fixing my misprints # This file is distributed under GNU Library Public License. Visit # http://www.gnu.org/copyleft/gpl.html # for details. # # Convert line, read from CSV file into proper TCL list # Commas inside quoted strings are not considered list delimiters, # Double quotes inside quoted strings are converted to single quotes # Double quotes are stripped out and replaced with correct Tcl quoting # proc csv2list {str {separator ","}} { #build a regexp> set regexp [subst -nocommands \ {^[ \t\r\n]*("(([^"]|"")*)"|[^"$separator \t\r]*)}] set regexp1 [subst -nocommands {$regexp[ \t\r\n]*$separator\(.*)$}] set regexp2 [subst -nocommands {$regexp[ \t\r\n]*\$}] set list {} while {[regexp $regexp1 $str junk1 unquoted quoted\ junk2 str]} { if {[string length $quoted]||$unquoted=="\"\""} { regsub -all {""} $quoted \" unquoted } lappend list $unquoted } if {[regexp $regexp2 $str junk unquoted quoted]} { if {[string length $quoted]||$unquoted=="\"\""} { regsub -all {""} $quoted \" unquoted } lappend list $unquoted if {[uplevel info exist csvtail]} { uplevel set csvtail {""} } } else { if {[uplevel info exist csvtail]} { uplevel [list set csvtail $str] } else { return -code error -errorcode {CSV 1 "CSV parse error"}\ "CSV parse error: unparsed tail \"$str\"" } } return $list } proc list2csv {list {separator ","}} { set l {} foreach elem $list { if {[string match {} $elem]|| [regexp {^[+-]?([0-9]+|([0-9]+\.?[0-9]*|\.[0-9]+)([eE][+-]?[0-9]+)?)$}\ $elem]} { lappend l $elem } else { regsub -all {"} $elem {""} selem lappend l "\"$selem\"" } } return [join $l $separator] } proc csvfile {f {separator ","}} { set csvtail "" set list {} set buffer {} while {[gets $f line]>=0} { if {[string length $csvtail]} { set line "$csvtail\n$line" } elseif {![string length $line]} { lappend list {} continue } set rec [csv2list $line $separator] set buffer [concat $buffer $rec] if {![ string length $csvtail]} { lappend list $buffer set buffer {} } } if {[string length $csvtail]} { return -code error -errorcode {CSV 2 "Multiline parse error"}\ "CSV file parse error" } return $list } proc csvstring {str {separator ","}} { set csvtail "" set list {} set buffer {} foreach line [split $str "\n"] { if {[string length $csvtail]} { set line "$csvtail\n$line" } elseif {![string length $line]} { lappend list {} continue } set rec [csv2list $line $separator] set buffer [concat $buffer $rec] if {![ string length $csvtail]} { lappend list $buffer set buffer {} } } if {[string length $cvstail]} { return -code error -errorcode {CSV 2 "Multiline parse error"}\ "CSV string parse error" } return $list } package provide Csv 2.1 #======================================================================== # The plan here is such: operations which happen fast or immediatly are # done at once. Count commands or anything given as command is appended # to a list for later execution. The idea is that this contains the # actual measuring payload of the row. # Drivables are immediatly started. # After processing the rows, there is a success to wait for motors to arrive # Then the commands for later execution are run. This frees the user of the # the necessity to have the count or whatever command as the last thing in the row #-------------------------------------------------------------------------------- proc testinterrupt {} { set int [getint] if {[string first continue $int] < 0} { error "Interrupted" } } #-------------------------------------------------------------------------------- proc processtablerow {line} { global __tableheader set parlist [csv2list $line] for {set i 0} {$i < [llength $__tableheader]} {incr i} { set type [lindex $__tableheader $i] set data [lindex $parlist $i] #--------- first process special types switch $type { monitor { lappend laterExe "count monitor $data" continue } timer { lappend laterExe "count timer $data" continue } compar { append command [join [lrange $parlist $i end]] lappend laterExe $command break } command { lappend laterExe $data continue } batch { lappend laterExe "exe $data" continue } } #----------- now look for drivables set test [sicstype $type] if {[string compare $test DRIV] == 0} { set status [catch {run $type $data} msg] if {$status != 0} { clientput "ERROR: $msg for $type with $data" } continue } #------------- now look for special objects set objtype [sicsdescriptor $type] switch $objtype { SicsVariable - MulMot - Macro { set status [catch {eval $type $data} msg] if {$status != 0} { clientput "ERROR: $msg for $type with $data" } continue } default { clientput "Skipping non recognized column $type with data $data" } } } set status [catch {success} msg] if {$status != 0} { clientput "ERROR: $msg while waiting for motors to arrive" } testinterrupt foreach command $laterExe { eval $command testinterrupt } } #------------------------------------------------------------------------ proc tableexe {tablefile} { global __tableheader if {[string first NULL $__tableheader] < 0} { error "Tableexe already running, terminated" } set fullfile [SplitReply [exe fullpath $tablefile]] set in [open $fullfile r] gets $in header set __tableheader [csv2list $header] while {[gets $in line] > 0} { set status [catch {processtablerow $line} msg] if {$status != 0} { set int [getint] if {[string first continue $int] < 0} { break } else { clientput "ERROR: $msg while processing row" } } } close $in set __tableheader NULL return "Done processing table" } #--------------------------------------------------------------------------- proc loop args { clientput $args if {[llength $args] < 2} { error \ "Usage: loop \n\t number of repetions\n\t any SICS command" } set len [lindex $args 0] set command [lrange $args 1 end] for {set i 1} {$i <= $len} {incr i} { clientput "Repetition $i of $len" set status [catch {eval [join $command]} msg] if {$status != 0} { clientput "ERROR: $msg while processing loop command" } testinterrupt } } #============================================================================== # This is an old attempt #============================================================================= proc __tablescan__ args { global __tableheader set idx [lsearch $__tableheader monitor] if {$idx >= 0} { set preset [lindex $args $idx] set mode monitor } set idx [lsearch $__tableheader timer] if {$idx >= 0} { set preset [lindex $args $idx] set mode timer } set idx [lsearch $__tableheader scanvar] if {$idx >= 0} { set var [lindex $args $idx] } else { error "ERROR: No scan variable in table" } set idx [lsearch $__tableheader scanstart] if {$idx >= 0} { set start [lindex $args $idx] } else { error "ERROR: No scan start in table" } set idx [lsearch $__tableheader scanend] if {$idx >= 0} { set end [lindex $args $idx] } else { error "ERROR: No scan end in table" } set idx [lsearch $__tableheader scanstep] if {$idx >= 0} { set step [lindex $args $idx] } else { error "ERROR: No scan step in table" } set np [expr abs($end - $start)/$step] xxxscan var $var $start $step xxxscan run $np $mode $preset }