How to capture raw console output?

Jun 19, 2011 at 1:16 PM

First of all, great work with R.Net. It's a fantastic project. One question though: say that I want to capture the raw output from the commands I send to R. How can I get it in a form other than a vector, list, matrix or dataframe?

Coordinator
Jun 19, 2011 at 2:23 PM

You can create an implementation of RDotNet.Devices.ICharacterDevice interface. WriteConsole method captures the usual output. Graphics device implementation is under development (you can find Graphics branch at changeset 76d7af907761).

Jun 19, 2011 at 2:56 PM

Great - one question though, could you give me an example of using it? I've been poking around and can't seem to work it out! Everything else I've done has been very straightforward though :)

Aug 20, 2011 at 4:18 AM
Edited Aug 20, 2011 at 4:19 AM

I have the same problem...  How can I get the raw console output into a string ? Could someone give me an example of how can I get this output ?

Thanks in advance.

Aug 21, 2011 at 4:11 AM

I created a class called RHelper to manage the REngine object (called "engine" in this code snippet).  This class has a method called RunRCommand that takes a string argument and passes it to  REngine.EagerEvaluate().  Before it does this though, it uses Console.SetOut() to redirect console output to a StringWriter.  I then use this StringWriter in the case of a ParseException (thrown by EagerEvaluate()) to instead throw a generic exception with a new message: the error output coming from R.  You should be able to use the Console.SetOut() idea more generally to do other stuff you need with R.net.  My code:

 

public SymbolicExpression RunRCommand(string rCommand)
        {
            StringWriter sw = new StringWriter();
            Console.SetOut(sw);
            try
            {
                return this.engine.EagerEvaluate(rCommand);
            }
            catch (ParseException pe)
            {
                throw new Exception(sw.ToString());            
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

 

 

Aug 21, 2011 at 4:50 AM

Ascruggs, thank you for your reply!! I will try to use the Console.SetOut to obtain the full output from R.

Thanks again!

Oct 16, 2011 at 6:48 PM
Edited Oct 20, 2011 at 2:41 AM

If you want to catch the console output, you can, of course, do it in class that inherits ICharacterDevice:

Then pass a reference to TextBox or RTF control or, and this is the best choice, the delegate to a function printing commands/errors in the constructor and use it instead of the Console.Write(output). But there is an issue: you have to wrap all commands in the "print(...)" statement.

delegate void ErrorPrinter(string message);

class MyCharacterDevice : RDotNet.Devices.ICharacterDevice
{
     ErrorPrinter printer;
     public MyCharacterDevice(ErrorPrinter printer)
     {
          this.printer = printer;
     }

     public void WriteConsole(string output, int length, RDotNet.Internals.ConsoleOutputType outputType)
     {
          if (output.StartsWith("Error"))
          {
               if (this.printer != null) printer(output);
          }
     }

......... and so on
}

Don't forget to pass your new device to the CreateInstance method:

private void SetupR()
{
     REngine.SetDllDirectory(this.RPath);
     MyCharacterDevice dev = new MyCharacterDevice(PrintError);
     this.rEngine = REngine.CreateInstance("instance", new[] { "-q" }, dev);
}

Here is the result for standard Console.Write(output) catched by the "Output" window in the debug mode: http://i54.tinypic.com/nevokw.jpg

 


But there is IMHO a better way to grab the full output, without any "prints" and it works very good. Just use sink() and source().

This example covers the idea plus the way to handle errors. Unfortunately, the ParseException doesn't contain any exception message:

List<string> temporaryFilesCollector = new List<string>();
REngine rEngine;
........
private void RunScript(ref string script)
{
    this.rEngine = REngine.GetInstanceFromID("instancja");

    string tmpFileNameForScript = Path.GetTempFileName();
    string tmpFileNameForSink = Path.GetTempFileName();

    this.temporaryFilesCollector.Add(tmpFileNameForScript);
    this.temporaryFilesCollector.Add(tmpFileNameForScript);

    string tmpFileNameForScript_RFormat = tmpFileNameForScript.Replace(@"\", "/");
    string tmpFileNameForSink_RFormat = tmpFileNameForSink.Replace(@"\", "/");

    string header = "sinkConnection <- file(\"" + tmpFileNameForSink_RFormat + "\");" + System.Environment.NewLine
                            + "sink(sinkConnection, append=TRUE);" + System.Environment.NewLine;

    string footer = "sink();";

    File.WriteAllText(tmpFileNameForScript, header + System.Environment.NewLine + script + System.Environment.NewLine + footer);

    try
    {
        this.rEngine.EagerEvaluate("source(\"" + tmpFileNameForScript_RFormat + " \", echo=TRUE);");

        string[] outputLines = File.ReadAllLines(tmpFileNameForSink);
        foreach (string line in outputLines)
        {
            if (!line.Contains("sink("))  // zb�dne w wynikach
            {
                this.rtfOutput.SelectionColor = (line.StartsWith(">") || line.StartsWith("+")) ? Color.Maroon : Color.Blue;
                this.rtfOutput.AppendText(line);
                this.rtfOutput.AppendText(System.Environment.NewLine);
            }
        }

        this.rtfOutput.ScrollToCaret();
        File.Delete(tmpFileNameForScript);
        File.Delete(tmpFileNameForSink);
    }
    catch (ParseException pe)
    {
    }
}
-----------------------------
private void PrintError(string error)
{
    this.rtfOutput.SelectionColor = Color.Red;
    this.rtfOutput.AppendText(error);
    this.rtfOutput.AppendText(System.Environment.NewLine);
}
-----------------------------
public Form1()
{
    InitializeComponent();

    Application.ApplicationExit += delegate (object s, EventArgs e)
    {
        this.rEngine.Close();

        // "garbage collector"
        foreach (string temp in this.temporaryFilesCollector)
        {
            if (File.Exists(temp)) File.Delete(temp);
        }
    };
}

PS: Don't try to remove these temporary files in a "finally" block because you'll get an exception saying they cannot be accessed.

The results: http://i51.tinypic.com/2kej4g.jpg

and http://i56.tinypic.com/2gvsx3a.jpg

Hope this help :)

May 26, 2013 at 5:25 PM
I have a simple hack, which is not perfect but still provide a R console Embedded into C#

using System;
using System.Linq;
using RDotNet;
using System.IO;

class Program
{
static void Main(string[] args)
{
    var Root = @"C:\Program Files\R\R-3.0.1";
    var envPath = Environment.GetEnvironmentVariable("PATH");
    // AnyCPU and x86 works with i386
    // x64 with x64
    // var arch = "x64"; 
    var arch = "i386";
    var rBinPath = Root + @"\bin\" + arch;
    Environment.SetEnvironmentVariable("PATH", envPath + Path.PathSeparator + rBinPath);
    Environment.SetEnvironmentVariable("R_LIBS", Root + @"\library");
    Environment.SetEnvironmentVariable("R_HOME", Root);
    String PersonalFolder = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
    Environment.SetEnvironmentVariable("R_LIBS_USER", Root + "/R/win-library/3.0");

    var parameters = new StartupParameter();
    parameters.Interactive = true;
    parameters.Quiet = false;
    parameters.Verbose = false;
    parameters.Slave = false;    


    using (REngine engine = REngine.CreateInstance("RDotNet"))
    {
        engine.Initialize(parameters);
        // .NET Framework array to R vector.
        NumericVector group1 = engine.CreateNumericVector(new double[] { 30.02, 29.99, 30.11, 29.97, 30.01, 29.99 });
        engine.SetSymbol("group1", group1);
        // Direct parsing from R script.
        NumericVector group2 = engine.Evaluate("group2 <- c(29.89, 29.93, 29.72, 29.98, 30.02, 29.98)").AsNumeric();

        // Test difference of mean (Student's t-test) and get the P-value.
        GenericVector testResult = engine.Evaluate("t.test(group1, group2)").AsList();
        double p = testResult["p.value"].AsNumeric().First();

        Console.WriteLine("Group1: [{0}]", string.Join(", ", group1.Select(value => value.ToString())));
        Console.WriteLine("Group2: [{0}]", string.Join(", ", group2.Select(value => value.ToString())));
        Console.WriteLine("P-value = {0:0.000}", p);
        string cmd;
        while (true)
        {
            cmd = System.Console.ReadLine();
            try
            {
                var exp = engine.Evaluate("toString("+cmd+")");
                // .Last.value holds the last value bur does not work
                foreach (var c in exp.AsCharacter())
                {
                    System.Console.Write(c);
                }
                System.Console.WriteLine();
            }
            catch (Exception e)
            {
                System.Console.WriteLine(e.Message);
            }
        }
    }
}
}