重构子零件码验证逻辑,提升兼容性与健壮性
将子零件码(摄像头码)验证规则全部下沉至 ScanService 服务层,支持多种日期格式(ddMMyy、ddMMyyyy、yyMMdd)智能提取与判定,统一以日期大于等于 2025-11-01 为合格标准。视图层仅负责调用服务、异常处理和流程控制,提升了代码可维护性和扫码兼容性。完善了异常处理和日志记录,优化用户体验。
This commit is contained in:
@@ -30,7 +30,89 @@ namespace LineTraceabilitySystem.Services
|
|||||||
|
|
||||||
public bool ValidateSubPartCode(string code)
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 判断指定年月日是否合法并且大于等于阈值日期
|
||||||
|
/// </summary>
|
||||||
|
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)
|
public bool ValidateTraceabilityCode(string code)
|
||||||
|
|||||||
@@ -602,46 +602,86 @@ namespace LineTraceabilitySystem.ViewModels
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 处理摄像头码扫描事件
|
/// 处理摄像头码扫描事件(异步)。
|
||||||
|
/// 验证规则已全部封装到 `IScanService.ValidateSubPartCode` 中,视图层只负责调用、展示状态与流程控制。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="code">扫描到的摄像头码</param>
|
/// <param name="code">扫描到的摄像头码</param>
|
||||||
private async Task OnSubPartCodeScannedAsync(string code)
|
private async Task OnSubPartCodeScannedAsync(string code)
|
||||||
{
|
{
|
||||||
|
// 当前子件名称(供后续上传使用)
|
||||||
string partCodeScanName = _scanList[CurrentStep];
|
string partCodeScanName = _scanList[CurrentStep];
|
||||||
|
|
||||||
|
// 记录扫码结果到视图模型以便绑定更新
|
||||||
SubPartScanCode = code;
|
SubPartScanCode = code;
|
||||||
// 验证摄像头码逻辑 - 注意:ITraceMainService中可能没有VerifySubPartScanCode方法
|
|
||||||
// 以下代码需要根据实际ITraceMainService接口定义进行调整
|
bool isSubPartScanCodeValid;
|
||||||
bool isSubPartScanCodeValid = code.Contains("2F1980546#")&& code.Length==31 && _scanService.ValidateSubPartCode(code);
|
try
|
||||||
|
{
|
||||||
|
// 将所有判定逻辑放到扫描服务中,此处仅调用并捕获可能的异常
|
||||||
|
isSubPartScanCodeValid = _scanService.ValidateSubPartCode(code);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
// 服务验证出现异常,记录并提示,视为验证失败
|
||||||
|
AddToOperationHistory($"验证子零件码异常: {ex.Message}");
|
||||||
|
_alertService.ShowError("验证子零件码时发生错误,请检查日志");
|
||||||
|
isSubPartScanCodeValid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新状态显示
|
||||||
SubPartScanCodeStatus = isSubPartScanCodeValid ? "OK" : "NG";
|
SubPartScanCodeStatus = isSubPartScanCodeValid ? "OK" : "NG";
|
||||||
SubPartScanCodeStatusColor = isSubPartScanCodeValid ? Brushes.Green : Brushes.Red;
|
SubPartScanCodeStatusColor = isSubPartScanCodeValid ? Brushes.Green : Brushes.Red;
|
||||||
|
|
||||||
if (!isSubPartScanCodeValid)
|
if (!isSubPartScanCodeValid)
|
||||||
{
|
{
|
||||||
|
// 验证不通过,更新UI状态并记录日志,结束本次扫描流程
|
||||||
ScanResult = "NG";
|
ScanResult = "NG";
|
||||||
IsScanOk = false;
|
IsScanOk = false;
|
||||||
|
AddToOperationHistory($"扫描摄像头码: {code}, 状态: {SubPartScanCodeStatus}");
|
||||||
_alertService.ShowWarning("不是有效的码");
|
_alertService.ShowWarning("不是有效的码");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 验证通过
|
||||||
|
ScanResult = "OK";
|
||||||
|
IsScanOk = true;
|
||||||
AddToOperationHistory($"扫描摄像头码: {code}, 状态: {SubPartScanCodeStatus}");
|
AddToOperationHistory($"扫描摄像头码: {code}, 状态: {SubPartScanCodeStatus}");
|
||||||
|
|
||||||
|
// 进入下一步
|
||||||
CurrentStep += 1;
|
CurrentStep += 1;
|
||||||
|
|
||||||
if (CurrentStep == _scanList.Count)
|
// 判断是否已完成所有待扫项。如果完成,触发PLC相关写入并进入上传步骤。
|
||||||
|
if (CurrentStep >= _scanList.Count)
|
||||||
{
|
{
|
||||||
Debug.WriteLine($"追溯数据上传服务器");
|
AddToOperationHistory("追溯数据准备上传(所有子件已扫)");
|
||||||
AddToOperationHistory($"追溯数据上传服务器");
|
try
|
||||||
TagServer tagServer = HostManager.GetTagServerByIp(PLCIP);
|
|
||||||
if (CheckTagNameValid(tagServer, ScrewGunEnable))
|
|
||||||
{
|
{
|
||||||
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;
|
CurrentStep = 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 上传阶段
|
||||||
if (CurrentStep == 1000)
|
if (CurrentStep == 1000)
|
||||||
{
|
{
|
||||||
await OnUploadScanned(SubPartScanCode, partCodeScanName);
|
await OnUploadScanned(SubPartScanCode, partCodeScanName);
|
||||||
|
// 重置流程,准备下一个追溯件
|
||||||
Initialize();
|
Initialize();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// 更新提示文本,等待下一个扫码
|
||||||
CurrentStepText = $"请扫{_scanList[CurrentStep]}码";
|
CurrentStepText = $"请扫{_scanList[CurrentStep]}码";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user