Transport independent send

Moved from the tkinspect page


JCW 7-4-2002: Pat, would there be a way to generalize things further? I'm looking for a way to streamline cross-platform work, especially ad-hoc, interactive work. There's a lot of flexibility in using TkInspect and TkCon to run in one's preferred environment, and having it connect to instances of Tcl elsewhere. You've already taken send, comm, and dde further, which seems like a great way forward.

First to add one more option: Matt Newman at one point wrote a "rexec" daemon, which I've been using occasionally - it lets you run a server on any platform, and then use standard "rexec" on Unix to execute commands in it, Tcl commands that is. The code is now in SDX (in sdarchive, [L1 ]) - type "sdx help rexec" to get a brief blurb.

There may also be a telnetd emulation somewhere, which does the same (and run tcl commands, as opposed to shell commands). Not sure, I couldn't find it anymore last time I looked.

But what if one were to decouple transport mechanisms from functional use? A sort of ODBC for network requests. What RPC and Corba do, and SOAP, and email for that matter. The point is not to create yet another layer, and simply add to the clutter, but to do this in terms of "interfaces", i.e. if you initialize with one package you get comm, initialize another and you get rexec, etc.

Not sure TkInspect is the right vehicle, but with such a general mechanism, one could do all sorts of things. I'd build a bridge to drive Windows builds running as VMware task on my Linux box, and totally rethink the Tequila shared array server, to name a few things I'm involved in...

LV It seems to me that an ideal situation would be to have one one interface to which people wrote, then somehow have the package underneath take a crack at determining what protocol to use, with of course a method of providing the package some concrete guidelines if the programmer so desired. This would allow one to just get on with the programming (one of the common attributes of Tcl related packages) but also provide the expert who knows better the means to fine tune, etc.

PT I'm not sure what we are after here. The interface here consists of two parts: first the send app cmd ?arg arg ...? method which is the client side of Tk's RPC; second the registration and inquiry methods winfo interps and tk appname There is also something needed that provides the server end of send.

Could we arrange for client code to do package require send and then just do send ... and not worry about the transport? Difficult I should think. We can happily map the send functionality over various transports. dde, X protocol, and comm (TCP sockets) are available, winsend provides this using Microsoft's COM and I could very easilly do the same using SOAP and that rexecd code. Some of these require user intervention to specify the available servers while others (dde, X protocol and winsend) can provide a list of interpreters. hmmmmmm.... I dunno - I'm thinking aloud really - send is an extremely useful capability. I don't really know how you'd generalise it though. I note that doing send over comm is quite a bit slower than over dde or COM.


PT writes: I've been looking at getting tkcon to use other transports to communicate with foreign interps under windows (something it's not good at right now). Part of this involves abstracting out the send functions hence the code below. The point of this is that if you do package require dde followed by send::interps then we will get all registered interps that are using the dde protocol. If we also then load in the comm package we get them too. As much as possible the correct protocol is selected automatically.

 # send.tcl - Copyright (C) 2002 Pat Thoyts <[email protected]>
 #
 # General send command
 #
 # This provides an abstraction of send, appname and interps that works for
 # all available send mechanisms. This is especially useful for Windows where
 # the usual Tk send is not available.
 #
 # We attempt to use the most efficient transport available. If the normal Tk
 # send command is present then this will be used. Otherwise we try winsend,
 # then dde for windows before defaulting to use comm.
 #
 # The interps command will try and build a list from all available transports.
 #
 # The appname command will try to apply the new name to all transports and
 # will read it from them all as well.
 #
 
 namespace eval send {
     variable version 1.0
     variable rcsid {$Id: 8198,v 1.4 2006-01-04 07:00:32 jcw Exp $}
 
     namespace export send interps appname
 }
 
 # Try using Tk send first, then look for a winsend interp,
 # then try dde and finally have a go at comm
 proc send::send {args} {
     array set opts [list displayof {} async 0]
     while {[string match -* [lindex $args 0]]} {
         switch -exact -- [lindex $args 0] {
             -displayof { set opts(displayof) [Pop args 1] }
             -async     { set opts(async) 1 }
             -- { Pop args ; break }
             default {
                 return -code error "bad option \"[lindex $args 0]\":\
                     should be -displayof, -async or --"
             }
         }
         Pop args
     }    
     set app [Pop args]
 
     if {[info command ::winfo] != {} 
         && [lsearch -exact [::winfo interps] $app] > -1} {
         set cmd ::send
         if {$opt(async) == 1} {append cmd " -async"}
         if {$opt(displayof) != {}} {append cmd " -displayof $opt(displayof)" }
         eval $cmd [list $app] $args
     } elseif {[info command ::winsend] != {}
               && [lsearch -exact [::winsend interps] $app] > -1} {
         eval ::winsend send [list $app] $args
     } elseif {[info command ::dde] != {}
               && [lsearch -exact [dde services TclEval {}] \
                       [list TclEval $app]] > -1} {
         eval ::dde eval [list $app] $args
     } elseif {[package provide comm] != {} 
               && [string is integer $app]} {
         eval ::comm::comm send [list $app] $args
     } else {
         return -code error "bad interp: \"$app\" could not be found"
     }
 }
 
 proc send::interps {args} {
     array set opts [list displayof {}]
     while {[string match -* [lindex $args 0]]} {
         switch -exact -- [lindex $args 0] {
             -displayof { set opts(displayof) [Pop args 1] }
             --         { Pop args ; break }
             default {
                 return -code error "bad option \"[lindex $args 0]\":\
                     should be -displayof or --"
             }
         }
         Pop args
     }
 
     set interps {}
     if {[info command ::winfo] != {}} {
         set cmd "::winfo interps"
         if {$opts(displayof) != {}} {           
             append cmd " -displayof $opts(displayof)"
         }
         set interps [concat $interps [eval $cmd]]
     }
     if {[info command ::winsend] != {}} {
         set interps [concat $interps [::winsend interps]]
     }
     if {[info command ::dde] != {}} {
         set server {}
         foreach server [::dde services TclEval {}] {
             lappend servers [lindex $server 1]
         }
         set interps [concat $interps $servers]
     }
     if {[package provide comm] != {}} {
         set interps [concat $interps [::comm::comm self]]
     }
     return $interps
 }
 
 proc send::appname {{newname {}}} {
     set appname {}
     if {[info command ::tk] != {}} {
         set appname [eval ::tk appname $newname]
     }
     if {[info command ::winsend] != {}} {
         set appname [concat $appname [eval ::winsend appname $newname]]
     }
     if {[info command ::dde] != {}} {
         set appname [concat $appname [eval ::dde servername $newname]]
     }
     # comm?
     return [lsort -unique $appname]
 }
 
 proc send::Pop {varname {nth 0}} {
     upvar $varname args
     set r [lindex $args $nth]
     set args [lreplace $args $nth $nth]
     return $r
 }
 
 package provide send $send::version
 
 # Local variables:
 #   mode: tcl
 #   indent-tabs-mode: nil
 # End: