swish/commit
New upstream files
author | Jan Wielemaker |
---|---|
Fri Aug 19 15:40:00 2016 +0200 | |
committer | Jan Wielemaker |
Fri Aug 19 15:40:00 2016 +0200 | |
commit | 14b6ce7103f206f18414c4d796f26ec969163b93 |
tree | 506758bcec9352d21395438e330190b64f596f7e |
parent | e8cde04e641642fd7879ce71f4a5980e9ae320c2 |
Diff style: patch stat
diff --git a/client/README.md b/client/README.md index e769a44..f666370 100644 --- a/client/README.md +++ b/client/README.md @@ -41,3 +41,16 @@ The Pengines infrastructure is designed to make JavaScript talk to Prolog servers. The file [sin-table.html](sin-table.html) illustrates this. +There an __NPM__ [package](https://www.npmjs.com/package/pengines) + +## Extracting results using Java + +Check out [JavaPengines](https://github.com/Anniepoo/JavaPengine) + +## Extracting results using Ruby + +Check out [RubyPengines](https://github.com/simularity/RubyPengine) + +--- +If you write or find another client, please make a pull request for this +page! diff --git a/client/swish-ask.sh b/client/swish-ask.sh index 5d19b6a..204ce6e 100755 --- a/client/swish-ask.sh +++ b/client/swish-ask.sh @@ -7,6 +7,7 @@ server=${SWISH_SERVER-http://localhost:3020} srctext= +curlarg= format=${SWISH_FORMAT-rdf} program=$(basename $0) @@ -57,6 +58,10 @@ while [ $done = false ]; do esac shift ;; + https://*.pl|http://*.pl) + curlarg+=" -d src_url=$1" + shift + ;; *.pl) script=$(echo $1 | sed 's/.*=//') srctext+=":- include('$script'). " @@ -84,4 +89,5 @@ curl -s \ -d format=csv \ -d chunk=10 \ -d solutions=all \ + $curlarg \ $server/pengine/create diff --git a/examples/Rdataframe.swinb b/examples/Rdataframe.swinb new file mode 100644 index 0000000..5a3a33f --- /dev/null +++ b/examples/Rdataframe.swinb @@ -0,0 +1,73 @@ +<div class="notebook"> + +<div class="nb-cell markdown"> +# Dealing with R data frames + +Data frames are a central concept in R. A data frame is a 2-dimensional matrix with optional +column and row names. The data is (normally) column-oriented. This differs from the row-oriented +view on data in Prolog, for example as a set of solutions for a predicate. The library(r_data) provides predicates for creating and accessing R data frames. + +## Creating a data frame from solutions + +Below we define a relation sin/3 based on the sine function and turn the resulting solutions into a data frame called `df`. The subsequent examples plot the relation using _ggplot2_ and provide some timing for exchanging large datasets. +</div> + +<div class="nb-cell program"> +% Y is sin(X) for X in 0..Max +sin(Max, X, Y) :- + between(0, Max, X), + Y is sin(X*pi/180). +</div> + +<div class="nb-cell query"> +r_data_frame(df, [x=X,y=Y], sin(10, X, Y)), +<- df. +</div> + +<div class="nb-cell query"> +time(r_data_frame(df, [x=X,y=Y], sin(360, X, Y))), +time(<- library("ggplot2")), +time(<- ggplot(data=df, aes(x=x, y=y)) + geom_line()). +</div> + +<div class="nb-cell query"> +time(r_data_frame(df, [x=X,y=Y], sin(1 000 000, X, Y))). +</div> + +<div class="nb-cell query"> +time(r_data_frame(df, [x=X,y=Y], sin(1 000 000, X, Y))), +time(r_data_frame_to_dicts(df, _Dicts)). +</div> + +<div class="nb-cell markdown"> +## Importing data frames to Prolog + +The predicates r_data_frame_to_dicts/2 and r_data_frame_to_rows/3 translate the column-oriented data frame to a list of row oriented dicts or terms. We use the example on the predefined R `mtcars` data frame. +</div> + +<div class="nb-cell program"> +:- use_rendering(table). +</div> + +<div class="nb-cell query" data-tabled="true"> +r_data_frame_to_dicts(mtcars, Dicts). +</div> + +<div class="nb-cell query" data-tabled="true"> +r_data_frame_rownames(mtcars, Rows). +</div> + +<div class="nb-cell markdown"> +## Documentation + +Below is the reference documentation for the predicates in library(r_data). + + - [[r_data_frame/3]] + - [[r_data_frame_from_rows/2]] + - [[r_data_frame_to_dicts/2]] + - [[r_data_frame_to_rows/3]] + - [[r_data_frame_colnames/2]] + - [[r_data_frame_rownames/2]] +</div> + +</div> diff --git a/examples/Rdownload.swinb b/examples/Rdownload.swinb new file mode 100644 index 0000000..215149c --- /dev/null +++ b/examples/Rdownload.swinb @@ -0,0 +1,56 @@ +<div class="notebook"> + +<div class="nb-cell markdown"> +# Downloading (graphics) files + +The SWISH R interface defines the predicates below for downloading files. This can be combined with the normal R device manipulation for downloading images. + + - [[r_download/0]] + - [[r_download/1]] + +## Download a simple plot + +The example illustrates the options using the sine function as defined below. +</div> + +<div class="nb-cell program"> +% Y is sin(X) for X in 0..Max +sin(Max, X, Y) :- + between(0, Max, X), + Y is sin(X*pi/180). +</div> + +<div class="nb-cell markdown"> +First, we create an [R dataframe](example/Rdataframe.swinb), load the "ggplot2" library and show the inline SVG. +</div> + +<div class="nb-cell query"> +r_data_frame(df, [x=X,y=Y], sin(360, X, Y)), +<- library("ggplot2"), +<- ggplot(data=df, aes(x=x, y=y)) + geom_line(). +</div> + +<div class="nb-cell markdown"> +In the next examples we use r_download/0. This displays the graphics inline, but also provides a download button that allows you to download the SVG to your computer. +</div> + +<div class="nb-cell query"> +r_data_frame(df, [x=X,y=Y], sin(360, X, Y)), +<- library("ggplot2"), +<- ggplot(data=df, aes(x=x, y=y)) + geom_line(), +r_download. +</div> + +<div class="nb-cell markdown"> +And finally, we save the graphics to a device (in this example PDF) that we create explicitly. Note that r_download/0 calls `graphics.off()` before trying to download the generated files. +</div> + +<div class="nb-cell query"> +<- pdf("sine.pdf"), +r_data_frame(df, [x=X,y=Y], sin(360, X, Y)), +<- library("ggplot2"), +<- ggplot(data=df, aes(x=x, y=y)) + geom_line(), +r_download. +</div> + +</div> diff --git a/examples/Rserve.swinb b/examples/Rserve.swinb new file mode 100644 index 0000000..3973352 --- /dev/null +++ b/examples/Rserve.swinb @@ -0,0 +1,169 @@ +<div class="notebook"> + +<div class="nb-cell markdown"> +# R/SWISH demos + +R for SWISH provides an interface to [Rserve](https://rforge.net/Rserve/) where the access predicates are inspired by [Real](http://stoics.org.uk/~nicos/sware/real/) by [Nicos Angelopoulos](http://stoics.org.uk/~nicos/). The interface defines two predicates and a [quasi quotation](http://www.swi-prolog.org/pldoc/man?section=quasiquotations): + + $ Var <- Expression : + Evaluate _Expression_ and bind the result to _Var_. _Var_ can both be a Prolog + variable, which causes the R expression to be converted into Prolog or an + R variable (atom), which causes the result to be bound to an R variable. + $ <- Expression : + Evaluate _Expression_ and display the resulting R console output. + $ {|r(Arg ...)||R-code|} : + In this quasi quotation, `r` specifies the syntax. `Arg ...` is a list of + Prolog terms that are translated to R and bound to an R variable with the + same name. `R-code` is arbitrary R code. This may contain any characters + except for the `|}` termination sequence. + +Above, *Expression* is either a Prolog term or a quasi quotation. If it is a +Prolog term, it is translated into R using these rules: + + - An identifier (atom with chars valid for R identifiers) are emitted verbatim. + - The Prolog atoms `true` and `false` are translated into the R expressions + =TRUE= and =FALSE=. + - A Prolog string is translated into an R string. Note that R strings may be + written as `"string"` or `'string'`, while in Prolog `'string'` is an atom, + which is treated as an identifier. Thus, use double quoted Prolog strings + to create a string in R. + - Numbers are translated into R numbers. + - A term =|functor(Arg ...)|= is translated into an R-function call. + - The R operators are supported: =|+, -, *, /, mod, '%%', ^, + >=, >, ==, <, <=, =<, \=, '!=', :, <-|= + - =|A$B|= and =|A[I]|= are supported + - Goal expansion is provided to turn =|a.b|= and =|a.b()|= into valid syntax. +</div> + +<div class="nb-cell markdown"> +## Exchange data between Prolog and R + +Data may be transferred using the assignment operator `Target <- Source`, where `Source` is the Prolog representation of an R expression. `Target` is either a Prolog variable, transferring the value of an R expression to Prolog, or an R variable or expression, transferring a Prolog expression to R. + +First we illustrate exchanging R data to Prolog. Note that the expression can both be the Prolog representation of an R expression or R source represented as a quasi quotation. +</div> + +<div class="nb-cell query"> +A <- 1:10. +</div> + +<div class="nb-cell query"> +A <- {|r||1:10|}. +</div> + +<div class="nb-cell markdown"> +Next, we transfer data from Prolog to R. We compute the mean and return it to show that `a <- List` actuall binds the R variable `a`. +</div> + +<div class="nb-cell query"> +numlist(0, 10, List), +a <- List, +Mean <- mean(a). +</div> + +<div class="nb-cell markdown"> +The left side of the assignment can be any valid R term as illustrated below, where we assign the column names of a data frame. See [Data frame](example/Rdataframe.swinb) for more high level operations on data frames. +</div> + +<div class="nb-cell query"> +df <- data.frame([100,200,300]), +colnames(df) <- ["hundreds"], +<- df. +</div> + +<div class="nb-cell markdown"> +## Simple plot examples + +First, we make some trivial plots using an R vector, Prolog list and a _quasi quotation_. +</div> + +<div class="nb-cell query"> +<- plot(c(1,2,3)). +</div> + +<div class="nb-cell query"> +<- plot([1,2,3,4]). +</div> + +<div class="nb-cell query"> +{|r||plot(c(1,2,3))|}. +</div> + +<div class="nb-cell query"> +numlist(1, 25, Data), +{|r(Data)||plot(Data)|}. +</div> + +<div class="nb-cell markdown"> +## Performance tests for transferring data + +Below we compare computing the mean from a list of 1M integers through R as well as natively in Prolog. It turns out R does the job faster on a large number of integers than Prolog. Why? Once in R, the array is a simple C array of integers, causing a blindly fast addition. The Prolog counterpart adds the numbers one-by-one and does rigid overflow checking and handling. +</div> + +<div class="nb-cell query"> +numlist(1, 1 000 000, _L), +time(A <- mean(_L)). +</div> + +<div class="nb-cell query"> +numlist(1, 1 000 000, _L), +time(sum_list(_L, Sum)). +</div> + +<div class="nb-cell markdown"> +## Show error handling +</div> + +<div class="nb-cell query"> +A <- {|r||a b|}. +</div> + +<div class="nb-cell markdown"> +## Using ggplot2 to render plots + +The library("ggplot2") can be used for rendering plots. Unlike native plot() however, the call should be made with <-/1 because ggplot relies on console output. We first give the example using an R quasi quotation, followed by using a Prolog term. Note that + + - Quasi quotations can be used to copy/paste R code into your Prolog + program without changing it (except when it contains =||}|=). + - Prolog code may need minor modifications. In the example, =|I(.5)|= + must be changed to `'I'(0.5)` because Prolog floats cannot start with + a =|.|= and functors (used as function symbols) cannot start with a + capital. The Prolog approach however + - Is better portable. + - Profits from Prolog syntax checking. + - Makes it easier to compose R expressions from smaller elements. +</div> + +<div class="nb-cell program"> +:- <- library("ggplot2"). +</div> + +<div class="nb-cell query"> +<- {|r||qplot(mpg, data=mtcars, geom="density", fill=gear, alpha=I(.5), main="Distribution of Gas Milage", xlab="Miles Per Gallon", ylab="Density")|}. +</div> + +<div class="nb-cell query"> +<- qplot(mpg, data=mtcars, geom="density", fill=gear, alpha='I'(0.5), main="Distribution of Gas Milage", xlab="Miles Per Gallon", ylab="Density"). +</div> + +<div class="nb-cell markdown"> +## Query and handle data frames + +This shows how to print a data frame and how to get R data into Prolog. Note that the interface merely fetches the matrix as a nested list of columns. The library(r/r_data) provides utilites for creating and fetching R data frames from Prolog. This [notebook](example/Rdataframe.swinb) illustrates the library. +</div> + +<div class="nb-cell program" data-background="true"> +:- use_rendering(table). +</div> + +<div class="nb-cell query"> +<- mtcars. +</div> + +<div class="nb-cell query"> +MtCars <- mtcars, +Cols <- colnames(mtcars), +Rows <- rownames(mtcars). +</div> + +</div> diff --git a/examples/swish_tutorials.swinb b/examples/swish_tutorials.swinb index e118ec1..6678834 100644 --- a/examples/swish_tutorials.swinb +++ b/examples/swish_tutorials.swinb @@ -10,4 +10,20 @@ This notebook provides an overview of tutorials about using SWISH. - [Access the SWISH interface from Prolog](example/jquery.swinb) </div> +<div class="nb-cell markdown"> +## Embedded R support + +The [R project](https://www.r-project.org/) provides statistical computing and data vizualization. SWISH can access R through [Rserve](https://rforge.net/Rserve/). The *prototype* client for Rserve is available as the _pack_ [rserve_client](http://www.swi-prolog.org/pack/list?p=rserve_client). The GitHub repository [rserve-sandbox](https://github.com/JanWielemaker/rserve-sandbox) provides the matching Rserve server as a [Docker](https://www.docker.com/) specification. + +The notebooks below explain the basics of using R from SWISH. You can test whether R is available from this server by running the query below. + + - [Basic access to R from SWISH](example/Rserve.swinb) + - [Exchanging R data frames](example/Rdataframe.swinb) + - [Downloading (graphics) files](example/Rdownload.swinb) +</div> + +<div class="nb-cell query"> +<- 'R.Version'(). +</div> + </div> diff --git a/lib/swish/download.pl b/lib/swish/download.pl new file mode 100644 index 0000000..ff87a55 --- /dev/null +++ b/lib/swish/download.pl @@ -0,0 +1,76 @@ +/* Part of SWI-Prolog + + Author: Jan Wielemaker + E-mail: J.Wielemaker@cs.vu.nl + WWW: http://www.swi-prolog.org + Copyright (C): 2016, VU University Amsterdam + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + As a special exception, if you link this library with other files, + compiled with a Free Software compiler, to produce an executable, this + library does not by itself cause the resulting executable to be covered + by the GNU General Public License. This exception does not however + invalidate any other reasons why the executable file might be covered by + the GNU General Public License. +*/ + +:- module(download, + [ download_button/2 % +Data, +Options + ]). +:- use_module(library(pengines)). +:- use_module(library(option)). +:- use_module(library(http/mimetype)). + +/** <module> Provide data for downloading +*/ + +%% download_button(+Data:string, +Options) +% +% Emit a button in the SWISH output window for downloading Data. +% The provided data is associated with the button and (thus) not +% stored on the server. A small tests indicates this works fairly +% well up to several tens of megabytes. +% +% Options: +% +% - name(+Name) +% (Base-)Name of the file created (default: 'swish-download') +% - ext(+Ext) +% Extension for the file (default: 'dat') +% - encoding(+Enc) +% Encoding to use. One of `utf8` or `octet`. default is `utf8` +% +% @see https://en.wikipedia.org/wiki/Data_URI_scheme + +download_button(Data, Options) :- + option(filename(FileName), Options, 'swish-download.dat'), + file_mime_type(FileName, Major/Minor), + atomics_to_string([Major, Minor], /, ContentType), + option(encoding(Enc), Options, utf8), + encode_data(Enc, Data, CharSet, EncData), + pengine_output( + json{action:downloadButton, + content_type:ContentType, + data:EncData, + filename:FileName, + charset:CharSet + }). + +encode_data(utf8, Data, "charset=UTF-8", Data). +encode_data(octet, Data, "base64", Data64) :- + string_codes(Data, Codes), + phrase(base64(Codes), Codes64), + string_codes(Data64, Codes64). diff --git a/lib/swish/r_swish.pl b/lib/swish/r_swish.pl new file mode 100644 index 0000000..1ec8e59 --- /dev/null +++ b/lib/swish/r_swish.pl @@ -0,0 +1,231 @@ +/* Part of SWI-Prolog + + Author: Jan Wielemaker + E-mail: J.Wielemaker@cs.vu.nl + WWW: http://www.swi-prolog.org + Copyright (C): 2016, VU University Amsterdam + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + As a special exception, if you link this library with other files, + compiled with a Free Software compiler, to produce an executable, this + library does not by itself cause the resulting executable to be covered + by the GNU General Public License. This exception does not however + invalidate any other reasons why the executable file might be covered by + the GNU General Public License. +*/ + +:- module(r_swish, + [ r_download/0, % Download all + r_download/1 % +File + ]). +:- use_module(library(pengines)). +:- use_module(library(debug)). +:- use_module(library(error)). +:- use_module(library(apply)). +:- use_module(library(http/html_write)). +:- use_module(library(http/js_write)). + +% We publish to the user module to avoid autoloading `real'. +:- use_module(user:library(r/r_call)). +:- use_module(user:library(r/r_data)). + +:- use_module(download). + +/** <Module> Bind Rserve to SWISH + +The user must provide the file search path =rserve= to local the R +connection library. +*/ + +:- multifile + r_call:r_console/2, + r_call:r_display_images/1. + +%% r_call:r_console(+Stream, ?Data) +% +% Relay Rserve captured output to SWISH using writeln. + +r_call:r_console(stdout, []) :- !. +r_call:r_console(stdout, Strings) :- + atomics_to_string(Strings, "\n", String), + send_html(pre(class(['R', console]), String)). + +send_html(HTML) :- + phrase(html(HTML), Tokens), + with_output_to(string(HTMlString), print_html(Tokens)), + pengine_output(HTMlString). + +%% r_call:r_display_images(+Images) +% +% Relay received images to the SWISH console using +% pengine_output/1. + +r_call:r_display_images(Images) :- + svg_html(Images, HTMlString), + pengine_output(HTMlString). + +%% svg_html(+Images, -HTMlString) is det. +% +% Turn a list of SVG images into an HTML string. + +svg_html(Images, HTMlString) :- + phrase(svg_html(Images), Tokens), + with_output_to(string(HTMlString), print_html(Tokens)). + +svg_html(Images) --> + html(div(class('Rplots'), \rplots(Images))). + +rplots([]) --> []. +rplots([H|T]) --> + html(div(class(['reactive-size', 'R', svg]), \plot(H, []))), + rplots(T). + + +plot(svg(SVG), _Options) --> !, + html(\[SVG]), + pan_zoom, + "". +plot(Term, _Options) --> !, + { domain_error(image, Term) }. + +%% pan_zoom +% +% Add pan and soom behaviour to embedded SVG. This function also +% renames the `id` attribute and their references. +% +% @bug We need a generic way to fix all references to the ID. +% Is there a list of such attributes? +% @bug Instead of `"use"`, we should use `"[xlink\\:href]"`, +% but this does not seem to work!? +% @bug When generalised, this could move into runner.js. + +pan_zoom --> + html(\js_script({|javascript|| +var svg = node.node().find("svg"); +var data = { w0: svg.width(), + h0: svg.height() + }; +var pan; + +function fixIDs(node, prefix1) { + var i=0; + node.each(function() { + var prefix = prefix1+(i++)+"_"; + var img = $(this); + var hprefix = "#"+prefix; + var re = /(url\()#([^)]*)(\))/; + + img.find("[id]").each(function() { + var elem = $(this); + elem.attr("id", prefix+elem.attr("id")); + }); + img.find("use").each(function() { + var elem = $(this); + var r = elem.attr("xlink:href"); + if ( r.charAt(0) == "#" ) + elem.attr("xlink:href", hprefix+r.slice(1)); + }); + img.find("[clip-path]").each(function() { + var elem = $(this); + var r = elem.attr("clip-path").match(re); + if ( r.length == 4 ) + elem.attr("clip-path", r[1]+hprefix+r[2]+r[3]); + }); + }); +} + +fixIDs(svg, "N"+node.unique_id()+"_"); + +function updateSize() { + var w = svg.closest("div.Rplots").innerWidth(); + console.log(data.w0, w); + + function reactive() { + if ( !data.reactive ) { + var div = svg.closest("div.reactive-size"); + data.reactive = true; + div.on("reactive-resize", updateSize); + } + } + + reactive(); + w = Math.max(w*0.95, 100); + if ( w < data.w0 ) { + svg.width(w); + svg.height(w = Math.max(w*data.h0/data.w0, w/4)); + if ( pan ) { + pan.resize(); + pan.fit(); + pan.center(); + } + } +} + +require(["svg-pan-zoom"], function(svgPanZoom) { + updateSize() + pan = svgPanZoom(svg[0], { + maxZoom: 50 + }); +}); + |})). + + +%% r_download +% +% Provide download buttons for all created files. First calls the +% R function `graphics.off()` to close all graphics devices. + +r_download :- + nb_current('R', _), !, + <- graphics.off(), + Files <- list.files(), + maplist(r_download, Files). +r_download. + +%% r_download(File) +% +% Provide a download button for the indicates file. + +r_download(File) :- + nb_current('R', _), !, + catch(r_read_file($, File, Content), E, + r_error(E, File)), + ( debugging(r(file)) + -> string_length(Content, Len), + debug(r(file), 'Got ~D bytes from ~p', [Len, File]) + ; true + ), + file_name_extension(_Name, Ext, File), + download_encoding(Ext, Enc), + download_button(Content, + [ filename(File), + encoding(Enc) + ]). +r_download(File) :- + existence_error(r_file, File). + +r_error(error(r_error(70),_), File) :- !, + existence_error(r_file, File). +r_error(Error, _) :- throw(Error). + +download_encoding(svg, utf8) :- !. +download_encoding(csv, utf8) :- !. +download_encoding(_, octet). + +:- multifile sandbox:safe_primitive/1. + +sandbox:safe_primitive(r_swish:r_download). +sandbox:safe_primitive(r_swish:r_download(_)). diff --git a/lib/swish/render/graphviz.pl b/lib/swish/render/graphviz.pl index 74d4329..1a93fcc 100644 --- a/lib/swish/render/graphviz.pl +++ b/lib/swish/render/graphviz.pl @@ -28,7 +28,8 @@ */ :- module(swish_render_graphviz, - [ term_rendering//3 % +Term, +Vars, +Options + [ term_rendering//3, % +Term, +Vars, +Options + svg//2 % +String, +Options ]). :- use_module(library(http/html_write)). :- use_module(library(http/js_write)). @@ -147,8 +148,19 @@ render_dot(DOTString, Program, _Options) --> % <svg> rendering -> html(div([ class(['render-graphviz', 'reactive-size']), 'data-render'('As Graphviz graph') ], - [ \[SVG], - \js_script({|javascript|| + \svg(SVG, []))) + ; html(div(style('color:red;'), + [ '~w'-[Program], ': ', Error])) + ). + +%% svg(+SVG:string, +Options:list)// +% +% Include SVG as pan/zoom image. Must be embedded in a <div> with +% class 'reactive-size'. + +svg(SVG, _Options) --> + html([ \[SVG], + \js_script({|javascript|| (function() { if ( $.ajaxScript ) { var div = $.ajaxScript.parent(); @@ -191,10 +203,8 @@ render_dot(DOTString, Program, _Options) --> % <svg> rendering } })(); |}) - ])) - ; html(div(style('color:red;'), - [ '~w'-[Program], ': ', Error])) - ). + ]). + %% data_to_graphviz_string(+Data, -DOTString, -Program) is semidet. % diff --git a/lib/swish/swish_debug.pl b/lib/swish/swish_debug.pl index 0e80797..978ce93 100644 --- a/lib/swish/swish_debug.pl +++ b/lib/swish/swish_debug.pl @@ -204,9 +204,17 @@ stats_ring(year, 5). swish_stats(Name, Ring, Stats) :- thread_self(Me), - thread_send_message(Name, Me-get_stats(Ring)), + catch(thread_send_message(Name, Me-get_stats(Ring)), E, + stats_died(Name, E)), thread_get_message(get_stats(Ring, Stats)). +stats_died(Alias, E) :- + print_message(error, E), + thread_join(Alias, Status), + print_message(error, swish_stats(died, Status)), + start_swish_stat_collector, + fail. + stat_collect(Dims, Interval) :- new_sliding_stats(Dims, SlidingStat), get_time(Now),