source: trunk/gui/scripts/tool.tcl @ 695

Last change on this file since 695 was 680, checked in by mmc, 17 years ago

Fixed the handling of MiddlewareTime? statements so that any extra parameters
sent from the subjobs (venue=, etc.) are passed right through.

File size: 9.8 KB
Line 
1# ----------------------------------------------------------------------
2#  COMPONENT: tool - represents an entire tool
3#
4#  This object represents an entire tool defined by Rappture.
5#  Each tool resides in an installation directory with other tool
6#  resources (libraries, examples, etc.).  Each tool is defined by
7#  its inputs and outputs, which are tied to various widgets in the
8#  GUI.  Each tool tracks the inputs, knows when they're changed,
9#  and knows how to run itself to produce new results.
10# ======================================================================
11#  AUTHOR:  Michael McLennan, Purdue University
12#  Copyright (c) 2004-2005  Purdue Research Foundation
13#
14#  See the file "license.terms" for information on usage and
15#  redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
16# ======================================================================
17package require BLT
18
19itcl::class Rappture::Tool {
20    inherit Rappture::ControlOwner
21
22    constructor {xmlobj installdir args} {
23        Rappture::ControlOwner::constructor ""
24    } { # defined below }
25
26    public method get {{option ""}}
27    public method installdir {} { return $_installdir }
28
29    public method run {args}
30    public method abort {}
31
32    protected method _output {data}
33
34    private variable _installdir ""  ;# installation directory for this tool
35    private variable _outputcb ""    ;# callback for tool output
36    private common job               ;# array var used for blt::bgexec jobs
37    private common jobnum 0          ;# counter for unique job number
38
39    # resources file tells us the application name
40    public common _appname ""
41    public proc setAppName {name} { set _appname $name }
42}
43
44# must use this name -- plugs into Rappture::resources::load
45proc tool_init_resources {} {
46    Rappture::resources::register \
47        application_name Rappture::Tool::setAppName
48}
49                                                                               
50# ----------------------------------------------------------------------
51# CONSTRUCTOR
52# ----------------------------------------------------------------------
53itcl::body Rappture::Tool::constructor {xmlobj installdir args} {
54    if {![Rappture::library isvalid $xmlobj]} {
55        error "bad value \"$xmlobj\": should be Rappture::Library"
56    }
57    set _xmlobj $xmlobj
58
59    if {![file exists $installdir]} {
60        error "directory \"$installdir\" doesn't exist"
61    }
62    set _installdir $installdir
63
64    eval configure $args
65}
66
67# ----------------------------------------------------------------------
68# USAGE: get ?-option?
69#
70# Clients use this to query information about the tool.
71# ----------------------------------------------------------------------
72itcl::body Rappture::Tool::get {{option ""}} {
73    set values(-name) $_appname
74    if {$option == ""} {
75        return [array get values]
76    }
77    if {![info exists values]} {
78        error "bad option \"$option\": should be [join [array names values] {, }]"
79    }
80    return $values($option)
81}
82
83# ----------------------------------------------------------------------
84# USAGE: run ?<path1> <value1> <path2> <value2> ...? ?-output <callbk>?
85#
86# This method causes the tool to run.  All widgets are synchronized
87# to the current XML representation, and a "driver.xml" file is
88# created as the input for the run.  That file is fed to the tool
89# according to the <tool><command> string, and the job is executed.
90#
91# Any "<path> <value>" arguments are used to override the current
92# settings from the GUI.  This is useful, for example, when filling
93# in missing simulation results from the analyzer.
94#
95# If the -output argument is included, then the next arg is a
96# callback command for output messages.  Any output that comes in
97# while the tool is running is sent back to the caller, so the user
98# can see progress running the tool.
99#
100# Returns a list of the form {status result}, where status is an
101# integer status code (0=success) and result is the output from the
102# simulator.  Successful output is something like {0 run1293921.xml},
103# where 0=success and run1293921.xml is the name of the file containing
104# results.
105# ----------------------------------------------------------------------
106itcl::body Rappture::Tool::run {args} {
107    global errorInfo
108
109    #
110    # Make sure that we save the proper application name.
111    # Actually, the best place to get this information is
112    # straight from the "installtool" script, but just in
113    # case we have an older tool, we should insert the
114    # tool name from the resources config file.
115    #
116    if {"" != $_appname && "" == [$_xmlobj get tool.name]} {
117        $_xmlobj put tool.name $_appname
118    }
119
120    # sync all widgets to the XML tree
121    sync
122
123    # if there are any args, use them to override parameters
124    set _outputcb ""
125    foreach {path val} $args {
126        if {$path == "-output"} {
127            set _outputcb $val
128        } else {
129            $_xmlobj put $path.current $val
130        }
131    }
132
133    foreach item {control output error} { set job($item) "" }
134
135    # write out the driver.xml file for the tool
136    set file "driver[pid].xml"
137    set status [catch {
138        set fid [open $file w]
139        puts $fid "<?xml version=\"1.0\"?>"
140        puts $fid [$_xmlobj xml]
141        close $fid
142    } result]
143
144    # set limits for cpu time and file size
145    set limit [$_xmlobj get tool.limits.cputime]
146    if {"" == $limit || [catch {Rappture::rlimit set cputime $limit}]} {
147        Rappture::rlimit set cputime 900  ;# 15 mins by default
148    }
149
150    set limit [$_xmlobj get tool.limits.filesize]
151    if {"" == $limit || [catch {Rappture::rlimit set filesize $limit}]} {
152        Rappture::rlimit set filesize 1000000  ;# 1MB by default
153    }
154
155    # execute the tool using the path from the tool description
156    if {$status == 0} {
157        set cmd [$_xmlobj get tool.command]
158        regsub -all @tool $cmd $_installdir cmd
159        regsub -all @driver $cmd $file cmd
160        regsub -all {\\} $cmd {\\\\} cmd
161        set cmd [string trimleft $cmd " "]
162
163        # starting job...
164        Rappture::rusage mark
165
166        if {0 == [string compare -nocase -length 5 $cmd "ECHO "] } {
167            set status 0;
168            set job(output) [string range $cmd 5 end]
169        } else {
170        set status [catch {eval blt::bgexec \
171            ::Rappture::Tool::job(control) \
172            -keepnewline yes \
173            -killsignal SIGTERM \
174            -onoutput [list [itcl::code $this _output]] \
175            -output ::Rappture::Tool::job(output) \
176            -error ::Rappture::Tool::job(error) $cmd} result]
177        }
178        # ...job is finished
179        array set times [Rappture::rusage measure]
180        puts stderr "MiddlewareTime: job=[incr jobnum] event=simulation start=$times(start) walltime=$times(walltime) cputime=$times(cputime) status=$status"
181
182        #
183        # Scan through stderr channel and look for statements that
184        # represent grid jobs that were executed.  The statements
185        # look like this:
186        #
187        # MiddlewareTime: job=1 event=simulation start=3.001094 ...
188        #
189        set subjobs 0
190        while {[regexp -indices {(^|\n)MiddlewareTime:( +[a-z]+=[^ \n]+)+(\n|$)} $job(error) match]} {
191            foreach {p0 p1} $match break
192            if {[string index $job(error) $p0] == "\n"} { incr p0 }
193
194            catch {unset data}
195            array set data {
196                job 1
197                event simulation
198                start 0
199                walltime 0
200                cputime 0
201                status 0
202            }
203            foreach arg [lrange [string range $job(error) $p0 $p1] 1 end] {
204                foreach {key val} [split $arg =] break
205                set data($key) $val
206            }
207            set data(job) [expr {$jobnum+$data(job)}]
208            set data(event) "subsimulation"
209            set data(start) [expr {$times(start)+$data(start)}]
210
211            set stmt "MiddlewareTime:"
212            foreach key {job event start walltime cputime status} {
213                # add required keys in a particular order
214                append stmt " $key=$data($key)"
215                unset data($key)
216            }
217            foreach key [array names data] {
218                # add anything else that the client gave -- venue, etc.
219                append stmt " $key=$data($key)"
220            }
221            puts stderr $stmt
222            incr subjobs
223
224            # done -- remove this statement
225            set job(error) [string replace $job(error) $p0 $p1]
226        }
227        incr jobnum $subjobs
228
229    } else {
230        set job(error) "$result\n$errorInfo"
231    }
232    if {$status == 0} {
233        file delete -force -- $file
234    }
235
236    # see if the job was aborted
237    if {[regexp {^KILLED} $job(control)]} {
238        return [list 0 "ABORT"]
239    }
240
241    #
242    # If successful, return the output, which should include
243    # a reference to the run.xml file containing results.
244    #
245    if {$status == 0} {
246        set file [string trim $job(output)]
247        return [list $status $file]
248    } elseif {"" != $job(output) || "" != $job(error)} {
249        return [list $status [string trim "$job(output)\n$job(error)"]]
250    }
251    return [list $status $result]
252}
253
254# ----------------------------------------------------------------------
255# USAGE: abort
256#
257# Clients use this during a "run" to abort the current job.
258# Kills the job and forces the "run" method to return.
259# ----------------------------------------------------------------------
260itcl::body Rappture::Tool::abort {} {
261    set job(control) "abort"
262}
263
264# ----------------------------------------------------------------------
265# USAGE: _output <data>
266#
267# Used internally to send each bit of output <data> coming from the
268# tool onto the caller, so the user can see progress.
269# ----------------------------------------------------------------------
270itcl::body Rappture::Tool::_output {data} {
271    if {[string length $_outputcb] > 0} {
272        uplevel #0 [list $_outputcb $data]
273    }
274}
Note: See TracBrowser for help on using the repository browser.