Platfrom_GatingHandlerOpenOrClose.cs
9.16 KB
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
212
213
214
215
216
217
218
219
220
221
222
223
224
using HaHRCS.Rcs.Executor.Enums;
using HaHRCS.Rcs.Executor.PLC;
using HaHRCS.Rcs.Model.Entities;
using MassTransit;
using Microsoft.Extensions.Logging;
using Rcs.Application.Common;
using Rcs.Application.MessageBus.Commands;
using Rcs.Application.MessageBus.Commands.PlatformInteraction;
using Rcs.Domain.Repositories;
using Rcs.Executor.PLC;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using PLCGatingStatus = HaHRCS.Rcs.Model.Entities.PLCGatingStatus;
namespace Rcs.Infrastructure.MessageBus.Handlers.Commands.PLCInteraction
{
public class Platfrom_GatingHandlerOpenOrClose : IConsumer<PLCStaionGating>
{
private readonly EquipmentExecutor _equipmentExecutor;
private readonly ILogger<Platfrom_GatingHandlerOpenOrClose> _logger;
private readonly PlatformPickRepository _platformRepository;
public Platfrom_GatingHandlerOpenOrClose(
ILogger<Platfrom_GatingHandlerOpenOrClose> logger,
PlatformPickRepository platformRepository,
EquipmentExecutor equipmentExecutor)
{
_logger = logger;
_platformRepository = platformRepository;
_equipmentExecutor = equipmentExecutor;
}
private void ValidateCommand(PLCStaionGating command, ConsumeContext<PLCStaionGating> context)
{
//if (string.IsNullOrEmpty(command.EquipmentCode))
//{
// context.RespondAsync(ApiResponse.ErrorToPlc("请确认设备"));
// return;
//}
//if (command.Operation == 0)
//{
// context.RespondAsync(ApiResponse.ErrorToPlc("请确认指令发送"));
// return;
//}
}
public async Task Consume(ConsumeContext<PLCStaionGating> context)
{
var command = context.Message;
try
{
//ValidateCommand(command, context);
Thread.Sleep(2000);
await context.RespondAsync(ApiResponse.Successful($"开门成功"));
//var equipment = _equipmentExecutor.equipment.First(t => t.Code == command.EquipmentCode);
//if (equipment == null || string.IsNullOrEmpty(equipment.Code))
// ApiResponse.ErrorToPlc("无效站台设备,请检查设备名称");
//await HandlePickupCommand(command, equipment, context);
}
catch (Exception ex)
{
_logger.LogError(ex, $"处理门控指令异常,设备编码:{command?.EquipmentCode},指令类型:{command?.Operation}");
throw new InvalidOperationException(ex.Message);
}
//throw new NotImplementedException();
}
/// <summary>
/// 处理不同伸出指令
/// </summary>
private async Task HandlePickupCommand(PLCStaionGating command, Equipment equipment, ConsumeContext<PLCStaionGating> context)
{
// 定义超时时间(可根据实际场景调整,比如30秒)
var timeoutSeconds = 30;
var pickupStatus = command.Operation;
switch (pickupStatus)
{
// 伸出请求(单个属性写入+重试)
case var s when s == PLCGatingStatus.Opendoor.GetIndexInt():
// 1. 下发伸出信号
await WritePlcPropWithRetry(
equipment,
GatingProps.Opendoor.ToString(),
"True",
"下发门控开门信号成功",
"下发门控开门信号异常",
context,
retryTimes: 3,
delayMs: 500);
// 2. 等待「开门到位」为True(带超时+异步等待+实时刷新值)
var extendStartTime = DateTime.Now;
bool extendTimeout = false;
while (true)
{
// 实时刷新PLC属性值(关键:避免读取缓存)
var isExtended = equipment[$"{GatingProps.Opendoorposition}"].Value;
var isExtendedBool = Convert.ToBoolean(isExtended);
// 条件1:到位信号为True → 跳出循环
if (isExtendedBool)
{
break;
}
// 条件2:超时 → 标记超时并跳出
if ((DateTime.Now - extendStartTime).TotalSeconds >= timeoutSeconds)
{
extendTimeout = true;
break;
}
// 异步等待(替代Thread.Sleep,避免阻塞线程)
await Task.Delay(100);
}
// 3. 根据结果返回响应
if (extendTimeout)
{
await context.RespondAsync(ApiResponse.ErrorToPlc($"等待开门到位超时({timeoutSeconds}秒)"));
}
else
{
await context.RespondAsync(ApiResponse.SuccessfulToPlc("开门信号完成"));
}
break;
// 缩回请求(批量属性写入+重试)
case var s when s == PLCGatingStatus.Closedoor.GetIndexInt():
// 1. 下发缩回信号
await WritePlcPropWithRetry(
equipment,
GatingProps.Closedoor.ToString(),
"True",
"关门信号下发门控成功",
"关门信号下发门控异常",
context,
retryTimes: 3,
delayMs: 500);
// 2. 等待「关门到位」为True(带超时+异步等待+实时刷新值)
var retractStartTime = DateTime.Now;
bool retractTimeout = false;
while (true)
{
// 实时刷新PLC属性值
var istrue = equipment[$"{GatingProps.Closedoorproperly}"].Value;
var isRetractedBool = Convert.ToBoolean(istrue);
// 条件1:到位信号为True → 跳出循环
if (isRetractedBool)
{
break;
}
// 条件2:超时 → 标记超时并跳出
if ((DateTime.Now - retractStartTime).TotalSeconds >= timeoutSeconds)
{
retractTimeout = true;
break;
}
// 异步等待
await Task.Delay(100);
}
// 3. 根据结果返回响应
if (retractTimeout)
{
await context.RespondAsync(ApiResponse.ErrorToPlc($"等待关门到位超时({timeoutSeconds}秒)"));
}
else
{
await context.RespondAsync(ApiResponse.SuccessfulToPlc("伸回关门完成"));
}
break;
}
}
/// <summary>
/// 单个PLC属性写入(带重试机制)
/// </summary>
/// <param name="retryTimes">重试次数</param>
/// <param name="delayMs">每次重试间隔(毫秒)</param>
private async Task WritePlcPropWithRetry(Equipment equipment, string propKey, string value,
string successMsg, string failMsg, ConsumeContext<PLCStaionGating> context,
int retryTimes = 3, int delayMs = 500)
{
// 重试逻辑
int attempt = 0;
while (attempt < retryTimes)
{
lock (_equipmentExecutor.WriteFlag)
{
var prop = equipment[$"{propKey}"];
prop.Value = value;
try
{
var result = _equipmentExecutor.EquipmentCommunicationHub.Write(prop);
if (result.Success)
{
//context.RespondAsync(ApiResponse.SuccessfulToPlc(successMsg));
return; // 成功则退出
}
}
catch (Exception ex)
{
_logger.LogWarning(ex, $"第{attempt + 1}次写入PLC属性【{propKey}】失败,将重试");
}
}
attempt++;
if (attempt < retryTimes)
await Task.Delay(delayMs); // 重试前等待
}
// 所有重试都失败,抛出异常
throw new InvalidOperationException($"{failMsg}(已重试{retryTimes}次)");
}
}
}