EquipmentExecutor.cs
21.6 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
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
using HHECS.Communication;
using RCS.Communication;
using RCS.Model.Comm;
using RCS.Model.Entity;
using RCS.Model.PLC;
using RCS.WinClient.Common;
using RCS.WinClient.Service;
using System.Collections.Concurrent;
using System.Diagnostics;
using static RCS.WinClient.PLC.EquipmentCommunicationExtension;
namespace RCS.WinClient.PLC
{
public class EquipmentExecutor
{
/// <summary>
/// 为true表示停止,为false表示执行
/// </summary>
public bool stop = true;
/// <summary>
/// 是否可执行
/// </summary>
public bool CanExcute { get; private set; } = false;
/// <summary>
/// 每次读取PLC后,执行请求的次数
/// </summary>
public int Count { get; } = 30;
/// <summary>
/// PLC每次执行请求后的延迟时间,单位为毫秒
/// </summary>
public int Delay { get; } = 20;
/// <summary>
/// 因为是异步的,如果需要成功才执行下一步,就设定个预估执行时间,单位是毫秒
/// </summary>
public int ExecuteTime { get; } = 100;
/// <summary>
/// 请求数据过期时间,单位为秒,因为AGV是1秒就请求好几次,一般3秒没请求就是不请求了。
/// </summary>
public int Expire { get; set; } = 3;
/// <summary>
/// PLC操作类的单例
/// </summary>
private static readonly EquipmentExecutor equipmentExecutor = new EquipmentExecutor();
/// <summary>
/// 不允许外部直接实例化
/// </summary>
private EquipmentExecutor() { }
/// <summary>
/// 单例模式来获取实例
/// </summary>
public static EquipmentExecutor Instance { get { return equipmentExecutor; } }
/// <summary>
/// 请求数据
/// </summary>
private ConcurrentDictionary<string, OperatorPLC> RequestDic = new ConcurrentDictionary<string, OperatorPLC>();
/// <summary>
/// 设备初始化
/// </summary>
/// <param name="equipmentList"></param>
/// <param name="equipmentPropList"></param>
/// <returns></returns>
public BllResult<List<Base_Equipment>> Init(List<Base_Equipment> equipmentList, List<Base_EquipmentProp> equipmentPropList)
{
var equipmentResult = PLCHelper.Instance.InitEquipment(equipmentList, equipmentPropList);
if (!equipmentResult.IsSuccess)
{
return BllResult<List<Base_Equipment>>.Error($"初始化设备失败:{equipmentResult.Message}");
}
//调度相关
var ebResult = EquipmentCommunicationHubBuilder.Build("HubBuilder3.x.json");
if (!ebResult.Success)
{
App.ExFile.MessageError("Window_Loaded", "PLCBuilderError:" + ebResult.Msg);
return BllResult<List<Base_Equipment>>.Error($"初始化PLC失败:{ebResult.Msg}"); ;
}
var plcResult = PLCHelper.Instance.InitPLC(ebResult.Data.EquipmentCommunications);
if (!plcResult.IsSuccess)
{
return BllResult<List<Base_Equipment>>.Error($"初始化PLC失败:{plcResult.Message}");
}
CanExcute = true;
// 启动PLC执行线程
EquipmentExcuting();
return equipmentResult;
}
/// <summary>
/// 开启PLC执行线程
/// </summary>
public void EquipmentExcuting()
{
var excuteThread = new Thread(EquipmentExcute);
excuteThread.IsBackground = true;
excuteThread.Start();
}
/// <summary>
/// 请求设备操作
/// </summary>
/// <param name="stationName">站台名称</param>
/// <param name="requestOperator">请求操作</param>
/// <param name="requestValues">请求参数</param>
/// <returns></returns>
public BllResult RequestOperator(string stationName, EnumMsg.RequestType requestOperator, params object[] requestValues)
{
try
{
//判断设备是否不存在,如果站台没有设备,就直接成功
//if (stationName.EndsWith("货架"))
//if (stationName.StartsWith("B"))//后续需要修改
//{
// return BllResult.Success();
//}
if (!App.EquipmentList.Any(x => x.Name == stationName))
{
return BllResult.Success();
}
var newOperatorPLC = new OperatorPLC
{
Result = BllResult.Error("等待EquipmentExcute处理中"),
StationName = stationName,
RequestOperator = requestOperator,
RequestTime = DateTime.Now,
RequestValues = ComnMethod.DeepCopy(requestValues),
};
RequestDic.TryGetValue(stationName, out OperatorPLC? existOperatorPLC);
if (existOperatorPLC == null)
{
//不存在请求,就新增一条请求
RequestDic.TryAdd(stationName, newOperatorPLC);
return newOperatorPLC.Result;
}
else
{
// 如果是对一个站台重复请求一个操作,且参数一样,就更新请求时间
if (existOperatorPLC.RequestOperator == requestOperator && ComnMethod.IsEquals(existOperatorPLC.RequestValues, requestValues))
{
existOperatorPLC.RequestTime = DateTime.Now;
return existOperatorPLC.Result;
}
// 如果请求的时候,已经有【清空数据】的请求,就返回错误提示
if (existOperatorPLC.RequestOperator == EnumMsg.RequestType.清空数据)
{
//如果数据还没有删除成功,或者删除不到数据过期时间,那就不处理新的请求,避免一遍删除,一遍写入
if (DateTime.Now.Subtract(existOperatorPLC.RequestTime).TotalSeconds < Expire || !existOperatorPLC.Result.IsSuccess)
{
return BllResult.Error($"EquipmentExcute正在清除数据,不能进行【{requestOperator}】请求");
}
}
// 如果操作、参数不一样,就更新数据(这里不能直接 existRequest = newOperatorPLC,这样集合中的对象不会变,这是个坑)
RequestDic.TryUpdate(stationName, newOperatorPLC, existOperatorPLC);
return newOperatorPLC.Result;
}
}
catch (Exception ex)
{
var msg = $"请求设备操作异常,原因:{ex.Message},堆栈:{ex.StackTrace}";
App.ExFile.MessageError("RequestOperator", msg);
return BllResult.Error(msg);
}
}
/// <summary>
/// 开启执行
/// </summary>
public BllResult StartExcute()
{
if (!CanExcute)
{
return BllResult.Error("未初始化成功,PLC处理启动失败");
}
stop = false;
return BllResult.Success();
}
/// <summary>
/// 关闭执行
/// </summary>
public void StopExcute()
{
stop = true;
}
/// <summary>
/// PLC处理
/// </summary>
private void EquipmentExcute()
{
try
{
Stopwatch stopwatch = new Stopwatch();
EquipmentManage equipmentManage = new EquipmentManage();
var equipmentGroupList = App.EquipmentList.GroupBy(t => t.Ip).ToList();
while (true)
{
//var plc1 = PLCHelper.Instance.PLCList.First(t => t.IP == "172.16.30.22");
////测试
//var props = new List<Base_EquipmentProp>
// {
// new Base_EquipmentProp
// {
// Address = "DB4010X320.0",
// Value = "True",
// Equipment=new(){Ip= "172.16.30.22" }
// }
// //, new Base_EquipmentProp
// //{
// // Address = "DB4001X32.0",
// // Value = "True",
// // Equipment=new(){Ip= "192.168.10.103" }
// //}
// };
//plc1.Reads(props.ToArray());
// plc1.Writes(props.ToArray());
//var plc1 = PLCHelper.Instance.PLCList.First(t => t.IP == "192.168.10.103");
////测试
//var props = new List<Base_EquipmentProp>
// {
// new Base_EquipmentProp
// {
// Address = "DB4001X32.4",
// Value = "True",
// Equipment=new(){Ip= "192.168.10.103" }
// }
// //, new Base_EquipmentProp
// //{
// // Address = "DB4001X32.0",
// // Value = "True",
// // Equipment=new(){Ip= "192.168.10.103" }
// //}
// };
//plc1.Writes(props.ToArray());
//var props1 = new List<Base_EquipmentProp>
// {
// new Base_EquipmentProp
// {
// Address = "DB4001X32.5",
// Value = "True",
// Equipment=new(){Ip= "192.168.10.103" }
// }
// };
//plc1.Writes(props1.ToArray());
//props.ForEach(x => x.Value = null);
//var readResult1 = plc1.Reads(props.ToArray());
//props.ForEach(x => x.Value = "False");
//plc1.Writes(props.ToArray());
//props.ForEach(x => x.Value = null);
//readResult1 = plc1.Reads(props.ToArray());
if (CanExcute && !stop)
{
#region 读取PLC数据
stopwatch.Restart();
//读取成功的设备
var equipmentNames = new List<string>();
var taskList = new List<Task>();
foreach (var equipmentGroup in equipmentGroupList)
{
var plc = PLCHelper.Instance.PLCList.FirstOrDefault(t => t.IP == equipmentGroup.Key);
if (plc == null)
{
var msg = $"读取PLC错误,未找到IP为{equipmentGroup.Key}的PLC的配置";
App.ExFile.MessageError("PLCHelperError", msg);
continue;
}
var acsHeart = equipmentGroup.Where(t => t.Name.EndsWith("心跳"))
.SelectMany(t => t.EquipmentProps)
.FirstOrDefault(t => t.Code == EnumPLC.RCSHuaHengProps.RCSHeart.ToString());
var equipmentProps = equipmentGroup.SelectMany(t => t.EquipmentProps).ToList();
taskList.Add(Task.Run(() =>
{
//var pingResult = ComnMethod.Ping(equipmentGroup.Key);
//if (!pingResult.IsSuccess)
//{
// App.ExFile.MessageError("Ping", $"对PLC设备{pingResult.Message}");
// return;
//}
var connectStatusRes = plc.GetConnectStatus();
if (!connectStatusRes.Success)
{
App.ExFile.MessageError("Ping", $"对PLC设备{connectStatusRes.Msg}");
return;
}
//PLC心跳处理
var writesResult = acsHeart == null ? BllResult.Success() : equipmentManage.Heart(acsHeart);
if (writesResult.IsSuccess)
{
// 读取PLC的设备属性
var readResult = plc.Reads(equipmentProps.ToArray());
if (!readResult.Success)
{
App.ExFile.MessageError("PLCHelperError", $"RCS读取设备PLC数据失败!原因:{readResult.Msg}");
return;
}
//foreach (var prop in equipmentProps)
//{
// var readResult1 = plc.Reads([prop]);
// if (!readResult1.Success)
// {
// App.ExFile.MessageError("PLCHelperError", $"RCS读取设备PLC{prop.Code}-{prop.Address}数据失败!原因:{readResult1.Msg}");
// return;
// }
//}
App.ExFile.MessageLog("PLCLog", $"{string.Join(",", equipmentProps)}");
//如果读取成功,就写入列表
equipmentNames.AddRange(equipmentGroup.Select(t => t.Name));
}
}));
Task.WaitAll(taskList.ToArray());
}
if (equipmentNames.Count == 0) continue;
stopwatch.Stop();
App.ExFile.MessageLog("EquipmentExcute", $"PLC心跳处理和读取完成,耗时:{stopwatch.ElapsedMilliseconds}毫秒!");
#endregion
#region 处理请求的PLC操作
stopwatch.Restart();
//循环多次,每次执行一次就休眠一段时间
for (var i = 0; i < Count; i++)
{
var enumerator = RequestDic.GetEnumerator();
while (enumerator.MoveNext())
{
var operatorPLC = enumerator.Current.Value;
//删除无效、过期的请求,AGV每秒会发送几次请求过来,会一直请求的
if (operatorPLC == null || DateTime.Now.Subtract(operatorPLC.RequestTime).TotalSeconds > Expire)
{
RequestDic.TryRemove(enumerator.Current.Key, out OperatorPLC removeOperatorPLC);
App.ExFile.MessageLog("EquipmentExcute", $"删除对设备【{enumerator.Current.Key}】的过期、无效的操作请求");
continue;
}
//已经成功处理过的,就跳过
if (operatorPLC.Result.IsSuccess)
{
continue;
}
//判断设备是否不存在
if (!App.EquipmentList.Exists(t => t.Name == operatorPLC.StationName))
{
App.ExFile.MessageError("EquipmentExcute", $"RCS处理【{operatorPLC.RequestOperator}】失败,因为设备【{operatorPLC.StationName}】不存在");
continue;
}
//不在读取成功的设备中,就跳过
if (!equipmentNames.Contains(operatorPLC.StationName))
{
continue;
}
switch (operatorPLC.RequestOperator)
{
//case EnumMsg.RequestType.请求站台预占:
// //因为请求预占是两个RCS互抢站台,读取PLC数据后60毫秒就不再处理,怕数据不是最新的
// if (stopwatch.ElapsedMilliseconds < Delay * 3)
// {
// operatorPLC.Result = equipmentManage.RequestPlaceholder(operatorPLC.StationName, operatorPLC.RequestValues);
// }
// break;
//case EnumMsg.RequestType.取货检查:
// operatorPLC.Result = equipmentManage.PickCheck(operatorPLC.StationName);
// break;
case EnumMsg.RequestType.取货请求:
operatorPLC.Result = equipmentManage.RequestPick(operatorPLC.StationName);
break;
case EnumMsg.RequestType.取货完成确认:
operatorPLC.Result = equipmentManage.CompletePick(operatorPLC.StationName);
break;
//case EnumMsg.RequestType.放货检查:
// operatorPLC.Result = equipmentManage.PutCheck(operatorPLC.StationName);
// break;
case EnumMsg.RequestType.放货请求:
operatorPLC.Result = equipmentManage.RequestPut(operatorPLC.StationName, operatorPLC.RequestValues);
break;
case EnumMsg.RequestType.放货完成确认:
operatorPLC.Result = equipmentManage.CompletePut(operatorPLC.StationName);
break;
//case EnumMsg.RequestType.开快卷门:
// operatorPLC.Result = equipmentManage.OpenDoor(operatorPLC.StationName);
// break;
//case EnumMsg.RequestType.关快卷门:
// operatorPLC.Result = equipmentManage.CloseDoor(operatorPLC.StationName);
// break;
case EnumMsg.RequestType.清空数据:
operatorPLC.Result = equipmentManage.ClearRequest(operatorPLC.StationName);
break;
}
App.ExFile.MessageLog("EquipmentExcute", $"执行对设备【{enumerator.Current.Key}】的【{operatorPLC.RequestOperator}】操作,操作结果是:{operatorPLC.Result.IsSuccess},操作信息:{operatorPLC.Result.Message}!");
//如果出现 写入PLC失败,当前循环就结束,不处理了。
if (operatorPLC.Result.Message != null && operatorPLC.Result.Message.Contains("写入PLC失败"))
{
i = Count;
break;
}
}
Thread.Sleep(Delay);
}
stopwatch.Stop();
App.ExFile.MessageLog("EquipmentExcute", $"PLC处理完成,耗时:{stopwatch.ElapsedMilliseconds}毫秒!");
#endregion
}
}
}
catch (Exception ex)
{
App.ExFile.MessageError("EquipmentExcute", $"创建PLC处理线程异常,原因:{ex.Message},堆栈:{ex.StackTrace}");
var excuteThread = new Thread(EquipmentExcute);
excuteThread.IsBackground = true;
excuteThread.Start();
}
}
}
}