[Arjen Markus] (7 February 2016) Long-running calculations are sometimes needed to solve a problem. But if you have to wait for a calculation to finish after say a day of computing and then examine the results only to find out that you made a mistake in the input, you wish there was some way of monitoring the results while the calculation is going on. A technique for this is "online visualization": you adapt the program so that it produces graphical output while running. But that is not possible in all environment. Here is one such environment: you start the calculation on a computer that has no graphical facilities. Another problem: the program needs to be adapted and you may not be able to do so - graphical libraries tend to be highly system-dependent and what do you want to visualize in the first place? The two programs below are a first experiment to deal with both problems: the first reads the output files from the computational program directly (or at least that is the idea, in the code below it simply produces numbers via a simple mathematical formula). It does so upon request from the second program. The second program presents a straightforward GUI and plots the results as it gets them from the first program. The communication between the two is via sockets. So, you can just start the first program on the same computer as the computational program and start the visualization program on another one, close that program if you have seen enough and restart to see how far your computation progressed. Note 1: the demo is not perfect. Something odd happens when you press the Select button several times. I have not figured out yet why this happens. And the programs are demos ;). Note 2: as the data come in as they are calculated, there is no way to scale the vertical axis properly. This has to come from the user. Hence the "scale" entry. The server program - start this first: ====== # olvserver.tcl -- # Server for the online visualisation # # Accept -- # Accept a connection # # Arguments: # socket Socket to accept the connection on # ip IP address # args Additional arguments (not used) # proc Accept {socket ip args} { chan configure $socket -block false fileevent $socket readable [list getCommand $socket] } # CloseServer -- # Close down the server # # Arguments: # socket Server socket to close # proc CloseServer {socket} { close $socket exit } # getCommand -- # Read the comand that was sent and act on it # # Arguments: # channe; Channel to read from/write to # proc getCommand {channel} { global line if { ! [eof $channel] } { gets $channel line switch -- [lindex $line 0] { "GETTIME" { # Send start and stop time puts $channel "TIMESTART 0" puts $channel "TIMESTOP 1000" flush $channel } "GETPARAMETERS" { # Send the names of the parameters/substances foreach p {A B C} { puts $channel [list PARAMETERNAME $p] } flush $channel } "GETLOCATIONS" { # Send the names of the parameters/substances foreach l {loc1 loc2 loc3 loc4} { puts $channel [list LOCATIONNAME $l] } flush $channel } "GETVALUE" { set record [lindex $line 1] if { $record >= 0 && $record <= 50 } { set t [expr {20.0 * $record}] puts $channel "VALUE $t [expr {sin($t/100.0)}]" } else { puts $channel "NOVALUE" } flush $channel } "DONEINITIAL" { puts $channel "DONEINITIAL" flush $channel } "RETRIEVEVALUES" { lassign $line keyword substanceIdx locationIdx } } } else { close $channel } } # main -- # Start the server - for debugging/development purposes: # make sure that the socket is closed. # set port 8081 set socket [socket -server [list Accept] $port] wm protocol . WM_DELETE_WINDOW [list CloseServer $socket] pack [label .label -textvariable line -width 80] ====== The client program: ====== # onlinevis.tcl -- # Online visualisation and inspection for DELWAQ # package require Plotchart # SetupWindow -- # Set up the main window # # global variables # global substance ;# Chosen substance global substanceList ;# List of substances to choose from global location ;# Chosen location global locationList ;# List of locations to choose from global scale ;# Scale for the vertical axis global status ;# Text showing what the program is doing global plotParameters ;# Dictionary containing plot parameters # # Arguments: # None # proc SetupWindow {} { global substance global substanceList global location global locationList global status global scale # # Simple menu # menu .menu menu .menu.file -tearoff false .menu add cascade -label File -menu .menu.file . configure -menu .menu .menu.file add command -label Exit -underline 1 -command exit # # Dropdown buttons for the substance and the location # ttk::label .textSubstance -text "Parameter:" ttk::label .textLocation -text "Location:" ttk::label .textScale -text "Scale:" ttk::combobox .selectSubstance -values $substanceList -textvariable substance ttk::combobox .selectLocation -values $locationList -textvariable location ttk::entry .editScale -textvariable scale ttk::button .getData -text "Select" -command [list getNewData] # # Create the canvas window # canvas .cnv -width 800 -height 500 -bg white # # Status bar for messages # ttk::label .status -textvariable status -relief sunken # # Put them in the main window # grid .textSubstance .selectSubstance .textLocation .selectLocation .textScale .editScale .getData -padx 4 -pady 4 -sticky w grid .cnv - - - - - - -sticky news grid .status - - - - - - -sticky news } # ConnectHost -- # Connect to the server (list to port 8081) # # Arguments: # host Name of the host # proc ConnectHost {host} { global channel global status set channel [socket $host 8081] #after 10000 [list BreakConnection $channel] fileevent $channel readable [list getData $channel] set status "Retrieving parameter and location names ..." SendInitialCommands $channel } # SendInitialCommands -- # Send the initial commands to the server # # Arguments: # channel Channel to the server # proc SendInitialCommands {channel} { global status global plot global plotParameters puts $channel "GETPARAMETERS" puts $channel "GETLOCATIONS" puts $channel "GETTIME" puts $channel "DONEINITIAL" flush $channel set status "Ready" } # PlotData -- # PLot the data just received # # Arguments: # time Value for x-axis # value Value for y-axis # proc PlotData {time value} { global plot global scale global plotParameters if { $plot == "" } { set tstart [dict get $plotParameters timestart] set tstop [dict get $plotParameters timestop] set plot [::Plotchart::createXYPlot .cnv [::Plotchart::determineScale $tstart $tstop] \ [::Plotchart::determineScale 0.0 $scale]] } $plot plot data $time $value } # getData -- # Get data from the server # # Arguments: # channel Channel to the server # proc getData {channel} { global plotParameters global substanceList global locationList global substance global location global record global status if { ! [eof $channel] } { gets $channel line #puts $line switch -- [lindex $line 0] { "PARAMETERNAME" { lappend substanceList [lindex $line 1] } "LOCATIONNAME" { lappend locationList [lindex $line 1] } "TIMESTART" { dict set plotParameters timestart [lindex $line 1] } "TIMESTOP" { dict set plotParameters timestop [lindex $line 1] } "VALUE" { puts $line PlotData [lindex $line 1] [lindex $line 2] after 10 [list getNextValue $channel [incr record]] } "NOVALUE" { # We must wait set status "Waiting ..." after 100 [list getNextValue $channel $record] } "DONEINITIAL" { .selectSubstance configure -values $substanceList .selectLocation configure -values $locationList set substance [lindex $substanceList 0] set location [lindex $locationList 0] } } } else { close $channel } } # getNextValue -- # Send the command to retrieve the next value # # Arguments: # channel The channel to the server # record The index of the record to retrieve # proc getNextValue {channel record} { puts $channel "GETVALUE $record" flush $channel } # getNewData -- # Get the data that are available for the selected substance and location # # Arguments: # None # proc getNewData {} { global status global channel global substance global location global substanceList global locationList global record global plot set record 0 set substanceIdx [lsearch $substanceList $substance] set locationIdx [lsearch $locationList $location] puts $channel "RETRIEVEVALUES $substanceIdx $locationIdx" puts $channel "GETVALUE $record" flush $channel if { $plot != "" } { .cnv delete all $plot deletedata set plot "" } set status "Retrieving data ..." } # main -- # Start the program # if { [llength $::argv] == 0 } { tk_messageBox -type ok -message "Usage: onlinevis " exit } set plot {} set plotParameters {} set substanceList {} set substance {} set locationList {} set location {} set scale 1.0 set status "Waiting for connection ..." SetupWindow ConnectHost [lindex $argv 0] ====== <>Category Plotting|Category Visualization