using System.Data; using System.IO; using System.Runtime.InteropServices; using System.Windows.Forms; using log4net; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using SparkClient.Model.Entity.ApiEntity; using SparkClient.Model.Helper; using System.Configuration; using System.Diagnostics; using System.Text; using System.Text.RegularExpressions; using SparkClient.Model.Common; using SparkClient.ViewModel.Configuration; namespace SparkClient.Model.Services { public class AlgorithmServer { private readonly AlgorithmConfigVM _algorithmConfigVM; public AlgorithmServer() { _algorithmConfigVM = new AlgorithmConfigVM(); } private static readonly ILog Logger = LogManager.GetLogger(typeof(AlgorithmServer)); // 导入 C++ DLL 中的 DetectDiamond 函数 [DllImport("diamond_cut_inspector.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] private static extern IntPtr DetectDiamond(string jsonData); // 导入 C++ DLL 中的 FreeMemory 函数 [DllImport("diamond_cut_inspector.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] private static extern void FreeString(IntPtr ptr); // 添加公共方法 public AlgorithmResultEntity CallParseJsonAndReturnActions(string shape, string shape_mode, string image_files) { try { //半圆 string circleSql = $"SELECT VALUE FROM CUTTER_CONFIG WHERE KEY = 'half_circle'"; DataTable circleTable = DataBaseHelper.ExecuteQuery(circleSql); object halfCircleValue = circleTable.Rows[0][0]; bool.TryParse(halfCircleValue.ToString(), out bool boolResult); bool half_circle = boolResult; //算法配置参数,初始化算法配置数据并获取 AlgorithmConfigJson _algorithmConfigVM.InitAlgorithmData(null); string algo_config = _algorithmConfigVM.AlgorithmConfigJson; //图片根目录 string? image_file_base_path = ConfigurationManager.AppSettings["ImageFileBasePath"]; if (string.IsNullOrEmpty(image_file_base_path)) { throw new InvalidOperationException("ImageFileBasePath is not configured in AppSettings."); } // 获取 log4net 日志文件所在的目录 string? log4NetLogDirectory = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location); // 构建C++ DLL 日志文件路径 string algorithm_log_path = Path.Combine(log4NetLogDirectory, "logs"); // 将所有变量拼接成一个 JSON 对象 JObject jsonData = new JObject( new JProperty("shape", shape), new JProperty("shape_mode", shape_mode), new JProperty("image_file_base_path", image_file_base_path), new JProperty("image_files", JToken.Parse(image_files)), new JProperty("half_circle", half_circle), new JProperty("algorithm_log_path", algorithm_log_path), new JProperty("algo_config", JObject.Parse(algo_config)) ); string jsonDataString = jsonData.ToString().Replace("\"", "\\\""); //string jsonDataString = jsonData.ToString(); // // 调用 C++ DLL 函数解析 JSON // IntPtr resultPtr = DetectDiamond(jsonDataString); // string resultJson = Marshal.PtrToStringAnsi(resultPtr); // // 释放 DLL 分配的内存 // FreeString(resultPtr); // // 检查返回的 JSON 字符串是否为空或无效 // if (string.IsNullOrEmpty(resultJson)) // { // Logger.Error("Algorithm failed or no result returned."); // // 返回一个默认的 AlgorithmResultEntity 对象表示解析失败 // return new AlgorithmResultEntity // { // facets = new List(), // measurements = new Measurements() // }; // } // 启动隔离的控制台应用程序 var startInfo = new ProcessStartInfo { FileName = "AlgorithmDllIsolationConsoleApp.exe", // 控制台应用程序路径 Arguments = $"\"{jsonDataString}\"", // 将 JSON 数据作为参数传递给控制台应用程序 RedirectStandardOutput = true, // 获取控制台应用程序的输出 RedirectStandardError = true, UseShellExecute = false, CreateNoWindow = true }; using (var process = Process.Start(startInfo)) { // 使用 StringBuilder 来捕获标准输出和标准错误 var outputBuilder = new StringBuilder(); var errorBuilder = new StringBuilder(); // 订阅标准输出事件 process.OutputDataReceived += (sender, e) => { if (e.Data == null) { // 当 e.Data 为 null 时,表示读取完成 outputBuilder.AppendLine(e.Data); Console.WriteLine($"Output: {e.Data}"); return; } // 将输出数据追加到 outputBuilder outputBuilder.AppendLine(e.Data); // 打印输出数据到控制台 Console.WriteLine($"Output: {e.Data}"); }; // 订阅标准错误事件 process.ErrorDataReceived += (sender, e) => { if (e.Data == null) { // 当 e.Data 为 null 时,表示读取完成 errorBuilder.AppendLine(e.Data); Logger.Error($"Error Output: {e.Data}"); return; } // 将错误数据追加到 errorBuilder errorBuilder.AppendLine(e.Data); // 记录错误数据到日志 Logger.Error($"Error Output: {e.Data}"); }; // 开始异步读取 process.BeginOutputReadLine(); process.BeginErrorReadLine(); // 等待控制台应用程序结束 process.WaitForExit(); // 等待异步读取完成 //Task.WaitAll(outputTcs.Task, errorTcs.Task); // 获取结果 string resultJson = outputBuilder.ToString(); string errorOutput = errorBuilder.ToString(); // 如果控制台应用程序有错误输出 if (!string.IsNullOrEmpty(errorOutput)) { Logger.Error($"Console App Error: {errorOutput}"); } // 如果没有结果或失败 if (string.IsNullOrEmpty(resultJson)) { Logger.Error("Algorithm failed or no result returned."); return new AlgorithmResultEntity { facets = new List(), measurements = new Measurements() }; } // 反序列化 JSON 字符串为 AlgorithmResultEntity 对象 var result = JsonConvert.DeserializeObject(resultJson); // 检查反序列化结果是否为 null if (result == null) { // 返回一个默认的 AlgorithmResultEntity 对象表示解析失败 return new AlgorithmResultEntity { facets = new List(), measurements = new Measurements() }; } // 记录算法失败的状态 Logger.Info($"Algorithm failed with status: {result.status}"); // 记录算法失败的错误信息 Logger.Info($"Algorithm failed with errorMsg: {result.error_msg}"); // 处理 C++ DLL 日志文件 //ProcessDllLog(); // 算法调用失败时,保存图片到历史记录文件夹 // if (result.status == StatusCodes.AlgorithmFailed) // { // HandleAlgorithmFailure(image_files); // } return result; } } catch (Exception ex) { // 记录日志或处理异常 Logger.Error($"Error in CallParseJsonAndReturnActions: {ex.Message}"); Logger.Error($"Stack Trace: {ex.StackTrace}"); // 如果有 InnerException,打印出来 if (ex.InnerException != null) { Logger.Error($"Inner Exception: {ex.InnerException.Message}"); Logger.Error($"Inner Stack Trace: {ex.InnerException.StackTrace}"); } // 返回一个默认的 AlgorithmResultEntity 对象表示解析失败 return new AlgorithmResultEntity { facets = new List(), measurements = new Measurements() }; } } /// /// 处理算法调用失败时的逻辑,包括保存图片到历史记录文件夹 /// /// 图片文件路径的 JSON 字符串 private void HandleAlgorithmFailure(string image_files) { // 从配置文件中读取 imageHistoryPath // 定义历史记录文件夹路径 string? imageHistoryPath = ConfigurationManager.AppSettings["ImageHistoryPath"]; if (string.IsNullOrEmpty(imageHistoryPath)) { Logger.Error("ImageHistoryPath is not configured in AppSettings."); return; } // 生成时间戳,格式为 yyyyMMddHHmmss string timestamp = DateTime.Now.ToString("yyyy-MM-dd-HH-mm-ss"); // 组合新的文件夹路径 string newFolderPath = Path.Combine(imageHistoryPath, $"image-{timestamp}"); // 检查 D 盘内存空间 DriveInfo dDrive = new DriveInfo("D"); long requiredSpace = 1000L * 1024 * 1024 * 1024; // 10GB if (dDrive.TotalFreeSpace < requiredSpace) { // 如果 D 盘空间不足 10GB,删除最早创建的10个文件夹 DirectoryInfo historyDir = new DirectoryInfo(imageHistoryPath); // 获取历史记录文件夹的信息 DirectoryInfo[] subDirs = historyDir.GetDirectories(); // 获取历史记录文件夹中的所有子文件夹 if (subDirs.Length > 0) { // 按创建时间排序子文件夹 var orderedSubDirs = subDirs.OrderBy(d => d.CreationTime).ToList(); int foldersToDelete = Math.Min(10, orderedSubDirs.Count); // 删除最早的 10 个文件夹,如果不够 10 个则全删掉 for (int i = 0; i < foldersToDelete; i++) { orderedSubDirs[i].Delete(true); } } } // 创建新文件夹 Directory.CreateDirectory(newFolderPath); // 保存图片到新文件夹 try { // 解析 image_files JSON 数组 JArray imageFilesArray = JArray.Parse(image_files); string[] imageFiles = imageFilesArray.Select(token => token.ToString()).ToArray(); string? imageBasePath = ConfigurationManager.AppSettings["ImageFileBasePath"]; // 图片根目录 if (string.IsNullOrEmpty(imageBasePath)) { Logger.Error("ImageFileBasePath is not configured in AppSettings."); return; } foreach (string imageFile in imageFiles) { // 获取文件名 string fileName = Path.GetFileName(imageFile); // 构建完整的源文件路径 string sourcePath = Path.Combine(imageBasePath, imageFile); // 组合目标路径 string destinationPath = Path.Combine(newFolderPath, fileName); // 复制文件到目标路径,如果目标文件已存在则覆盖 File.Copy(sourcePath, destinationPath, true); } } catch (JsonException ex) { // 记录日志或处理异常 Logger.Error($"Error parsing image_files JSON: {ex.Message}"); Logger.Error($"Stack Trace: {ex.StackTrace}"); } catch (Exception ex) { // 记录其他异常 Logger.Error($"Error saving images: {ex.Message}"); Logger.Error($"Stack Trace: {ex.StackTrace}"); } } /// /// 处理 C++ DLL 的日志文件,将其复制到 log4net 日志目录下的 logs 文件夹,并记录其内容到日志中 /// private void ProcessDllLog() { try { // 获取系统临时目录路径(TMP 或 TEMP 环境变量) string? tempDirectory = Environment.GetEnvironmentVariable("TMP") ?? Environment.GetEnvironmentVariable("TEMP"); if (tempDirectory != null) { // 构建搜索模式 string searchPattern = "*diamond_cut_inspector*"; // 获取所有匹配的文件 string[] logFiles = Directory.GetFiles(tempDirectory, searchPattern, SearchOption.TopDirectoryOnly); if (logFiles.Length > 0) { // 找到最新的日志文件 string latestLogFile = logFiles.OrderByDescending(file => new FileInfo(file).LastWriteTime).First(); // 获取 log4net 日志文件所在的目录 string log4netLogDirectory = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location); if (log4netLogDirectory != null) { // 构建 logs 目录路径 string logsDirectory = Path.Combine(log4netLogDirectory, "logs"); // 确保 logs 目录存在 Directory.CreateDirectory(logsDirectory); // 构建目标文件路径 string targetFilePath = Path.Combine(logsDirectory, Path.GetFileName(latestLogFile)); // 复制最新日志文件到 logs 目录 File.Copy(latestLogFile, targetFilePath, true); // 读取复制后的日志文件的内容 string dllLogContent = File.ReadAllText(targetFilePath); // 记录 C++ DLL 日志文件的内容到日志中 Logger.Error("C++ DLL Log Content:"); Logger.Error(dllLogContent); } else { Logger.Error("Unable to determine the log4net log directory."); } } else { // 如果没有找到匹配的日志文件,记录错误信息 Logger.Error("No C++ DLL log files found matching the pattern '*diamond_cut_inspector*.log'."); } } else { // 如果无法获取临时目录路径,记录错误信息 Logger.Error("Unable to determine the temporary directory path."); } } catch (Exception ex) { // 记录读取或复制日志文件时的异常 Logger.Error($"Error processing C++ DLL log file: {ex.Message}"); Logger.Error($"Stack Trace: {ex.StackTrace}"); } } } }