source: trunk/gui/scripts/service.tcl @ 4503

Last change on this file since 4503 was 3513, checked in by gah, 11 years ago

Add string trim to select 'xml get' calls

File size: 11.8 KB
Line 
1# -*- mode: tcl; indent-tabs-mode: nil -*-
2# ----------------------------------------------------------------------
3#  COMPONENT: service - represents a tool embedded within another tool
4#
5#  The tool.xml description for a Rappture tool can contain one or
6#  more embedded <tool> references.  Each Rappture::Service object
7#  represents an embedded <tool> and manages its operation,
8#  contributing some service to the overall tool.
9# ======================================================================
10#  AUTHOR:  Michael McLennan, Purdue University
11#  Copyright (c) 2004-2012  HUBzero Foundation, LLC
12#
13#  See the file "license.terms" for information on usage and
14#  redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
15# ======================================================================
16package require BLT
17
18itcl::class Rappture::Service {
19    constructor {owner path args} { # defined below }
20    destructor { # defined below }
21
22    public method control {}
23    public method input {}
24    public method output {args}
25
26    public method run {}
27    public method abort {}
28    public method clear {}
29
30    protected method _link {from to}
31
32    private variable _owner ""    ;# thing managing this service
33    private variable _path ""     ;# path to the <tool> description in _owner
34    private variable _tool ""     ;# underlying tool for this service
35    private variable _control ""  ;# control: auto/manual
36    private variable _show        ;# <show> info for input/output methods
37    private variable _obj2path    ;# maps object in _show => service path
38    private variable _path2widget ;# maps path for object in _show => widget
39    private variable _path2path   ;# maps path for object in _show => link path
40    private variable _result ""   ;# result from last run
41}
42
43# ----------------------------------------------------------------------
44# CONSTRUCTOR
45# ----------------------------------------------------------------------
46itcl::body Rappture::Service::constructor {owner path args} {
47    if {[catch {$owner isa Rappture::ControlOwner} valid] != 0 || !$valid} {
48        error "bad object \"$owner\": should be Rappture::ControlOwner"
49    }
50    set _owner $owner
51    set _path $path
52
53    #
54    # Load up the tool description from the <interface> file.
55    #
56    set intf [string trim [$_owner xml get $path.interface]]
57    if {"" == $intf} {
58        puts "can't find <interface> description for tool at $path"
59    } else {
60        set installdir [[$_owner tool] installdir]
61        regsub -all @tool $intf $installdir intf
62
63        set xmlobj [Rappture::library $intf]
64        set installdir [file dirname $intf]
65        set _tool [Rappture::Tool ::\#auto $xmlobj $installdir]
66        set _control [string trim [$_tool xml get tool.control]]
67
68        #
69        # Scan through the <tool> and establish all of the
70        # relationships:
71        #
72        #   <show> ... Add to list of "input" for this service.
73        #              Caller will add controls to the interface.
74        #
75        #   <link> ... Link this value to another input/output
76        #              that exists in the containing tool.
77        #
78        #   <set> .... Hard-code the value for this input/output.
79        #
80        foreach dir {input output} {
81            set _show($dir) ""
82            foreach cname [$_owner xml children $path.$dir] {
83                set ppath $path.$dir.$cname
84
85                set spath [string trim [$_owner xml get $ppath.path]]
86                if {"" == $spath} {
87                    error "missing <path> at $ppath"
88                }
89
90                set type [$_owner xml element -as type $ppath]
91                switch -- $type {
92                  show {
93puts "show: $spath"
94                      set tpath [string trim [$_owner xml get $ppath.to]]
95                      if {"" == $tpath && $dir == "input"} {
96                          error "missing <to> at $ppath"
97                      }
98                      set obj [$_tool xml element -as object $spath]
99                      puts " => $obj"
100                      lappend _show($dir) $obj
101                      set _obj2path($obj) $spath
102                     
103                      if {$dir == "input"} {
104                          puts "link: $tpath => $spath"
105                          $_owner notify add $this $tpath \
106                              [itcl::code $this _link $tpath $spath]
107                      }
108                  }
109                  link {
110                      set tpath [string trim [$_owner xml get $ppath.to]]
111                      if {"" == $tpath} {
112                          error "missing <to> at $ppath"
113                      }
114                      if {"" == [$_owner xml element $tpath]} {
115                          error "bad <to> path \"$tpath\" at $ppath"
116                      }
117                      if {$dir == "input"} {
118                          puts "link: $tpath => $spath"
119                          $_owner notify add $this $tpath \
120                              [itcl::code $this _link $tpath $spath]
121                      } else {
122                          puts "path2path: $spath => $tpath"
123                          set _path2path($spath) $tpath
124                      }
125                  }
126                    set {
127                        if {"" == [$_owner xml element $ppath.value]} {
128                        error "missing <value> at $ppath"
129                    }
130puts "set: $spath from $ppath.value"
131                    $_tool xml copy $spath from \
132                        [$_owner xml object] $ppath.value
133                  }
134                }
135            }
136        }
137
138        if {$_control != "auto"} {
139            set _show(control) [$_tool xml element -as object tool.control]
140        } else {
141            set _show(control) ""
142        }
143    }
144
145    eval configure $args
146}
147
148# ----------------------------------------------------------------------
149# DESTRUCTOR
150# ----------------------------------------------------------------------
151itcl::body Rappture::Service::destructor {} {
152    foreach dir [array names _show] {
153        foreach obj $_show($dir) {
154            itcl::delete object $obj
155        }
156    }
157}
158
159# ----------------------------------------------------------------------
160# USAGE: control
161#
162# Used by the container that displays this service to determine what
163# the control for this service should look like.  Returns "" if
164# there is no control--the service is invoked automatically whenever
165# the inputs change.  Otherwise, it returns a list of the form
166# {key value key value ...} with attributes that configure the button
167# controlling this service.
168# ----------------------------------------------------------------------
169itcl::body Rappture::Service::control {} {
170    return $_show(control)
171}
172
173# ----------------------------------------------------------------------
174# USAGE: input
175#
176# Used by the container that displays this service to describe any
177# inputs for this service that should be added to the main service.
178# Returns a list of XML objects representing various input controls.
179# ----------------------------------------------------------------------
180itcl::body Rappture::Service::input {} {
181    return $_show(input)
182}
183
184# ----------------------------------------------------------------------
185# USAGE: output
186# USAGE: output for <object> <widget>
187#
188# Used by the container that displays this service to describe any
189# outputs for this service that should be added to the main service.
190#
191# With no args, it returns a list of XML objects representing various
192# outputs.  The caller uses this information to create various output
193# widgets.
194# ----------------------------------------------------------------------
195itcl::body Rappture::Service::output {args} {
196    if {[llength $args] == 0} {
197        return $_show(output)
198    }
199    set option [lindex $args 0]
200    switch -- $option {
201        for {
202            if {[llength $args] != 3} {
203                error "wrong # args: should be \"output for object widget\""
204            }
205            set obj [lindex $args 1]
206            set win [lindex $args 2]
207            if {[info exists _obj2path($obj)]} {
208                set path $_obj2path($obj)
209puts "OUTPUT $path => $win"
210                set _path2widget($path) $win
211            } else {
212                puts "error: don't recognize output object $obj"
213            }
214        }
215        default {
216            error "bad option \"$option\": should be for"
217        }
218    }
219}
220
221# ----------------------------------------------------------------------
222# USAGE: run
223#
224# This method causes the service to run.  All widgets are synchronized
225# to the current XML representation, and a "driver.xml" file is
226# created as the input for the run.  That file is fed to the tool
227# according to the <tool><command> string, and the job is executed.
228#
229# All outputs are pushed out to the tool containing this service
230# according to the <outputs> directive for the service.
231# ----------------------------------------------------------------------
232itcl::body Rappture::Service::run {} {
233puts "running..."
234    clear
235    foreach {status result} [$_tool run] break
236
237    if {$status == 0 && $result != "ABORT"} {
238        if {[regexp {=RAPPTURE-RUN=>([^\n]+)} $result match file]} {
239            set xmlobj [Rappture::library $file]
240            #
241            # Scan through all outputs and copy them to the final output
242            # for the tool.  If they have widgets associated with them,
243            # set the value for the widget.
244            #
245puts "showing output..."
246            foreach cname [$xmlobj children output] {
247                set path output.$cname
248puts " for $path"
249                $_owner xml copy $path from $xmlobj $path
250
251                if {[info exists _path2widget($path)]} {
252                    set obj [$xmlobj element -as object $path]
253puts " sending $obj to $_path2widget($path)"
254                    $_path2widget($path) value $obj
255                }
256                if {[info exists _path2path($path)]} {
257puts " sending $path to $_path2path($path)"
258                    $_owner xml copy $_path2path($path) from $xmlobj $path
259                    set w [$_owner widgetfor $_path2path($path)]
260                    if {$w != ""} {
261                        set obj [$_owner xml element -as object $_path2path($path)]
262                        $w value $obj
263                    }
264                }
265            }
266            set _result $xmlobj
267        } else {
268            set status 1
269            set result "Can't find result file in output:\n\n$result"
270        }
271    }
272}
273
274# ----------------------------------------------------------------------
275# USAGE: abort
276#
277# Clients use this during a "run" to abort the current job.
278# Kills the job and forces the "run" method to return.
279# ----------------------------------------------------------------------
280itcl::body Rappture::Service::abort {} {
281    $_tool abort
282}
283
284# ----------------------------------------------------------------------
285# USAGE: clear
286#
287# Clears any result hanging around from the last run.
288# ----------------------------------------------------------------------
289itcl::body Rappture::Service::clear {} {
290    if {"" != $_result} {
291        foreach cname [$_result children output] {
292            set path output.$cname
293
294            if {[info exists _path2widget($path)]} {
295                $_path2widget($path) value ""
296            }
297        }
298        itcl::delete object $_result
299        set _result ""
300    }
301}
302
303# ----------------------------------------------------------------------
304# USAGE: _link <fromPath> <toPath>
305#
306# Used internally to link the value at <fromPath> in the outer tool
307# to the value at <toPath> for this service.  If this service is
308# automatic and <toPath> is an input, this also invokes the service.
309# ----------------------------------------------------------------------
310itcl::body Rappture::Service::_link {from to} {
311    $_tool xml copy $to from [$_owner xml object] $from
312    if {$_control == "auto" && [regexp -nocase {^input\.} $to]} {
313        after cancel [itcl::code $this run]
314        after idle [itcl::code $this run]
315    }
316}
Note: See TracBrowser for help on using the repository browser.