using Microsoft.Extensions.Options; using WIDESEAWCS_S7Simulator.Core.Interfaces; using WIDESEAWCS_S7Simulator.Core.Protocol; namespace WIDESEAWCS_S7Simulator.Application.Protocol; /// /// 输送线设备协议处理器。 /// public class WcsLineProtocolHandler : IDeviceProtocolHandler { private readonly MirrorAckProtocolHandler _mirrorAckHandler; private readonly ProtocolMonitoringOptions _options; public WcsLineProtocolHandler( MirrorAckProtocolHandler mirrorAckHandler, IOptions options) { _mirrorAckHandler = mirrorAckHandler; _options = options.Value; } public string ProtocolName => "WcsLineProtocol"; public bool Process(IMemoryStore memoryStore, ProtocolTemplate template, ProtocolRuntimeState runtimeState) { var configRules = ResolveConfiguredRules(template); var autoRules = ResolveAutoRulesFromTemplate(template, configRules.Select(x => x.RuleId)); bool changed = false; foreach (var rule in configRules.Concat(autoRules)) { var stateKey = $"{ProtocolName}:{rule.RuleId}"; changed |= _mirrorAckHandler.Process(memoryStore, template, runtimeState, rule, stateKey); } return changed; } private IReadOnlyList ResolveConfiguredRules(ProtocolTemplate template) { if (_options.WcsLineRuleIds.Count == 0) { return Array.Empty(); } // 按模板字段过滤规则,避免把 11001 这类规则错误套到其他线体模板上。 var keySet = new HashSet(template.Fields.Select(x => x.FieldKey), StringComparer.OrdinalIgnoreCase); return _options.WcsLineRuleIds .Where(x => !string.IsNullOrWhiteSpace(x)) .Distinct(StringComparer.OrdinalIgnoreCase) .Select(ruleId => _options.MirrorAckRules.FirstOrDefault(x => string.Equals(x.RuleId, ruleId, StringComparison.OrdinalIgnoreCase))) .Where(rule => rule != null) .Select(rule => rule!) .Where(rule => keySet.Contains(rule.WcsAckFieldKey) && keySet.Contains(rule.PlcStbFieldKey)) .ToArray(); } private static IReadOnlyList ResolveAutoRulesFromTemplate( ProtocolTemplate template, IEnumerable excludedRuleIds) { var excluded = new HashSet(excludedRuleIds, StringComparer.OrdinalIgnoreCase); var keySet = new HashSet(template.Fields.Select(x => x.FieldKey), StringComparer.OrdinalIgnoreCase); var wcsAckFields = template.Fields .Where(x => x.FieldKey.EndsWith("_WCS_ACK", StringComparison.OrdinalIgnoreCase)) .Select(x => x.FieldKey) .ToArray(); var rules = new List(); foreach (var ackField in wcsAckFields) { var prefix = ackField[..^"_WCS_ACK".Length]; var ruleId = $"auto-{prefix}"; if (excluded.Contains(ruleId)) { continue; } var plcStb = $"{prefix}_PLC_STB"; if (!keySet.Contains(plcStb)) { continue; } var taskKey = FindFirstExisting( keySet, $"{prefix}_WCS_TASK_ID", $"{prefix}_TaskNo", $"{prefix}_TaskNum"); var targetKey = FindFirstExisting( keySet, $"{prefix}_WCS_TARGET_ID", $"{prefix}_Target"); var barcodeKey = FindFirstExisting( keySet, $"{prefix}_Barcode", $"{prefix}_PALLET_CODE"); var clearKeys = new List(); if (!string.IsNullOrWhiteSpace(taskKey)) clearKeys.Add(taskKey); if (!string.IsNullOrWhiteSpace(targetKey)) clearKeys.Add(targetKey); if (!string.IsNullOrWhiteSpace(barcodeKey)) clearKeys.Add(barcodeKey); rules.Add(new MirrorAckRuleOptions { RuleId = ruleId, WcsAckFieldKey = ackField, PlcStbFieldKey = plcStb, WcsTaskIdFieldKey = null, PlcTaskIdFieldKey = null, WcsTargetIdFieldKey = null, PlcTargetIdFieldKey = null, ClearFieldKeysOnAck0 = clearKeys, ClearFieldKeysOnAck2 = clearKeys }); } return rules; } private static string? FindFirstExisting(HashSet keySet, params string[] candidates) { return candidates.FirstOrDefault(keySet.Contains); } }