wsl support

This commit is contained in:
meelstorm
2025-08-28 16:30:29 +02:00
parent 07002dd875
commit bf13a5ea2d
4 changed files with 169 additions and 45 deletions

View File

@@ -8,6 +8,7 @@ public class CSharpDataTransferHTTP
private readonly HttpClient _client; private readonly HttpClient _client;
private readonly CancellationTokenSource _pingCts = new(); private readonly CancellationTokenSource _pingCts = new();
private readonly Thread _pingThread; private readonly Thread _pingThread;
private readonly object _lock = new();
public CSharpDataTransferHTTP() public CSharpDataTransferHTTP()
{ {
@@ -15,22 +16,17 @@ public class CSharpDataTransferHTTP
{ {
PooledConnectionIdleTimeout = TimeSpan.FromSeconds(10), PooledConnectionIdleTimeout = TimeSpan.FromSeconds(10),
MaxConnectionsPerServer = 100 MaxConnectionsPerServer = 100
}; };
_client = new HttpClient(handler) _client = new HttpClient(handler)
{ {
// i've added this
DefaultRequestHeaders = DefaultRequestHeaders =
{ {
ExpectContinue = false ExpectContinue = false
} }
}; };
_api = new PythonModelAPI("http://localhost:8000",_client); _api = new PythonModelAPI("http://localhost:8000", _client);
_pingThread = new Thread(PingLoop) { IsBackground = true }; _pingThread = new Thread(PingLoop) { IsBackground = true };
_pingThread.Start(); _pingThread.Start();
} }
private void PingLoop() private void PingLoop()
@@ -51,38 +47,40 @@ public class CSharpDataTransferHTTP
public void LoadModel(string path, string modelName) public void LoadModel(string path, string modelName)
{ {
_api.LoadModelAsync(new LoadModelIn() lock (_lock)
{ {
Name = modelName, _api.LoadModelAsync(new LoadModelIn()
Path = Path.GetFileName(path) {
}).Wait(); Name = modelName,
Path = Path.GetFileName(path)
}).Wait();
}
} }
public void ActivateModel(string modelName) public void ActivateModel(string modelName)
{ {
_api.ActivateModelAsync(new ActivateModelIn(){Name = modelName}).Wait(); lock (_lock)
{
_api.ActivateModelAsync(new ActivateModelIn() { Name = modelName }).Wait();
}
} }
public int[] GetAcceptSize() public int[] GetAcceptSize()
{ {
var response = _api.GetAcceptSizeAsync().Result; var response = _api.GetAcceptSizeAsync().Result;
return response.Accept_size.ToArray(); return response.Accept_size.ToArray();
} }
public int[] GetOutputSize() public int[] GetOutputSize()
{ {
var response = _api.GetOutputSizeAsync().Result; var response = _api.GetOutputSizeAsync().Result;
return response.Accept_size.ToArray(); return response.Accept_size.ToArray();
} }
public byte[] PredictRaw(byte[] byteArray) public byte[] PredictRaw(byte[] byteArray)
{ {
var response = _api.PredictRawAsync(new FileParameter(new MemoryStream(byteArray))).Result; var response = _api.PredictRawAsync(new FileParameter(new MemoryStream(byteArray))).Result;
var stream = response.Stream; var stream = response.Stream;
// read the stream to byte array
using var memoryStream = new MemoryStream(); using var memoryStream = new MemoryStream();
stream.CopyTo(memoryStream); stream.CopyTo(memoryStream);
memoryStream.Seek(0, SeekOrigin.Begin); memoryStream.Seek(0, SeekOrigin.Begin);
@@ -92,16 +90,15 @@ public class CSharpDataTransferHTTP
public byte[] Predict(byte[] byteArray) public byte[] Predict(byte[] byteArray)
{ {
var response = _api.PredictAsync(new FileParameter(new MemoryStream(byteArray))).Result; lock (_lock)
var stream = response.Stream; {
// read the stream to byte array var response = _api.PredictAsync(new FileParameter(new MemoryStream(byteArray))).Result;
using var memoryStream = new MemoryStream(); var stream = response.Stream;
stream.CopyTo(memoryStream); using var memoryStream = new MemoryStream();
memoryStream.Seek(0, SeekOrigin.Begin); stream.CopyTo(memoryStream);
var responseBytes = memoryStream.ToArray(); memoryStream.Seek(0, SeekOrigin.Begin);
return responseBytes; var responseBytes = memoryStream.ToArray();
return responseBytes;
}
} }
} }

View File

@@ -0,0 +1,78 @@
using System.Diagnostics;
using static IronPython.Modules._ast;
namespace Hawkeye.VisionBuilder.Workflow.DataTransfer;
public class DockerScriptRunner
{
private Process process;
private readonly string _workingDirectory;
public DockerScriptRunner(string workingDirectory)
{
_workingDirectory = workingDirectory;
}
public static string ConvertToWslPath(string windowsPath)
{
if (string.IsNullOrWhiteSpace(windowsPath))
throw new ArgumentException("Path cannot be null or empty.", nameof(windowsPath));
// Replace backslashes with forward slashes
string wslPath = windowsPath.Replace('\\', '/');
// Extract the drive letter and convert it to WSL format
if (wslPath.Length > 1 && wslPath[1] == ':')
{
char driveLetter = char.ToLower(wslPath[0]);
wslPath = $"/mnt/{driveLetter}{wslPath.Substring(2)}";
}
return wslPath;
}
public void Start()
{
var killContainerArg = @"-d ubuntu -u root -e sh -c ""docker rm -f cv || true""";
var killProcess = new Process
{
StartInfo = new ProcessStartInfo("wsl", killContainerArg)
{
UseShellExecute = false
}
};
killProcess.Start();
killProcess.WaitForExit();
killProcess.Dispose();
//startInfo.CreateNoWindow = true;
// create and start the process
process = new Process();
var convertedWd = ConvertToWslPath(_workingDirectory);
var arg =
$@"-d ubuntu -u root -e sh -c ""cd \""{convertedWd}\"" && docker run --rm --name cv --gpus all -it -p 8000:8000 -v \""$(pwd)\"":/app -w /app cv uvicorn --host 0.0.0.0 PythonModelAPI:app """;
Console.WriteLine("args:"+arg);
process.StartInfo = new ProcessStartInfo("wsl", arg)
{
UseShellExecute = false,
CreateNoWindow = true
};
process.Start();
// on windows only
if (Environment.OSVersion.Platform == PlatformID.Win32NT)
ChildProcessTracker.AddProcess(process);
}
public void Stop()
{
if (process != null && !process.HasExited)
{
process.Kill();
process.Dispose();
process = null;
}
}
}

View File

@@ -1,21 +1,53 @@
namespace Hawkeye.VisionBuilder.Workflow.DataTransfer; using Serilog;
namespace Hawkeye.VisionBuilder.Workflow.DataTransfer;
public class PythonModelProxyHTTP public class PythonModelProxyHTTP
{ {
private static bool _isInitialized = false; private static bool _isInitialized = false;
private static UvicornScriptRunner _pythonScriptRunner; private static DockerScriptRunner _pythonScriptRunner;
private static CSharpDataTransferHTTP _cSharpDataTransferHTTP; private static CSharpDataTransferHTTP _cSharpDataTransferHTTP;
static object _lock = new object();
public static void Initialize() public static void Initialize()
{ {
if (_isInitialized) return; lock (_lock)
{
if (_isInitialized) return;
_isInitialized = true;
var workDirAbsPath = Path.GetFullPath(@"..\Data\Models"); var workDirAbsPath = Path.GetFullPath(@"..\Data\Models");
_pythonScriptRunner = new UvicornScriptRunner(workDirAbsPath); _pythonScriptRunner = new DockerScriptRunner(workDirAbsPath);
_cSharpDataTransferHTTP = new CSharpDataTransferHTTP();
_pythonScriptRunner.Start();
Thread.Sleep(1000); // Wait for the server to start _pythonScriptRunner.Start();
// Wait for the server to start by checking if port 8000 is open
while (true)
{
try
{
HttpClient http = new HttpClient();
// check if 404 occurs on localhost:8000
var response = http.GetAsync("http://127.0.0.1:8000/").Result;
if (response.StatusCode == System.Net.HttpStatusCode.NotFound ||
response.StatusCode == System.Net.HttpStatusCode.OK)
{
break; // Port is open
}
}
catch
{
Log.Debug("Waiting for port to open");
// Ignore exceptions and retry
}
Thread.Sleep(1000); // Wait before retrying
}
Thread.Sleep(2000);
_cSharpDataTransferHTTP = new CSharpDataTransferHTTP();
}
} }
public static CSharpDataTransferHTTP GetInterface() public static CSharpDataTransferHTTP GetInterface()

View File

@@ -16,7 +16,7 @@ public class RawModelAIOperation: BaseOperation
public RawModelAIOperation() public RawModelAIOperation()
{ {
_cSharpDataTransferMQRPC = PythonModelProxyHTTP.GetInterface();
CanHaveProcessingError = false; CanHaveProcessingError = false;
} }
@@ -46,6 +46,7 @@ public class RawModelAIOperation: BaseOperation
protected override void InterpretInternal(Context context) protected override void InterpretInternal(Context context)
{ {
_cSharpDataTransferMQRPC = PythonModelProxyHTTP.GetInterface();
if (!CheckImageExists(context)) return; if (!CheckImageExists(context)) return;
if (!CheckColorful(context)) return; if (!CheckColorful(context)) return;
@@ -53,7 +54,7 @@ public class RawModelAIOperation: BaseOperation
{ {
if (!File.Exists(ModelFilePath?.Path)) if (!File.Exists(ModelFilePath?.Path))
{ {
this.SetError("Model file not found"); this.SetError($"Model file \"{ModelFilePath?.Path}\" not found");
return; return;
} }
@@ -74,31 +75,47 @@ public class RawModelAIOperation: BaseOperation
_cSharpDataTransferMQRPC.ActivateModel(_modelName); _cSharpDataTransferMQRPC.ActivateModel(_modelName);
var sizeEncoded = _cSharpDataTransferMQRPC.GetAcceptSize(); int[] sizeEncoded=[];
var size = sizeEncoded.Select(Convert.ToInt32).ToArray(); int[] outputSize = [];
_imageWidth = size[0]; sizeEncoded = _cSharpDataTransferMQRPC.GetAcceptSize();
_imageHeight = size[1]; outputSize = _cSharpDataTransferMQRPC.GetOutputSize();
var outputSize = _cSharpDataTransferMQRPC.GetOutputSize();
var outputSizeArray = outputSize.Select(Convert.ToInt32).ToArray();
var currentImage = context.ActiveImage; var currentImage = context.ActiveImage;
var rightColor = currentImage.ImageData.CvtColor(ColorConversionCodes.BGR2RGB); var rightColor = currentImage.ImageData.CvtColor(ColorConversionCodes.BGR2RGB);
var size = sizeEncoded.Select(Convert.ToInt32).ToArray();
_imageWidth = size[0];
_imageHeight = size[1];
var resized = rightColor.Resize(new Size(_imageHeight, _imageWidth)); var resized = rightColor.Resize(new Size(_imageHeight, _imageWidth));
int dataSize = resized.Rows * resized.Cols * resized.ElemSize(); int dataSize = resized.Rows * resized.Cols * resized.ElemSize();
var byteArray = new byte[dataSize]; var byteArray = new byte[dataSize];
Marshal.Copy(resized.Data, byteArray, 0, dataSize); Marshal.Copy(resized.Data, byteArray, 0, dataSize);
var allowed = FilterClasses.Split(',', StringSplitOptions.RemoveEmptyEntries).Select(x => Convert.ToInt32(x)).ToArray();
var result = _cSharpDataTransferMQRPC.PredictRaw(byteArray); var result = _cSharpDataTransferMQRPC.PredictRaw(byteArray);
byte[] resultColor; byte[] resultColor;
resultColor = result.Select(x => (byte)(x) ).ToArray(); resultColor = result.Select(x => (byte)(x) ).ToArray();
// black and white mask
var outputSizeArray = outputSize.Select(Convert.ToInt32).ToArray();
var mask = new Mat(outputSizeArray[0], outputSizeArray[1], MatType.CV_8UC1, resultColor); var mask = new Mat(outputSizeArray[0], outputSizeArray[1], MatType.CV_8UC1, resultColor);
var maskResized = mask.Resize(new Size(currentImage.ImageData.Width, currentImage.ImageData.Height)); var maskResized = mask.Resize(new Size(currentImage.ImageData.Width, currentImage.ImageData.Height));