Move away from Python Interop for better stability
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Controllers.Processes {
|
||||
|
||||
public class PyProcess {
|
||||
|
||||
public static (bool, string) RunPythonProcess(string PyExecutable, string PyScript, bool returns = false, string PyArgs = "") {
|
||||
var start = new ProcessStartInfo {
|
||||
FileName = PyExecutable,
|
||||
Arguments = $"-u {PyScript} {PyArgs}",
|
||||
UseShellExecute = false,
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardError = true,
|
||||
CreateNoWindow = true
|
||||
};
|
||||
|
||||
using (Process? process = Process.Start(start)) {
|
||||
|
||||
// Try to start the process
|
||||
if (process == null) {
|
||||
return (false, "Failed to start the process");
|
||||
}
|
||||
|
||||
// Read the stdouts and wait for process to end
|
||||
string result = process.StandardOutput.ReadToEnd();
|
||||
string errors = process.StandardError.ReadToEnd();
|
||||
process.WaitForExit();
|
||||
|
||||
// If the process Errored
|
||||
if (process.ExitCode != 0) {
|
||||
return (false, $"Python Error : {errors}");
|
||||
}
|
||||
|
||||
// If the process is supposed to return
|
||||
if (returns) {
|
||||
string markerStart = "---RESULT_START---";
|
||||
string markerEnd = "---RESULT_END---";
|
||||
|
||||
int startPos = result.IndexOf(markerStart) + markerStart.Length;
|
||||
int endPos = result.IndexOf(markerEnd);
|
||||
|
||||
if (startPos > -1 && endPos > startPos) {
|
||||
string cleanResult = result.Substring(startPos, endPos - startPos).Trim();
|
||||
return (true, cleanResult);
|
||||
}
|
||||
} else {
|
||||
return (true, "");
|
||||
}
|
||||
|
||||
// Fail Safe
|
||||
return (false, "Result not found");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,65 +1,55 @@
|
||||
using Python.Runtime;
|
||||
using Controllers.Processes;
|
||||
|
||||
namespace Controllers.PythonInterop {
|
||||
|
||||
public class AIModule {
|
||||
|
||||
public AIModule(string PythonPathBase = "/usr/local/", string PythonVersion = "python3.11") {
|
||||
// Use the user provided python runner
|
||||
Runtime.PythonDLL = PythonPathBase + $"lib/lib{PythonVersion}.so";
|
||||
string _PyPath = "";
|
||||
string _ExecPath = "";
|
||||
|
||||
// Use our local environment for the python libraries
|
||||
PythonEngine.PythonHome = PythonPathBase;
|
||||
|
||||
// Include all the paths for python packages most importantly our venv
|
||||
PythonEngine.PythonPath = $"{PythonPathBase}lib/{PythonVersion}:{PythonPathBase}lib/{PythonVersion}/lib-dynload:{PythonPathBase}lib/{PythonVersion}/site-packages:{Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "AIPython")}";
|
||||
|
||||
// Initiilize python
|
||||
PythonEngine.Initialize();
|
||||
|
||||
// Needed because C# calls the python from each connections worker thread
|
||||
PythonEngine.BeginAllowThreads();
|
||||
public AIModule() {
|
||||
_PyPath = "/usr/bin/python3.11";
|
||||
_ExecPath = $"{Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "AIPython")}";
|
||||
}
|
||||
|
||||
public void PullAI() {
|
||||
using (Py.GIL()) {
|
||||
dynamic datapuller = Py.Import("datapuller");
|
||||
using (datapuller.pull()){ }
|
||||
(bool, string) Success = PyProcess.RunPythonProcess(_PyPath, _ExecPath + "/datapuller.py");
|
||||
if (!Success.Item1) {
|
||||
Console.WriteLine(Success.Item2);
|
||||
}
|
||||
}
|
||||
|
||||
public void TrainAI() {
|
||||
using (Py.GIL()) {
|
||||
dynamic trainer = Py.Import("ai-trainer");
|
||||
using (trainer.TrainAI()){ }
|
||||
(bool, string) Success = PyProcess.RunPythonProcess(_PyPath, _ExecPath + "/aitrainer.py");
|
||||
if (!Success.Item1) {
|
||||
Console.WriteLine(Success.Item2);
|
||||
}
|
||||
}
|
||||
|
||||
// Return ( Error, Signal )
|
||||
public (string, int) PredictAI(string StockSymbol) {
|
||||
try {
|
||||
using (Py.GIL()) {
|
||||
dynamic predictor = Py.Import("ai-predictor");
|
||||
using (dynamic x = predictor.Predict(StockSymbol)) {
|
||||
int result = (int)x;
|
||||
return ("", result);
|
||||
}
|
||||
(bool, string) Success = PyProcess.RunPythonProcess(_PyPath, _ExecPath + "/aipredictor.py", returns: true, PyArgs: StockSymbol);
|
||||
if (!Success.Item1) {
|
||||
return (Success.Item2, 0);
|
||||
} else {
|
||||
if (int.TryParse(Success.Item2, out int parsed)) {
|
||||
return ("", parsed);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
return (ex.ToString(), 0);
|
||||
return ("Python returns an unknown value", 0);
|
||||
}
|
||||
}
|
||||
|
||||
public float GetCurrentPrice(string StockSymbol) {
|
||||
using (Py.GIL()) {
|
||||
dynamic price = Py.Import("currentprice");
|
||||
using (dynamic x = price.getCurrentPrice(StockSymbol)) {
|
||||
float CurrentPrice = (float)x;
|
||||
return x;
|
||||
(bool, string) Success = PyProcess.RunPythonProcess(_PyPath, _ExecPath + "/currentprice.py", returns: true, PyArgs: StockSymbol);
|
||||
if (!Success.Item1) {
|
||||
return 0;
|
||||
} else {
|
||||
if (float.TryParse(Success.Item2, out float parsed)) {
|
||||
return parsed;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user