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);
}
}