I have a problem with executing python script over ssh (using c# and SharpSSH).
I have connection and can write simple commands like "startx" and "sudo shutdown ..."
When I try to run a python script freezes the program and nothing happens. I have tried exactly the same procedure with "putty" and it work as expected.
The python script runs and i get data (print 'No Laser, fast movement: Dx=', stepx, ' Dy=', stepy) whitch is importen for my application.
C# SharpSSH connection
ssh = new SshStream(host, user, password);
//Set the end of response matcher character
ssh.Prompt = "#";
//Remove terminal emulation characters
ssh.RemoveTerminalEmulationCharacters = true;
//Reading from the SSH channel
string response = ssh.ReadResponse();
return response;
C# start Gcode_executer.py does not work and freezes program(on pc) raspberry pi dont run python file
try
{
ssh.Write("sudo python cnc/Gcode_executer.py"); // Write ssh to RPI
// works it in putty
if (ssh.CanRead == true)
{
return ssh.ReadResponse(); // return string from terminal from RPI
}
else
{
return "No response";
}
}
catch
{
return "Catch NO response";
}
What I can do to solve the problem?
Try writing a shell script (on the pi) that runs the .py file and calling that shell script.
It works now.
My python script have sub files. therefore must be cd Into the folder until it has the correct path to sub files
Related
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.
I'm currently doing an assignment where I want my program to be able to read and update a database. The database itself runs on oracle and was provided by my university (I have my own schema I believe?)
Right now I can connect via SSH using programs such as teraterm or putty, once I log in it takes me to an interactive menu which allows me to select a few various options. One of which is shell. Once I select that I am able to use bash commands to log into the SQL section and use these:
bash$ export ORACLE_HOME=/opt/oracle/product/client/11.2.0
bash$ export TWO_TASK=SSID
bash$ $ORACLE_HOME/bin/sqlplus
to connect to the SQL database. Easy.
However, I want to be able to do that through my program and it is proving difficult for me. I am using SSH.NET and can connect via SSH seemingly well. The problem is I cannot access the SQL section. When I run the commands the first two work correctly I believe, but the last one does not. It seems to not be able to see the anything past $ORACLE_HOME. When I "echo $ORACLE_HOME /*" it even tells me that /bin is a folder:
/bin /boot /dev /etc /export /hey.php /home /lib /lib64 /local /lost+found /media /misc
/mnt /opt /proc /root /sbin /selinux /srv /stage /sys /tmp /usr /var
But instead, when I run the last line of code I get the error message:
Error = "bash: /bin/sqlplus: No such file or directory\n"
I'm not sure whether there is an easier way of accessing the SQL stuff... But I am very close using SSH.NET but I just can't see why I can't open the SQL section like I can in putty or teraterm...
Any help would be greatly appreciated, thank you for your time.
My actual C# code is this:
//Connection information
string user = "SSHusername";
string pass = "password";
string host = "address";
//Set up the SSH connection
using (var client = new SshClient(host, user, pass))
{
//Start the connection
client.Connect();
var output = client.RunCommand("export ORACLE_HOME=/opt/oracle/product/client/11.2.0");
Console.WriteLine(output.Result);
output = client.RunCommand("export TWO_TASK=SSID");
Console.WriteLine(output.Result);
output = client.RunCommand("$ORACLE_HOME/bin/sqlplus");
Console.WriteLine(output.Result);
output = client.RunCommand("username");
Console.WriteLine(output.Result);
output = client.RunCommand("password");
Console.WriteLine(output.Result);
output = client.RunCommand("SELECT * FROM users;");
Console.WriteLine(output.Result);
client.Disconnect();
Console.WriteLine(output.Result);
}
1.I suggest you use native C# package for connect Oracle. You will get wrong format of output.
I see your variable is not work. Because SQLPLUS client should be under
$ORACLE_HOME/bin/sqlplus. But your code show /bin/sqlplus. Means $ORACE_HOME not work.
You can directly change code and run directly sqlplus like /opt/oracle/product/client/11.2.0/bin/sqlplus user/pass#SSID
You can set some script on remote oracle server and get result over that or upload script from C# host to remote each time.
If you're using the SSH.NET library, using a Shell instead of separate Commands should work - something like (untested):
using (var client = new SshClient(host, user, pass)) {
client.Connect();
client.CreateCommand("export ORACLE_HOME=/opt/oracle/product/client/11.2.0").Execute();
client.CreateCommand("export TWO_TASK=SSID").Execute();
client.CreateCommand("$ORACLE_HOME/bin/sqlplus").Execute());
...
client.Disconnect();
}
Original source code found at SSH.NET example
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.
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
I need to be able to execute a PS1 script that resides on a remote machine against another remote machine through a C# runspace.
To be clear what I mean by this: The service I'm creating resides on server A. It creates a remote runspace to server B using the method below. Through the runspace I'm trying to call a script residing on server C against server B. If it helps, currently server A IS server C, but it's not guaranteed that will always be the case.
Here's the method I'm using to make the remote call:
internal Collection<PSObject> RunRemoteScript(string remoteScript, string remoteServer, string scriptName, out bool scriptSuccessful)
{
bool isLocal = (remoteServer == "localhost" || remoteServer == "127.0.0.1" || remoteServer == Environment.MachineName);
WSManConnectionInfo connectionInfo = null;
if (!isLocal)
{
connectionInfo = new WSManConnectionInfo(new Uri("http://" + remoteServer + ":5985"));
}
PsHostImplementation myHost = new PsHostImplementation(scriptName);
using (Runspace remoteRunspace = (isLocal ? RunspaceFactory.CreateRunspace(myHost) : RunspaceFactory.CreateRunspace(myHost, connectionInfo)))
{
remoteRunspace.Open();
using (PowerShell powershell = PowerShell.Create())
{
powershell.Runspace = remoteRunspace;
Pipeline pipeline = remoteRunspace.CreatePipeline();
pipeline.Commands.AddScript(remoteScript);
Collection<PSObject> results = pipeline.Invoke();
remoteRunspace.Close();
scriptSuccessful = myHost.ScriptSuccessful;
return results;
}
}
}
"remoteScript" is set to the Powershell script I want to run. For example:
"& \"\\\\remoteserveraddress\\PathToScript\\Install.ps1\" -Parameter;Import-Module Modulename;CustomCommand-FromModule -parameter(s) -ErrorAction stop"
If I'm on the remote machine that I want to run the script on, in the powershell console I can just give the following command:
& "\\remoteserverC\PathToScript\Install.ps1" -Parameter
However this simply refuses to work for me if I try to run it through the c# runspace.
If I send in the following as a parameter to "remoteScript":
"& \"\\\\remoteserverC\\PathToScript\\Install.ps1\" -Parameter"
I get the following error:
The term '\remoteserverC\PathToScript\Install.ps1' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
I've tried with and without '&' and with and without the parameter. I can already call a script that resides directly on the remote machine "c:\...\Install.ps1" instead of "\\remoteserver\...\Install.ps1", but it would be greatly beneficial to be able to call the remote script directly.
I've searched many many pages in google and here on stackoverflow, but I haven't been able to find anything that helps to overcome this issue. Any help would be appreciated! Thanks!
I never did get this to work directly and seems to be a security "feature" that you can't access a third machine using a UNC address while working remotely. I was however able to find a workaround that worked great for me.
Instead of trying to call directly to a \\server\share address, I dynamically map a network drive on the machine I'm trying to run the script against to a share on the machine that has the script. (Running remotely from A, map a drive on B to a share on C). Then I call my scripts through that drive and it works like a charm. This string is what I pass in to the RunRemoteScript method above:
"$net = new-object -ComObject WScript.Network;" +
"if(!($net.EnumNetworkDrives() -contains \"S:\"))" +
"{Write-Host \"S: Drive Not Currently Mapped. Mapping to \\\\" + RemoteServerC + "\\Share.\";" +
"$net.MapNetWorkDrive(\"S:\",\"\\\\" + RemoteServerC + "\\Share\",$false,\"username\",\"password\")};" +
"Get-PSDrive | Write-Verbose;" +
"& \"S:\\PathToScript\\Install.ps1\" -noPrompt;"
RemoteServerC is a variable I pass in that is defined in a user config file.
Here is the same code as just powershell script if anyone needs it (replacing RemoteServerC with a powershell variable you'd need to set before or just hardcode:
$net = new-object -ComObject WScript.Network
if(!($net.EnumNetworkDrives() -contains "S:"))
{ Write-Host "S: Drive Not Currently Mapped. Mapping to \\remoteserverC\Share."
$net.MapNetWorkDrive("S:","\\remoteserverC\Share",$false,"username","password")
}
Get-PSDrive | Write-Verbose
& "S:\\PathToScript\\Install.ps1\" -noPrompt;"
First I set up the object to be able to map the drive. I then check if there is already an "s" drive mapped on the remote machine. If it hasn't I then map the share to the "s" drive on the remote machine using a name and password we set up in active directory to run this service.
I included the Get-PSDrive command because it appears to force powershell to reload the list of available drives. This seems to only matter the very first time you try to run this in a powershell session (and through c sharp I don't know if it is truly necessary or not, I included it to be safe). Apparently powershell does not recognize a drive addition in the same session it was created if you use MapNetworkDrive.