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

Last change on this file since 2035 was 1986, checked in by dkearney, 13 years ago

adding -runfile flag to simsim
updating tool.tcl to cover the condition where resource file has results_dir directive

File size: 13.1 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 installdir {} { return $_installdir }
27
28    public method run {args}
29    public method abort {}
30    public method getRunFile {}
31
32    protected method _mkdir {dir}
33    protected method _output {data}
34
35    private variable _installdir ""  ;# installation directory for this tool
36    private variable _outputcb ""    ;# callback for tool output
37    private variable _runFile ""     ;# location of last created run.xml
38    private common job               ;# array var used for blt::bgexec jobs
39    private common jobnum 0          ;# counter for unique job number
40
41    # get global resources for this tool session
42    public proc resources {{option ""}}
43
44    public common _resources
45    public proc setAppName {name}   { set _resources(-appname) $name }
46    public proc setHubName {name}   { set _resources(-hubname) $name }
47    public proc setHubURL {name}    { set _resources(-huburl) $name }
48    public proc setSession {name}   { set _resources(-session) $name }
49    public proc setJobPrt {name}    { set _resources(-jobprotocol) $name }
50    public proc setResultDir {name} { set _resources(-resultdir) $name }
51}
52
53# must use this name -- plugs into Rappture::resources::load
54proc tool_init_resources {} {
55    Rappture::resources::register \
56        application_name  Rappture::Tool::setAppName \
57        application_id    Rappture::Tool::setAppId \
58        hub_name          Rappture::Tool::setHubName \
59        hub_url           Rappture::Tool::setHubURL \
60        session_token     Rappture::Tool::setSession \
61        job_protocol      Rappture::Tool::setJobPrt \
62        results_directory Rappture::Tool::setResultDir
63}
64                                                                               
65# ----------------------------------------------------------------------
66# CONSTRUCTOR
67# ----------------------------------------------------------------------
68itcl::body Rappture::Tool::constructor {xmlobj installdir args} {
69    if {![Rappture::library isvalid $xmlobj]} {
70        error "bad value \"$xmlobj\": should be Rappture::Library"
71    }
72    set _xmlobj $xmlobj
73
74    if {![file exists $installdir]} {
75        error "directory \"$installdir\" doesn't exist"
76    }
77    set _installdir $installdir
78
79    eval configure $args
80}
81
82# ----------------------------------------------------------------------
83# USAGE: resources ?-option?
84#
85# Clients use this to query information about the tool.
86# ----------------------------------------------------------------------
87itcl::body Rappture::Tool::resources {{option ""}} {
88    if {$option == ""} {
89        return [array get _resources]
90    }
91    if {[info exists _resources($option)]} {
92        return $_resources($option)
93    }
94    return ""
95}
96
97# ----------------------------------------------------------------------
98# USAGE: run ?<path1> <value1> <path2> <value2> ...? ?-output <callbk>?
99#
100# This method causes the tool to run.  All widgets are synchronized
101# to the current XML representation, and a "driver.xml" file is
102# created as the input for the run.  That file is fed to the tool
103# according to the <tool><command> string, and the job is executed.
104#
105# Any "<path> <value>" arguments are used to override the current
106# settings from the GUI.  This is useful, for example, when filling
107# in missing simulation results from the analyzer.
108#
109# If the -output argument is included, then the next arg is a
110# callback command for output messages.  Any output that comes in
111# while the tool is running is sent back to the caller, so the user
112# can see progress running the tool.
113#
114# Returns a list of the form {status result}, where status is an
115# integer status code (0=success) and result is the output from the
116# simulator.  Successful output is something like {0 run1293921.xml},
117# where 0=success and run1293921.xml is the name of the file containing
118# results.
119# ----------------------------------------------------------------------
120itcl::body Rappture::Tool::run {args} {
121    global errorInfo
122
123    #
124    # Make sure that we save the proper application name.
125    # Actually, the best place to get this information is
126    # straight from the "installtool" script, but just in
127    # case we have an older tool, we should insert the
128    # tool name from the resources config file.
129    #
130    if {[info exists _resources(-appname)]
131          && "" != $_resources(-appname)
132          && "" == [$_xmlobj get tool.name]} {
133        $_xmlobj put tool.name $_resources(-appname)
134    }
135
136    # sync all widgets to the XML tree
137    sync
138
139    # if there are any args, use them to override parameters
140    set _outputcb ""
141    foreach {path val} $args {
142        if {$path == "-output"} {
143            set _outputcb $val
144        } else {
145            $_xmlobj put $path.current $val
146        }
147    }
148
149    foreach item {control output error} { set job($item) "" }
150
151    # write out the driver.xml file for the tool
152    set file "driver[pid].xml"
153    set status [catch {
154        set fid [open $file w]
155        puts $fid "<?xml version=\"1.0\"?>"
156        puts $fid [$_xmlobj xml]
157        close $fid
158    } result]
159
160    # set limits for cpu time
161    set limit [$_xmlobj get tool.limits.cputime]
162    if {"" == $limit || [catch {Rappture::rlimit set cputime $limit}]} {
163        Rappture::rlimit set cputime 900  ;# 15 mins by default
164    }
165
166    # execute the tool using the path from the tool description
167    if {$status == 0} {
168        set cmd [$_xmlobj get tool.command]
169        regsub -all @tool $cmd $_installdir cmd
170        regsub -all @driver $cmd $file cmd
171        regsub -all {\\} $cmd {\\\\} cmd
172        set cmd [string trimleft $cmd " "]
173
174        # if job_protocol is "submit", then use use submit command
175        if {[resources -jobprotocol] == "submit"} {
176            set cmd [linsert $cmd 0 submit --local]
177        }
178
179        # starting job...
180        Rappture::rusage mark
181
182        if {0 == [string compare -nocase -length 5 $cmd "ECHO "] } {
183            set status 0;
184            set job(output) [string range $cmd 5 end]
185        } else {
186            set status [catch {eval blt::bgexec \
187                ::Rappture::Tool::job(control) \
188                -keepnewline yes \
189                -killsignal SIGTERM \
190                -onoutput [list [itcl::code $this _output]] \
191                -output ::Rappture::Tool::job(output) \
192                -error ::Rappture::Tool::job(error) $cmd} result]
193        }
194        # ...job is finished
195        array set times [Rappture::rusage measure]
196
197        if {[resources -jobprotocol] != "submit"} {
198            set id [$_xmlobj get tool.id]
199            set vers [$_xmlobj get tool.version.application.revision]
200            set simulation simulation
201            if { $id != "" && $vers != "" } {
202                set pid [pid]
203                set simulation ${pid}_${id}_r${vers}
204            }
205            puts stderr "MiddlewareTime: job=[incr jobnum] event=$simulation start=$times(start) walltime=$times(walltime) cputime=$times(cputime) status=$status"
206
207            #
208            # Scan through stderr channel and look for statements that
209            # represent grid jobs that were executed.  The statements
210            # look like this:
211            #
212            # MiddlewareTime: job=1 event=simulation start=3.001094 ...
213            #
214            set subjobs 0
215            while {[regexp -indices {(^|\n)MiddlewareTime:( +[a-z]+=[^ \n]+)+(\n|$)} $job(error) match]} {
216                foreach {p0 p1} $match break
217                if {[string index $job(error) $p0] == "\n"} { incr p0 }
218
219                catch {unset data}
220                array set data {
221                    job 1
222                    event simulation
223                    start 0
224                    walltime 0
225                    cputime 0
226                    status 0
227                }
228                foreach arg [lrange [string range $job(error) $p0 $p1] 1 end] {
229                    foreach {key val} [split $arg =] break
230                    set data($key) $val
231                }
232                set data(job) [expr {$jobnum+$data(job)}]
233                set data(event) "subsimulation"
234                set data(start) [expr {$times(start)+$data(start)}]
235
236                set stmt "MiddlewareTime:"
237                foreach key {job event start walltime cputime status} {
238                    # add required keys in a particular order
239                    append stmt " $key=$data($key)"
240                    unset data($key)
241                }
242                foreach key [array names data] {
243                    # add anything else that the client gave -- venue, etc.
244                    append stmt " $key=$data($key)"
245                }
246                puts stderr $stmt
247                incr subjobs
248
249                # done -- remove this statement
250                set job(error) [string replace $job(error) $p0 $p1]
251            }
252            incr jobnum $subjobs
253        }
254
255    } else {
256        set job(error) "$result\n$errorInfo"
257    }
258    if {$status == 0} {
259        file delete -force -- $file
260    }
261
262    # see if the job was aborted
263    if {[regexp {^KILLED} $job(control)]} {
264        set _runFile ""
265        return [list 0 "ABORT"]
266    }
267
268    #
269    # If successful, return the output, which should include
270    # a reference to the run.xml file containing results.
271    #
272    if {$status == 0} {
273        set result [string trim $job(output)]
274        if {[regexp {=RAPPTURE-RUN=>([^\n]+)} $result match file]} {
275            set status [catch {Rappture::library $file} result]
276            if {$status != 0} {
277                global errorInfo
278                set result "$result\n$errorInfo"
279            }
280
281       
282            # if there's a results_directory defined in the resources
283            # file, then move the run.xml file there for storage
284            if {[info exists _resources(-resultdir)]
285                  && "" != $_resources(-resultdir)} {
286                catch {
287                    if {![file exists $_resources(-resultdir)]} {
288                        _mkdir $_resources(-resultdir)
289                    }
290                    file rename -force -- $file $_resources(-resultdir)
291                    set _runFile [file join $_resources(-resultdir) $file]
292                }
293            } else {
294                set _runFile $file
295            }
296        } else {
297            set status 1
298            set result "Can't find result file in output.\nDid you call Rappture
299::result in your simulator?"
300            set _runFile ""
301        }
302        return [list $status $result]
303    } elseif {"" != $job(output) || "" != $job(error)} {
304        return [list $status [string trim "$job(output)\n$job(error)"]]
305    }
306    return [list $status $result]
307}
308
309# ----------------------------------------------------------------------
310# USAGE: _mkdir <directory>
311#
312# Used internally to create the <directory> in the file system.
313# The parent directory is also created, as needed.
314# ----------------------------------------------------------------------
315itcl::body Rappture::Tool::_mkdir {dir} {
316    set parent [file dirname $dir]
317    if {"." != $parent && "/" != $parent} {
318        if {![file exists $parent]} {
319            _mkdir $parent
320        }
321    }
322    file mkdir $dir
323}
324
325
326# ----------------------------------------------------------------------
327# USAGE: abort
328#
329# Clients use this during a "run" to abort the current job.
330# Kills the job and forces the "run" method to return.
331# ----------------------------------------------------------------------
332itcl::body Rappture::Tool::abort {} {
333    set job(control) "abort"
334}
335
336# ----------------------------------------------------------------------
337# USAGE: _output <data>
338#
339# Used internally to send each bit of output <data> coming from the
340# tool onto the caller, so the user can see progress.
341# ----------------------------------------------------------------------
342itcl::body Rappture::Tool::_output {data} {
343    if {[string length $_outputcb] > 0} {
344        uplevel #0 [list $_outputcb $data]
345    }
346}
347
348# ----------------------------------------------------------------------
349# USAGE: getRunFile
350#
351# Returns the file name of the last generated run.xml, as opposed to a
352# library object as returned by the run method.
353# ----------------------------------------------------------------------
354itcl::body Rappture::Tool::getRunFile {} {
355    return $_runFile
356}
357
Note: See TracBrowser for help on using the repository browser.