08e623410f
新到文件是否仍然正常出现 匹配成功 -> 待解析区 -> 解析成功 -> 正式区 -> 双侧完成 -> 客户报告生成。 历史遗留文件是否出现 已补充分发到正式区 这类新日志,并从源目录消失。 已经真正分发完成的文件,是否仍然保持 已处理且目标区域已存在,跳过,没有重复搬运。
833 lines
26 KiB
C#
833 lines
26 KiB
C#
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
|
|
{
|
|
/// <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
|
|
{
|
|
private sealed class TaskRule
|
|
{
|
|
public string ModelName { get; set; }
|
|
public string ModelCode { get; set; }
|
|
public string Position { get; set; }
|
|
public string SourceDir { get; set; }
|
|
public string TargetDir { get; set; }
|
|
public string MatchToken { get; set; }
|
|
}
|
|
|
|
private sealed class StagingPaths
|
|
{
|
|
public string FinalDir { get; set; }
|
|
public string PendingDir { get; set; }
|
|
public string ErrorDir { get; set; }
|
|
}
|
|
|
|
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, 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} 个分发任务,开始逐一执行。");
|
|
|
|
var rulesBySource = new Dictionary<string, List<TaskRule>>(StringComparer.OrdinalIgnoreCase);
|
|
foreach (DataRow task in tasks.Rows)
|
|
{
|
|
var rule = new TaskRule
|
|
{
|
|
ModelName = task["modelsName"].ToString(),
|
|
ModelCode = task["modelsCode"].ToString(),
|
|
Position = task["position"].ToString(),
|
|
SourceDir = task["sourceFile"].ToString(),
|
|
TargetDir = task["targetFile"].ToString()
|
|
};
|
|
rule.MatchToken = NormalizeMatchToken($"{rule.ModelCode}_{rule.Position}");
|
|
|
|
if (!rulesBySource.ContainsKey(rule.SourceDir))
|
|
{
|
|
rulesBySource[rule.SourceDir] = new List<TaskRule>();
|
|
}
|
|
rulesBySource[rule.SourceDir].Add(rule);
|
|
}
|
|
|
|
foreach (var item in rulesBySource)
|
|
{
|
|
string sourceDir = item.Key;
|
|
List<TaskRule> rules = item.Value;
|
|
Trace($"[ProcessFiles] 开始扫描源路径: {sourceDir},共 {rules.Count} 条匹配规则。");
|
|
foreach (var rule in rules)
|
|
{
|
|
Trace($"[ProcessFiles] 已加载分发任务 - 源路径: {rule.SourceDir}, 目标路径: {rule.TargetDir}, 匹配字符: {rule.ModelCode} 位置:{rule.Position}, 标准化特征符: {rule.MatchToken}");
|
|
}
|
|
|
|
if (!Directory.Exists(sourceDir))
|
|
{
|
|
Trace($"[ProcessFiles] 源文件地址不存在或错误: {sourceDir}");
|
|
continue;
|
|
}
|
|
|
|
ProcessDirectory(sourceDir, rules);
|
|
}
|
|
}
|
|
|
|
// 获取任务记录
|
|
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, List<TaskRule> rules)
|
|
{
|
|
foreach (string file in Directory.GetFiles(sourceDir, "*.csv"))
|
|
{
|
|
Trace($"正在处理文件 : {file}");
|
|
|
|
if (!ConfigDfn.iEnableSort)
|
|
{
|
|
return;
|
|
}
|
|
|
|
string rawMatchValue;
|
|
string normalizedMatchValue;
|
|
if (!TryReadMatchToken(file, readRowIndex, readColIndex, out rawMatchValue, out normalizedMatchValue))
|
|
{
|
|
Trace($"无法读取匹配特征,跳过文件: {file}");
|
|
emitProcessStep($"---> 2、无法读取匹配特征,跳过文件: {file}");
|
|
continue;
|
|
}
|
|
|
|
TaskRule matchedRule = null;
|
|
foreach (var rule in rules)
|
|
{
|
|
if (string.Equals(rule.MatchToken, normalizedMatchValue, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
matchedRule = rule;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (matchedRule == null)
|
|
{
|
|
Trace($"未匹配到文件: {file},原始特征符: {rawMatchValue},标准化后: {normalizedMatchValue}");
|
|
emitProcessStep($"---> 5、未匹配到文件: {Path.GetFileName(file)},特征符: {normalizedMatchValue}");
|
|
continue;
|
|
}
|
|
|
|
StagingPaths staging = EnsureStagingDirectories(matchedRule.TargetDir);
|
|
|
|
if (_dal.IsFileProcessed(file))
|
|
{
|
|
if (IsFileAlreadyInTargetArea(file, staging))
|
|
{
|
|
Trace($"文件已处理且目标区域已存在,跳过后续匹配和解析: {file}");
|
|
emitProcessStep($"---> 2、文件已处理且目标区域已存在,跳过: {file}");
|
|
continue;
|
|
}
|
|
|
|
string finalFile = MoveMatchedFile(file, staging.FinalDir);
|
|
Trace($"文件已有历史解析记录,但仍停留在源目录,已补充分发到正式区: {file} -> {finalFile}");
|
|
emitProcessStep($"---> 5、检测到历史已解析未分发文件,已补充分发: -> {finalFile}");
|
|
InsertTaskDetail(matchedRule, file, finalFile, 1, "历史已解析文件补充分发到正式区");
|
|
continue;
|
|
}
|
|
|
|
Trace($"匹配成功,准备移入待解析区: {file} -> {staging.PendingDir},原始特征符: {rawMatchValue},标准化后: {normalizedMatchValue}");
|
|
|
|
string pendingFile = MoveMatchedFile(file, staging.PendingDir);
|
|
Trace($"已移入待解析区: {file} -> {pendingFile}");
|
|
emitProcessStep($"---> 5、文件已移入待解析区: -> {pendingFile}");
|
|
InsertTaskDetail(matchedRule, file, pendingFile, 1, "文件已移入待解析区");
|
|
|
|
CsvParseResult parseResult = AnalysisNxsCSV(pendingFile);
|
|
if (parseResult.Success)
|
|
{
|
|
string finalFile = MoveMatchedFile(pendingFile, staging.FinalDir);
|
|
Trace($"解析成功,已转入正式区: {pendingFile} -> {finalFile}");
|
|
emitProcessStep($"---> 6、解析成功,已转入正式区: -> {finalFile}");
|
|
InsertTaskDetail(matchedRule, pendingFile, finalFile, 1, "解析成功并转入正式区");
|
|
}
|
|
else
|
|
{
|
|
if (File.Exists(pendingFile))
|
|
{
|
|
string errorFile = MoveMatchedFile(pendingFile, staging.ErrorDir);
|
|
Trace($"解析失败,已转入错误区: {pendingFile} -> {errorFile}");
|
|
emitProcessStep($"---> 6、解析失败,已转入错误区: -> {errorFile}");
|
|
InsertTaskDetail(matchedRule, pendingFile, errorFile, 2, "解析失败并转入错误区");
|
|
}
|
|
else
|
|
{
|
|
Trace($"解析失败后文件已不存在,无法转入错误区: {pendingFile}");
|
|
emitProcessStep($"---> 6、解析失败后文件已不存在,无法转入错误区: {pendingFile}");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 检查CSV文件中指定行列的字符串是否匹配目标值
|
|
/// </summary>
|
|
public static bool MatchCsvValue(string filePath, string targetValue, int rowIndex, int colIndex)
|
|
{
|
|
MyBase.TraceWriteLine($"[MatchCsvValue] 检查文件: {filePath}, 行索引: {rowIndex}, 列索引: {colIndex}, 目标值: {targetValue}");
|
|
try
|
|
{
|
|
string rawValue;
|
|
if (!TryReadCsvCellValue(filePath, rowIndex, colIndex, out rawValue))
|
|
return false;
|
|
|
|
string normalizedSource = NormalizeMatchToken(rawValue);
|
|
string normalizedTarget = NormalizeMatchToken(targetValue);
|
|
MyBase.TraceWriteLine($"[MatchCsvValue] 原始值: {rawValue}, 标准化原始值: {normalizedSource}, 标准化目标值: {normalizedTarget}");
|
|
return string.Equals(normalizedSource, normalizedTarget, StringComparison.OrdinalIgnoreCase);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
MyBase.TraceWriteLine($"处理CSV文件时出错: {ex.Message}");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private static bool TryReadMatchToken(string filePath, int rowIndex, int colIndex, out string rawValue, out string normalizedValue)
|
|
{
|
|
rawValue = string.Empty;
|
|
normalizedValue = string.Empty;
|
|
if (!TryReadCsvCellValue(filePath, rowIndex, colIndex, out rawValue))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
normalizedValue = NormalizeMatchToken(rawValue);
|
|
MyBase.TraceWriteLine($"[MatchCsvValue] 检查文件: {filePath}, 行索引: {rowIndex}, 列索引: {colIndex}, 原始值: {rawValue}, 标准化值: {normalizedValue}");
|
|
return true;
|
|
}
|
|
|
|
private static bool TryReadCsvCellValue(string filePath, int rowIndex, int colIndex, out string cellValue)
|
|
{
|
|
cellValue = string.Empty;
|
|
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;
|
|
}
|
|
|
|
cellValue = columns[colIndex].Trim().Trim('"');
|
|
return !string.IsNullOrEmpty(cellValue);
|
|
}
|
|
|
|
private static string NormalizeMatchToken(string rawValue)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(rawValue))
|
|
{
|
|
return string.Empty;
|
|
}
|
|
|
|
string cleaned = rawValue.Trim().Trim('"');
|
|
string[] parts = cleaned.Split(new[] { '_' }, StringSplitOptions.RemoveEmptyEntries);
|
|
if (parts.Length == 0)
|
|
{
|
|
return cleaned.ToUpperInvariant();
|
|
}
|
|
|
|
if (parts.Length >= 3 && IsSideToken(parts[parts.Length - 1]))
|
|
{
|
|
string prefix = string.Join("_", parts, 0, parts.Length - 1).ToUpperInvariant();
|
|
return $"{prefix}_{char.ToUpperInvariant(parts[parts.Length - 1][0])}";
|
|
}
|
|
|
|
if (parts.Length >= 2 && IsSideToken(parts[1]))
|
|
{
|
|
return $"{parts[0].ToUpperInvariant()}_{char.ToUpperInvariant(parts[1][0])}";
|
|
}
|
|
|
|
return cleaned.ToUpperInvariant();
|
|
}
|
|
|
|
private static bool IsSideToken(string token)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(token))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
char side = char.ToUpperInvariant(token.Trim()[0]);
|
|
return side == 'L' || side == 'R';
|
|
}
|
|
|
|
private static StagingPaths EnsureStagingDirectories(string targetDir)
|
|
{
|
|
var paths = new StagingPaths
|
|
{
|
|
FinalDir = targetDir,
|
|
PendingDir = Path.Combine(targetDir, "_pending"),
|
|
ErrorDir = Path.Combine(targetDir, "_error")
|
|
};
|
|
EnsureDirectory(paths.FinalDir);
|
|
EnsureDirectory(paths.PendingDir);
|
|
EnsureDirectory(paths.ErrorDir);
|
|
return paths;
|
|
}
|
|
|
|
private static void EnsureDirectory(string dir)
|
|
{
|
|
if (!Directory.Exists(dir))
|
|
{
|
|
Directory.CreateDirectory(dir);
|
|
MyBase.TraceWriteLine($"创建目标文件夹: {dir}");
|
|
}
|
|
}
|
|
|
|
private static bool IsFileAlreadyInTargetArea(string sourceFile, StagingPaths staging)
|
|
{
|
|
string fileName = Path.GetFileName(sourceFile);
|
|
return File.Exists(Path.Combine(staging.FinalDir, fileName))
|
|
|| File.Exists(Path.Combine(staging.PendingDir, fileName))
|
|
|| File.Exists(Path.Combine(staging.ErrorDir, fileName));
|
|
}
|
|
|
|
private static string MoveMatchedFile(string sourceFile, string targetDir)
|
|
{
|
|
string destFile = Path.Combine(targetDir, Path.GetFileName(sourceFile));
|
|
if (File.Exists(destFile))
|
|
{
|
|
string backupFile = destFile + ".bak_" + DateTime.Now.ToString("yyyyMMdd_HHmmss");
|
|
File.Move(destFile, backupFile);
|
|
MyBase.TraceWriteLine($"目标文件已存在,已重命名为备份文件: {backupFile}");
|
|
}
|
|
|
|
File.Move(sourceFile, destFile);
|
|
return destFile;
|
|
}
|
|
|
|
private void InsertTaskDetail(TaskRule rule, string sourceFile, string targetFile, int taskStatus, string taskDetail)
|
|
{
|
|
var detailModel = new CjlrTaskReleaseDetailModel
|
|
{
|
|
ModelsName = rule.ModelName,
|
|
ModelsCode = rule.ModelCode,
|
|
Position = rule.Position,
|
|
SourceFile = sourceFile,
|
|
TargetFile = targetFile,
|
|
TaskFileName = Path.GetFileName(sourceFile),
|
|
TaskStatus = taskStatus,
|
|
TaskDetail = taskDetail,
|
|
CreateDate = DateTime.Now
|
|
};
|
|
_dal.InsertTaskDetail(detailModel);
|
|
}
|
|
|
|
// 生成单侧统计信息的方法(使用局部参数,不再依赖全局变量)
|
|
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");
|
|
}
|
|
}
|
|
}
|