using Microsoft.Extensions.Options;
|
using WIDESEAWCS_S7Simulator.Core.Interfaces;
|
using WIDESEAWCS_S7Simulator.Core.Protocol;
|
|
namespace WIDESEAWCS_S7Simulator.Application.Protocol;
|
|
/// <summary>
|
/// 输送线设备协议处理器。
|
/// </summary>
|
public class WcsLineProtocolHandler : IDeviceProtocolHandler
|
{
|
private readonly MirrorAckProtocolHandler _mirrorAckHandler;
|
private readonly ProtocolMonitoringOptions _options;
|
|
public WcsLineProtocolHandler(
|
MirrorAckProtocolHandler mirrorAckHandler,
|
IOptions<ProtocolMonitoringOptions> 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<MirrorAckRuleOptions> ResolveConfiguredRules(ProtocolTemplate template)
|
{
|
if (_options.WcsLineRuleIds.Count == 0)
|
{
|
return Array.Empty<MirrorAckRuleOptions>();
|
}
|
|
// 按模板字段过滤规则,避免把 11001 这类规则错误套到其他线体模板上。
|
var keySet = new HashSet<string>(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<MirrorAckRuleOptions> ResolveAutoRulesFromTemplate(
|
ProtocolTemplate template,
|
IEnumerable<string> excludedRuleIds)
|
{
|
var excluded = new HashSet<string>(excludedRuleIds, StringComparer.OrdinalIgnoreCase);
|
var keySet = new HashSet<string>(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<MirrorAckRuleOptions>();
|
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<string>();
|
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<string> keySet, params string[] candidates)
|
{
|
return candidates.FirstOrDefault(keySet.Contains);
|
}
|
}
|