I have the following R script and C# program that is used as an example of calling R from C#. If I run the script from within R-GUI, I see the expected output. However, if I run it from within C# as below, I get no output. I tried moving the R script inside the C# executable directory, and this doesn't work either. I tried running Visual Studio as Administrator, again no output.
Not sure what I am doing wrong:
R code:
# Gradient Boosting model with gbm
# Turn off library warning messages
suppressWarnings(library(gbm))
# gbm result for simulated data
get_gbm <- function()
{
set.seed(123)
a <- sample(1:10, 250, replace = T)
b <- sample(10:20, 250, replace = T)
flag <- ifelse(a > 5 & b > 10, "red", ifelse(a < 3, "yellow", "green"))
df <- data.frame(a = a, b = b, flag = as.factor(flag))
train <- df[1:200,]
test <- df[200:250,]
mod_gb <- gbm(flag ~ a + b,
data = train,
distribution = "multinomial",
shrinkage = .01,
n.minobsinnode = 10,
n.trees = 100)
pred <- predict.gbm(object = mod_gb,
newdata = test,
n.trees = 100,
type = "response")
res <- cbind(test, pred)
return(res)
}
# need to call function to get the output
get_gbm()
C# code:
using System;
using System.Diagnostics;
using System.IO;
class Program
{
static void Main(string[] args)
{
var rmainpath = #"C:\Program Files\R\R-4.0.3";
var rpath = rmainpath + #"\bin\Rscript.exe";
var mainGoogleDrivePath = #"C:\Users\Administrator\Google Drive";
//var scriptpath = mainGoogleDrivePath + #"\repos\rsource\Script.R";
var scriptpath = mainGoogleDrivePath + #"\Projects\RFromCSharp\RFromCSharp\bin\Debug\Script.R";
var output = RunRScript(rpath, scriptpath);
Console.WriteLine(output); // output is empty
Console.ReadLine();
}
private static string RunRScript(string rpath, string scriptpath)
{
try
{
var info = new ProcessStartInfo
{
FileName = rpath,
WorkingDirectory = Path.GetDirectoryName(scriptpath),
Arguments = scriptpath,
RedirectStandardOutput = true,
CreateNoWindow = true,
UseShellExecute = false
};
using (var proc = new Process { StartInfo = info })
{
if (false == proc.Start())
throw new Exception("Didn't start R");
return proc.StandardOutput.ReadToEnd();
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
return string.Empty;
}
}
Related
We are trying to execute an R script from C# for .NET core GET API in visual studio and are not able to return any status from R script (as execution status). Below is what we have tried.
Versions - .NET Core 3.1, R 4.2
C# Code:
[HttpGet]
public IActionResult RunRScript()
{
try
{
var rpath = #"C:\Users\AppData\Local\Programs\R\R-4.2.0\bin\Rscript.exe";
string FilePath = Directory.GetCurrentDirectory()
+ "\\RScripts\\Sample.R";
var output = RFromCmd(rpath, FilePath, "");
return Ok(output);
}
catch (Exception e)
{
return BadRequest(e.Message.ToString());
}
}
public static string RFromCmd(string rScriptExecutablePath,
string rCodeFilePath, string args)
{
string result = string.Empty;
try
{
var info = new ProcessStartInfo();
info.FileName = rScriptExecutablePath;
info.WorkingDirectory = Path.GetDirectoryName(rScriptExecutablePath);
info.Arguments = rCodeFilePath;
info.RedirectStandardInput = false;
info.RedirectStandardOutput = true;
info.UseShellExecute = false;
info.CreateNoWindow = true;
using (var proc = new Process())
{
proc.StartInfo = info;
proc.Start();
}
}
}
Sample.R
getwd()
#Dummy data
sample <- data.frame(Date = seq(as.Date("2000/1/1"), by = "month",
length.out = 200), Volume = rnorm(200))
summary(sample)
#To save the Data in csv format
write.csv(sample, "Sample_data.csv")
View(summary(sample))
#Trying to include return statement
end_of_script <- function() {
x = 1 #also tried string "complete"
return(x)
}
end_of_script()
Please let me know what we are missing or doing wrong.
The R script is a test script, based on the success of this we will be running different R Scripts using this method.
Instead of running it like this you can build the restapi with one endpoint using R ,and can call the endpoint in c#
Reference for building APIs in r .
https://www.google.com/amp/s/www.geeksforgeeks.org/building-rest-api-using-r-programming/amp/
I am trying to execute a simple function written in R using C# (Installed R.NET) as under
using Microsoft.Win32;
using RDotNet;
using System;
using System.IO;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
using (RegistryKey registryKey = Registry.LocalMachine.OpenSubKey(#"SOFTWARE\R-core\R"))
{
var envPath = Environment.GetEnvironmentVariable("PATH");
string rBinPath = (string)registryKey.GetValue("InstallPath");
string rVersion = (string)registryKey.GetValue("Current Version");
rBinPath = System.Environment.Is64BitProcess ? rBinPath + "\\bin\\x64" :
rBinPath + "\\bin\\i386";
Environment.SetEnvironmentVariable("PATH",
envPath + Path.PathSeparator + rBinPath);
}
using (REngine engine = REngine.CreateInstance("RDotNet"))
{
// Initializes settings.
engine.Initialize();
var myAddFunc = engine.Evaluate(#"addFunc <- function(a,b) {
a+b
}").AsFunction();
var sum = engine.Evaluate("#d <- myAddFunc(10,20))").AsNumeric();
engine.Evaluate("print(sum)");
Console.ReadKey();
}
}
}
}
But getting error at
var sum = engine.Evaluate("#d <- myAddFunc(10,20))").AsNumeric();
An unhandled exception of type 'System.ArgumentNullException' occurred in RDotNet.dll
Additional information: Value cannot be null.
What is the mistake that I am doing ?
Running the program in R-Console works fine
> addFunc <- function(a,b) {
+ a+b
+ }
> d<- addFunc(30,40)
> print(d)
[1] 70
typo here? # should be outside of the string. Also it should not be closed by 2 parenthesises.
"#d <- myAddFunc(10,20))"
should be
#"d <- myAddFunc(10,20)"
I know I am late by more than 2 years for this, have you fixed it?
Anyways, try this
using (REngine engine = REngine.CreateInstance("RDotNet"))
{
// Initializes settings.
engine.Initialize();
var myAddFunc = engine.Evaluate(#"addFunc <- function(a,b) {
a+b
}").AsFunction();
var sum = engine.Evaluate(#"addFunc(12,30)").AsNumeric();
engine.Evaluate("print(sum)");
Console.Write(sum[0].ToString());
Console.ReadKey();
}
you need to use addFunc instead of myAddFunc at second Evaluate statement (i.e.
var sum = engine.Evaluate(#"addFunc(12,30)").AsNumeric();
)
I want to free a TCP port during startup of my application (asking confirmation to user), how to get the PID number and then, if the user confirm, kill it?
I know I can get this information by netstat, but how to do it in a script or better in a C# method.
You can run netstat then redirect the output to a text stream so you can parse and get the info you want.
Here is what i did.
Run netstat -a -n -o as a Process
redirect the standard out put and capture the output text
capture the result, parse and return all the processes in use
check if the port is being used
find the process using linq
Run Process.Kill()
you will have to do the exception handling.
namespace test
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
static void Main()
{
Console.WriteLine("Port number you want to clear");
var input = Console.ReadLine();
//var port = int.Parse(input);
var prc = new ProcManager();
prc.KillByPort(7972); //prc.KillbyPort(port);
}
}
public class PRC
{
public int PID { get; set; }
public int Port { get; set; }
public string Protocol { get; set; }
}
public class ProcManager
{
public void KillByPort(int port)
{
var processes = GetAllProcesses();
if (processes.Any(p => p.Port == port))
try{
Process.GetProcessById(processes.First(p => p.Port == port).PID).Kill();
}
catch(Exception ex)
{
Console.WriteLine(ex.Message);
}
else
{
Console.WriteLine("No process to kill!");
}
}
public List<PRC> GetAllProcesses()
{
var pStartInfo = new ProcessStartInfo();
pStartInfo.FileName = "netstat.exe";
pStartInfo.Arguments = "-a -n -o";
pStartInfo.WindowStyle = ProcessWindowStyle.Maximized;
pStartInfo.UseShellExecute = false;
pStartInfo.RedirectStandardInput = true;
pStartInfo.RedirectStandardOutput = true;
pStartInfo.RedirectStandardError = true;
var process = new Process()
{
StartInfo = pStartInfo
};
process.Start();
var soStream = process.StandardOutput;
var output = soStream.ReadToEnd();
if(process.ExitCode != 0)
throw new Exception("somethign broke");
var result = new List<PRC>();
var lines = Regex.Split(output, "\r\n");
foreach (var line in lines)
{
if(line.Trim().StartsWith("Proto"))
continue;
var parts = line.Split(new char[]{' '}, StringSplitOptions.RemoveEmptyEntries);
var len = parts.Length;
if(len > 2)
result.Add(new PRC
{
Protocol = parts[0],
Port = int.Parse(parts[1].Split(':').Last()),
PID = int.Parse(parts[len - 1])
});
}
return result;
}
}
}
Since I installed the new Windows Azure SDK 2.3 I got a warning from csrun:
"DevStore interaction through CSRun has been depricated. Use WAStorageEmulator.exe instead."
So there are two questions:
1) How to start the new storage emulator correctly from code?
2) How to determine from code if the storage emulator is already running?
I found the solution myself. Here is my C# code. The old code used for SDK 2.2 is commented out.
public static void StartStorageEmulator()
{
//var count = Process.GetProcessesByName("DSServiceLDB").Length;
//if (count == 0)
// ExecuteCSRun("/devstore:start");
var count = Process.GetProcessesByName("WAStorageEmulator").Length;
if (count == 0)
ExecuteWAStorageEmulator("start");
}
/*
private static void ExecuteCSRun(string argument)
{
var start = new ProcessStartInfo
{
Arguments = argument,
FileName = #"c:\Program Files\Microsoft SDKs\Windows Azure\Emulator\csrun.exe"
};
var exitCode = ExecuteProcess(start);
Assert.AreEqual(exitCode, 0, "Error {0} executing {1} {2}", exitCode, start.FileName, start.Arguments);
}
*/
private static void ExecuteWAStorageEmulator(string argument)
{
var start = new ProcessStartInfo
{
Arguments = argument,
FileName = #"c:\Program Files (x86)\Microsoft SDKs\Windows Azure\Storage Emulator\WAStorageEmulator.exe"
};
var exitCode = ExecuteProcess(start);
Assert.AreEqual(exitCode, 0, "Error {0} executing {1} {2}", exitCode, start.FileName, start.Arguments);
}
private static int ExecuteProcess(ProcessStartInfo start)
{
int exitCode;
using (var proc = new Process { StartInfo = start })
{
proc.Start();
proc.WaitForExit();
exitCode = proc.ExitCode;
}
return exitCode;
}
using System;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using Xunit;
namespace UnitTests.Persistence
{
public class AzureStorageEmulatorManagerV3
{
private const string ProcessName = "WAStorageEmulator";
public static void StartStorageEmulator()
{
var count = Process.GetProcessesByName(ProcessName).Length;
if (count == 0)
ExecuteWAStorageEmulator("start");
}
public static void StopStorageEmulator()
{
Process process = GetWAstorageEmulatorProcess();
if (process != null)
{
process.Kill();
}
}
private static void ExecuteWAStorageEmulator(string argument)
{
var start = new ProcessStartInfo
{
Arguments = argument,
FileName = #"c:\Program Files (x86)\Microsoft SDKs\Windows Azure\Storage Emulator\WAStorageEmulator.exe"
};
var exitCode = ExecuteProcess(start);
if (exitCode != 0)
{
string message = string.Format(
"Error {0} executing {1} {2}",
exitCode,
start.FileName,
start.Arguments);
throw new InvalidOperationException(message);
}
}
private static int ExecuteProcess(ProcessStartInfo start)
{
int exitCode;
using (var proc = new Process { StartInfo = start })
{
proc.Start();
proc.WaitForExit();
exitCode = proc.ExitCode;
}
return exitCode;
}
public static Process GetWAstorageEmulatorProcess()
{
return Process.GetProcessesByName(ProcessName).FirstOrDefault();
}
[Fact]
public void StartingAndThenStoppingWAStorageEmulatorGoesOk()
{
// Arrange Start
AzureStorageEmulatorManagerV3.StartStorageEmulator();
// Act
Thread.Sleep(2000);
Process WAStorageEmulatorProcess = GetWAstorageEmulatorProcess();
// Assert
Assert.NotNull(WAStorageEmulatorProcess);
Assert.True(WAStorageEmulatorProcess.Responding);
// Arrange Stop
AzureStorageEmulatorManagerV3.StopStorageEmulator();
Thread.Sleep(2000);
// Act
WAStorageEmulatorProcess = GetWAstorageEmulatorProcess();
// Assert
Assert.Null(WAStorageEmulatorProcess);
}
}
}
See my answer here. It actually uses the WAStorageEmulator status API instead of simply relying on testing whether or not the process exists as in #huha's own answer.
See my answer here. It uses a neat little NuGet package to assist with starting/stopping the Azure Storage Emulator programmatically: RimDev.Automation.StorageEmulator.
I'm working on writing a program to batch convert video files using HandbrakeCLI as the converter. I've got most of it set up and am actually working on passing the file to Handbrake now. I create a new process with the location of HandbrakeCLI and pass the arguments. I also make it so it executes in the same shell. It spins up and then starts to go through the converting and gets to three frames or so and kills itself. I'm using Visual Studio 12 on Windows 8 64 bit. Here's my code:
static void EncodeVideos()
{
var continueConverting = true;
while (continueConverting)
{
var converted = 0;
if (settings.Optimize == true)
{
videos = videos.OrderBy(x => x.InputSize).ToList();
}
foreach (var v in videos)
{
if (!v.AlreadyConverted())
{
v.CreateOutputPath();
var input = String.Format("-i \"{0}\" ", v.InputPath);
var output = String.Format("-o \"{0}\" ", v.OutputPath);
var preset = String.Format("-Z {0}", settings.Preset);
var convertString = String.Format(" {0} {1} {2}", input, output, preset);
//Converting is not working correctly yet.
var p = new Process();
p.StartInfo = new ProcessStartInfo(settings.HandBrakeLocation, convertString)
{
UseShellExecute = false,
};
p.Start();
p.WaitForExit();
converted++;
}
}
if (settings.Loop == true)
{
if (converted == 0)
{
continueConverting = false;
}
}
else
{
continueConverting = false;
}
}
}
If you would like more context for the code, I've put it all on github and you can find it on Github.
Edit: Fixed code
if (settings.Loop == true)
{
if (converted == 0)
{
continueConverting = false;
}
continueConverting = false;
}
else
{
continueConverting = false;
}
I guess you REALLY don't want to continue converting!
This looks like it's incorrect for a start.