C# app to call Python script that uses USB-to-CAN transceiver - c#

Just wondering if someone out there might have some insight to what's going wrong here...
I have a python script that connects to a USB-to-CAN transceiver/dongle (made by PEAK System) to do some CAN communications. The script works pretty flawlessly. The script accepts command-line arguments and works fine when called from the Windows command-line.
I am trying to integrate this script into a C# Forms project. I have been successful at calling the Python script from the C# app, but things fall apart when it gets to the point at which the Python script tries to use the CAN transceiver.
It feels like the C# app front-end is not allowing the Python script to access the serial port.
Here is the error I get (Python script writing to StandardOut on the Visual Studio output):
line 86, in canSendRec
self.bus.send(canMessage, timeout=0.1)
AttributeError: 'Node' object has no attribute 'bus'
Unable to Connect to USB-CAN Device
Here is the line from canSendRec -- where the exception handler came from (which we wrote):
try:
self.bus = can.interface.Bus('PCAN_USBBUS1',bitrate=1000000)
self.bus.flush_tx_buffer()
except:
print("Unable to Connect to USB-CAN Device")
Here is my C# code calling the Python script:
public string pythonMakeCall(string script, string arg1){
ProcessStartInfo pyProcessStartInfo = new ProcessStartInfo(py_path);
pyProcessStartInfo.FileName = py_path;
pyProcessStartInfo.Arguments = string.Format("{0} {1}", script, arg1);
pyProcessStartInfo.CreateNoWindow = true;
pyProcessStartInfo.UseShellExecute = false;
pyProcessStartInfo.RedirectStandardOutput = true;
pyProcessStartInfo.RedirectStandardError = true;
Process pyProcess = new Process();
pyProcess.StartInfo = pyProcessStartInfo;
pyProcess.Start();
retString = pyProcess.StandardOutput.ReadToEnd();
pyProcess.WaitForExit();
return retString;}
Like I said, it feels like there is something going on with the C# app not allowing Python to access the USB ports, but I'm not sure where to begin with debugging that hunch, since, Disclaimer: this is my first time dabbling in C#/Visual Studio and I'm no Python expert either.

Related

Communication between Python and C#

I have a Python backend running machine learning algorithms. I want to use the same backend for both an Excel plugin (C#) and a website. I want both interfaces to send my training data (thousands of lines of numbers in arrays) to the same Python application and retrieve the results in the form of another array up to a few thousand lines.
The website would fetch data from a SQL database and send that data to Python, while the Excel plugin would take the data that is in the current worksheet and send that data to Python. I need to be able to create numpy arrays in Python before continuing to process the data. Note that the website would be running on the same machine where the Python application resides. I still haven't decided what I will use to code the website, but I was leaning towards Node.js.
I have done some research and found a few options:
1- Named pipes
2- Sockets
3- RPC server such as gRPC or XML-RPC.
4- Writing the data to a file and reading it back in Python
5- Web Service
Note: I would need the Python "server" to be stateful and keep the session running between calls. So I would need to have a kind of daemon running, waiting for calls.
Which one would you experts recommend and why? I need flexibility to handle several parameters and also large arrays of numbers. Using IronPython is not an option because I am running Keras on Python, which apparently does not support IronPython.
I had the same problem recently.
I used a named pipe to transport data from python to my c# server, hope it helps you.
Python:
import win32pipe, win32file
class PipeServer():
def __init__(self, pipeName):
self.pipe = win32pipe.CreateNamedPipe(
r'\\.\pipe\\'+pipeName,
win32pipe.PIPE_ACCESS_OUTBOUND,
win32pipe.PIPE_TYPE_MESSAGE | win32pipe.PIPE_READMODE_MESSAGE | win32pipe.PIPE_WAIT,
1, 65536, 65536,
0,
None)
#Carefull, this blocks until a connection is established
def connect(self):
win32pipe.ConnectNamedPipe(self.pipe, None)
#Message without tailing '\n'
def write(self, message):
win32file.WriteFile(self.pipe, message.encode()+b'\n')
def close(self):
win32file.CloseHandle(self.pipe)
t = PipeServer("CSServer")
t.connect()
t.write("Hello from Python :)")
t.write("Closing now...")
t.close()
For this code to work you need to install pywin32 (best choice is from binarys): https://github.com/mhammond/pywin32
C#-Server:
using System;
using System.IO;
using System.IO.Pipes;
class PipeClient
{
static void Main(string[] args)
{
using (NamedPipeClientStream pipeClient =
new NamedPipeClientStream(".", "CSServer", PipeDirection.In))
{
// Connect to the pipe or wait until the pipe is available.
Console.Write("Attempting to connect to pipe...");
pipeClient.Connect();
Console.WriteLine("Connected to pipe.");
Console.WriteLine("There are currently {0} pipe server instances open.",
pipeClient.NumberOfServerInstances);
using (StreamReader sr = new StreamReader(pipeClient))
{
// Display the read text to the console
string temp;
while ((temp = sr.ReadLine()) != null)
{
Console.WriteLine("Received from server: {0}", temp);
}
}
}
Console.Write("Press Enter to continue...");
Console.ReadLine();
}
}
You can use Python for .NET (Python.NET). It may require some changes to your code, but then it should work very well, once everything is in good shape.
Python.NET allows two-way communication between CPython and CLR.
Let me give you a neat and quick recipe, in the form of example code.
There are basically two ways to tie python in the backend of C# (or a C# winform app or gui or something similar).
Method1: Iron Python. In this method you install a .net package in your visual studio called IronPython. I would not prefer this, because assuming your machine learning model uses keras or a lot of other libraries. It would be another quest to get you installations ready and working in IronPython. And most importantly, it is not as good as your common virtual env or conda environment.
Method2: (The Good Method): Create a Custom Process in your C# that takes arguments from your GUI, knows the path to your script and your python env. Using all these things, it calls your python code exactly the way you would call it in your terminal and pass arguments to it.
Now the tasty example code (I have used this simple trick and it always helps make my black screen python stuff look good with the cover of C# apps).
Python Part
import sys
a = sys.argv[1]
b = sys.argv[2]
print("The Sum = ", float(a)+float(b))
The C# Part
So here is the python process/function that you need to call on the click event of your sum button in the application
static void PythonProcess()
{
//1) Create Process Info
var psi = new ProcessStartInfo();
//Conda Env Path
psi.FileName = #"C:\Users\jd\.conda\pkgs\py\python.exe";
//2) Provide Script and the Arguments
var script = #"C:\Users\jd\Desktop\script.py";
var a = "15";
var b = "18";
psi.Arguments = $"\"{script}\" \"{a}\" \"{b}\"";
//3) Process Configuration
psi.UseShellExecute = false;
psi.CreateNoWindow = true;
psi.RedirectStandardOutput = true;
psi.RedirectStandardError = true;
//4) Execute Process and get Output.
var errors = "";
var results = "";
using(var process = Process.Start(psi))
{
errors = process.StandardError.ReadToEnd();
results = process.StandardOutput.ReadToEnd();
}
//5) Display Output
Console.WriteLine("ERRORS: ");
Console.WriteLine(errors);
Console.WriteLine();
Console.WriteLine("RESULTS: ");
Console.WriteLine(results);
}
Calling Python from C# is easily possible via Pyrolite where your Python code is running as a Pyro4 server. It should be fast enough to handle "large arrays of numbers" however you didn't specify any performance constraints.
I had the same issue and seem to end up with named pipes. Here is a nice example of how to set it up to talk C# => Python, assuming C# is the server.
It can use the same way to talk back or just Python.net to call directly through CLR as shown here. I use the latter.

Executing python script with zmq from C# using IronPython not working

I'm using ZeroMQ for inter-process communication between C# managed application and python script. When calling script from C#, using IronPython I get following error :
An unhandled exception of type
IronPython.Runtime.Exceptions.ImportException occurred in
Microsoft.Dynamic.dll
Additional information: cannot import constants from
zmq.backend.cython
Python code (test.py file):
import sys, zmq, time
def execute(input):
context = zmq.Context()
publisher = context.socket(zmq.PUB)
publisher.bind("tcp://*:18800")
time.sleep(1)
publisher.send(input) #just echo input parameter
And this is how I execute .py code from my C# app :
static void Main(string[] args)
{
var options = new Dictionary<string, object>();
options["Frames"] = true;
options["FullFrames"] = true;
ScriptEngine _engine = Python.CreateEngine(options);
ScriptRuntime _runtime = _engine.Runtime;
ICollection<string> _searchPaths = _engine.GetSearchPaths();
_searchPaths.Add(#"C:\Python27\");
_searchPaths.Add(#"C:\Python27\Lib\");
_searchPaths.Add(#"C:\Python27\Scripts\");
_searchPaths.Add(#"C:\Python27\libs\");
_searchPaths.Add(#"C:\Python27\DLLs\");
_searchPaths.Add(#"C:\Python27\include\");
_searchPaths.Add(#"C:\Python27\lib\site-packages");
_engine.SetSearchPaths(_searchPaths);
dynamic wrapperObj = _runtime.UseFile("test.py");
wrapperObj.execute("test");
}
Note that some code is omitted for brevity, and not actually relevant for this scenario. When manually invoking .py script through the command line, everything works fine, I'm able to pass data around and my C# app is receiving messages.
Anyone knows what is this error and how can be solved?
EDIT : While I was desperate and almost gave up, I tried to write quick named-pipes support, and guess what - similar error:
An unhandled exception of type IronPython.Runtime.Exceptions.ImportException occurred in
Microsoft.Dynamic.dll
Additional information: No module named win32file
This time, my test.py looks like this:
import win32file
def execute(input):
handle = win32file.CreateFile(r"\\.\pipe\test_pipe",
win32file.GENERIC_WRITE,
0, None,
win32file.OPEN_EXISTING,
0, None)
if handle:
win32file.WriteFile(self.handle, input, None)
win32file.FlushFileBuffers(self.handle)
win32file.SetFilePointer(self.handle, 0, win32file.FILE_BEGIN)
I'm not really sure if I have more options for this kind of interoperability, but I'm starting to think that IronPython can just cover basic usage scenarios (when calling pure python code from C#), not to mention matplotlib, numpy or astropy that I will certanly need later on.
Thanks in advance!
Regards,
Civa

Embedding IronPython and overwriting Environment Variables

I embed an IronPython interpreter in a C# application to execute a Python script. The script evaluates a specific environment variable VAR1 using os.environ['VAR1']
I need to change the value of VAR1 temporarily before the script is executed. To do this I call
Environment.SetEnvironmentVariable("VAR1", "NEW_VALUE");
in the C# code. Unfortunately this does not work. The Python script still "sees" the old value of VAR1 (the value it had when the hosting C# application started).
Here is a complete example code:
Environment.SetEnvironmentVariable("VAR1", "NEW_VALUE");
var engine = Python.CreateEngine();
engine.SetSearchPaths(new string[] { #"c:\Program Files (x86)\IronPython 2.7\Lib\" });
var ms = new MemoryStream();
engine.Runtime.IO.SetOutput(ms, Encoding.Unicode);
var script = engine.CreateScriptSourceFromString("import os\nprint os.environ['VAR1']");
script.Execute();
System.Windows.Forms.MessageBox.Show(Encoding.Unicode.GetString(ms.ToArray()));
My understanding is that the IronPython engine runs in the same process as the C# host application. If this is correct, how can the Python code and the C# code see different environment variables?
Is there a better (working) way to set an environment variable for the Python script?
Well, I still don't know why the above code doesn't work but one can simply set an environment variable in the Python environment by executing Python code that sets the variable BEFORE executing the real code:
engine.CreateScriptSourceFromString("import os\nos.environ['VAR1'] = 'NEW_VALUE'").Execute();
var CodeIReallyWantToRun = engine.CreateScriptSourceFromFile(...);
CodeIReallyWantToRun.Execute();

asp.net c# connecting to putty ssh and running a command line like status

ok folks i have seen alot of questions about this but none that i can use or understand
What i am attempting to do is connect to putty from asp.net c# and then run a command to get the status
i will then use the results to draw a report every 3 seconds and display it on my web page
this is the first time a have attempted this so i am rather ignorant
private void connect_putty()
{
Process sh = new Process();
sh.StartInfo.FileName = "Putty.exe";
sh.StartInfo.UseShellExecute = false;
sh.StartInfo.CreateNoWindow = false;
sh.StartInfo.Arguments = "";
}
what i presently have which to be honest is pathetic any help will be appreciated
Thanks in advance
I would suggest using Tamir.SSH.
This will allow you to do everything from C#.
Also, I wrote some code once, it may help you.
https://github.com/daneb/Push2Linux/blob/master/Form1.cs
Sample:
SshShell ssh; // create our shell
ssh = new SshShell(aHost.host, aHost.username, aHost.password);
// Command Output
string commandoutput = string.Empty;
// Remove Terminal Emulation Characters
ssh.RemoveTerminalEmulationCharacters = true;
// Connect to the remote server
ssh.Connect();
//Specify the character that denotes the end of response
commandoutput = ssh.Expect(promptRegex);
PuTTY includes all the terminal emulation (hence the name), so assuming you mean 'connect via ssh', instead of the putty app specifically, then SSH.NET and SharpSSH are 2 good choices.
See this related question: C# send a simple SSH command

Embedding Perl Interpreter

just downloaded ActivePerl. I want to embed the perl interpreter in a C# application (or at least call the perl interpreter from C#). I need to be able to send send out data to Perl from C#, then receive the output back into C#.
I just installed ActivePerl, and added MS Script Control 1.0 as a reference. I found this code on the internet, but am having trouble getting it to work.
MSScriptControl.ScriptControlClass Interpreter = new MSScriptControl.ScriptControlClass();
Interpreter.Language = #"ActivePerl";
string Program = #"reverse 'abcde'";
string Results = (string)Interpreter.Eval(Program);
return Results;
Originally, it had 'PerlScript' instead of 'ActivePerl', but neither work for me. I'm not entirely sure what Interpreter.Language expects. Does it require the path to the interpreter?
Solved... I'm not sure how, but when I changed it back to PerlScript it works now. Still, I would like to know if MSScript Control is using ActivePerl or another interpreter.
You can run an external program as Maxwell suggests, in which case the external program can be Perl or anything else. It might be easier to use temp files to send the input data and get the output, but that depends on how the external program expects to get its data.
The alternative, which is what I think you're looking for, is to use the PerlNET compiler that comes with ActiveState's Perl Dev Kit. It lets you add a class wrapper around the Perl code so you can expose it to C# just like any C# class. It's fairly simple to use; you add POD comments to your Perl code to specify the method names and signatures to expose, including type information, then you compile your Perl module into a DLL .NET assembly. Once that's done you can reference the assembly from any .NET program, construct an object from your Perl class, and call its methods.
I am not sure about the script control but I have done a similar thing where I had to 'embed' spamassasin (which is a Perl program). I basically used the Process to do the job. Something along the lines of:
var proc = new Process
{
StartInfo =
{
FileName = "perl",
WorkingDirectory = HttpRuntime.AppDomainAppPath,
Arguments = " myscript.pl arg1 arg2",
RedirectStandardOutput = true,
RedirectStandardError = true,
RedirectStandardInput = true,
UseShellExecute = false
}
};
proc.Start();
proc.StandardInput.BaseStream.Write... // feed STDIN
proc.StandardOutput.Read... // Read program output
var procStdErr = proc.StandardError.ReadToEnd(); // errors
proc.StandardError.Close();
proc.StandardOutput.Close();
proc.WaitForExit(3000);
int exitCode = proc.ExitCode;
proc.Close();
This obviously not just Perl specific and it has the process creation overhead, so if you are running your script too often probably you need to think of a different solution.

Categories

Resources