diff --git a/src/LineTraceabilitySystem/Services/ScanService.cs b/src/LineTraceabilitySystem/Services/ScanService.cs
index 1d9556b..9a068c5 100644
--- a/src/LineTraceabilitySystem/Services/ScanService.cs
+++ b/src/LineTraceabilitySystem/Services/ScanService.cs
@@ -30,7 +30,89 @@ namespace LineTraceabilitySystem.Services
public bool ValidateSubPartCode(string code)
{
- return true;
+ // 将子零件码的判定规则集中在此处,视图层不再包含具体规则。
+ // 新规则:
+ // 1. 不限制长度
+ // 2. 在二维码中提取日期信息,日期小于 2025-11-01 判定为不合格,>= 判定为合格
+ // 3. 日期可能以 6 位 (ddMMyy) 或 8 位 (ddMMyyyy) 等形式存在,解析应具备较高兼容性,避免简单的固定位置截取
+ if (string.IsNullOrWhiteSpace(code))
+ {
+ return false;
+ }
+
+ code = code.Trim();
+
+ // 优先尝试在整个字符串中查找 6 或 8 位连续数字序列作为候选日期字段
+ // 使用正则以提高兼容性(避免基于固定下标截取)
+ try
+ {
+ var matches = System.Text.RegularExpressions.Regex.Matches(code, "\\d{6,8}");
+ var threshold = new DateTime(2025, 11, 1);
+
+ foreach (System.Text.RegularExpressions.Match m in matches)
+ {
+ var seq = m.Value;
+
+ // 优先尝试 ddMMyyyy (8 位)
+ if (seq.Length == 8)
+ {
+ if (int.TryParse(seq.Substring(0, 2), out var dd)
+ && int.TryParse(seq.Substring(2, 2), out var MM)
+ && int.TryParse(seq.Substring(4, 4), out var yyyy))
+ {
+ if (IsValidDate(yyyy, MM, dd, threshold))
+ return true;
+ }
+ }
+
+ // 兼容 6 位形式,通常为 ddMMyy(例:261125 表示 26-11-25 -> 2025-11-26)
+ if (seq.Length == 6)
+ {
+ if (int.TryParse(seq.Substring(0, 2), out var dd)
+ && int.TryParse(seq.Substring(2, 2), out var MM)
+ && int.TryParse(seq.Substring(4, 2), out var yy))
+ {
+ var yyyy = 2000 + yy; // 以 2000 年为基准
+ if (IsValidDate(yyyy, MM, dd, threshold))
+ return true;
+ }
+ // 兼容性扩展:如果 6 位不能按 ddMMyy 解析,通过 yyMMdd 尝试解析
+ if (int.TryParse(seq.Substring(0, 2), out var alt1)
+ && int.TryParse(seq.Substring(2, 2), out var alt2)
+ && int.TryParse(seq.Substring(4, 2), out var alt3))
+ {
+ // 尝试 yyMMdd -> year = 2000 + alt1, month=alt2, day=alt3
+ var yyyy = 2000 + alt1;
+ if (IsValidDate(yyyy, alt2, alt3, threshold))
+ return true;
+ }
+ }
+ }
+ }
+ catch
+ {
+ // 解析异常视为不合格
+ return false;
+ }
+
+ // 未找到满足条件的日期序列,判定为不合格
+ return false;
+ }
+
+ ///
+ /// 判断指定年月日是否合法并且大于等于阈值日期
+ ///
+ private static bool IsValidDate(int year, int month, int day, DateTime threshold)
+ {
+ try
+ {
+ var dt = new DateTime(year, month, day);
+ return dt >= threshold;
+ }
+ catch
+ {
+ return false;
+ }
}
public bool ValidateTraceabilityCode(string code)
diff --git a/src/LineTraceabilitySystem/ViewModels/MainWindowViewModel.cs b/src/LineTraceabilitySystem/ViewModels/MainWindowViewModel.cs
index 7a2ef7b..06c6907 100644
--- a/src/LineTraceabilitySystem/ViewModels/MainWindowViewModel.cs
+++ b/src/LineTraceabilitySystem/ViewModels/MainWindowViewModel.cs
@@ -602,46 +602,86 @@ namespace LineTraceabilitySystem.ViewModels
}
///
- /// 处理摄像头码扫描事件
+ /// 处理摄像头码扫描事件(异步)。
+ /// 验证规则已全部封装到 `IScanService.ValidateSubPartCode` 中,视图层只负责调用、展示状态与流程控制。
///
/// 扫描到的摄像头码
private async Task OnSubPartCodeScannedAsync(string code)
{
+ // 当前子件名称(供后续上传使用)
string partCodeScanName = _scanList[CurrentStep];
+
+ // 记录扫码结果到视图模型以便绑定更新
SubPartScanCode = code;
- // 验证摄像头码逻辑 - 注意:ITraceMainService中可能没有VerifySubPartScanCode方法
- // 以下代码需要根据实际ITraceMainService接口定义进行调整
- bool isSubPartScanCodeValid = code.Contains("2F1980546#")&& code.Length==31 && _scanService.ValidateSubPartCode(code);
+
+ bool isSubPartScanCodeValid;
+ try
+ {
+ // 将所有判定逻辑放到扫描服务中,此处仅调用并捕获可能的异常
+ isSubPartScanCodeValid = _scanService.ValidateSubPartCode(code);
+ }
+ catch (Exception ex)
+ {
+ // 服务验证出现异常,记录并提示,视为验证失败
+ AddToOperationHistory($"验证子零件码异常: {ex.Message}");
+ _alertService.ShowError("验证子零件码时发生错误,请检查日志");
+ isSubPartScanCodeValid = false;
+ }
+
+ // 更新状态显示
SubPartScanCodeStatus = isSubPartScanCodeValid ? "OK" : "NG";
SubPartScanCodeStatusColor = isSubPartScanCodeValid ? Brushes.Green : Brushes.Red;
+
if (!isSubPartScanCodeValid)
{
+ // 验证不通过,更新UI状态并记录日志,结束本次扫描流程
ScanResult = "NG";
IsScanOk = false;
+ AddToOperationHistory($"扫描摄像头码: {code}, 状态: {SubPartScanCodeStatus}");
_alertService.ShowWarning("不是有效的码");
return;
}
+
+ // 验证通过
+ ScanResult = "OK";
+ IsScanOk = true;
AddToOperationHistory($"扫描摄像头码: {code}, 状态: {SubPartScanCodeStatus}");
+
+ // 进入下一步
CurrentStep += 1;
- if (CurrentStep == _scanList.Count)
+ // 判断是否已完成所有待扫项。如果完成,触发PLC相关写入并进入上传步骤。
+ if (CurrentStep >= _scanList.Count)
{
- Debug.WriteLine($"追溯数据上传服务器");
- AddToOperationHistory($"追溯数据上传服务器");
- TagServer tagServer = HostManager.GetTagServerByIp(PLCIP);
- if (CheckTagNameValid(tagServer, ScrewGunEnable))
+ AddToOperationHistory("追溯数据准备上传(所有子件已扫)");
+ try
{
- tagServer.WriteIntSingle(tagCommon.TagAddress, 1);
+ TagServer tagServer = HostManager.GetTagServerByIp(PLCIP);
+ if (CheckTagNameValid(tagServer, ScrewGunEnable))
+ {
+ // 通知PLC启用拧紧枪或触发拍照
+ tagServer.WriteIntSingle(tagCommon.TagAddress, 1);
+ }
}
+ catch (Exception ex)
+ {
+ AddToOperationHistory($"向PLC写入触发位失败: {ex.Message}");
+ }
+
+ // 使用特殊值表示上传阶段
CurrentStep = 1000;
}
+
+ // 上传阶段
if (CurrentStep == 1000)
{
await OnUploadScanned(SubPartScanCode, partCodeScanName);
+ // 重置流程,准备下一个追溯件
Initialize();
}
else
{
+ // 更新提示文本,等待下一个扫码
CurrentStepText = $"请扫{_scanList[CurrentStep]}码";
}
}