wanshenmean
2026-03-24 ab2076893b8df3c14f1a126d47e9eee132a38f4b
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
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);
    }
}