[Sciviews-commits] r310 - in pkg/svSocket: . R inst/etc man

noreply at r-forge.r-project.org noreply at r-forge.r-project.org
Sat Sep 25 09:31:45 CEST 2010


Author: phgrosjean
Date: 2010-09-25 09:31:45 +0200 (Sat, 25 Sep 2010)
New Revision: 310

Added:
   pkg/svSocket/R/socketClientConnection.R
   pkg/svSocket/man/socketClientConnection.Rd
Modified:
   pkg/svSocket/NAMESPACE
   pkg/svSocket/NEWS
   pkg/svSocket/R/parSocket.R
   pkg/svSocket/R/processSocket.R
   pkg/svSocket/R/startSocketServer.R
   pkg/svSocket/inst/etc/ReadMe.txt
   pkg/svSocket/man/parSocket.Rd
Log:
processSocket() modified to call parseText()
A new socketclientconn R connection is added
A slight modification in parSocket() is also done

Modified: pkg/svSocket/NAMESPACE
===================================================================
--- pkg/svSocket/NAMESPACE	2010-09-25 07:23:55 UTC (rev 309)
+++ pkg/svSocket/NAMESPACE	2010-09-25 07:31:45 UTC (rev 310)
@@ -11,4 +11,7 @@
        closeSocketClients,
        startSocketServer,
        stopSocketServer,
-	   svTaskCallbackManager)
+	   svTaskCallbackManager,
+	   socketClientConnection)
+
+S3method(summary, sockclientconn)
\ No newline at end of file

Modified: pkg/svSocket/NEWS
===================================================================
--- pkg/svSocket/NEWS	2010-09-25 07:23:55 UTC (rev 309)
+++ pkg/svSocket/NEWS	2010-09-25 07:31:45 UTC (rev 310)
@@ -4,7 +4,24 @@
 
 * processSocket() now calls parseText() from svMisc >= 0.9-60 instead of Parse().
 
+* When Echo is TRUE and we are not in hidden mode, results are echoed directly
+  in the R console as they are available, and not any more at the end of the
+  calculation.
+  
+* A new type of connection is added: a 'sockclientconn' that allows to redirect
+  output (append or write-only, for the moment) to a SciViews socket client. It
+  is created by using socketClientConnection() and has a specific summary()
+  method. It inherits from a 'sockconn' object and should behave similarly.
+  
+* parSocket() has a new argument, clientsocket, that allows to pass the Tcl name
+  of the client's socket. This is required to use socketClientConnection() by
+  providing only the client's name (and thus, the required Tcl socket name is
+  obtained through the property parSocket(....)$clientsocket, if it was
+  previously recorded). The default process function, processSocket() is changed
+  to record the Tcl socket in parSocket() each time a client connects to the
+  server and sends its first command through it.
 
+
 == Changes in svSocket 0.9-49
 
 * Small change in startSocketServer(): the Tcl/Tk callback function now calls

Modified: pkg/svSocket/R/parSocket.R
===================================================================
--- pkg/svSocket/R/parSocket.R	2010-09-25 07:23:55 UTC (rev 309)
+++ pkg/svSocket/R/parSocket.R	2010-09-25 07:31:45 UTC (rev 310)
@@ -1,4 +1,4 @@
-parSocket <- function (client, serverport = 8888, ...)
+parSocket <- function (client, serverport = 8888, clientsocket = client, ...)
 {
     ## Set or get parameters for a given socket client
     ## No attempt is made to make sure this client exists
@@ -7,6 +7,7 @@
         ## Create a new environment with default values
         e <- new.env(parent = TempEnv())
         e$client <- client
+		e$clientsocket <- clientsocket
         e$serverport <- serverport
         e$prompt <- ":> "    # Default prompt
         e$continue <- ":+ "  # Default continuation prompt
@@ -21,7 +22,11 @@
         assign(sc, e, envir = TempEnv())
     } else e <- get(sc, envir = TempEnv(), mode = "environment")
     ## Change or add parameters if they are provided
-    args <- list(...)
+    ## There is no reason that serverport changes
+	## but if a client disconnects and reconnects, the clientsocket may be
+	## different! But only change if it is sockXXX
+	if (grepl("^sock[0-9]+$", clientsocket)) e$clientsocket <- clientsocket
+	args <- list(...)
     if (l <- length(args)) {
         change.par <- function (x, val, env) {
             if (is.null(x)) return(FALSE)  # Do nothing without a valid name

Modified: pkg/svSocket/R/processSocket.R
===================================================================
--- pkg/svSocket/R/processSocket.R	2010-09-25 07:23:55 UTC (rev 309)
+++ pkg/svSocket/R/processSocket.R	2010-09-25 07:31:45 UTC (rev 310)
@@ -1,6 +1,6 @@
 processSocket <- function (msg, socket, serverport, ...)
 {
-    ## This is the default R function that processes a command send by a socket
+	## This is the default R function that processes a command send by a socket
     ## client. 'msg' is assumed to be R code contained in a string
 
     ## Do we receive a <<<id=myID>>> sequence?
@@ -16,7 +16,8 @@
 
 	## Do we receive <<<esc>>>? => break (currently, only break multiline mode)
     if (substr(msg, 1, 9) == "<<<esc>>>") {
-        pars <- parSocket(client, serverport, code = "")  # Reset multiline code
+		## Reset multiline code and update clientsocket
+        pars <- parSocket(client, serverport, clientsocket = socket, code = "")
         msg <- substr(msg, 10, 1000000)
     }
 
@@ -41,34 +42,35 @@
     } else if (startmsg == "<<<q>>>") {
 		msg <- substr(msg, 8, 1000000)
         ## Remember to indicate disconnection at the end
-        parSocket(client, serverport, last = "\n\f")
+        parSocket(client, serverport, clientsocket = socket, last = "\n\f")
     } else if (startmsg == "<<<e>>>") {
         msg <- substr(msg, 8, 1000000)
         ## We just configure the server correctly
-        parSocket(client, serverport, bare = FALSE, echo = TRUE,
-			prompt = ":> ", continue = ":+ ",
-            multiline = TRUE, last = "\n\f")
+        parSocket(client, serverport, clientsocket = socket, bare = FALSE,
+			echo = TRUE, prompt = ":> ", continue = ":+ ", multiline = TRUE,
+			last = "\n\f")
         ## Add a command to the command history
         #timestamp("my R command", "", "", quiet = TRUE)
     } else if (startmsg == "<<<h>>>") {
 		msg <- substr(msg, 8, 1000000)
 		## Do not echo command on the server (silent execution)
 		hiddenMode <- TRUE
-		parSocket(client, serverport, bare = TRUE, last = "\n\f")
+		parSocket(client, serverport, clientsocket = socket, bare = TRUE,
+			last = "\n\f")
     } else if (startmsg == "<<<H>>>") {
 		msg <- substr(msg, 8, 1000000)
 		## Do not echo command on the server (silent execution with no return)
         closeSocketClients(sockets = socket, serverport = serverport)
 		hiddenMode <- TRUE
 		returnResults <- FALSE
-		parSocket(client, serverport, bare = TRUE)
+		parSocket(client, serverport, clientsocket = socket, bare = TRUE)
 	} else if (startmsg == "<<<u>>>") {
 		msg <- substr(msg, 8, 1000000)
 		## Silent execution, nothing is returned to the client
 		## (but still echoed to the server)
 		hiddenMode <- FALSE
 		returnResults <- FALSE
-		parSocket(client, serverport, bare = TRUE)
+		parSocket(client, serverport, clientsocket = socket, bare = TRUE)
 	}
 
     ## Get parameters for the client
@@ -123,7 +125,10 @@
     ## Is it something to evaluate?
     if (length(expr) < 1) return(paste(pars$last, Prompt, sep = ""))
     ## Correct code,... we evaluate it
-    results <- captureAll(expr)
+	## Something like this should allow for real-time echo in client, but it is too slow
+	## and it outputs all results at the end...
+	#results <- captureAll(expr, split = Echo, file = socketClientConnection(socket))
+	results <- captureAll(expr, split = Echo)
 	## Should we run taskCallbacks?
 	if (!hiddenMode) {
 		h <- getTemp(".svTaskCallbackManager", default = NULL, mode = "list")
@@ -131,7 +136,7 @@
 	}
     ## Collapse and add last and the prompt at the end
     results <- paste(results, collapse = "\n")
-    if (Echo) cat(results)
+	#if (Echo) cat(results)
     if (!returnResults) return("")
 	Prompt <- if (pars$bare) "" else pars$prompt
     results <- paste(results, pars$last, Prompt, sep = "")

Added: pkg/svSocket/R/socketClientConnection.R
===================================================================
--- pkg/svSocket/R/socketClientConnection.R	                        (rev 0)
+++ pkg/svSocket/R/socketClientConnection.R	2010-09-25 07:31:45 UTC (rev 310)
@@ -0,0 +1,74 @@
+socketClientConnection <- function (client, serverport = 8888, socket,
+blocking = FALSE, open = "a", encoding = getOption("encoding"))
+{
+	## Only accepts "a" or "w" modes currently
+	if (!open %in% c("a", "w"))
+		stop("Only modes \"a\" or \"w\" are currently supported")
+	
+	## Connect to a client of the svSocket server, serving on 'serverport'
+	## First check that the server is running and is serving 'socket'
+	if (is.null(serverport) || !is.numeric(serverport[1]) || serverport[1] < 1) 
+        stop("'serverport' must be a positive integer!")
+    portnum <- round(serverport[1])
+	if (!portnum %in% getSocketServers())
+		stop("There is no currently running socket server on port ",
+			portnum, "\n    Start one by using startSocketServer() first")
+	## If socket is not provided, try to get it from client's infos
+	if (missing(socket))
+		socket <- parSocket(client, serverport)$clientsocket
+	## Check that 'socket' is a currently opened Tcl socket and is a client
+	res <- try(.Tcl(paste("fconfigure", socket, "-peername")), silent = TRUE)
+	if (inherits(res, "try-error"))
+		stop("This client or this socket is not currently connected")
+	res <- as.character(res)
+	redir <- paste("->", res[1], ":", res[length(res)], sep = "")
+	## That's OK, we could proceed in opening a socketConnection and redirect it
+	## to the client's socket...
+#	currSocks <- getSocketClientsNames(portnum)
+	sck <- socketConnection(host = "127.0.0.1", port = portnum, server = FALSE,
+		blocking = blocking, open = open, encoding = encoding)
+	## We need to leave enough time in the background to Tcl to establish the
+	## connection
+#	i <- 0
+#	mySock <- character(0)
+#	while (length(mySock) < 1 && i < 10) {
+#		i <- i + 1
+		.Tcl("update idletasks")
+#		Sys.sleep(0.05)
+#		currSocks2 <- getSocketClientsNames(portnum)
+#		mySock <- currSocks2[!currSocks2 %in% currSocks]
+#	}
+#	if (length(mySock) < 1) {
+#		try(close(sck), silent = TRUE)
+#		stop("Unable to connect to the client socket")
+#	}
+#	mySock <- mySock[1]  # Just a precaution
+	## Now, activate the redirection in Tcl
+#	.Tcl(paste("fileevent", mySock, "readable [list sockRedirect", mySock,
+#		socket, "]"))
+	## ... and eliminate this client for the list
+#	.Tcl(paste("unset Rserver_", portnum, "(", mySock, ")", sep = ""))
+	
+	## Instruct the socket server to redirect to socket
+	cat(">>>>>>", socket, "\n", sep = "", file = sck)
+	.Tcl("update idletasks")
+	
+	## Finalize the "sockclientconn" object
+#	attr(sck, "conn_tclsocket") <- mySock
+	attr(sck, "conn_redirsocket") <- socket
+	attr(sck, "conn_redirection") <- redir
+	class(sck) <- c("sockclientconn", class(sck))
+	return(sck)
+}
+
+## Summary method for sockclientconn object
+summary.sockclientconn <- function (object, ...)
+{
+	obj2 <- object
+	class(obj2) <- "connection"
+	res <- summary(obj2)
+	## Change description and class
+	res$description <- attr(object, "conn_redirection")
+	res$class <- "sockclientconn"
+	return(res)
+}

Modified: pkg/svSocket/R/startSocketServer.R
===================================================================
--- pkg/svSocket/R/startSocketServer.R	2010-09-25 07:23:55 UTC (rev 309)
+++ pkg/svSocket/R/startSocketServer.R	2010-09-25 07:31:45 UTC (rev 310)
@@ -147,14 +147,20 @@
 		paste("    #puts \"Close $Rserver_", port, "($sock)\"", sep = ""),
 		paste("    unset Rserver_", port, "($sock)", sep = ""),
 		"} else {",
-		"    global sockPort",
-		"    global sockClient",
-		"    global sockMsg",
-		paste("    set ::sockPort", port),
-		"    set ::sockClient $sock",
-		"    set ::sockMsg $line",
-		"    SocketServerProc    ;# process the command in R",
-		"}\n}"),
+		"    # Do we have to redirect the connection?",
+		"    if {[string compare \">>>>>>sock\" [string range $line 0 9]] == 0} {",
+		"        set redirSock [string range $line 6 12]",
+		"        fileevent $sock readable [list sockRedirect $sock $redirSock]",
+		paste("        unset Rserver_", port, "($sock)", sep = ""),
+		"    } else {",
+		"        global sockPort",
+		"        global sockClient",
+		"        global sockMsg",
+		paste("        set ::sockPort", port),
+		"        set ::sockClient $sock",
+		"        set ::sockMsg $line",
+		"        SocketServerProc    ;# process the command in R",
+		"}\n}\n}"),
 	collapse = "\n")
     ## if {[gets $sock line] < 0} {return} # To handle incomplete lines!
     .Tcl(cmd)
@@ -190,6 +196,20 @@
 		collapse = "\n")
 	}
 	.Tcl(cmd)
+	
+	## Create a Tcl procedure to redirect output (used in socketClientConnection())
+	if (!tclProcExists("sockRedirect")) {
+		cmd <- paste(c("proc sockRedirect {sock tosock} {",
+			"if {[eof $sock] == 1 || [catch {gets $sock line}]} {",
+			"    # end of file or abnormal connection drop",
+			"    fileevent $sock readable {}",
+			"    close $sock",
+			"} else {",
+			"    puts $tosock $line",
+			"}\n}"),
+			collapse = "\n")
+		.Tcl(cmd)
+	}
 
 	## Create the socket server itself in Tcl (a different one for each port)
 	## If we want a secure server, use the tls secured socket instead

Modified: pkg/svSocket/inst/etc/ReadMe.txt
===================================================================
--- pkg/svSocket/inst/etc/ReadMe.txt	2010-09-25 07:23:55 UTC (rev 309)
+++ pkg/svSocket/inst/etc/ReadMe.txt	2010-09-25 07:31:45 UTC (rev 310)
@@ -19,7 +19,7 @@
 Follow instructions: you can type R commands in the console of the client
 application and R returns the results of the calculation. You have no command
 line edition, and no command history, but you can test various R commands
-by typing then in the client or pasting them one by one.
+by typing them in the client or pasting them one by one.
 
 To get something closer to a real console, you should display the prompt and
 enable multiline mode. You achieve this by configuring the client with:

Modified: pkg/svSocket/man/parSocket.Rd
===================================================================
--- pkg/svSocket/man/parSocket.Rd	2010-09-25 07:23:55 UTC (rev 309)
+++ pkg/svSocket/man/parSocket.Rd	2010-09-25 07:31:45 UTC (rev 310)
@@ -11,7 +11,7 @@
 }
 
 \usage{
-parSocket(client, serverport = 8888, ...)
+parSocket(client, serverport = 8888, clientsocket = client, \dots)
 }
 
 \arguments{
@@ -21,6 +21,11 @@
     fake ones, outside of the socket server, to test your code for instance. }
   \item{serverport}{ the port on which the server is running, 8888 by default.
     Not important for fake socket client configurations. }
+  \item{clientsocket}{ the Tcl name of the socket where the client is
+    connected. By default, it is the same as \code{client} name, but in case
+    it was modified, do provide a correct \code{clientsocket} string if you
+    want to be able to activate a redirection to it (see
+    \code{socketClientConnection()}). }
   \item{\dots}{ the parameters you want to change as named arguments. Non
     named arguments are ignored with a warning. If you specify
     \code{arg = NULL}, the corresponding variable is deleted from the
@@ -100,7 +105,7 @@
 \author{Philippe Grosjean (\email{phgrosjean at sciviews.org})}
 
 \seealso{ \code{\link{startSocketServer}}, \code{\link{sendSocketClients}},
-  \code{\link{getSocketClients}} }
+  \code{\link{getSocketClients}}, \code{\link{socketClientConnection}}. }
 
 \examples{
 ## We use a fake socket client configuration environment

Added: pkg/svSocket/man/socketClientConnection.Rd
===================================================================
--- pkg/svSocket/man/socketClientConnection.Rd	                        (rev 0)
+++ pkg/svSocket/man/socketClientConnection.Rd	2010-09-25 07:31:45 UTC (rev 310)
@@ -0,0 +1,53 @@
+\name{socketClientConnection}
+\alias{socketClientConnection}
+\alias{summary.sockclientconn}
+
+\title{ Open a connection to a SciViews socket client for write access }
+
+\description{
+  A 'sockclientconn' object is created that opens a connection from R to a
+  SciViews socket client (that must be currently connected).
+}
+
+\usage{
+socketClientConnection(client, serverport = 8888, socket, blocking = FALSE,
+    open = "a", encoding = getOption("encoding"))
+
+\method{summary}{sockclientconn}(object, \dots)
+}
+
+\arguments{
+  \item{client}{ the client identification. By default, it is the socket
+    identifier as it appears in \code{getSocketClients()}. The client must be
+    currently connecte. }
+  \item{serverport}{ the port on which the server is running, 8888 by default.
+    This server must be currently running. }
+  \item{socket}{ the Tcl socket name where the targetted client is connected. If
+    not provided, it will be guessed from \code{client}, otherwise,
+    \code{client} is ignored. }
+  \item{blocking}{ logical. Should the connection wait that the data is written
+    before exiting? }
+  \item{open}{ character. How the connection is opened. Currently, only
+    \code{"a"} for append (default) or \code{"w"} for write access are usable. }
+  \item{encoding}{ the name of the encoding to use. }
+  \item{object}{ A 'sockclientconn' object as returned by
+    \code{socketClientConnection()}. }
+  \item{\dots}{ further arguments passed to the method (not used for the
+    moment). }
+}
+
+\value{
+  \code{socketClientConnection()} creates a 'sockclientconn' object redirects
+  text send to it to the SciViews socket server client. It is inherits from a
+  'sockconn' object (see \code{socketConnection()}), and the only difference is
+  that output is redirected to a Tcl socket corresponding to a given SciViews
+  socket client currently connected.
+}
+
+\author{Philippe Grosjean (\email{phgrosjean at sciviews.org})}
+
+\seealso{ \code{\link[base]{socketConnection}}, \code{\link{sendSocketClients}} }
+
+\keyword{ IO }
+
+\concept{ stateful socket server interprocess communication }


Property changes on: pkg/svSocket/man/socketClientConnection.Rd
___________________________________________________________________
Added: svn:executable
   + *



More information about the Sciviews-commits mailing list