fix: 修复并发安全问题导致的数据库崩溃和数据错乱
问题根因:
FileSortTimer 每15秒触发 Task.Run 执行 ProcessFiles(),当P盘文件量大时
上一轮未完成新一轮又启动,多线程并发操作同一数据库连接和全局变量,导致:
1. SQLHelper 静态 SqlConnection/SqlCommand/SqlDataReader 被多线程覆盖,连接状态异常
2. ConfigDfn.strEquipNo 等全局静态变量被并发覆盖,事件触发时使用错误车号
3. 定时器无重入保护,多个 ProcessFiles() 实例并发执行
4. HasBothSidesMeasureResult 查询返回空表时直接访问列导致崩溃
修复内容:
[P0] SQLHelper.cs - 线程安全改造
- 删除静态 conn/cmd/sdr 字段和 GetConn() 方法
- 所有方法(ExecuteQuery/ExecuteNonQuery/ExecuteDs/BulkCopy)改为 using 独立连接模式
- 每次调用创建独立 SqlConnection,彻底消除连接竞争
[P0] FormMain.cs - 防止定时器重入
- 新增 _isProcessing 原子标志位(Interlocked.CompareExchange)
- FileSortTimer_Tick 检测到上一轮仍在执行时跳过本次触发
- finally 块确保标志位一定被释放
[P1] FileSorter.cs - 消除全局变量竞争
- 新增 CsvParseResult 类封装解析结果(CarID/CarModel/Position/MeasureTime)
- ImportCsv2Sql 改为返回 CsvParseResult,不再写入 ConfigDfn 全局变量
- GenerateSingleSideStatistics 改为参数传入(carID/carModel/groupName/position/measureTime)
- GenCustomerReport/GenerateCsvReport 改为参数传入 carModel
- OnFileParsed 事件改为三参数签名(carID, position, measureTime)
[P1] FormMain.cs - UI事件适配
- FileSorter_OnFileParsed 适配新的三参数签名
- DisplayMeasureData 接收 measureTime 参数,不再从全局变量读取
[P2] CjlrDAL.cs - 防御性检查
- HasBothSidesMeasureResult 增加 carId 空值检查
- 增加 dt.Columns.Contains("HasBothSides") 检查,避免空表异常
[P2] FileSorter.cs - 增强日志
- ProcessFiles() 开头打印查询到的任务总数,便于现场确认任务是否全部加载
影响文件:
- Analysis/DAL/SQLHelper.cs
- Analysis/CjlrForm/FileSorter.cs
- Analysis/FormMain.cs
- Analysis/DAL/CjlrDAL.cs
This commit is contained in:
@@ -10,6 +10,19 @@ using System.Text;
|
||||
|
||||
namespace NSAnalysis
|
||||
{
|
||||
/// <summary>
|
||||
/// CSV解析结果,替代全局静态变量 ConfigDfn.strEquipNo 等,消除多线程竞争
|
||||
/// </summary>
|
||||
public class CsvParseResult
|
||||
{
|
||||
public string CarID { get; set; }
|
||||
public string CarModel { get; set; }
|
||||
public string Position { get; set; }
|
||||
public string GroupName { get; set; }
|
||||
public string MeasureTime { get; set; }
|
||||
public bool Success { get; set; }
|
||||
}
|
||||
|
||||
public class FileSorter
|
||||
{
|
||||
public CjlrDAL _dal = new CjlrDAL();
|
||||
@@ -21,6 +34,650 @@ namespace NSAnalysis
|
||||
|
||||
public event Action<string> OnLog; // 日志事件
|
||||
|
||||
public event Action<string, string, string> OnFileParsed; // 解析完成后通知:车号、位置、测量时间
|
||||
|
||||
// 关键流程节点日志事件
|
||||
public event Action<string> OnProcessStep; // 处理步骤日志事件
|
||||
|
||||
// 封装 OnProcessStep 事件
|
||||
private void emitProcessStep(string message)
|
||||
{
|
||||
OnProcessStep?.Invoke(message);
|
||||
}
|
||||
|
||||
public FileSorter()
|
||||
{
|
||||
}
|
||||
|
||||
// 主逻辑处理
|
||||
public void ProcessFiles()
|
||||
{
|
||||
var tasks = GetTaskRecords();
|
||||
// 增加空检查
|
||||
if (tasks == null || tasks.Rows.Count == 0)
|
||||
{
|
||||
Trace("没有找到任何任务记录,处理终止。");
|
||||
return;
|
||||
}
|
||||
|
||||
Trace($"[ProcessFiles] 共查询到 {tasks.Rows.Count} 个分发任务,开始逐一执行。");
|
||||
|
||||
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($"[ProcessFiles] 正在执行分发任务 - 源路径: {sourceDir}, 目标路径: {targetDir}, 匹配字符: {modelCode} 位置:{position}");
|
||||
if (Directory.Exists(sourceDir))
|
||||
{
|
||||
ProcessDirectory(sourceDir, targetDir, modelCode, modelName, position);
|
||||
}
|
||||
else
|
||||
{
|
||||
Trace($"[ProcessFiles] 源文件地址不存在或错误: {sourceDir}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 获取任务记录
|
||||
private DataTable GetTaskRecords()
|
||||
{
|
||||
DataTable dt = _dal.SelectTaskByCondition("", "", "start");
|
||||
|
||||
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}");
|
||||
|
||||
// 解析入库,返回局部结果,不再写全局变量
|
||||
CsvParseResult parseResult = AnalysisNxsCSV(file);
|
||||
|
||||
// 如果未启用分发功能,直接返回
|
||||
if (!ConfigDfn.iEnableSort)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
#region 分发逻辑
|
||||
|
||||
if (MatchCsvValue(file, matchStr, readRowIndex, readColIndex))
|
||||
{
|
||||
// 记录日志,匹配到
|
||||
Trace($"匹配成功,准备移动文件: {file} -> {targetDir}");
|
||||
|
||||
string destFile = Path.Combine(targetDir, Path.GetFileName(file));
|
||||
if (File.Exists(destFile))
|
||||
{
|
||||
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,
|
||||
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,
|
||||
TaskDetail = "文件未匹配",
|
||||
CreateDate = DateTime.Now
|
||||
};
|
||||
try
|
||||
{
|
||||
_dal.InsertTaskDetail(detailModel);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Trace($"记录错误到数据库失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion 分发逻辑
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查CSV文件中指定行列的字符串是否匹配目标值
|
||||
/// </summary>
|
||||
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().Contains(targetValue);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MyBase.TraceWriteLine($"处理CSV文件时出错: {ex.Message}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 生成单侧统计信息的方法(使用局部参数,不再依赖全局变量)
|
||||
private void GenerateSingleSideStatistics(string carID, string carModel, string groupName, string position, string measureTime)
|
||||
{
|
||||
#region 统计信息显示
|
||||
|
||||
DataTable sampleData = _dal.SelectMeasureResultByCarID(carID, groupName);
|
||||
if (sampleData == null || sampleData.Rows.Count == 0)
|
||||
{
|
||||
MyBase.TraceWriteLine("没有测量数据,无法生成统计信息。");
|
||||
emitProcessStep(position + " 没有测量数据,无法生成统计信息。");
|
||||
return;
|
||||
}
|
||||
|
||||
AnalysisResult analysis = AnalysisResult.AnalyzeMeasureData(sampleData);
|
||||
|
||||
int dtRowCount = analysis.TotalCount;
|
||||
double OutCount = analysis.OutCount;
|
||||
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 = carID;
|
||||
tmrm.CarType = carModel;
|
||||
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 = measureTime;
|
||||
_dal.InsertTMeasureResult(tmrm);
|
||||
|
||||
MyBase.TraceWriteLine("将总结果插入数据库完毕。");
|
||||
emitProcessStep($"---> 3、统计信息已生成: 位置: {position}, 总测量项: {dtRowCount}, 合格项: {OKCount}, 不合格项: {OutCount}, 异常项: {RejectedCount}, FPY: {FPYPercent:F4}");
|
||||
|
||||
#endregion 统计信息显示
|
||||
}
|
||||
|
||||
// 导入CSV文件到数据库,返回解析结果(不再写全局变量)
|
||||
public CsvParseResult ImportCsv2Sql(string filePath)
|
||||
{
|
||||
var result = new CsvParseResult { Success = false };
|
||||
var records = new List<CJLR_MeaDataModel>();
|
||||
var lineNo = 0;
|
||||
var groupName = string.Empty;
|
||||
var position = string.Empty;
|
||||
|
||||
emitProcessStep($"正在处理: {filePath}");
|
||||
|
||||
// 判断文件是否已经处理过
|
||||
if (_dal.IsFileProcessed(filePath))
|
||||
{
|
||||
MyBase.TraceWriteLine($"文件已处理过,跳过: {filePath}");
|
||||
emitProcessStep($"---> 2、文件已处理过,跳过: {filePath}");
|
||||
return result;
|
||||
}
|
||||
|
||||
// 备份原始文件
|
||||
try
|
||||
{
|
||||
string backupDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Backup");
|
||||
if (!Directory.Exists(backupDir))
|
||||
{
|
||||
Directory.CreateDirectory(backupDir);
|
||||
}
|
||||
string backupFilePath = Path.Combine(backupDir, Path.GetFileName(filePath));
|
||||
File.Copy(filePath, backupFilePath, true);
|
||||
MyBase.TraceWriteLine("已备份文件到: " + backupFilePath);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MyBase.TraceWriteLine("备份文件时发生错误:" + ex.Message);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
using (var reader = new StreamReader(filePath))
|
||||
{
|
||||
reader.ReadLine(); // Skip header
|
||||
|
||||
lineNo = 1;
|
||||
while (!reader.EndOfStream)
|
||||
{
|
||||
lineNo++;
|
||||
var line = reader.ReadLine();
|
||||
var values = line.Split(',');
|
||||
|
||||
if (string.IsNullOrWhiteSpace(line) || line.StartsWith("#") || line.StartsWith("//") || line.StartsWith("MeasPoint.Name"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (values.Length < 22)
|
||||
{
|
||||
MyBase.TraceWriteLine("CSV行数据不完整,跳过该行:" + line);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(values[2]))
|
||||
{
|
||||
MyBase.TraceWriteLine($"第 {lineNo} 行数据为0,跳过该行:" + line);
|
||||
continue;
|
||||
}
|
||||
|
||||
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);
|
||||
emitProcessStep($"---> 2、导入CSV文件时发生错误: {ex.Message}");
|
||||
|
||||
string errorDir = Path.Combine(Path.GetDirectoryName(filePath), "Error");
|
||||
if (!Directory.Exists(errorDir))
|
||||
{
|
||||
Directory.CreateDirectory(errorDir);
|
||||
}
|
||||
string errorFilePath = Path.Combine(errorDir, Path.GetFileName(filePath));
|
||||
File.Move(filePath, errorFilePath);
|
||||
MyBase.TraceWriteLine("已将文件移动到错误文件夹: " + errorFilePath);
|
||||
return result;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (records.Count > 0)
|
||||
{
|
||||
var firstRecord = records[0];
|
||||
|
||||
// 所有解析结果存入局部变量,不写全局 ConfigDfn
|
||||
result.MeasureTime = firstRecord.MeasureDate.ToString("yyyy-MM-dd") + " " + firstRecord.MeasureTime.ToString(@"hh\:mm\:ss");
|
||||
result.CarID = firstRecord.ProductNum;
|
||||
result.CarModel = firstRecord.Model;
|
||||
|
||||
groupName = firstRecord.GroupName;
|
||||
result.GroupName = groupName;
|
||||
|
||||
MyBase.TraceWriteLine("测量时间:" + result.MeasureTime);
|
||||
MyBase.TraceWriteLine("--------------------------------------------------------");
|
||||
MyBase.TraceWriteLine("车号:" + result.CarID);
|
||||
MyBase.TraceWriteLine("车型:" + result.CarModel);
|
||||
MyBase.TraceWriteLine("组名:" + groupName);
|
||||
|
||||
if (string.IsNullOrEmpty(firstRecord.GroupName))
|
||||
{
|
||||
MyBase.TraceWriteLine("组名为空,使用默认位置。");
|
||||
result.Position = "Default";
|
||||
}
|
||||
else
|
||||
{
|
||||
var parts2 = firstRecord.GroupName.Split('_');
|
||||
if (parts2.Length > 1 && !string.IsNullOrEmpty(parts2[1]))
|
||||
{
|
||||
result.Position = parts2[1].Substring(0, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Position = firstRecord.GroupName;
|
||||
}
|
||||
}
|
||||
|
||||
MyBase.TraceWriteLine("位置:" + result.Position);
|
||||
MyBase.TraceWriteLine("测量时间:" + result.MeasureTime);
|
||||
|
||||
emitProcessStep($"---> 1、解析到, 车号: {result.CarID} 车型: {result.CarModel} 位置: {result.Position} 测量时间: {result.MeasureTime}");
|
||||
}
|
||||
else
|
||||
{
|
||||
MyBase.TraceWriteLine("没有找到有效的测量记录,无法设置测量时间。CSV文件导入到数据库失败!");
|
||||
emitProcessStep($"---> 2、没有找到有效的测量记录,无法设置测量时间。CSV文件导入到数据库失败!");
|
||||
|
||||
string errorDir = Path.Combine(Path.GetDirectoryName(filePath), "Error");
|
||||
if (!Directory.Exists(errorDir))
|
||||
{
|
||||
Directory.CreateDirectory(errorDir);
|
||||
}
|
||||
string errorFilePath = Path.Combine(errorDir, Path.GetFileName(filePath));
|
||||
File.Move(filePath, errorFilePath);
|
||||
MyBase.TraceWriteLine("已将文件移动到错误文件夹: " + errorFilePath);
|
||||
return result;
|
||||
}
|
||||
|
||||
// 逐条插入数据到数据库
|
||||
foreach (var record in records)
|
||||
{
|
||||
_dal.InsertOrUpdateCJLRMeaData(record);
|
||||
}
|
||||
|
||||
// 记录处理文件到数据库
|
||||
_dal.InsertProcessedFile(filePath);
|
||||
MyBase.TraceWriteLine("CSV文件导入到数据库成功!");
|
||||
emitProcessStep($"---> 2、CSV文件导入到数据库成功");
|
||||
|
||||
result.Success = true;
|
||||
|
||||
#region 处理单侧统计信息
|
||||
|
||||
GenerateSingleSideStatistics(result.CarID, result.CarModel, groupName, result.Position, result.MeasureTime);
|
||||
|
||||
#endregion 处理单侧统计信息
|
||||
|
||||
MyBase.TraceWriteLine("--------------------------------------------------------");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MyBase.TraceWriteLine("导入CSV文件时发生错误:" + ex.Message);
|
||||
emitProcessStep($"---> 2、导入CSV文件时发生错误: {ex.Message}");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// 解析CSV文件,返回解析结果(不再依赖/写入全局变量)
|
||||
public CsvParseResult AnalysisNxsCSV(string strCSVName)
|
||||
{
|
||||
var emptyResult = new CsvParseResult { Success = false };
|
||||
|
||||
if (!File.Exists(strCSVName))
|
||||
{
|
||||
MyBase.TraceWriteLine("文件不存在:" + strCSVName);
|
||||
return emptyResult;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// 解析 CSV 文件并导入到数据库,获取局部结果
|
||||
CsvParseResult parseResult = ImportCsv2Sql(strCSVName);
|
||||
|
||||
if (!parseResult.Success || string.IsNullOrEmpty(parseResult.CarID))
|
||||
{
|
||||
return parseResult;
|
||||
}
|
||||
|
||||
// 解析完成后触发事件,传递局部结果(不依赖全局变量)
|
||||
Trace($"触发事件,车号: {parseResult.CarID} 位置: {parseResult.Position}");
|
||||
OnFileParsed?.Invoke(parseResult.CarID, parseResult.Position, parseResult.MeasureTime);
|
||||
|
||||
// 检查是否有双侧测量结果(防御性检查)
|
||||
bool isMeasureComplete = _dal.HasBothSidesMeasureResult(parseResult.CarID);
|
||||
|
||||
if (isMeasureComplete)
|
||||
{
|
||||
MyBase.TraceWriteLine("双侧测量结果已完成,开始生成客户报告。");
|
||||
emitProcessStep($"---> 4、双侧测量结果已完成,开始生成客户报告,车号: {parseResult.CarID}");
|
||||
|
||||
if (parseResult.CarModel != null && parseResult.CarModel.Contains("MY"))
|
||||
{
|
||||
MyBase.TraceWriteLine("车型为 551_MY21,跳过客户报告生成。");
|
||||
emitProcessStep($"---> 4、车型为 551_MY21,跳过客户报告生成,车号: {parseResult.CarID}");
|
||||
return parseResult;
|
||||
}
|
||||
|
||||
GenCustomerReport(parseResult.CarID, parseResult.CarModel);
|
||||
}
|
||||
else
|
||||
{
|
||||
MyBase.TraceWriteLine("双侧测量结果未完成,跳过客户报告生成。");
|
||||
emitProcessStep($"---> 4、双侧测量结果未完成,跳过客户报告生成,车号: {parseResult.CarID}");
|
||||
}
|
||||
|
||||
return parseResult;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MyBase.TraceWriteLine("解析 CSV 文件时发生错误:" + ex.Message);
|
||||
return emptyResult;
|
||||
}
|
||||
}
|
||||
|
||||
// 生成客户报告函数(使用参数传入,不依赖全局变量)
|
||||
private void GenCustomerReport(string strCarID, string carModel)
|
||||
{
|
||||
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 backupDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "ReportBackup");
|
||||
if (!Directory.Exists(backupDir))
|
||||
{
|
||||
Directory.CreateDirectory(backupDir);
|
||||
}
|
||||
|
||||
string savePath = Path.Combine(backupDir, fileName);
|
||||
string target = Path.Combine(ConfigDfn.strReportPath, fileName);
|
||||
|
||||
GenerateCsvReport(strCarID, carModel, dtCSVContent, ConfigDfn.strCSVReportTemplatePath, savePath);
|
||||
|
||||
MyBase.TraceWriteLine($"客户报告已生成: {savePath}");
|
||||
emitProcessStep($"---> 5、客户报告已生成: {savePath}");
|
||||
try
|
||||
{
|
||||
File.Copy(savePath, target, true);
|
||||
MyBase.TraceWriteLine("已拷贝客户报告到目标路径: " + target);
|
||||
emitProcessStep($"---> 6、已拷贝客户报告到目标路径: {target}");
|
||||
}
|
||||
catch
|
||||
{
|
||||
MyBase.TraceWriteLine("拷贝客户报告到目标路径失败: " + target);
|
||||
emitProcessStep($"---> 6、拷贝客户报告到目标路径失败: {target}");
|
||||
}
|
||||
}
|
||||
|
||||
// 生成CSV报告函数(carModel 作为参数传入,不依赖全局变量)
|
||||
private void GenerateCsvReport(string strCarID, string carModel, DataTable measureData, string templatePath, string savePath)
|
||||
{
|
||||
var templateLines = File.ReadAllLines(templatePath);
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
foreach (var line in templateLines)
|
||||
{
|
||||
string processedLine = line;
|
||||
if (processedLine.Contains("{datetime}"))
|
||||
{
|
||||
processedLine = processedLine.Replace("{datetime}", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture));
|
||||
}
|
||||
if (processedLine.Contains("{Part_ident}"))
|
||||
{
|
||||
processedLine = processedLine.Replace("{Part_ident}", strCarID);
|
||||
}
|
||||
if (processedLine.Contains("{model}"))
|
||||
{
|
||||
processedLine = processedLine.Replace("{model}", carModel);
|
||||
}
|
||||
if (processedLine.Contains("{Part_code}"))
|
||||
{
|
||||
if (carModel == "E03")
|
||||
processedLine = processedLine.Replace("{Part_code}", "E03_5000000FAonline");
|
||||
else if (carModel == "E0Y")
|
||||
processedLine = processedLine.Replace("{Part_code}", "E0Y_5000000FAonline");
|
||||
else
|
||||
processedLine = processedLine.Replace("{Part_code}", "UnknownModel_5000000FAonline");
|
||||
}
|
||||
processedLine = processedLine.Trim('"');
|
||||
Console.WriteLine($"处理行: {processedLine}");
|
||||
sb.AppendLine(processedLine);
|
||||
}
|
||||
|
||||
sb.AppendLine();
|
||||
sb.AppendLine();
|
||||
sb.AppendLine();
|
||||
sb.AppendLine();
|
||||
|
||||
sb.AppendLine("Characteristic,Extension,Measured_Value");
|
||||
|
||||
foreach (DataRow row in measureData.Rows)
|
||||
{
|
||||
var dimensionValue = row["DimensionValue"]?.ToString();
|
||||
if (dimensionValue != null && dimensionValue.Contains("1.#R"))
|
||||
{
|
||||
dimensionValue = "";
|
||||
}
|
||||
sb.AppendFormat("{0},{1},{2}\n",
|
||||
row["PointName"], row["DimensionName"], dimensionValue);
|
||||
}
|
||||
|
||||
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();
|
||||
GenCustomerReport("K0902906", "E03");
|
||||
}
|
||||
}
|
||||
}
|
||||
public CjlrDAL _dal = new CjlrDAL();
|
||||
|
||||
//定义读取的位置
|
||||
public int readRowIndex = 2; // 默认读取第3行(从0开始计数)
|
||||
|
||||
public int readColIndex = 1; // 默认读取第2列(从0开始计数)
|
||||
|
||||
public event Action<string> OnLog; // 日志事件
|
||||
|
||||
public event Action<string, string> OnFileParsed; // 解析完成后通知文件名
|
||||
|
||||
// 关键流程节点日志事件
|
||||
|
||||
@@ -234,6 +234,9 @@ NULLIF(
|
||||
// check 是否有左右两侧的测量结果
|
||||
public bool HasBothSidesMeasureResult(string carId)
|
||||
{
|
||||
if (string.IsNullOrEmpty(carId))
|
||||
return false;
|
||||
|
||||
string sql = @"
|
||||
SELECT CASE
|
||||
WHEN COUNT(DISTINCT Remark) = 2 THEN 1
|
||||
@@ -246,7 +249,10 @@ NULLIF(
|
||||
new SqlParameter("@CarID", carId)
|
||||
};
|
||||
DataTable dt = SQLHelper.ExecuteQuery(sql, paras, CommandType.Text);
|
||||
return dt.Rows.Count > 0 && Convert.ToInt32(dt.Rows[0]["HasBothSides"]) == 1;
|
||||
// 防御性检查:空表或列不存在时返回 false,避免 Column 'HasBothSides' does not belong to table 异常
|
||||
if (dt == null || dt.Rows.Count == 0 || !dt.Columns.Contains("HasBothSides"))
|
||||
return false;
|
||||
return Convert.ToInt32(dt.Rows[0]["HasBothSides"]) == 1;
|
||||
}
|
||||
|
||||
// 查询指定车辆ID的测量点维度数据
|
||||
|
||||
+83
-110
@@ -7,36 +7,10 @@ namespace NSAnalysis.DAL
|
||||
{
|
||||
public class SQLHelper
|
||||
{
|
||||
private static SqlConnection conn = null;
|
||||
private static SqlCommand cmd = null;
|
||||
private static SqlDataReader sdr = null;
|
||||
public static string connStr = "";
|
||||
|
||||
public static int iFlag = 0;
|
||||
|
||||
private static SqlConnection GetConn()
|
||||
{
|
||||
conn = new SqlConnection(connStr);
|
||||
try
|
||||
{
|
||||
if (conn.State == ConnectionState.Closed)
|
||||
{
|
||||
conn.Open();
|
||||
}
|
||||
return conn;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (iFlag == 0)
|
||||
{
|
||||
iFlag++;
|
||||
MessageBox.Show("数据库打开连接失败,请检查数据库是否正确连接!原因:" + ex.ToString(), "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
}
|
||||
// 这里建议抛出异常而不是返回未打开的连接
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
#region 执行不带参数的增删改SQL语句或存储过程 返回int类型 返回受影响的行数
|
||||
|
||||
/// <summary>
|
||||
@@ -47,25 +21,15 @@ namespace NSAnalysis.DAL
|
||||
/// <returns>返回受影响的行数</returns>
|
||||
public static int ExecuteNonQuery(string cmdText, CommandType ct)
|
||||
{
|
||||
int res = 0;
|
||||
try
|
||||
using (var conn = new SqlConnection(connStr))
|
||||
{
|
||||
cmd = new SqlCommand(cmdText, GetConn());
|
||||
cmd.CommandType = ct;
|
||||
res = cmd.ExecuteNonQuery(); //返回受影响的行数
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw ex;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (conn.State == ConnectionState.Open)
|
||||
conn.Open();
|
||||
using (var cmd = new SqlCommand(cmdText, conn))
|
||||
{
|
||||
conn.Close();
|
||||
cmd.CommandType = ct;
|
||||
return cmd.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
#endregion 执行不带参数的增删改SQL语句或存储过程 返回int类型 返回受影响的行数
|
||||
@@ -80,7 +44,6 @@ namespace NSAnalysis.DAL
|
||||
/// <returns>返回受影响的行数</returns>
|
||||
public static int ExecuteNonQuery(string cmdText, SqlParameter[] paras, CommandType ct)
|
||||
{
|
||||
int res = 0;
|
||||
using (var conn = new SqlConnection(connStr))
|
||||
{
|
||||
conn.Open();
|
||||
@@ -88,10 +51,9 @@ namespace NSAnalysis.DAL
|
||||
{
|
||||
cmd.CommandType = ct;
|
||||
cmd.Parameters.AddRange(paras);
|
||||
res = cmd.ExecuteNonQuery();
|
||||
return cmd.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
#endregion 执行带参数的增删改SQL语句或存储过程 返回int类型 返回受影响的行数
|
||||
@@ -107,11 +69,17 @@ namespace NSAnalysis.DAL
|
||||
public static DataTable ExecuteQuery(string cmdText, CommandType ct)
|
||||
{
|
||||
DataTable dt = new DataTable();
|
||||
cmd = new SqlCommand(cmdText, GetConn());
|
||||
cmd.CommandType = ct;
|
||||
using (sdr = cmd.ExecuteReader(CommandBehavior.CloseConnection))
|
||||
using (var conn = new SqlConnection(connStr))
|
||||
{
|
||||
dt.Load(sdr);
|
||||
conn.Open();
|
||||
using (var cmd = new SqlCommand(cmdText, conn))
|
||||
{
|
||||
cmd.CommandType = ct;
|
||||
using (var sdr = cmd.ExecuteReader(CommandBehavior.CloseConnection))
|
||||
{
|
||||
dt.Load(sdr);
|
||||
}
|
||||
}
|
||||
}
|
||||
return dt;
|
||||
}
|
||||
@@ -130,12 +98,18 @@ namespace NSAnalysis.DAL
|
||||
public static DataTable ExecuteQuery(string cmdText, SqlParameter[] paras, CommandType ct)
|
||||
{
|
||||
DataTable dt = new DataTable();
|
||||
cmd = new SqlCommand(cmdText, GetConn());
|
||||
cmd.CommandType = ct;
|
||||
cmd.Parameters.AddRange(paras);
|
||||
using (sdr = cmd.ExecuteReader(CommandBehavior.CloseConnection))
|
||||
using (var conn = new SqlConnection(connStr))
|
||||
{
|
||||
dt.Load(sdr);
|
||||
conn.Open();
|
||||
using (var cmd = new SqlCommand(cmdText, conn))
|
||||
{
|
||||
cmd.CommandType = ct;
|
||||
cmd.Parameters.AddRange(paras);
|
||||
using (var sdr = cmd.ExecuteReader(CommandBehavior.CloseConnection))
|
||||
{
|
||||
dt.Load(sdr);
|
||||
}
|
||||
}
|
||||
}
|
||||
return dt;
|
||||
}
|
||||
@@ -149,11 +123,15 @@ namespace NSAnalysis.DAL
|
||||
/// <returns></returns>
|
||||
public static DataSet ExecuteDs(String Sqlstr)
|
||||
{
|
||||
using (SqlDataAdapter da = new SqlDataAdapter(Sqlstr, GetConn()))
|
||||
using (var conn = new SqlConnection(connStr))
|
||||
{
|
||||
DataSet ds = new DataSet();
|
||||
da.Fill(ds);
|
||||
return ds;
|
||||
conn.Open();
|
||||
using (SqlDataAdapter da = new SqlDataAdapter(Sqlstr, conn))
|
||||
{
|
||||
DataSet ds = new DataSet();
|
||||
da.Fill(ds);
|
||||
return ds;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -214,28 +192,32 @@ namespace NSAnalysis.DAL
|
||||
public static int InsertMeasureDataToDB(DataTable InsertDT)
|
||||
{
|
||||
int iResult = 1;
|
||||
using (SqlBulkCopy bulkCopy = new SqlBulkCopy(GetConn()))
|
||||
using (var conn = new SqlConnection(connStr))
|
||||
{
|
||||
try
|
||||
conn.Open();
|
||||
using (SqlBulkCopy bulkCopy = new SqlBulkCopy(conn))
|
||||
{
|
||||
bulkCopy.DestinationTableName = "TMeasureData";//要插入的表的表明,创造映射关系,比下面的直接写表名称 更加灵活
|
||||
bulkCopy.ColumnMappings.Add("CarID", "CarID");//映射字段名 DataTable列名 ,数据库 对应的列名
|
||||
bulkCopy.ColumnMappings.Add("CarType", "CarType");//映射字段名 DataTable列名 ,数据库 对应的列名
|
||||
bulkCopy.ColumnMappings.Add("MeasPointName", "MeasPointName");
|
||||
bulkCopy.ColumnMappings.Add("DimensionName", "DimensionName");
|
||||
bulkCopy.ColumnMappings.Add("NormalValue", "NormalValue");
|
||||
bulkCopy.ColumnMappings.Add("LowerTolVal", "LowerTolVal");
|
||||
bulkCopy.ColumnMappings.Add("UpperTolVal", "UpperTolVal");
|
||||
bulkCopy.ColumnMappings.Add("MeasureValue", "MeasureValue");
|
||||
bulkCopy.ColumnMappings.Add("MeasureItemResult", "MeasureItemResult");
|
||||
bulkCopy.ColumnMappings.Add("MeasureDate", "MeasureDate");
|
||||
bulkCopy.ColumnMappings.Add("Remark", "Remark");
|
||||
bulkCopy.WriteToServer(InsertDT);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show("批量插入测量数据到数据库失败!原因:" + ex.ToString(), "提示", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
iResult = -1;
|
||||
try
|
||||
{
|
||||
bulkCopy.DestinationTableName = "TMeasureData";
|
||||
bulkCopy.ColumnMappings.Add("CarID", "CarID");
|
||||
bulkCopy.ColumnMappings.Add("CarType", "CarType");
|
||||
bulkCopy.ColumnMappings.Add("MeasPointName", "MeasPointName");
|
||||
bulkCopy.ColumnMappings.Add("DimensionName", "DimensionName");
|
||||
bulkCopy.ColumnMappings.Add("NormalValue", "NormalValue");
|
||||
bulkCopy.ColumnMappings.Add("LowerTolVal", "LowerTolVal");
|
||||
bulkCopy.ColumnMappings.Add("UpperTolVal", "UpperTolVal");
|
||||
bulkCopy.ColumnMappings.Add("MeasureValue", "MeasureValue");
|
||||
bulkCopy.ColumnMappings.Add("MeasureItemResult", "MeasureItemResult");
|
||||
bulkCopy.ColumnMappings.Add("MeasureDate", "MeasureDate");
|
||||
bulkCopy.ColumnMappings.Add("Remark", "Remark");
|
||||
bulkCopy.WriteToServer(InsertDT);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show("批量插入测量数据到数据库失败!原因:" + ex.ToString(), "提示", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
iResult = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return iResult;
|
||||
@@ -251,27 +233,26 @@ namespace NSAnalysis.DAL
|
||||
/// <param name="InsertDT">要插入的数据表</param>
|
||||
public static void TWorkpieceListToSQLServer(DataTable InsertDT)
|
||||
{
|
||||
using (SqlBulkCopy bulkCopy = new SqlBulkCopy(GetConn()))
|
||||
using (var conn = new SqlConnection(connStr))
|
||||
{
|
||||
try
|
||||
conn.Open();
|
||||
using (SqlBulkCopy bulkCopy = new SqlBulkCopy(conn))
|
||||
{
|
||||
bulkCopy.DestinationTableName = "TWorkpieceList";//要插入的表的表明,创造映射关系,比下面的直接写表名称 更加灵活
|
||||
bulkCopy.ColumnMappings.Add("WorkpieceID", "WorkpieceID");//映射字段名 DataTable列名 ,数据库 对应的列名
|
||||
bulkCopy.ColumnMappings.Add("DrawerID", "DrawerID");
|
||||
bulkCopy.ColumnMappings.Add("WorkpieceType", "WorkpieceType");
|
||||
bulkCopy.ColumnMappings.Add("TrayType", "TrayType");
|
||||
bulkCopy.ColumnMappings.Add("WorkpieceStatus", "WorkpieceStatus");
|
||||
bulkCopy.ColumnMappings.Add("WorkpiecePos", "WorkpiecePos");
|
||||
bulkCopy.WriteToServer(InsertDT);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(ex.Message);
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Close the SqlDataReader. The SqlBulkCopy object is automatically closed at
|
||||
// the end of the using block.
|
||||
try
|
||||
{
|
||||
bulkCopy.DestinationTableName = "TWorkpieceList";
|
||||
bulkCopy.ColumnMappings.Add("WorkpieceID", "WorkpieceID");
|
||||
bulkCopy.ColumnMappings.Add("DrawerID", "DrawerID");
|
||||
bulkCopy.ColumnMappings.Add("WorkpieceType", "WorkpieceType");
|
||||
bulkCopy.ColumnMappings.Add("TrayType", "TrayType");
|
||||
bulkCopy.ColumnMappings.Add("WorkpieceStatus", "WorkpieceStatus");
|
||||
bulkCopy.ColumnMappings.Add("WorkpiecePos", "WorkpiecePos");
|
||||
bulkCopy.WriteToServer(InsertDT);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -287,24 +268,16 @@ namespace NSAnalysis.DAL
|
||||
/// <param name="InsertDataTable">数据集</param>
|
||||
public static void SqlBulkCopyInsert(string strDBTableName, DataTable InsertDataTable)
|
||||
{
|
||||
try
|
||||
using (var conn = new SqlConnection(connStr))
|
||||
{
|
||||
using (SqlBulkCopy sqlRevdBulkCopy = new SqlBulkCopy(GetConn()))//引用SqlBulkCopy
|
||||
conn.Open();
|
||||
using (SqlBulkCopy sqlRevdBulkCopy = new SqlBulkCopy(conn))
|
||||
{
|
||||
sqlRevdBulkCopy.DestinationTableName = strDBTableName;//数据库中对应的表名
|
||||
|
||||
sqlRevdBulkCopy.NotifyAfter = InsertDataTable.Rows.Count;//有几行数据
|
||||
|
||||
sqlRevdBulkCopy.WriteToServer(InsertDataTable);//数据导入数据库
|
||||
|
||||
sqlRevdBulkCopy.Close();//关闭连接
|
||||
sqlRevdBulkCopy.DestinationTableName = strDBTableName;
|
||||
sqlRevdBulkCopy.NotifyAfter = InsertDataTable.Rows.Count;
|
||||
sqlRevdBulkCopy.WriteToServer(InsertDataTable);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine("数据库处理出错,SqlBulkCopyInsert,原因:" + ex.Message);
|
||||
throw (ex);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion 使用SqlBulkCopy将DataTable中的数据批量插入数据库中
|
||||
|
||||
+20
-13
@@ -6,6 +6,7 @@ using System.Data;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using Telerik.WinControls;
|
||||
@@ -48,6 +49,7 @@ namespace NSAnalysis
|
||||
{
|
||||
private Timer fileSortTimer = new Timer(); // 定时器,用于定时分发任务
|
||||
private FileSorter fileSorter = new FileSorter();
|
||||
private int _isProcessing = 0; // 0=空闲, 1=处理中,防止定时器重入
|
||||
|
||||
#region 全局变量
|
||||
|
||||
@@ -282,7 +284,7 @@ namespace NSAnalysis
|
||||
#endregion 分页相关
|
||||
}
|
||||
|
||||
private void DisplayMeasureData(string strCarID) // 其中 string strCarID 是车身ID_L 或 车身ID_R
|
||||
private void DisplayMeasureData(string strCarID, string measureTime = "") // 其中 string strCarID 是车身ID_L 或 车身ID_R
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -293,27 +295,21 @@ namespace NSAnalysis
|
||||
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;
|
||||
|
||||
// 更新统计信息显示
|
||||
labVIN.Text = strCarID;
|
||||
labCarType.Text = ConfigDfn.strCarModel;
|
||||
//labPosition_L.Text = "左侧";
|
||||
labCarType.Text = analysis.CarID; // 使用查询结果中的数据
|
||||
labOKCount_L.Text = OKCount.ToString();
|
||||
labNGCount_L.Text = OutCount.ToString();
|
||||
labRejectCount_L.Text = RejectedCount.ToString();
|
||||
labSumMeasureCount_L.Text = dtRowCount.ToString();
|
||||
labMeaTime_L.Text = ConfigDfn.strMeasureTime;
|
||||
labMeaTime_L.Text = measureTime; // 使用传入的测量时间,不依赖全局变量
|
||||
|
||||
#region 表单区填充
|
||||
|
||||
@@ -359,16 +355,16 @@ namespace NSAnalysis
|
||||
}
|
||||
}
|
||||
|
||||
private void FileSorter_OnFileParsed(string strCarID, string Position) //形如
|
||||
private void FileSorter_OnFileParsed(string strCarID, string Position, string measureTime) //形如
|
||||
{
|
||||
// 线程安全更新 UI
|
||||
if (InvokeRequired)
|
||||
{
|
||||
Invoke(new Action<string, string>(FileSorter_OnFileParsed), strCarID, Position);
|
||||
Invoke(new Action<string, string, string>(FileSorter_OnFileParsed), strCarID, Position, measureTime);
|
||||
return;
|
||||
}
|
||||
// 显示数据 并更新单侧汇总结果, 此时为左侧或右侧数据
|
||||
DisplayMeasureData(strCarID);
|
||||
DisplayMeasureData(strCarID, measureTime);
|
||||
}
|
||||
|
||||
/// 通过给定的文件流,判断文件的编码类型
|
||||
@@ -682,6 +678,13 @@ namespace NSAnalysis
|
||||
|
||||
private void FileSortTimer_Tick(object sender, EventArgs e) //分发定时器
|
||||
{
|
||||
// 防止上一轮未完成时重入
|
||||
if (Interlocked.CompareExchange(ref _isProcessing, 1, 0) != 0)
|
||||
{
|
||||
MyBase.TraceWriteLine("[FileSortTimer] 上一轮分发任务仍在执行,跳过本次触发。");
|
||||
return;
|
||||
}
|
||||
|
||||
// 后台运行,避免阻塞UI
|
||||
Task.Run(() =>
|
||||
{
|
||||
@@ -693,6 +696,10 @@ namespace NSAnalysis
|
||||
{
|
||||
MyBase.TraceWriteLine($"分发任务异常: {ex.Message}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
Interlocked.Exchange(ref _isProcessing, 0);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user