using BaseFunction; using NSAnalysis.DAL; using NSAnalysis.Model; using System; using System.Collections.Generic; using System.Data; using System.Globalization; using System.IO; using System.Text; namespace NSAnalysis { public class FileSorter { public CjlrDAL _dal = new CjlrDAL(); //定义读取的位置 public int readRowIndex = 2; // 默认读取第3行(从0开始计数) public int readColIndex = 1; // 默认读取第2列(从0开始计数) public event Action OnLog; // 日志事件 public event Action OnFileParsed; // 解析完成后通知文件名 // 关键流程节点日志事件 public event Action OnProcessStep; // 处理步骤日志事件 // 封装 OnProcessStep 事件 private void emitProcessStep(string message) { OnProcessStep?.Invoke(message); //Console.WriteLine(message); // 控制台输出 } public FileSorter() { //SQLHelper.connStr = DatabaseDfn.SqlConnectStr(); } // 主逻辑处理 public void ProcessFiles() { var tasks = GetTaskRecords(); foreach (DataRow task in tasks.Rows) { string modelName = task["modelsName"].ToString(); string modelCode = task["modelsCode"].ToString(); string position = task["position"].ToString(); string sourceDir = task["sourceFile"].ToString(); string targetDir = task["targetFile"].ToString(); // 打印信息 Trace($"Processing Task - Source: {sourceDir}, Target: {targetDir}, ModelCode: {modelCode} Position: {position}"); if (Directory.Exists(sourceDir)) { ProcessDirectory(sourceDir, targetDir, modelCode, modelName, position); } else { Trace($"[ProcessFiles] 源文件地址不存在或错误: {sourceDir}"); ////记录到数据库 //try //{ // //插入分发详情 // CjlrTaskReleaseDetailModel detailModel = new CjlrTaskReleaseDetailModel // { // ModelsName = modelName, // 这里可以根据需要填写车型名称 // ModelsCode = modelCode, // Position = position, // 这里可以根据需要填写位置 // SourceFile = "", // TargetFile = "", // TaskFileName = "", // TaskStatus = 2, // 假设1表示已处理, 2表示未处理 // TaskDetail = $"源文件地址不存在或错误: {sourceDir}", // CreateDate = DateTime.Now // }; // _dal.InsertTaskDetail(detailModel); //} //catch (Exception ex) //{ // Trace($"[ProcessFiles] 记录错误到数据库失败: {ex.Message}"); //} } } } // 获取任务记录 private DataTable GetTaskRecords() { DataTable dt = _dal.SelectTaskByCondition("", "", "start"); //打印 dt //PrintDataTable(dt); if (dt == null || dt.Rows.Count == 0) { Trace("未发现移动任务."); return null; } return dt; } // 处理目录中的文件 private void ProcessDirectory(string sourceDir, string targetDir, string modelCode, string modelName, string position) { // 匹配信息 string matchStr = $"{modelCode}_{position}"; Trace($"匹配文件特征符: {matchStr}"); // 判断目标目录是否存在,如果不存在记录到日志 // 确保目标目录存在 if (!Directory.Exists(targetDir)) { Directory.CreateDirectory(targetDir); Trace($"创建目标文件夹: {targetDir}"); } // 遍历源目录中的所有CSV文件 foreach (string file in Directory.GetFiles(sourceDir, "*.csv")) { // 打印正在处理的文件 Trace($"正在处理文件 : {file}"); // 解析入库 AnalysisNxsCSV(file); if (!ConfigDfn.iEnableSort) { return; } // 分发逻辑 if (MatchCsvValue(file, matchStr, readRowIndex, readColIndex)) { string destFile = Path.Combine(targetDir, Path.GetFileName(file)); if (File.Exists(destFile)) { // 生成备份文件名,格式如:xxx.csv.bak_20240613_153012 string backupFile = destFile + ".bak_" + DateTime.Now.ToString("yyyyMMdd_HHmmss"); File.Move(destFile, backupFile); Trace($"目标文件已存在,已重命名为备份文件: {backupFile}"); } File.Move(file, destFile); Trace($"移动完成,: {file} -> {destFile}"); emitProcessStep($"---> 5、文件移动完成: -> {destFile}"); //插入分发详情 CjlrTaskReleaseDetailModel detailModel = new CjlrTaskReleaseDetailModel { ModelsName = modelName, // 这里可以根据需要填写车型名称 ModelsCode = modelCode, Position = position, // 这里可以根据需要填写位置 SourceFile = file, TargetFile = destFile, TaskFileName = Path.GetFileName(file), TaskStatus = 1, // 假设1表示已处理 TaskDetail = "文件移动成功", CreateDate = DateTime.Now }; _dal.InsertTaskDetail(detailModel); } else { Trace($"未匹配到文件: {file}"); emitProcessStep($"---> 5、未匹配到文件: {file}"); //记录到数据库 CjlrTaskReleaseDetailModel detailModel = new CjlrTaskReleaseDetailModel { ModelsName = modelName, // 这里可以根据需要填写车型名称 ModelsCode = modelCode, Position = position, // 这里可以根据需要填写位置 SourceFile = file, TargetFile = "", TaskFileName = Path.GetFileName(file), TaskStatus = 2, // 假设2表示未处理 TaskDetail = "文件未匹配", CreateDate = DateTime.Now }; try { _dal.InsertTaskDetail(detailModel); } catch (Exception ex) { Trace($"记录错误到数据库失败: {ex.Message}"); } } } } /// /// 检查CSV文件中指定行列的字符串是否匹配目标值 /// /// CSV文件路径 /// 要匹配的目标字符串 /// 行索引(从0开始) /// 列索引(从0开始) /// 匹配成功返回true,否则false public static bool MatchCsvValue(string filePath, string targetValue, int rowIndex, int colIndex) { // 记录日志 输入 MyBase.TraceWriteLine($"[MatchCsvValue] 检查文件: {filePath}, 行索引: {rowIndex}, 列索引: {colIndex}, 目标值: {targetValue}"); try { string[] lines = File.ReadAllLines(filePath); // 检查行索引是否有效 if (rowIndex < 0 || rowIndex >= lines.Length) return false; string[] columns = lines[rowIndex].Split(','); // 检查列索引是否有效 if (colIndex < 0 || colIndex >= columns.Length) return false; return columns[colIndex].Trim().Equals(targetValue); } catch (Exception ex) { MyBase.TraceWriteLine($"处理CSV文件时出错: {ex.Message}"); return false; } } // 生成单侧统计信息的方法 private void GenerateSingleSideStatistics(string GroupName, string Position) { #region 统计信息显示 DataTable sampleData = _dal.SelectMeasureResultByCarID(ConfigDfn.strEquipNo, GroupName); if (sampleData == null || sampleData.Rows.Count == 0) { MyBase.TraceWriteLine("没有测量数据,无法生成统计信息。"); // 触发日志事件 emitProcessStep(Position + " 没有测量数据,无法生成统计信息。"); return; } // 提取分析结果 AnalysisResult analysis = AnalysisResult.AnalyzeMeasureData(sampleData); //AnalysisResult.DisplayAnalysisResult(analysis); //表格行数 int dtRowCount = analysis.TotalCount; //超差个数 double OutCount = analysis.OutCount; //Ok个数 double OKCount = analysis.OKCount; //异常个数 double RejectedCount = analysis.RejectedCount; //合格率 double FPYPercent = analysis.FPYPercent; TMeasureResultModel tmrm = new TMeasureResultModel(); FPYPercent = OKCount / (OKCount + OutCount); if (FPYPercent >= ConfigDfn.dFPY) { tmrm.Result = 1; } else if (FPYPercent >= ConfigDfn.dFPY2 && FPYPercent < ConfigDfn.dFPY) { tmrm.Result = 1; } else { tmrm.Result = 2; } tmrm.CarID = ConfigDfn.strEquipNo; tmrm.CarType = ConfigDfn.strCarModel; tmrm.SumMeasureItems = dtRowCount; tmrm.GoodMeasureItems = (int)OKCount; tmrm.NoGoodMeasureItems = (int)OutCount; tmrm.RejectMeasureItems = (int)RejectedCount; tmrm.FPY = FPYPercent.ToString("F4"); tmrm.Remark = Position; //区分左右侧 tmrm.MeasureDate = ConfigDfn.strMeasureTime; _dal.InsertTMeasureResult(tmrm); MyBase.TraceWriteLine("将总结果插入数据库完毕。"); emitProcessStep($"---> 3、统计信息已生成: 位置: {Position}, 总测量项: {dtRowCount}, 合格项: {OKCount}, 不合格项: {OutCount}, 异常项: {RejectedCount}, FPY: {FPYPercent:F4}"); #endregion 统计信息显示 } // 导入CSV文件到数据库 public void ImportCsv2Sql(string filePath) { var records = new List(); var lineNo = 0; // 行号计数器 var groupName = string.Empty; // 组名变量 var position = string.Empty; // 位置变量 emitProcessStep($"正在处理: {filePath}"); // 判断文件是否已经处理过 if (_dal.IsFileProcessed(filePath)) { MyBase.TraceWriteLine($"文件已处理过,跳过: {filePath}"); emitProcessStep($"---> 2、文件已处理过,跳过: {filePath}"); return; } try { using (var reader = new StreamReader(filePath)) { // Skip header reader.ReadLine(); lineNo = 1; // 初始化行号计数器 while (!reader.EndOfStream) { lineNo++; // 增加行号计数器 var line = reader.ReadLine(); var values = line.Split(','); // 假设 CSV 使用制表符分隔 // 跳过前两行(如果有标题行或其他非数据行 if (string.IsNullOrWhiteSpace(line) || line.StartsWith("#") || line.StartsWith("//") || line.StartsWith("MeasPoint.Name")) { continue; // 跳过空行或注释行 } // 跳过前两行(如果有标题行或其他非数据行) if (values.Length < 22) { MyBase.TraceWriteLine("CSV行数据不完整,跳过该行:" + line); continue; // 跳过不完整的行 } // 判断 values[2] 是否为 ,如果是则跳过该行 if (string.IsNullOrEmpty(values[2])) { MyBase.TraceWriteLine($"第 {lineNo} 行数据为0,跳过该行:" + line); continue; // 跳过该行 } // 创建 MeasurementRecord 对象并填充数据 var record = new CJLR_MeaDataModel { PointName = values[0], GroupName = values[1], ProductNum = values[2], Model = values[3], Station = values[4], Method = values[5], Standard = values[6], DimensionName = values[7], DimensionValue = values[8], DimensionUnit = values[9], IsManual = bool.Parse(values[10]), Classification = values[11], ToleranceName0 = values[12], ToleranceLower0 = values[13], ToleranceUpper0 = values[14], ToleranceName1 = values[15], ToleranceLower1 = values[16], ToleranceUpper1 = values[17], NominalValue = values[18], MeasureDate = DateTime.ParseExact(values[19], "yyyyMMdd", CultureInfo.InvariantCulture), MeasureTime = TimeSpan.ParseExact(values[20], "hhmmss", CultureInfo.InvariantCulture), SequenceNum = int.Parse(values[21]) }; records.Add(record); } } } catch (Exception ex) { MyBase.TraceWriteLine("导入CSV文件时发生错误:" + ex.Message); return; } try { // 取第一条记录的时间作为测量时间 if (records.Count > 0) { var firstRecord = records[0]; ConfigDfn.strMeasureTime = firstRecord.MeasureDate.ToString("yyyy-MM-dd") + " " + firstRecord.MeasureTime.ToString(@"hh\:mm\:ss"); // 获取车号 MyBase.TraceWriteLine("--------------------------------------------------------"); ConfigDfn.strEquipNo = firstRecord.ProductNum; MyBase.TraceWriteLine("车号:" + ConfigDfn.strEquipNo); ConfigDfn.strCarModel = firstRecord.Model; //获取车型 MyBase.TraceWriteLine("车型:" + ConfigDfn.strCarModel); // 从 firstRecord.GroupName 中提取位置 ,使用下划线分隔 例如:X540_R,提取 R 作为位置 groupName = firstRecord.GroupName; MyBase.TraceWriteLine("组名:" + firstRecord.GroupName); // 如果 GroupName 为空,则使用默认位置 if (string.IsNullOrEmpty(firstRecord.GroupName)) { MyBase.TraceWriteLine("组名为空,使用默认位置。"); ConfigDfn.strEquipPosition = "Default"; // 默认位置 } else { // 使用下划线分隔 GroupName,提取最后一部分作为位置 var parts2 = firstRecord.GroupName.Split('_'); ConfigDfn.strEquipPosition = parts2.Length > 1 ? parts2[parts2.Length - 1] : firstRecord.GroupName; // 如果没有下划线,直接使用原值 } // 打印提取后位置 MyBase.TraceWriteLine("位置:" + ConfigDfn.strEquipPosition); MyBase.TraceWriteLine("测量时间:" + ConfigDfn.strMeasureTime); emitProcessStep($"---> 1、解析到, 车号:{ConfigDfn.strEquipNo} 车型:{ConfigDfn.strCarModel} 位置:{ConfigDfn.strEquipPosition} 测量时间:{ConfigDfn.strMeasureTime}"); } else { MyBase.TraceWriteLine("没有找到有效的测量记录,无法设置测量时间。"); // 将文件移动到 未导入文件夹,如果不存在则新建该文件夹 //string strNotImportPath = ConfigDfn.strFileFolder + "\\NextSenseCSVNotImport\\"; //if (!Directory.Exists(strNotImportPath)) //{ // Directory.CreateDirectory(strNotImportPath); //} //string destFilePath = Path.Combine(strNotImportPath, Path.GetFileName(filePath)); //File.Move(filePath, destFilePath); //MyBase.TraceWriteLine("将文件移动到未导入文件夹,路径为:" + destFilePath); return; } // 逐条插入数据到数据库 foreach (var record in records) { _dal.InsertOrUpdateCJLRMeaData(record); } MyBase.TraceWriteLine("CSV文件导入到数据库成功!"); // 记录处理文件到数据库 _dal.InsertProcessedFile(filePath); emitProcessStep($"---> 2、CSV文件导入到数据库成功"); #region 处理单侧统计信息 GenerateSingleSideStatistics(groupName, ConfigDfn.strEquipPosition); #endregion 处理单侧统计信息 MyBase.TraceWriteLine("--------------------------------------------------------"); } catch (Exception ex) { MyBase.TraceWriteLine("导入CSV文件时发生错误:" + ex.Message); emitProcessStep($"---> 2、导入CSV文件时发生错误: {ex.Message}"); } } //解析EH3 CSV文件函数 public void AnalysisNxsCSV(string strCSVName) { // 判断文件是否存在 if (!File.Exists(strCSVName)) { MyBase.TraceWriteLine("文件不存在:" + strCSVName); return; } try { // 解析 CSV 文件并导入到数据库 ImportCsv2Sql(strCSVName); // 解析完成后触发事件 if (!string.IsNullOrEmpty(ConfigDfn.strEquipNo)) { // 构造 事件参数 Trace($"触发事件,车号: {ConfigDfn.strEquipNo} 位置: {ConfigDfn.strEquipPosition}"); OnFileParsed?.Invoke(ConfigDfn.strEquipNo, ConfigDfn.strEquipPosition); } // 每次解析完一个文件后,检查是否有双侧测量结果 bool isMeasureComplete = _dal.HasBothSidesMeasureResult(ConfigDfn.strEquipNo); if (isMeasureComplete) { MyBase.TraceWriteLine("双侧测量结果已完成,开始生成客户报告。"); emitProcessStep($"---> 4、双侧测量结果已完成,开始生成客户报告,车号: {ConfigDfn.strEquipNo}"); // 生成客户报告 GenCustomerReport(ConfigDfn.strEquipNo); } else { MyBase.TraceWriteLine("双侧测量结果未完成,跳过客户报告生成。"); emitProcessStep($"---> 4、双侧测量结果未完成,跳过客户报告生成,车号: {ConfigDfn.strEquipNo}"); } } catch (Exception ex) { MyBase.TraceWriteLine("解析 CSV 文件时发生错误:" + ex.Message); } } // 生成客户报告函数 private void GenCustomerReport(string strCarID) { // 从数据库获取测量数据 DataTable dtCSVContent = _dal.SelectPointDimensionByCarID(strCarID); if (dtCSVContent == null || dtCSVContent.Rows.Count == 0) { MyBase.TraceWriteLine("没有找到测量数据,无法生成客户报告。"); return; } // 生成报告 string fileName = strCarID + "_" + DateTime.Now.ToString("yyyyMMddHHmmss") + ".csv"; // 确保报告路径存在 if (!Directory.Exists(ConfigDfn.strReportPath)) { Directory.CreateDirectory(ConfigDfn.strReportPath); MyBase.TraceWriteLine($"创建报告目录: {ConfigDfn.strReportPath}"); } string savePath = Path.Combine(ConfigDfn.strReportPath, fileName); GenerateCsvReport(strCarID, dtCSVContent, ConfigDfn.strCSVReportTemplatePath, savePath); MyBase.TraceWriteLine($"客户报告已生成: {savePath}"); emitProcessStep($"---> 5、客户报告已生成: {savePath}"); } // 生成CSV报告函数 private void GenerateCsvReport(string strCarID, DataTable measureData, string templatePath, string savePath) { // 读取模板内容 var templateLines = File.ReadAllLines(templatePath); StringBuilder sb = new StringBuilder(); // 替换模板中的变量 foreach (var line in templateLines) { string replaced = line .Replace("{DateTime}", DateTime.Now.ToString("yyyy/MM/dd HH:mm")) .Replace("{CarID}", strCarID); sb.AppendLine(replaced); } // 空行分隔 sb.AppendLine(); sb.AppendLine(); // 添加测量数据表头 sb.AppendLine("Characteristic,Extension,Measured_Value"); // 添加测量数据内容 foreach (DataRow row in measureData.Rows) { sb.AppendFormat("{0},{1},{2}\n", row["PointName"], row["DimensionName"], row["DimensionValue"]); } // 可根据实际需求添加统计行 sb.AppendLine("POP,P,99.99"); // 写入文件 File.WriteAllText(savePath, sb.ToString(), Encoding.UTF8); } // 日志记录方法 private void Trace(string msg) { OnLog?.Invoke(msg); MyBase.TraceWriteLine(msg); // 保持原有日志 } // 打印 DataTable 对象的方法,输入是对象 public static void PrintDataTable(DataTable dt) { if (dt == null || dt.Rows.Count == 0) { Console.WriteLine("[PrintDataTable] DataTable is empty or null."); return; } foreach (DataColumn column in dt.Columns) { Console.WriteLine($"{column.ColumnName}\t"); } Console.WriteLine(""); foreach (DataRow row in dt.Rows) { foreach (var item in row.ItemArray) { Console.WriteLine($"{item}\t"); } Console.WriteLine(""); } } // 测试方法 public void test() { SQLHelper.connStr = DatabaseDfn.SqlConnectStr(); //string testPath = @"D:\CJLR\DATA\Input\LLL\K0902906.csv"; //bool result = MatchCsvValue(testPath, "X540_L", 3, 1); //MyBase.TraceWriteLine($"匹配结果: {result}"); } } }