Nothing
###########################################################################/**
# @RdocClass Matlab
#
# @title "MATLAB client for remote or local MATLAB access"
#
# \description{
# @classhierarchy
# }
#
# @synopsis
#
# \arguments{
# \item{host}{Name of host to connect to.}
# \item{port}{Port number on host to connect to.}
# \item{remote}{If @TRUE, all data to and from the MATLAB server will
# be transferred through the socket @connection, otherwise the data will
# be transferred via a temporary file.}
# }
#
# \section{Fields and Methods}{
# @allmethods
# }
#
# \section{Requirements}{
# In order for \R to communicate with MATLAB, MATLAB v6 or higher is
# needed. It will \emph{not} work with previous versions, because they
# do not support Java.\cr
#
# We use the term \emph{server} to say that MATLAB acts like a server
# with regard to \R. Note that it a standard MATLAB session that runs.\cr
#
# Also, the starting of the MatlabServer is simpler from MATLAB v7,
# although it is pretty straightforward for MATLAB v6 too.
# It is easier in MATLAB v7 and above, because the Java class required
# for remote-data-transfer can be automatically/dynamically added to
# the MATLAB Java classpath, whereas for MATLAB v6 it has to be
# added manually (see below).
# }
#
# \section{Remote and non-remote connections}{
# When a remote connection (argument \code{remote = TRUE}) is used,
# data is send to and from MATLAB via a data stream. This is needed
# when \R is running on a host with a separated file system than
# the one MATLAB is running on.
#
# If not connection "remotely" (\code{remote = FALSE}), data is
# communicated via the file system, that is, by saving and reading
# it to temporary MAT files. \cr
#
# Troubleshooting: If "remote" transfers are used, the
# InputStreamByteWrapper Java class must be found by MATLAB,
# otherwise an error will occur in MATLAB as soon as data is
# send from \R to MATLAB. In all other cases, the above Java class
# is \emph{not} needed.
# }
#
# \section{Starting the MATLAB server from within \R}{
# The MATLAB server may be started from within \R by
# calling \code{Matlab$startServer()}. By default 'matlab' is called
# if named differently set \code{options(matlab = "matlab6.5")}, say.
# \emph{The method is experimental and may not work on your system.}
# By default the MATLAB server listens for connections on port 9999.
# For other ports, set argument \code{port}, e.g.
# \code{Matlab$startServer(port = 9998)}.
#
# Note that the code will \emph{not} halt and wait for MATLAB to get
# started. Thus, you have to make sure you will wait long enough for
# the server to get up and running before the \R client try to
# connect. By default, the client will try once a second for 30 seconds
# before giving up.
# Moreover, on non-Windows systems, the above command will start MATLAB
# in the background making all MATLAB messages be sent to the \R output
# screen.
# In addition, the method will copy the MatlabServer.m and
# InputStreamByteWrapper.class files to the current directory
# and start MATLAB from there.
# }
#
# \section{Starting the MATLAB server without \R}{
# If the above does not work, the MATLAB server may be started manually
# from MATLAB itself. Please follow the below instructions carefully.
#
# \bold{To be done once:}\cr
# In MATLAB, add the path to the directory where MatlabServer.m sits.
# See \code{help pathtool} in MATLAB on how to do this.
# In R you can type \code{system.file("externals", package = "R.matlab")}
# to find out the path to MatlabServer.m.
#
# \bold{For MATLAB v6 only:} Contrary to MATLAB v7 and above, MATLAB v6
# cannot find the InputStreamByteWrapper class automatically. Instead,
# the so called Java classpath has to be set manually. In MATLAB, type
# \code{which('classpath.txt')} to find where the default
# MATLAB classpath.txt file is located. Copy this file to the
# \emph{current directory}, and append the \emph{path} (the directory)
# of InputStreamByteWrapper.class to the end of classpath.txt.
# The path of InputStreamByteWrapper.class can be identified by
# \code{system.file("java", package = "R.matlab")} in R.\cr
#
# \bold{Lazy alternative:} Instead of setting path and classpaths,
# you may try to copy the MatlabServer.m and InputStreamByteWrapper.class
# to the current directory from which MATLAB is then started.
#
# \bold{To start the server:}\cr
# In order to start the MATLAB server, type\cr
#
# \code{matlab -nodesktop -nosplash -r MatlabServer}\cr
#
# If using MATLAB v6, make sure your \code{classpath.txt} is the
# current directory!
#
# This will start MATLAB and immediately call the MatlabServer(.m)
# script. Here is how it should look like when the server starts:
# \preformatted{
# @include{../incl/MatlabServer.out}
# }
# Alternatively you can start MATLAB and type \code{MatlabServer}
# at the prompt.
#
# By default the MATLAB server listens for connections on port 9999.
# For other ports, set environment variable \code{MATLABSERVER_PORT}.
# }
#
# \section{Confirmed MATLAB versions}{
# This package has been confirmed to work \emph{successfully} out of
# the box together with the following MATLAB versions:
# MATLAB v6.1.0.450 (R12.1) [Jun 2001],
# MATLAB v6.5.0.180913a (R13) [Jul 2002],
# MATLAB v7.0.0.19901 (R14) [Jun 2004],
# MATLAB v7.0.1.24704 (R14SP1) [Oct 2004],
# MATLAB v7.0.4.365 (R14SP2) [Mar 2005],
# MATLAB v7.2.0.232 (R2006a) [Mar 2006],
# MATLAB v7.4.0 (R2007a) [Mar 2007]],
# MATLAB v7.7.0.471 (R2008b) [Oct 2008],
# MATLAB v7.10.0.499 (R2010a) [Mar 2010],
# MATLAB v7.11.0.584 (R2010b) [Sep 2010],
# MATLAB v7.14.0.739 (R2012a) [Mar 2012],
# MATLAB v8.2.0.701 (R2013b) [Sep 2013], and
# MATLAB v8.4.0 (R2014b) [Oct 2014].
# If you successfully use a different/higher MATLAB version,
# please tell us, so we can share it here.
#
# It does \emph{not} work with MATLAB v5 or before.
# }
#
# \section{Security}{
# There is \emph{no} security in the communication with the MATLAB
# server. This means that if you start the MATLAB server, it will
# wait for requests via the connection at the specified port. As
# long as your \R session has not connected to this port, others
# may be able to steal the connection and send malicious commands
# (if they know the R.matlab protocol). The MATLAB server only
# allows one connection. In other words, if you are connected it
# is not possible for others to connect to the MATLAB server.
# }
#
# \section{MATLAB server is timing out}{
# It might be that an @seemethod "evaluate" call to the MATLAB server
# takes a long time for the server to finish resulting in a time-out
# exception. By default this happens after 30 seconds, but it can
# be changed by modifying options, cf. @see "setOption".
# }
#
# \section{Multiple parallel MATLAB instances}{
# You can launch multiple parallel MATLAB instance using this interface.
# This can be done in separate R sessions or in a single one. As long
# as each MATLAB server/session is communicating on a separate port,
# there is no limitation in the number of parallel MATLAB instances
# that can be used. Example:
#
# \preformatted{
# > library('R.matlab')
# ## Start two separate MATLAB servers
# > Matlab$startServer(port = 9997)
# > Matlab$startServer(port = 9999)
#
# ## Connect to each of them
# > matlab1 <- Matlab(port = 9997); open(matlab1)
# > matlab2 <- Matlab(port = 9999); open(matlab2)
#
# ## Evaluate expression in each of them
# > evaluate(matlab1, "x = 1+2; x")
# > evaluate(matlab2, "y = 1+2; y")
# }
#
# Note that the two MATLAB instance neither communicate nor
# share variables.
# }
#
# \examples{\dontrun{@include "../incl/Matlab.R"}}
#
# @author
#
# \seealso{
# Stand-alone methods @see "readMat" and @see "writeMat"
# for reading and writing MAT file structures.
# }
#
# @visibility public
#*/###########################################################################
setConstructorS3("Matlab", function(host = "localhost", port = 9999,
remote = !(host %in%
c("localhost", "127.0.0.1"))) {
# Argument 'port':
if (!is.null(port)) {
port <- Arguments$getInteger(port, range = c(1023, 65535))
}
extend(Object(), "Matlab",
con = NULL,
host = as.character(host),
port = as.integer(port),
remote = as.logical(remote),
.options = Options(),
.verbose = NullVerbose()
)
})
###########################################################################/**
# @RdocMethod as.character
#
# @title "Gets a string describing the current MATLAB connection"
#
# \description{
# @get "title".
# }
#
# @synopsis
#
# \arguments{
# \item{...}{Not used.}
# }
#
# \value{
# Returns a @character string.
# }
#
# @author
#
# \seealso{
# @seeclass
# }
#*/###########################################################################
setMethodS3("as.character", "Matlab", function(x, ...) {
# To please R CMD check
this <- x
s <- sprintf("%s: The MATLAB host is '%s' and communication is on port %d.",
class(this)[1], this$host, this$port)
s <- sprintf("%s Objects are passed via the %s (remote = %s).", s,
(if (this$remote) "socket connection" else "local file system"),
as.character(this$remote)
)
s <- sprintf("%s The connection to the MATLAB server is %s.", s,
(if (isOpen(this)) "opened" else "closed (not opened)")
)
s
})
###########################################################################/**
# @RdocMethod getOption
#
# @title "Gets the value of an option"
#
# \description{
# @get "title" where the option is specified like a file pathname, e.g.
# "readResult/maxTries". See @seemethod "setOption" for what options
# are available.
# See the \link[R.utils]{Options} class for details.
# }
#
# @synopsis
#
# \arguments{
# \item{...}{Arguments passed to
# \code{\link[R.utils:getOption.Options]{getOption}}()
# of the Options class.}
# }
#
# \value{
# Returns the value of the option.
# }
#
# @author
#
# \seealso{
# @seemethod "setOption".
# @seeclass
# }
#*/###########################################################################
setMethodS3("getOption", "Matlab", function(this, ...) {
getOption(this$.options, ...)
})
###########################################################################/**
# @RdocMethod setOption
#
# @title "Sets the value of an option"
#
# \description{
# @get "title" where the option is specified like a file pathname, e.g.
# "readResult/maxTries". See the \link[R.utils]{Options} class for details.
# }
#
# @synopsis
#
# \arguments{
# \item{...}{Arguments passed to
# \code{\link[R.utils:getOption.Options]{setOption()}}
# of the Options class.}
# }
#
# \value{
# Returns the previous value of the option.
# }
#
# \section{Available options}{
# \itemize{
# \item{readResult/maxTries}{The maximum number of times the connection
# is check for an answer from the MATLAB server before giving up.
# Default values is 30 times.}
# \item{readResult/interval}{The interval in seconds between each poll
# for an answer. Default interval is 1 (second).}
# }
#
# With default values of the above options, the \R MATLAB client waits
# 30 seconds for a reply from the MATLAB server before giving up.
# }
#
# @author
#
# \seealso{
# @seemethod "getOption".
# @seeclass
# }
#*/###########################################################################
setMethodS3("setOption", "Matlab", function(this, ...) {
setOption(this$.options, ...)
})
###########################################################################/**
# @RdocMethod open
#
# @title "Tries to open a connection to the MATLAB server"
#
# \description{
# Tries to open a socket connection to the MATLAB server. If the connection
# could not be opened it the first time it will try to open it every
# \code{interval} second up to \code{trials} times.
# }
#
# @synopsis
#
# \arguments{
# \item{trials}{The number of trials before giving up.}
# \item{interval}{The interval in seconds between trials.}
# \item{timeout}{The timeout for the socket connection}
# \item{...}{Not used.}
# }
#
# \value{
# Returns @TRUE if a socket @connection to the MATLAB server was
# successfully opened, otherwise @FALSE.
# }
#
# @author
#
# \seealso{
# @seeclass
# }
#*/###########################################################################
setMethodS3("open", "Matlab", function(con, trials = 30, interval = 1,
timeout = getOption("timeout"), ...) {
# To please R CMD check.
# close() is already a generic function in 'base'.
this <- con
enter(this$.verbose,
sprintf("Opens a blocked connection to host '%s' (port %d)",
this$host, as.integer(this$port)))
suffix <- "...done"
on.exit(exit(this$.verbose, suffix = suffix), add = TRUE)
count <- 0
while (count < trials) {
ok <- FALSE
tryCatch({
printf(this$.verbose, level = -1, "Try #%d.\n", as.integer(count))
suppressWarnings({
this$con <- socketConnection(host = this$host,
port = as.integer(this$port),
open = "a+b", blocking = TRUE,
timeout = timeout)
})
ok <- TRUE
# It is not possible to return() from tryCatch()! /HB 050224
}, error = function(ex) {
# The MATLAB server is not up and running yet.
# Wait a bit and try again.
Sys.sleep(interval)
count <<- count + 1
})
if (ok) {
# Connection to MATLAB server was successful!
return(TRUE)
}
} # while (count < trials)
suffix <- sprintf("...failed (after %d tries)", as.integer(count))
throw(sprintf("Failed to connect to MATLAB on host '%s' (port %d) after trying %d times for approximately %.1f seconds.", this$host, as.integer(this$port), count, count * interval))
})
###########################################################################/**
# @RdocMethod isOpen
#
# @title "Checks if connection to the MATLAB server is open"
#
# \description{
# Checks if connection to the MATLAB server is open.
# }
#
# @synopsis
#
# \arguments{
# \item{...}{Not used.}
# }
#
# \value{
# Returns @TRUE if @connection is open, otherwise @FALSE.
# }
#
# @author
#
# \seealso{
# @seeclass
# }
#*/###########################################################################
setMethodS3("isOpen", "Matlab", function(con, ...) {
# isOpen() is already a function in 'base'.
this <- con
(!is.null(this$con) && isOpen(this$con))
})
###########################################################################/**
# @RdocMethod finalize
#
# @title "(internal) Finalizes the object if deleted"
#
# \description{
# @get "title". If a MATLAB connection is opened, it is closed.
#
# Note that you should never have to call this method explicitly. It is
# called automatically whenever the object is removed from memory
# (by the garbage collector).
# }
#
# @synopsis
#
# \arguments{
# \item{...}{Not used.}
# }
#
# \value{
# Returns nothing.
# }
#
# @author
#
# \seealso{
# @seeclass
# }
#*/###########################################################################
setMethodS3("finalize", "Matlab", function(this, ...) {
enter(this$.verbose, level = -100, "Finalizing the Matlab object")
on.exit(exit(this$.verbose), add = TRUE)
close(this)
}, createGeneric = FALSE)
###########################################################################/**
# @RdocMethod close
#
# @title "Closes connection to MATLAB server"
#
# \description{
# Closes connection to MATLAB server. After closing the connection the MATLAB
# server can never more be access again.
# }
#
# @synopsis
#
# \arguments{
# \item{...}{Not used.}
# }
#
# \value{
# Returns what @seemethod "close" returns.
# }
#
# @author
#
# \seealso{
# @seeclass
# }
#*/###########################################################################
setMethodS3("close", "Matlab", function(con, ...) {
# close() is already a generic function in 'base'.
this <- con
enter(this$.verbose, sprintf("Closing connection to host '%s' (port %d)", this$host, as.integer(this$port)))
suffix <- "...done"
on.exit(exit(this$.verbose, suffix = suffix), add = TRUE)
if (isOpen(this)) {
printf(this$.verbose, level = -2, "Sending a command to the MATLAB server telling it to close down...\n")
writeCommand(this, "quit")
printf(this$.verbose, level = -2, "Sending a command to the MATLAB server telling it to close down...ok\n")
printf(this$.verbose, level = -2, "Waiting for a reply from the MATLAB server...\n")
res <- readResult(this)
resStr <- if (is.null(res)) 0 else res
printf(this$.verbose, level = -2, "Waiting for a reply from the MATLAB server...ok: '%d'\n", as.integer(resStr))
if (!is.null(res)) {
warning(res)
}
printf(this$.verbose, level = -2, "Closing the connection to the MATLAB server...\n")
close(this$con)
this$con <- NULL
printf(this$.verbose, level = -2, "Closing the connection to the MATLAB server...ok\n")
} else if (!is.null(this$con)) {
suffix <- "...already closed"
warning("MATLAB session is already closed.")
}
})
###########################################################################/**
# @RdocMethod writeCommand
#
# @title "(internal) Writes (sends) an R-to-MATLAB command to the MATLAB server"
#
# \description{
# @get "title".
#
# \emph{This method is for internal use only.}
# }
#
# @synopsis
#
# \arguments{
# \item{cmd}{A @character string specifying the command.}
# \item{...}{Not used.}
# }
#
# \value{
# Returns nothing.
# }
#
# @author
#
# \seealso{
# @seeclass
# }
#*/###########################################################################
setMethodS3("writeCommand", "Matlab", function(this, cmd, ...) {
getCommandByte <- function(this, cmd) {
commands <- c("quit", "", "eval", "send", "receive", "send-remote",
"receive-remote", "echo", "evalc")
idx <- match(cmd, table = commands, nomatch = NA_integer_)
if (is.na(idx)) {
throw(sprintf("INTERNAL ERROR: Unknown R-to-MATLAB command: %s (known commands are %s", sQuote(cmd), paste(sQuote(setdiff(commands, "")), collapse = ", ")))
}
idx - 2L
}
b <- getCommandByte(this, cmd)
printf(this$.verbose, level = -2, "Sending a command '%s' (%d) to the MATLAB server...\n", cmd, as.integer(b))
res <- Java$writeByte(this$con, b)
printf(this$.verbose, level = -2, "Sending a command '%s' (%d) to the MATLAB server...ok\n", cmd, as.integer(b))
res
}, private = TRUE)
###########################################################################/**
# @RdocMethod readResult
#
# @title "(internal) Reads results from the MATLAB server"
#
# \description{
# @get "title".
#
# \emph{This method is for internal use only.}
# }
#
# @synopsis
#
# \arguments{
# \item{...}{Not used.}
# }
#
# \value{
# Returns an \R object.
# }
#
# @author
#
# \seealso{
# @seeclass
# }
#*/###########################################################################
setMethodS3("readResult", "Matlab", function(this, ...) {
# BUG FIX?!? Linda Werner found out that it sometimes happend that
# answer == NULL below, which is strange because we read from a
# connection that blocks if there is nothing to read. See ?open
# for more details. Anyway, it seems that there is a timing problem
# and that the 'answer' MATLAB sends is not transfered in time. In
# other words, if we try again a little bit later it seems to work.
# This is should be considered a temporary work-around until we
# understand what really happens. /HB 2004-02-24
# Thomas Romary, France, reported the a similar problem on 2005-05-25
# for his 30 minutes MATLAB process. Added the option for users to set
# the number times and at what intervals (in sections) readResult()
# should query MATLAB for. /HB 2005-05-25
maxTries <- getOption(this, "readResult/maxTries", defaultValue = 30)
interval <- getOption(this, "readResult/interval", defaultValue = 1)
count <- 0
ready <- FALSE
while (!ready) {
count <- count + 1
printf(this$.verbose, level = -2, "Retrieve reply from the MATLAB server (try %d)...\n", as.integer(count))
answer <- Java$readByte(this$con)
ready <- (length(answer) > 0)
if (!ready) {
printf(this$.verbose, level = -2, "Strange! Received an \"empty\" reply although the connection should be blocking. Will try again...\n")
if (count <= maxTries) {
Sys.sleep(interval)
} else {
throw("Expected an 'answer' from MATLAB, but kept receiving nothing. Tried ", maxTries, " times at intervals of approximately ", interval, " seconds. To change this, see ?setOption.Matlab.")
}
}
}
if (answer == 0) {
printf(this$.verbose, level = -1, "Received an 'OK' reply (%d) from the MATLAB server.\n", answer)
res <- NULL
} else if (answer == -1) {
lasterr <- Java$readUTF(this$con)
printf(this$.verbose, level = -1, "Received an 'MatlabException' reply (%d) from the MATLAB server: '%s'\n", answer, as.character(lasterr))
throw("MatlabException: ", lasterr)
} else {
printf(this$.verbose, level = -1, "Received an generic reply from the MATLAB server: %d\n", answer)
res <- answer
}
res
}, private = TRUE)
###########################################################################/**
# @RdocMethod startServer
#
# @title "Static method which starts a MATLAB server"
#
# \description{
# Static method which starts a MATLAB server on the local machine. Note
# that MATLAB v6 or later is required, since the MATLAB server relies on
# Java.
# }
#
# @synopsis
#
# \arguments{
# \item{matlab}{An optional @character string specifying the name of
# the matlab command, if different from \code{"matlab"}. An absolute
# path are possible.}
# \item{port}{An optional @integer in [1023, 65535].
# If given, the environment variable \code{MATLABSERVER_PORT} is
# set specifying which port the MATLAB server should listen to for
# clients trying to connect. The default port is 9999.}
# \item{minimize}{When starting MATLAB on Windows, it is always opened
# in a new window (see @see "1. The MATLAB server running in MATLAB").
# If this argument is @TRUE, the new window is minimized, otherwise not.
# This argument is ignored on non-Windows systems.}
# \item{options}{A @character @vector of options used to call the
# MATLAB application.}
# \item{workdir}{The working directory to be used by MATLAB.}
# \item{...}{Not used.}
# }
#
# \value{
# Returns nothing.
# }
#
# \details{
# This method is currently supported on Windows and Unix systems. Other
# systems are untested, but might work anyway.
#
# Note that this method will return immediately upon calling
# \code{system()} internally, i.e. you will not receive a return value
# telling whether MATLAB was successfully started or not.
#
# To specify the full path to the matlab software set the \code{matlab}
# option, e.g. \code{options(matlab = "/opt/bin/matlab6.1")}. If no such
# option exists, the value \code{"matlab"} will be used.
#
# The MATLAB server relies on two files:
# 1) MatlabServer.m and 2) InputStreamByteWrapper.class
# These files exists in the externals/ and java/ directories of this
# package. However, if they do not exist in the current directory,
# which is the directory where MATLAB is started, copies of them will
# automatically be made.
# }
#
# @author
#
# \seealso{
# @seeclass
# }
#*/###########################################################################
setMethodS3("startServer", "Matlab", function(this,
matlab = getOption("matlab"),
port = 9999, minimize = TRUE,
options = c("nodesktop",
"nodisplay",
"nosplash"),
workdir = ".", ...) {
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Validate arguments
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Argument 'port':
if (!is.null(port)) {
port <- Arguments$getInteger(port, range = c(1023, 65535))
Sys.setenv("MATLABSERVER_PORT" = port)
}
# Argument 'workdir':
workdir <- Arguments$getWritablePath(workdir)
enter(this$.verbose, "Starting the MATLAB server")
on.exit(exit(this$.verbose), add = TRUE)
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Set working directory
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
if (!is.null(workdir) && (workdir != ".")) {
opwd <- setwd(workdir)
on.exit(setwd(opwd), add = TRUE)
cat(this$.verbose, level = -1, "MATLAB working directory: ", getwd())
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Make MATLAB server files available in the current directory
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
src1 <- system.file(package = "R.matlab", "externals", "MatlabServer.m")
src2 <- system.file(package = "R.matlab", "java",
"InputStreamByteWrapper.class")
srcs <- c(src1, src2)
for (src in srcs) {
filename <- basename(src)
enter(this$.verbose, level = -1, sprintf("MATLAB server file '%s'", filename))
copyFile(src, filename, overwrite = TRUE, verbose = less(this$.verbose, 50))
# Sanity check
filename <- Arguments$getReadablePathname(filename, mustExist = TRUE)
exit(this$.verbose)
} # for (filename ...)
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Setup call string to start MATLAB
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
cmd <- matlab
if (is.null(cmd))
cmd <- "matlab"
OST <- .Platform$OS.type
if (OST == "windows") {
optionPrefix <- "/"
if (minimize)
options <- c(options, "minimize")
} else {
optionPrefix <- "-"
}
options <- c(options, "r MatlabServer")
options <- paste(optionPrefix, options, sep = "")
options <- paste(options, collapse = " ")
cmd <- paste(cmd, options, sep = " ")
if (OST != "windows")
cmd <- paste(cmd, "&", sep = " ")
printf(this$.verbose, level = -1, "MATLAB server start command: '%s'\n", cmd)
if (OST == "windows") {
res <- system(cmd, wait = FALSE)
} else {
res <- system(cmd)
}
printf(this$.verbose, level = -1, "Return value: %d\n", as.integer(res))
res
}, static = TRUE)
###########################################################################/**
# @RdocMethod evaluate
#
# @title "Evaluates a MATLAB expression"
#
# \description{
# Evaluates one or several MATLAB expressions on the MATLAB server. This
# method will not return until the MATLAB server is done.
#
# If an error occurred in MATLAB an exception will be thrown. This
# exception can be caught by \code{\link[base:conditions]{tryCatch}()}.
#
# If you receive error message \emph{Expected an 'answer' from MATLAB,
# but kept receiving nothing}, see "Troubleshooting" under ?R.matlab.
# }
#
# @synopsis
#
# \arguments{
# \item{...}{One or several string with MATLAB expressions. If several
# strings are given they will be concatenated with the separator
# \code{collapse}.}
# \item{collapse}{Separator to be used to concatenate expressions.}
# \item{capture}{If @TRUE, MATLAB output is captured into a string,
# otherwise not.}
# }
#
# \value{
# If \code{caputure} is @TRUE, then a @character string of MATLAB output
# is returned, otherwise the MATLAB status code.
# The MATLAB status code is also/always returned as attribute \code{status}.
# }
#
# @author
#
# \seealso{
# @seeclass
# }
#*/###########################################################################
setMethodS3("evaluate", "Matlab", function(this, ..., collapse = ";",
capture = FALSE) {
capture <- Arguments$getLogical(capture)
expr <- paste(..., collapse = collapse)
printf(this$.verbose, level = 0, "Sending expression on the MATLAB server to be evaluated...: '%s'\n", expr)
cmd <- if (capture) "evalc" else "eval"
writeCommand(this, cmd)
Java$writeUTF(this$con, expr)
## Retrieve result from MATLAB - throws exception if error
status <- readResult(this)
statusT <- if (is.null(status)) 0L else as.integer(status)
printf(this$.verbose, level = 0, "Evaluated expression on the MATLAB server with return code %d.\n", statusT)
if (capture) {
statusT <- Java$readUTF(this$con)
attr(statusT, "status") <- status
status <- statusT
} else {
attr(status, "status") <- status
}
invisible(status)
})
###########################################################################/**
# @RdocMethod getVariable
#
# @title "Gets one or several MATLAB variables"
#
# \description{
# Gets one or several MATLAB variables from the MATLAB server.
# The transfer of the data can be done locally via a temporary file
# (\code{remote = FALSE}) or through the socket @connection (\code{remote = TRUE}),
# which is always available.
# }
#
# @synopsis
#
# \arguments{
# \item{variables}{String vector of MATLAB containing names of variable that
# are to be retrieved from the MATLAB server.}
# \item{remote}{If @TRUE the variables are transferred on the
# socket @connection, otherwise they are transferred via a temporary file.}
# \item{...}{Not used.}
# }
#
# \value{
# Returns a \code{list} structure containing the MATLAB variables as named
# values.
# }
#
# @author
#
# \seealso{
# @seeclass
# }
#*/###########################################################################
setMethodS3("getVariable", "Matlab", function(this, variables,
remote = this$remote, ...) {
vars <- paste("'", variables, "'", sep = "")
vars <- paste(vars, collapse = ", ")
printf(this$.verbose, level = 0, "Retrieving variables from the MATLAB server: %s\n", vars)
expr <- paste("MatlabServer_variables = {", vars, "};", sep = "")
answer <- evaluate(this, expr)
if (remote == FALSE) {
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Communication of data via the file system
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
printf(this$.verbose, level = -1, "Asks the MATLAB server to send variables via the local file system...\n")
writeCommand(this, "send")
result <- Java$readInt(this$con)
if (result == -1L) {
lasterr <- Java$readUTF(this$con)
Java$writeByte(this$con, 0) # Send ACK back to Matlab
throw("MatlabException: ", lasterr)
}
filename <- Java$readUTF(this$con)
printf(this$.verbose, level = -1, "Reading variables from the local MAT file '%s'...\n", filename)
data <- readMat(filename)
} else {
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Communication of data via data stream
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
printf(this$.verbose, level = -1, "Asks the MATLAB server to send variables via the socket connection...\n")
writeCommand(this, "send-remote")
maxLength <- Java$readInt(this$con)
printf(this$.verbose, level = -1, "Will read a MAT file of length %.0f bytes...", maxLength)
if (maxLength == -1) {
lasterr <- Java$readUTF(this$con)
Java$writeByte(this$con, 0) # Send ACK back to Matlab
throw("MatlabException: ", lasterr)
}
data <- readMat(this$con, maxLength = maxLength)
}
printf(this$.verbose, level = 0, "Retrieved the variables via a MAT file structure:\n")
str(this$.verbose, level = 0, data)
printf(this$.verbose, level = -1, "Replying to the MATLAB server that the data was retrieve successfully: 0\n")
Java$writeByte(this$con, 0)
data
})
###########################################################################/**
# @RdocMethod setVariable
#
# @title "Sets one or several MATLAB variables"
#
# \description{
# Sets one or several \R variables on the MATLAB server.
# The transfer of the data can be done locally via a temporary file
# (\code{remote = FALSE}) or through the socket @connection (\code{remote = TRUE}),
# which is always available.
# }
#
# @synopsis
#
# \arguments{
# \item{...}{Named \R variables to be set in MATLAB.}
# \item{remote}{If @TRUE the variables are transferred on the
# socket @connection, otherwise they are transferred via a temporary file.}
# }
#
# \value{
# Returns nothing. If the MATLAB server did not received the variables
# successfully an exception is thrown.
# }
#
# @author
#
# \seealso{
# @seeclass
# }
#*/###########################################################################
setMethodS3("setVariable", "Matlab", function(this, ..., remote = this$remote) {
printf(this$.verbose, level = -1, "Sends R variables to the MATLAB server: %s\n", paste(paste("'", names(list(...)), "'", sep = ""), collapse = ", "))
enter(this$.verbose, "Setting MATLAB variable on server")
on.exit(exit(this$.verbose), add = TRUE)
if (remote == FALSE) {
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Communication of data via the file system
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
printf(this$.verbose, level = -1, "Sends R variables to the MATLAB server via the local file system...\n")
# Write to file first to get file size...
tmpname <- paste(tempfile(), ".mat", sep = "")
on.exit(unlink(tmpname))
printf(this$.verbose, level = -1, "Writes R variables to the MAT file '%s'...\n", tmpname)
writeMat(tmpname, ...) # Write first and then tell MATLAB to read.
printf(this$.verbose, level = -1, "Writes R variables to the MAT file '%s'...ok\n", tmpname)
printf(this$.verbose, level = -1, "Tells the MATLAB server where to find the MAT file.\n")
writeCommand(this, "receive")
Java$writeUTF(this$con, tmpname)
} else {
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Communication of data via data stream
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# This requires that InputStreamByteWrapper is in the Java CLASSPATH of
# MATLAB, otherwise an error will be given in MATLAB, but not before this
# command is sent.
printf(this$.verbose, level = -1, "Sends R variables to the MATLAB server via the socket connection...\n")
writeCommand(this, "receive-remote")
# Note the usage of onWrite to write the length before sending the
# MAT file structure.
writeMat(this$con, ..., onWrite = function(x) Java$writeInt(this$con, x$length))
}
# Wait for MATLAB to tell if it received the variables sucessfully or not.
printf(this$.verbose, level = -1, "Waits for the MATLAB server to reply...\n")
answer <- Java$readByte(this$con)
printf(this$.verbose, level = -1, "Received a reply from the MATLAB server: %d\n", answer)
invisible(answer)
})
###########################################################################/**
# @RdocMethod setFunction
#
# @title "Defines a MATLAB function"
#
# \description{
# Creates an M-file on the MATLAB server machine (in the working directory)
# containing the specified MATLAB function definition.
# }
#
# @synopsis
#
# \arguments{
# \item{code}{The MATLAB function definition.}
# \item{name}{Optional name of the function, which will defined the
# name of the M-file where the function is stored. If @NULL,
# the name is extracted from the code.}
# \item{collapse}{The string that the code lines, if there are more than
# one, is going to be concatenated with.}
# \item{...}{Not used.}
# }
#
# \value{
# Returns nothing.
# }
#
# \examples{\dontrun{@include "../incl/Matlab.setFunction.R"}}
#
# @author
#
# \seealso{
# @seeclass
# }
#*/###########################################################################
setMethodS3("setFunction", "Matlab", function(this, code, name = NULL,
collapse = "\n", ...) {
enter(this$.verbose, "Setting MATLAB function on server")
on.exit(exit(this$.verbose), add = TRUE)
printf(this$.verbose, level = -1, "Building MATLAB source code for the function to be passed to the MATLAB server...\n")
code <- paste(code, collapse = collapse)
pos <- regexpr("^[ \t\n\r\v]*function[^=]*=[^ \t\n\r\v(]", code)
if (pos == -1) {
throw("The code does not contain a proper MATLAB function definition: ", substring(code, 1, 20), "...")
}
if (is.null(name)) {
nameStart <- as.integer(pos + attr(pos, "match.length") - 1L)
pos <- regexpr("^[ \t\n\r\v]*function[^=]*=[^( \t\n\r\v]*[( \t\n\r\v]", code)
if (pos == -1) {
throw("The code does not contain a open parentesis ('(') or a whitespace that defines the end of the function name: ", substring(code, 1, 20), "...")
}
nameStop <- as.integer(pos + attr(pos, "match.length") - 2L)
name <- substring(code, nameStart, nameStop)
}
printf(this$.verbose, level = -1, "Building MATLAB source code for the function to be passed to the MATLAB server...done\n")
printf(this$.verbose, level = -2, "Function name: %s\n", name)
str(this$.verbose, level = -2, code)
filename <- paste(name, ".m", sep = "")
setVariable(this, MatlabServer_tmp_fcndef = list(name = name, filename = filename, code = code))
expr <- "MatlabServer_tmp_fid = fopen(MatlabServer_tmp_fcndef.filename, 'w'); fprintf(MatlabServer_tmp_fid, '%s', MatlabServer_tmp_fcndef.code); fclose(MatlabServer_tmp_fid); rehash; clear MatlabServer_tmp_fcndef MatlabServer_tmp_fid;"
res <- evaluate(this, expr)
})
###########################################################################/**
# @RdocMethod setVerbose
#
# @title "Sets the verbose level to get more details about the MATLAB access"
#
# \description{
# @get "title".
# }
#
# @synopsis
#
# \arguments{
# \item{threshold}{A threshold that the \code{level} argument
# of any write method has to be equal to or larger than in order to the
# message being written.
# Thus, the lower the threshold is the more and more details will be
# outputted. If a large @numeric or @FALSE, no verbose output will be
# given.}
# \item{...}{Not used.}
# }
#
# \value{
# Returns the previous threshold value (an @integer) used.
# }
#
# \details{
# If the threshold is set to zero (default) general comments about the
# MATLAB access is given, such as the MATLAB server is started etc.
# If the threshold is \code{-1}, details about the communication with the
# MATLAB server is given.
# If the threshold is \code{-2}, low-level details about the communication
# with the MATLAB server is given, such as what commands are sent etc.
# }
#
# @author
#
# \seealso{
# @seeclass
# }
#*/###########################################################################
setMethodS3("setVerbose", "Matlab", function(this, threshold = 0, ...) {
verbose <- this$.verbose
oldThreshold <- getThreshold(verbose)
if (is.logical(threshold)) {
threshold <- -as.integer(threshold)
}
stop_if_not(is.numeric(threshold))
if (threshold >= 0) {
verbose <- NullVerbose()
} else {
verbose <- Verbose()
}
setThreshold(verbose, threshold)
this$.verbose <- verbose
invisible(oldThreshold)
})
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.