dialog.tcl

Go to the documentation of this file.
00001 /*  -*- tcl -*-*/
00002 /*  Dialog - Dialog Demon (Server, or Client)*/
00003 /*  Copyright (c) 2004, Andreas Kupries <andreas_kupries@users.sourceforge.net>*/
00004 
00005 puts "- dialog (coserv-based)"
00006 
00007 /*  ### ### ### ######### ######### #########*/
00008 /*  Commands on top of a plain comm server.*/
00009 /*  Assumes that the comm server environment*/
00010 /*  is present. Provides set up and execution*/
00011 /*  of a fixed linear dialog, done from the*/
00012 /*  perspective of a server application.*/
00013 
00014 /*  ### ### ### ######### ######### #########*/
00015 /*  Load "comm" into the master.*/
00016 
00017 namespace ::dialog {
00018     variable dtrace    {}
00019 }
00020 
00021 /*  ### ### ### ######### ######### #########*/
00022 /*  Start a new dialog server.*/
00023 
00024 ret  ::dialog::setup (type type , type cookie) {
00025     variable id
00026     variable port
00027 
00028     switch -- $type {
00029     server  {set server 1}
00030     client  {set server 0}
00031     default {return -code error "Bad dialog type \"$type\", expected server, or client"}
00032     }
00033 
00034     set id [::coserv::start "$type: $cookie"]
00035     ::coserv::run $id {
00036     set responses {}
00037     set strace    {}
00038     set received  {}
00039     set conn      {}
00040     set ilog      {}
00041 
00042     proc Log {text} {
00043         global ilog ; lappend ilog $text
00044     }
00045     proc Strace {text} {
00046         global strace ; lappend strace $text
00047     }
00048     proc Exit {sock reason} {
00049         Strace $reason
00050         Log    [list $reason $sock]
00051         close  $sock
00052         Done
00053     }
00054     proc Done {} {
00055         global main strace ilog
00056         comm::comm send $main [list dialog::done [list $strace $ilog]]
00057         return
00058     }
00059     proc ClearTraces {} {
00060         global strace ; set strace {}
00061         global ilog   ; set ilog   {}
00062         return
00063     }
00064     proc Step {sock} {
00065         global responses trace
00066 
00067         if {![llength $responses]} {
00068         Exit $sock empty
00069         return
00070         }
00071 
00072         set now       [lindex $responses 0]
00073         set responses [lrange $responses 1 end]
00074 
00075         Log  [list ** $sock $now]
00076         eval [linsert $now end $sock]
00077         return
00078     }
00079 
00080     # Step commands ...
00081 
00082     proc .Crlf {sock} {
00083         Strace crlf
00084         Log crlf
00085         fconfigure $sock -translation crlf
00086         Step $sock
00087         return
00088     }
00089     proc .Binary {sock} {
00090         Strace bin
00091         Log binary
00092         fconfigure $sock -translation binary
00093         Step $sock
00094         return
00095     }
00096     proc .HaltKeep {sock} {
00097         Log halt.keep
00098         Done
00099         global responses
00100         set    responses {}
00101         # No further stepping.
00102         # This keeps the socket open.
00103         # Needs external reset/cleanup
00104         return
00105     }
00106     proc .Send {line sock} {
00107         Strace [list >> $line]
00108         Log    [list >> $line]
00109 
00110         if {[catch {
00111         puts  $sock $line
00112         flush $sock
00113         } msg]} {
00114         Exit $sock broken
00115         return
00116         }
00117         Step $sock
00118         return
00119     }
00120     proc .Geval {script sock} {
00121         Log geval
00122         uplevel #0 $script
00123         Step $sock
00124         return
00125     }
00126     proc .Eval {script sock} {
00127         Log eval
00128         eval $script
00129         Step $sock
00130         return
00131     }
00132     proc .SendGvar {vname sock} {
00133         upvar #0 $vname line
00134         .Send $line $sock
00135         return
00136     }
00137     proc .Receive {sock} {
00138         set aid     [after 10000 [list Timeout    $sock]]
00139         fileevent $sock readable [list Input $aid $sock]
00140         # No "Step" here. Comes through input.
00141         Log "   Waiting    \[$aid\]"
00142         return
00143     }
00144     proc Input {aid sock} {
00145         global received
00146         if {[eof $sock]} {
00147         # Clean the timer up
00148         after cancel $aid
00149         Exit $sock close
00150         return
00151         }
00152         if {[gets $sock line] < 0} {
00153         Log "   **|////|**"
00154         return
00155         }
00156 
00157         Log "-- -v-"
00158         Log "   Events off \[$aid, $sock\]"
00159         fileevent    $sock readable {}
00160         after cancel $aid
00161 
00162         Strace [list << $line]
00163         Log    [list << $line]
00164         lappend received $line
00165 
00166         # Now we can step further
00167         Step $sock
00168         return
00169     }
00170     proc Timeout {sock} {
00171         Exit $sock timeout
00172         return
00173     }
00174     proc Accept {sock host port} {
00175         fconfigure $sock -blocking 0
00176         ClearTraces
00177         Step $sock
00178         return
00179     }
00180 
00181     proc Server {} {
00182         global port
00183         # Start listener for dialog
00184         set listener [socket -server Accept 0]
00185         set port     [lindex [fconfigure $listener -sockname] 2]
00186         # implied return of <port>
00187     }
00188 
00189     proc Client {port} {
00190         global conn
00191         catch {close $conn}
00192 
00193         set conn [set sock [socket localhost $port]]
00194         fconfigure $sock -blocking 0
00195         ClearTraces
00196         Log [list Client @ $port = $sock]
00197         Log [list Channels $port = [lsort [file channels]]]
00198         Step $sock
00199         return
00200     }
00201     }
00202 
00203     if {$server} {
00204     set port [coserv::run $id {Server}]
00205     }
00206 }
00207 
00208 ret  ::dialog::runclient (type port) {
00209     variable id
00210     variable dtrace {}
00211     coserv::task $id [list Client $port]
00212     return
00213 }
00214 
00215 ret  ::dialog::dialog_set (type response_, type script) {
00216     begin
00217     uplevel 1 $response_script
00218     end
00219     return
00220 }
00221 
00222 ret  ::dialog::begin (optional cookie ={)} {
00223     variable id
00224     ::coserv::task $id [list set responses {}]
00225     log::log debug "+============================================ $cookie \\\\"
00226     return
00227 }
00228 
00229 ret  ::dialog::cmd (type command) {
00230     variable id
00231     ::coserv::task $id [list lappend responses $command]
00232     return
00233 }
00234 
00235 ret  ::dialog::end () {
00236     # This implicitly waits for all preceding commands (which are async) to complete.
00237     variable id
00238     set responses [::coserv::run $id [list set responses]]
00239     ::coserv::run $id {set received {}}
00240     log::log debug |\t[join $responses \n|\t]
00241     log::log debug +---------------------------------------------
00242     return
00243 }
00244 
00245 ret  ::dialog::crlf.      ()       {cmd .Crlf}
00246 ret  ::dialog::binary.    ()       {cmd .Binary}
00247 ret  ::dialog::send.      (type line)   {cmd [list .Send $line]}
00248 ret  ::dialog::receive.   ()       {cmd .Receive}
00249 ret  ::dialog::respond.   (type line)   {receive. ; send. $line}
00250 ret  ::dialog::request.   (type line)   {send. $line ; receive.}
00251 ret  ::dialog::halt.keep. ()       {cmd .HaltKeep}
00252 ret  ::dialog::sendgvar.  (type vname)  {cmd [list .SendGvar $vname]}
00253 ret  ::dialog::reqgvar.   (type vname)  {sendgvar. $vname ; receive.}
00254 ret  ::dialog::geval.     (type script) {cmd [list .Geval $script]}
00255 ret  ::dialog::eval.      (type script) {cmd [list .Eval  $script]}
00256 
00257 ret  ::dialog::done (type traces) {
00258     variable dtrace $traces
00259     return
00260 }
00261 
00262 ret  ::dialog::waitdone () {
00263     variable dtrace
00264 
00265     # Loop until we have data from the dialog subprocess.
00266     # IOW writes which do not create data are ignored.
00267     while {![llength $dtrace]} {
00268     vwait ::dialog::dtrace
00269     }
00270 
00271     foreach {strace ilog} $dtrace break
00272     set dtrace {}
00273 
00274     log::log debug  +---------------------------------------------
00275     log::log debug  |\t[join $strace \n|\t]
00276     log::log debug  +---------------------------------------------
00277     log::log debug  /\t[join $ilog \n/\t]
00278     log::log debug "+============================================ //"
00279     return $strace
00280 }
00281 
00282 ret  ::dialog::received () {
00283     # Wait for all preceding commands to complete.
00284     variable id
00285     set received [::coserv::run $id [list set received]]
00286     ::coserv::run $id [list set received {}]
00287     return $received
00288 }
00289 
00290 ret  ::dialog::listener () {
00291     variable port
00292     return $port
00293 }
00294 
00295 ret  ::dialog::shutdown () {
00296     variable id
00297     variable port
00298     variable dtrace
00299 
00300     ::coserv::shutdown $id
00301 
00302     set id     {}
00303     set port   {}
00304     set dtrace {}
00305     return
00306 }
00307 
00308 /*  ### ### ### ######### ######### #########*/
00309 

Generated on 21 Sep 2010 for Gui by  doxygen 1.6.1