wanshenmean
2026-03-26 8e42d0c1b7ae36cff2e7c69999117911a4b6f300
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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
using WIDESEAWCS_QuartzJob;
 
namespace WIDESEAWCS_Tasks
{
    /// <summary>
    /// 输送线目标地址选择器 - 处理拘束机/插拔钉机的上下层请求
    /// </summary>
    /// <remarks>
    /// 核心职责:
    /// 1. 处理入库场景的目标地址选择
    /// 2. 处理出库场景的目标地址选择
    /// 3. 判断拘束机和插拔钉机的物料请求状态
    /// 4. 协调输送线与上下层设备之间的物料流转
    ///
    /// 拘束机和插拔钉机都有上下两层结构,
    /// 每层都有独立的物料请求和出料信号,需要分别处理。
    /// </remarks>
    public class ConveyorLineTargetAddressSelector
    {
        /// <summary>
        /// 拘束机名称常量
        /// </summary>
        private const string ConstraintMachineName = "拘束机";
 
        /// <summary>
        /// 插拔钉机名称常量
        /// </summary>
        private const string PinMachineName = "插拔钉机";
 
        /// <summary>
        /// 拘束机对应的点位编码列表
        /// </summary>
        /// <remarks>
        /// 当目标地址在这些编码中时,表示需要与拘束机交互。
        /// </remarks>
        private static readonly List<string> ConstraintMachineCodes = new List<string> { "10180", "20090" };
 
        /// <summary>
        /// 插拔钉机对应的点位编码列表
        /// </summary>
        /// <remarks>
        /// 当目标地址在这些编码中时,表示需要与插拔钉机交互。
        /// </remarks>
        private static readonly List<string> PinMachineCodes = new List<string> { "10190", "20100" };
 
        /// <summary>
        /// 处理入库场景的下一地址请求
        /// </summary>
        /// <remarks>
        /// 当入库任务执行到某个位置时调用此方法。
        /// 判断目标设备是否需要物料或可以出料。
        /// </remarks>
        /// <param name="conveyorLine">输送线设备对象</param>
        /// <param name="nextAddress">下一地址/目标设备编码</param>
        /// <param name="childDeviceCode">当前子设备编码</param>
        public void HandleInboundNextAddress(CommonConveyorLine conveyorLine, string nextAddress, string childDeviceCode)
        {
            // 调用通用处理方法,isUpper = true 表示处理上层
            HandleDeviceRequest(conveyorLine, nextAddress, childDeviceCode, isUpper: true);
        }
 
        /// <summary>
        /// 处理出库场景的下一地址请求
        /// </summary>
        /// <remarks>
        /// 当出库任务执行到某个位置时调用此方法。
        /// 判断目标设备是否需要物料或可以出料。
        /// </remarks>
        /// <param name="conveyorLine">输送线设备对象</param>
        /// <param name="nextAddress">下一地址/目标设备编码</param>
        /// <param name="childDeviceCode">当前子设备编码</param>
        public void HandleOutboundNextAddress(CommonConveyorLine conveyorLine, string nextAddress, string childDeviceCode)
        {
            // 调用通用处理方法,isUpper = false 表示处理下层
            HandleDeviceRequest(conveyorLine, nextAddress, childDeviceCode, isUpper: false);
        }
 
        /// <summary>
        /// 通用设备请求处理方法
        /// </summary>
        /// <remarks>
        /// 根据目标地址类型(拘束机/插拔钉机)调用相应的处理逻辑。
        /// 处理上下层设备的物料请求和出料协调。
        /// </remarks>
        /// <param name="conveyorLine">输送线设备对象</param>
        /// <param name="nextAddress">下一地址/目标设备编码</param>
        /// <param name="childDeviceCode">当前子设备编码</param>
        /// <param name="isUpper">是否处理上层(true=上层,false=下层)</param>
        private void HandleDeviceRequest(CommonConveyorLine conveyorLine, string nextAddress, string childDeviceCode, bool isUpper)
        {
            // 获取全局设备列表
            var devices = Storage.Devices;
 
            // 判断目标设备类型
            if (ConstraintMachineCodes.Contains(nextAddress))
            {
                // 拘束机处理分支
                // 查找拘束机设备
                ConstraintMachine? constraint = devices.OfType<ConstraintMachine>().FirstOrDefault(d => d.DeviceName == ConstraintMachineName);
                if (constraint == null)
                {
                    // 未找到拘束机设备,直接返回
                    return;
                }
 
                // 处理拘束机的请求
                ProcessDeviceRequest(
                    conveyorLine,
                    childDeviceCode,
                    // 获取物料请求标志(上层或下层)
                    getMaterialRequest: () => isUpper
                        ? constraint.GetValue<ConstraintMachineDBName, short>(ConstraintMachineDBName.MaterialRequestUpper) != 0
                        : constraint.GetValue<ConstraintMachineDBName, short>(ConstraintMachineDBName.MaterialRequestLower) != 0,
                    // 获取出料请求标志(上层或下层)
                    getOutputRequest: () => isUpper
                        ? constraint.GetValue<ConstraintMachineDBName, short>(ConstraintMachineDBName.OutputRequestUpper) != 0
                        : constraint.GetValue<ConstraintMachineDBName, short>(ConstraintMachineDBName.OutputRequestLower) != 0,
                    // 设置输出就绪标志(上层或下层)
                    setOutputReady: outputReq =>
                    {
                        if (isUpper)
                        {
                            constraint.SetValue(ConstraintMachineDBName.ConstraintTrayOutputReadyUpper, outputReq ? 1 : 0);
                        }
                        else
                        {
                            constraint.SetValue(ConstraintMachineDBName.ConstraintTrayOutputReadyLower, outputReq ? 1 : 0);
                        }
                    });
            }
            else if (PinMachineCodes.Contains(nextAddress))
            {
                // 插拔钉机处理分支
                // 查找插拔钉机设备
                PinMachine? pinMachine = devices.OfType<PinMachine>().FirstOrDefault(d => d.DeviceName == PinMachineName);
                if (pinMachine == null)
                {
                    return;
                }
 
                // 处理插拔钉机的请求
                ProcessDeviceRequest(
                    conveyorLine,
                    childDeviceCode,
                    // 获取物料请求标志(上层或下层)
                    getMaterialRequest: () => isUpper
                        ? pinMachine.GetValue<PinMachineDBName, short>(PinMachineDBName.MaterialRequestUpper) != 0
                        : pinMachine.GetValue<PinMachineDBName, short>(PinMachineDBName.MaterialRequestLower) != 0,
                    // 获取出料请求标志(上层或下层)
                    getOutputRequest: () => isUpper
                        ? pinMachine.GetValue<PinMachineDBName, short>(PinMachineDBName.OutputRequestUpper) != 0
                        : pinMachine.GetValue<PinMachineDBName, short>(PinMachineDBName.OutputRequestLower) != 0,
                    // 设置输出就绪标志(上层或下层)
                    setOutputReady: outputReq =>
                    {
                        if (isUpper)
                        {
                            pinMachine.SetValue(PinMachineDBName.PlugPinTrayOutputReadyUpper, outputReq ? 1 : 0);
                        }
                        else
                        {
                            pinMachine.SetValue(PinMachineDBName.PlugPinTrayOutputReadyLower, outputReq ? 1 : 0);
                        }
                    });
            }
        }
 
        /// <summary>
        /// 处理设备请求的核心逻辑
        /// </summary>
        /// <remarks>
        /// 根据物料请求和出料请求的状态:
        /// - 如果有物料请求,设置目标地址并发送 ACK
        /// - 如果有出料请求,设置设备的输出就绪标志
        /// </remarks>
        /// <param name="conveyorLine">输送线设备对象</param>
        /// <param name="childDeviceCode">当前子设备编码</param>
        /// <param name="getMaterialRequest">获取物料请求状态的委托</param>
        /// <param name="getOutputRequest">获取出料请求状态的委托</param>
        /// <param name="setOutputReady">设置输出就绪标志的委托</param>
        private static void ProcessDeviceRequest(
            CommonConveyorLine conveyorLine,
            string childDeviceCode,
            Func<bool> getMaterialRequest,
            Func<bool> getOutputRequest,
            Action<bool> setOutputReady)
        {
            // 获取物料请求状态
            bool materialReq = getMaterialRequest();
 
            // 获取出料请求状态
            bool outputReq = getOutputRequest();
 
            // 如果设备需要物料
            if (materialReq)
            {
                // 设置目标地址为 1(表示有料进来)
                conveyorLine.SetValue(ConveyorLineDBNameNew.Target, 1, childDeviceCode);
 
                // 回复 ACK 确认信号
                conveyorLine.SetValue(ConveyorLineDBNameNew.WCS_ACK, 1, childDeviceCode);
            }
            else
            {
                // 设备不需要物料,设置输出就绪标志
                // 通知设备可以继续出料
                setOutputReady(outputReq);
            }
        }
    }
}