杨前锦
2025-07-07 c8f338feee0b6003d8f069b1d37fd9b90dd1b7f4
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
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
using HH.WCS.JunzhouNongfu.device;
using HH.WCS.Mobox3.HD.api;
using HH.WCS.Mobox3.HD.device;
using HH.WCS.Mobox3.HD.dispatch;
using HH.WCS.Mobox3.HD.models;
using HH.WCS.Mobox3.HD.util;
using HH.WCS.Mobox3.HD.wms;
using Newtonsoft.Json;
using NLog;
using S7.Net;
using SqlSugar;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using static HH.WCS.Mobox3.HD.api.DigitHelper;
using static HH.WCS.Mobox3.HD.core.Monitor;
using static HH.WCS.Mobox3.HD.dispatch.ShopFloorControl;
using static HH.WCS.Mobox3.HD.process.TaskProcess;
using static HH.WCS.Mobox3.HD.util.Settings;
using static HH.WCS.Mobox3.HD.wms.WCSHelper;
using static System.Runtime.CompilerServices.RuntimeHelpers;
 
namespace HH.WCS.Mobox3.HD.core
{
    /// <summary>
    /// 定时轮询任务
    /// </summary>
    internal class Monitor
    {
 
        // 每十分钟对未连接的pic,尝试进行重新连接
        public static void PlcAgainLink()
        {
            foreach (var item in S7Helper.plcDic)
            {
                S7Helper.Link(item.Value);
            }
        }
 
 
        /// <summary>
        /// 监听分拣区设备和货位,自动发布空盘垛出库任务
        /// </summary>
        public static void MonitorSortArea() {
            List<LinePlcInfo> linePlcInfos = WCSHelper.GetLinePlcInfoList(2);
            foreach (var line in linePlcInfos)
            {
                var wmsTask = WMSHelper.GetWmsTaskByEnd(line.localtion);
                if (wmsTask == null) {
                    // 线体货位
                    Location location = LocationHelper.GetLoc(line.localtion);
                    // 线体信号
                    PipelineSignalInfo lineSignalInfo = WCSHelper.readPipelineInfo(line);
                    LogHelper.Info("分拣区设备编号:"+ line.deviceNo +"线体编号:" + line.code+",设备信号:" + JsonConvert.SerializeObject(lineSignalInfo), "分拣区");
                    try
                    {
                        if (lineSignalInfo != null)
                        {
                            if (lineSignalInfo.agvTaskFeedback == 1 || lineSignalInfo.agvTaskFeedback == 2)
                            {
                                // 清除AGV任务反馈
                                S7Helper.WriteInt(line.deviceNo, 101, line.writeAddr + 14, 0);
                                LogHelper.Info("清除AGV任务反馈", "分拣区");
                            }
 
                            if (line.actType.Equals("入库"))
                            {
                                if (lineSignalInfo.lineState == 0 && lineSignalInfo.linePhotoelectric == 0 && lineSignalInfo.agvInfo == 1)
                                {
                                    Location endLoc = location;
                                    if (endLoc != null && endLoc.N_LOCK_STATE == 0)
                                    {
                                        Location startLoc = WMSHelper.GetEmptyTrayStartLocation(endLoc.S_CODE);
                                        if (startLoc != null)
                                        {
                                            var locCntrRels = LocationHelper.GetLocCntr(startLoc.S_CODE);
                                            if (locCntrRels.Count == 1) 
                                            {
                                                var cntrItemRels = ContainerHelper.GetCntrItemRel(locCntrRels[0].S_CNTR_CODE);
                                                if (cntrItemRels == null || cntrItemRels.Count == 0) 
                                                {
                                                    // 外侧接驳位
                                                    LogHelper.Info($"分拣区设备编号:{line.deviceNo} ,线体编号:{line.code}", "Mobox");
                                                    var connectLocCode = LocationHelper.GetConnectLocation(startLoc.S_AREA_CODE, 2, startLoc.N_ROADWAY, 1).FirstOrDefault(); // 查询接驳位 
                                                    var connectLoc = LocationHelper.GetLoc(connectLocCode);
                                                    if (connectLoc != null)
                                                    {
                                                        // 内测接驳位
                                                        /*  string conLocCode = WCSHelper.GetLinePlcInfoByDesc(connectLoc.S_CODE).localtion;
                                                          connectLoc = LocationHelper.GetLoc(conLocCode);*/
                                                        wmsTask = new WMSTask()
                                                        {
                                                            S_CNTR_CODE = locCntrRels[0].S_CNTR_CODE,
                                                            S_CODE = WMSHelper.GenerateTaskNo(),
                                                            S_START_LOC = startLoc.S_CODE,
                                                            S_START_AREA = startLoc.S_AREA_CODE,
                                                            S_END_LOC = endLoc.S_CODE,
                                                            S_END_AREA = endLoc.S_AREA_CODE,
                                                            S_TYPE = "空盘垛出库",
                                                            S_OP_DEF_NAME = "空盘垛出库",
                                                            T_START_TIME = DateTime.Now,
                                                            N_PRIORITY = 8,
                                                        };
                                                        if (WMSHelper.CreateWmsTask(wmsTask))
                                                        {
                                                            string eqNo = ApiHelper.getEqNo(startLoc.S_AREA_CODE, startLoc.N_ROADWAY);
                                                            WCSTask wcsTask = new WCSTask
                                                            {
                                                                S_OP_NAME = wmsTask.S_OP_DEF_NAME,
                                                                S_CODE = WCSHelper.GenerateTaskNo(),
                                                                S_TYPE = wmsTask.S_TYPE + "-1",
                                                                S_START_LOC = wmsTask.S_START_LOC,
                                                                S_START_AREA = wmsTask.S_START_AREA,
                                                                S_END_LOC = connectLoc.S_CODE,
                                                                S_END_AREA = connectLoc.S_AREA_CODE,
                                                                S_CNTR_CODE = wmsTask.S_CNTR_CODE,
                                                                S_SCHEDULE_TYPE = "RB",
                                                                S_OP_CODE = wmsTask.S_CODE,
                                                                S_EQ_NO = eqNo,
                                                                N_PRIORITY = wmsTask.N_PRIORITY,
                                                            };
                                                            if (WCSHelper.CreateTask(wcsTask))
                                                            {
                                                                // 对开始货位、接驳货位、终点货位进行加锁
                                                                LocationHelper.LockLoc(startLoc.S_CODE, 2);
                                                                LocationHelper.LockLoc(connectLoc.S_CODE, 1);
 
                                                                // 更新作业任务状态
                                                                wmsTask.N_B_STATE = 1;
                                                                WMSHelper.UpdateTaskState(wmsTask);
                                                            }
                                                        }
                                                    }
                                                    else
                                                    {
                                                        LogHelper.Info("没有可出库的接驳位", "分拣区");
                                                    }
                                                }
                                            }  
                                        }
                                        else
                                        {
                                            LogHelper.Info("没有可出库的空盘垛", "分拣区");
                                        }
                                    }
                                }
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        LogHelper.Info("监听分拣区错误,错误原因:" + ex.Message, "分拣区");
                    }
                }
            }             
        }
 
        public static Dictionary<NotificationInfo, int> logisticsTaskIdDict= new Dictionary<NotificationInfo, int>();
 
        public class NotificationInfo {
            public string logisticsTaskId { get; set; }
            public string locCode { get; set; }
        }
 
        /// <summary>
        /// 定时通知车间下发灵动任务(之前通知失败的任务)
        /// 每10秒通知一次,如5分钟内没有通知成功,则不再通知
        /// </summary>
        public static void TimerNotification() {
            foreach (var item in logisticsTaskIdDict)
            {
                var notificationResult = ShopFloorControl.notificationCreateTransportOrder(new TransferTiledData() { logisticsTaskId = item.Key.logisticsTaskId });
                if (notificationResult)
                {
                    logisticsTaskIdDict.Remove(item.Key);
                    LogHelper.Info("灵动AGV出库任务已下发,并通知车间下发灵动AGV任务,物流ID:" + item.Key.logisticsTaskId, "输送线");
                }
                else {
                    LogHelper.Info("通知车间下发灵动AGV任务失败,已重置发送次数:"+ item.Value / 10000 +",物流ID:" + item.Key.logisticsTaskId, "输送线");
                    if (item.Value == 300000)
                    {
                        logisticsTaskIdDict.Remove(item.Key);
                        LogHelper.Info("向输送线发送报警信号" + item.Key, "输送线");
                        LinePlcInfo line = WCSHelper.GetLinePlcInfoByLoc(item.Key.locCode);
                        S7Helper.WriteInt(line.deviceNo, 101, line.writeAddr + 18, 1);
                    }
                    else {
                        logisticsTaskIdDict[item.Key] = item.Value + 10000;
                    }
                }
            }
        }
 
        /// <summary>
        /// 监测输送线
        /// </summary>
        public static void MonitorPipeline()
        {
            List<LinePlcInfo> linePlcInfos = WCSHelper.GetLinePlcInfoList(1);
            LogHelper.Info("-------------------------------------------------------------------------------------", "输送线");
            foreach (var line in linePlcInfos)
            {
                // 线体货位
                Location originLocation = LocationHelper.GetLoc(line.localtion);
                Location descLocation = LocationHelper.GetLoc(line.descLocation);
                LocCntrRel locCntrRel = ContainerHelper.getLocCntrByLoc(originLocation.S_CODE);
 
                // 线体信号
                LogHelper.Info("输送线,线体:"+line.code, "输送线");
                PipelineSignalInfo lineSignalInfo = WCSHelper.readPipelineInfo(line);
                try
                {
                    if (lineSignalInfo != null)
                    {
                        LogHelper.Info("000000", "输送线");
                        if (lineSignalInfo.agvTaskFeedback == 1 || lineSignalInfo.agvTaskFeedback == 2) 
                        {
                            // 清除AGV任务反馈
                            S7Helper.WriteInt(line.deviceNo, 101, line.writeAddr + 14, 0);
                            LogHelper.Info("清除输送线【"+ line.deviceNo + "】的AGV任务反馈", "输送线");
                        }
 
                        if (locCntrRel != null)
                        {
                            LogHelper.Info("1111111", "输送线");
                            WMSTask wmsTask = WMSHelper.GetWmsTaskByCntr(locCntrRel.S_CNTR_CODE);
                            if (wmsTask == null)
                            {
                                LogHelper.Info("没有正在执行的任务,货位:" + locCntrRel.S_LOC_CODE + " 容器:" + locCntrRel.S_CNTR_CODE, "输送线");
                                continue;
                            }
                            
                            if (lineSignalInfo.faultMessage != 0) 
                            {
                                // 回报车间控制器
                                WMSStatusInfo statusInfo = new WMSStatusInfo()
                                {
                                    wmsId = wmsTask.S_CODE,
                                    errMsg = $"{line.code}输送线故障,故障编码:{lineSignalInfo.faultMessage}"
                                };
                                ShopFloorControl.wmsUpdateStatus(statusInfo);
 
                                if (lineSignalInfo.faultMessage == 1)
                                {
                                    LogHelper.Info("输送线,线体【" + line.code + "】调整为手动状态", "输送线");
                                    continue;
                                }
                            }
 
                            var date = DateTime.Now.ToString("yyMMdd");
                            string opTaskId = lineSignalInfo.taskId.ToString().PadLeft(4, '0');
                            LogHelper.Info("任务号:" + opTaskId, "输送线");
 
                            if (line.actType.Equals("入库") && line.sort == 1)
                            {
                                // 判断 请求去向 = 1 、线体光电 = 1
                                if (lineSignalInfo.requestReadCode == 1 && lineSignalInfo.linePhotoelectric == 1)
                                {
                                    // 向该线体发送指令,任务号,目标地址
                                    var descLinePlcInfo = WCSHelper.GetLinePlcInfo(line.descLocation);
                                    if (locCntrRel.S_CNTR_CODE.Equals(lineSignalInfo.trayCode))
                                    {
                                        issueCommand(line, int.Parse(wmsTask.S_CODE.Substring(8)), (short)descLinePlcInfo.code, 2);
                                        LogHelper.Info("向线体发送入库指令 ,任务号:" + int.Parse(wmsTask.S_CODE.Substring(4)) + ", 目标地址:" + (short)descLinePlcInfo.code, "输送线");
                                    }
                                    else if(lineSignalInfo.receiveInstruction != 3)
                                    {
                                        issueCommand(line, int.Parse(wmsTask.S_CODE.Substring(8)), (short)descLinePlcInfo.code, 3);
                                        LogHelper.Info("托盘码不一致,向线体发送退回指令 ,输送线托盘码:" + lineSignalInfo.trayCode + "任务托盘码:" + locCntrRel.S_CNTR_CODE, "输送线");
 
                                        TrayErrorFeedbackInfo info = new TrayErrorFeedbackInfo()
                                        {
                                            wmsId = wmsTask.S_CODE,
                                            errMsg = "托盘码错误"
                                        };
                                        ShopFloorControl.trayErrorFeedback(info);
                                    }
                                }
 
                                // 指令已读,清除指令
                                if (lineSignalInfo.receiveInstruction == 2)
                                {
                                    issueCommand(line, 0, 0, 0);
                                    LocationHelper.UnBindingLoc(originLocation.S_CODE, new List<string>() { locCntrRel.S_CNTR_CODE });
                                    LocationHelper.BindingLoc(descLocation.S_CODE, new List<string>() { locCntrRel.S_CNTR_CODE });
                                    LogHelper.Info("入库指令已读,清除入库指令", "输送线");
                                }
                                else if (lineSignalInfo.receiveInstruction == 3)
                                {
                                    issueCommand(line, 0, 0, 0);
                                    LogHelper.Info("退回指令已读,清除退回指令", "输送线");
                                }
 
                                try
                                {
                                    // 更新物料重量数据
                                    LogHelper.Info("更新物料重量数据 ,重量:" + lineSignalInfo.weight, "输送线");
                                    var cntrItemRels = ItemHelper.GetCntrItemByCntrCode(wmsTask.S_CNTR_CODE);
                                    if (cntrItemRels != null && cntrItemRels.Count > 0)
                                    {
                                        foreach (var cntrItemRel in cntrItemRels)
                                        {
                                            cntrItemRel.F_WEIGHT = lineSignalInfo.weight;
                                            if (cntrItemRel.F_INIT_WEIGHT == 0)
                                            {
                                                cntrItemRel.F_INIT_WEIGHT = lineSignalInfo.weight;
                                            }
                                            ItemHelper.updateCntrItem(cntrItemRel);
                                        }
                                    }
 
                                    // 输送线称重回报车间控制器
                                    LogHelper.Info("输送线称重回报车间控制器", "输送线");
                                    ProductWeightInfo productWeightInfo = new ProductWeightInfo()
                                    {
                                        wmsId = wmsTask.S_CODE,
                                        trayCode = lineSignalInfo.trayCode,
                                        weight = lineSignalInfo.weight.ToString(),
                                        weightTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
                                    };
                                    ShopFloorControl.reportSemilFinishedProductWeight(productWeightInfo);
                                }
                                catch (Exception ex) {
                                    LogHelper.Info("输送线称重回报车间控制器错误 ,错误原因:" + ex.Message, "输送线");
                                }
                            }
 
                            if (line.actType.Equals("入库") && line.sort == 2)
                            {
                                // 判断 任务号、目标地址、是否允许堆垛机取货
                                LogHelper.Info("输送线里侧货位入库" , "输送线");
                                
                                if (opTaskId == wmsTask.S_CODE.Substring(8) && lineSignalInfo.linePhotoelectric == 1 && lineSignalInfo.ddjInfo == 2)
                                {
                                    LogHelper.Info("输送线条件满足", "输送线");
                                    
                                    WCSTask wcsTask = WCSHelper.GetTaskByStart(originLocation.S_CODE, wmsTask.S_CODE);
                                    if (wcsTask == null)
                                    {
                                        LogHelper.Info("开始下发堆垛机入库任务", "输送线");
                                        string itemCode = null;
                                        var cntrItems = ContainerHelper.GetCntrItemRel(wmsTask.S_CNTR_CODE);
                                        if (cntrItems != null && cntrItems.Count > 0)
                                        {
                                            itemCode = cntrItems[0].S_ITEM_CODE;
                                        }
                                        LogHelper.Info("堆垛机入库任务终点", "输送线");
                                        Location endLoc = WMSHelper.GetEndLocation(wmsTask.S_END_AREA, itemCode, originLocation.N_ROADWAY, originLocation.N_ROW);
                                        if (endLoc != null)
                                        {
                                            LogHelper.Info($"堆垛机入库任务终点:{endLoc.S_CODE}", "输送线");
                                            wmsTask.S_END_LOC = endLoc.S_CODE;
                                            WMSHelper.UpdateTask(wmsTask);
                                            string eqNo = ApiHelper.getEqNo(endLoc.S_AREA_CODE, endLoc.N_ROADWAY);
 
                                            List<string> areaCdoes = Settings.getStoreAreaCodes(2, 1);
                                            WCSTask twoWcsTask = new WCSTask
                                            {
                                                S_OP_NAME = wmsTask.S_OP_DEF_NAME,
                                                S_OP_CODE = wmsTask.S_CODE,
                                                S_CODE = WCSHelper.GenerateTaskNo(),
                                                S_CNTR_CODE = wmsTask.S_CNTR_CODE,
                                                S_TYPE = wmsTask.S_TYPE + "-2",
                                                S_START_LOC = originLocation.S_CODE,
                                                S_START_AREA = originLocation.S_AREA_CODE,
                                                S_END_LOC = wmsTask.S_END_LOC,
                                                S_END_AREA = wmsTask.S_END_AREA,
                                                S_EQ_NO = eqNo,
                                                S_SCHEDULE_TYPE = "RB",
                                                T_START_TIME = DateTime.Now,
                                            };
                                            if (WCSHelper.CreateTask(twoWcsTask))
                                            {
                                                // 接驳位加出库锁,终点货位加入库锁
                                                LocationHelper.LockLoc(twoWcsTask.S_START_LOC, 2);
                                                LocationHelper.LockLoc(twoWcsTask.S_END_LOC, 1);
                                                LogHelper.Info("堆垛机入库任务已下发,任务信息:" + JsonConvert.SerializeObject(twoWcsTask), "输送线");
                                            }
                                        }
                                        else 
                                        {
                                            LogHelper.Info($"堆垛机入库任务终点:null", "输送线");
                                        }
                                    }
                                    else {
                                        LogHelper.Info("任务已下发", "输送线");
                                    }
                                }
                            }
 
                            if (line.actType.Equals("出库") && line.sort == 1)
                            {
                                if (lineSignalInfo.linePhotoelectric == 1)
                                {
                                    // 向该线体发送入库指令,任务号,目标地址
                                    var descLinePlcInfo = WCSHelper.GetLinePlcInfo(line.descLocation);
                                    issueCommand(line, int.Parse(wmsTask.S_CODE.Substring(8)), (short)descLinePlcInfo.code, 1);
                                    LogHelper.Info("向线体发送出库指令 ,任务号:" + int.Parse(wmsTask.S_CODE.Substring(8)) + ", 目标地址:" + (short)descLinePlcInfo.code, "输送线");
                                }
 
                                if (lineSignalInfo.receiveInstruction == 1)
                                {
                                    issueCommand(line, 0, 0, 0);
                                    LocationHelper.UnBindingLoc(originLocation.S_CODE, new List<string>() { locCntrRel.S_CNTR_CODE });
                                    LocationHelper.BindingLoc(descLocation.S_CODE, new List<string>() { locCntrRel.S_CNTR_CODE });
                                    LogHelper.Info("出库指令已读,清除出库指令", "输送线");
                                }
                            }
 
                            if (line.actType.Equals("出库") && line.sort == 2)
                            {
                                // 判断 任务号、目标地址、托盘码、是否允许堆垛机取货
                                if (opTaskId == wmsTask.S_CODE.Substring(8) && lineSignalInfo.linePhotoelectric == 1 )
                                {
                                    if (locCntrRel.S_CNTR_CODE.Equals(lineSignalInfo.trayCode))
                                    {
                                        WCSTask wcsTask = WCSHelper.GetTaskByStart(originLocation.S_CODE, wmsTask.S_CODE);
                                        if (wcsTask == null)
                                        {
                                            LogHelper.Info("开始下发灵动AGV出库任务", "输送线");
                                            var twoWcsTask = new WCSTask
                                            {
                                                S_OP_NAME = wmsTask.S_OP_DEF_NAME,
                                                S_CODE = WCSHelper.GenerateTaskNo(),
                                                S_TYPE = wmsTask.S_TYPE + "-2",
                                                S_START_LOC = originLocation.S_CODE,
                                                S_START_AREA = originLocation.S_AREA_CODE,
                                                S_END_LOC = wmsTask.S_END_LOC,
                                                S_END_AREA = wmsTask.S_END_AREA,
                                                S_SCHEDULE_TYPE = "LD",
                                                S_CNTR_CODE = wmsTask.S_CNTR_CODE,
                                                N_CNTR_COUNT = 1,
                                                S_OP_CODE = wmsTask.S_CODE,
                                                N_PRIORITY = wmsTask.N_PRIORITY,
                                                T_START_TIME = DateTime.Now,
                                            };
                                            if (wmsTask.S_TYPE.Equals("半成品/成品出库") || wmsTask.S_TYPE.Equals("叫托盘出库"))
                                            {
                                                twoWcsTask.S_SCHEDULE_TYPE = "LD-AUTO";
                                            }
                                            if (WCSHelper.CreateTask(twoWcsTask))
                                            {
                                                LocationHelper.LockLoc(twoWcsTask.S_START_LOC, 2);
                                                LocationHelper.LockLoc(twoWcsTask.S_END_LOC, 1);
 
                                                var notificationResult = ShopFloorControl.notificationCreateTransportOrder(new TransferTiledData() { logisticsTaskId = wmsTask.S_OP_DEF_CODE });
                                                if (notificationResult)
                                                {
                                                    WCSHelper.UpdateStatus(twoWcsTask,"已推送");
                                                    LogHelper.Info("灵动AGV出库任务已下发,并通知车间下发灵动AGV任务,任务信息:" + JsonConvert.SerializeObject(twoWcsTask), "输送线");
                                                }
                                                else 
                                                {
                                                    NotificationInfo notificationInfo = new NotificationInfo() {
                                                        logisticsTaskId = wmsTask.S_OP_DEF_CODE,
                                                        locCode = originLocation.S_CODE
                                                    };
                                                    LogHelper.Info("物流任务信息:" + JsonConvert.SerializeObject(notificationInfo), "输送线");
                                                    if (notificationInfo.logisticsTaskId != null && notificationInfo.locCode != null) {
                                                        logisticsTaskIdDict.Add(notificationInfo, 0);
                                                    }
                                                    LogHelper.Info("通知车间下发灵动AGV任务失败,任务信息:" + JsonConvert.SerializeObject(twoWcsTask), "输送线");
                                                }
                                            }
                                        }
                                    }
                                    else 
                                    {
                                        LogHelper.Info("托盘码不一致,请进行人工干预 ,输送线托盘码:" + lineSignalInfo.trayCode + "任务托盘码:" + locCntrRel.S_CNTR_CODE, "输送线");
                                    }
                                }
                            }
                        }
 
                        // 向数字孪生推送输送线状态信息
                        MQTTCore.MqttClientService mqttClientService = null;
                        try
                        {
                            SSXStatusInfo info = new SSXStatusInfo()
                            {
                                deviceNo = line.code.ToString(),
                                status = lineSignalInfo.lineState.ToString(),
                                existCargo = lineSignalInfo.linePhotoelectric.ToString(),
                                alertorInfo = lineSignalInfo.faultMessage.ToString(),
                                dataDate = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
                            };
                            MQTTCore.mqqtClients.TryGetValue("数字孪生", out mqttClientService);
                            mqttClientService.Publish("pipelineStatus", JsonConvert.SerializeObject(info));
                        }
                        catch (Exception ex)
                        {
                            LogHelper.Error("推送输送线状态信息错误", ex, "数字孪生");
                        }
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.ToString());
                    LogHelper.Info("输送线错误,错误信息:" + ex.Message, "哈电");
                }
            }
        }
 
        /// <summary>
        /// 清除输送线命令
        /// </summary>
        /// <param name="line"></param>
        /// <param name="taskId"></param>
        /// <param name="descLine"></param>
        /// <param name="command"></param>
        public static void issueCommand(LinePlcInfo line,int taskId ,short descLine, short command) {
            S7Helper.WriteDint(line.deviceNo, 101, line.writeAddr + 2, taskId); // 任务号
            S7Helper.WriteInt(line.deviceNo, 101, line.writeAddr + 6, descLine); // 目标线体
            S7Helper.WriteInt(line.deviceNo, 101, line.writeAddr + 8, command); // 下发指令(默认=0 入库=2 出库=1 不合格退回=3)
        }
 
        /// <summary>
        /// 清除输送线命令
        /// </summary>
        /// <param name="line"></param>
        /// <param name="command"> 1.agv任务异常取消</param>
        public static void triggerAlarmCommand(LinePlcInfo line, short command)
        {
            S7Helper.WriteDint(line.deviceNo, 101, line.writeAddr + 20, command); // 任务号
        }
 
        public class SSXStatusInfo {
            public string deviceNo { get; set; }
            public string status { get; set; }
            public string existCargo { get; set; }
            public string alertorInfo { get; set; }
            public string dataDate { get; set; }
        }
 
 
 
 
        /// <summary>
        /// 检测堆垛机信号
        /// </summary>
        internal static void MonitorDDJSignal()
        {
            var ddjPlcInfos =  Settings.devicePlcInfos.Where(a => a.deviceType == 1).ToList();
            foreach (var item in ddjPlcInfos)
            {
                //1.检测堆垛机信号
                LogHelper.Info("读取堆垛机 " + item.deviceNo, "堆垛机");
                short[] resSignal1 = S7Helper.ReadInt(item.deviceNo, 551, 0, 8);
                int resSignal2 = S7Helper.ReadDint(item.deviceNo, 551, 16);
                short[] resSignal3 = S7Helper.ReadInt(item.deviceNo, 551, 20, 3);
                LogHelper.Info("读取堆垛机 "+item.deviceNo + " 信号:  resSignal1:" + JsonConvert.SerializeObject(resSignal1)+ "  resSignal2(任务号):" + JsonConvert.SerializeObject(resSignal2) + "  resSignal3:" + JsonConvert.SerializeObject(resSignal3), "堆垛机");
                if (resSignal1 != null && resSignal2 != 0 && resSignal3 != null)
                {
                    ResSignalInfo resSignalInfo = new ResSignalInfo()
                    {
                        HandShake = resSignal1[0],
                        SRM_Num = resSignal1[1],
                        Auto = resSignal1[2],
                        Alarm_Code = resSignal1[3],
                        State = resSignal1[4],
                        TaskStatus1 = resSignal1[5],
                        Loaded1 = resSignal1[6],
                        Busines1 = resSignal1[7],
                        TaskId1 = resSignal2,
                        Row = resSignal3[0],
                        Layer = resSignal3[1],
                        b_Fork1Pos = resSignal3[2],
                    };
                    LogHelper.Info("检测堆垛机信号:" + JsonConvert.SerializeObject(resSignalInfo), "堆垛机");
                    // 3.堆垛机任务处理
                    taskProcessing(item.deviceNo, resSignalInfo);
                }
            }
        }
 
        /// <summary>
        /// 堆垛机任务处理
        /// </summary>
        internal static void taskProcessing(string deviceNo, ResSignalInfo resSignalInfo)
        {
            // 堆垛机当前模式为联机自动 且 报警代码为1正常
            if (resSignalInfo.Auto == 1)
            {
                // 查询任务
                /*var wcsTask = WCSHelper.GetTask("TN25" + resSignalInfo.TaskId1.ToString().PadLeft(9, '0'));*/
 
                LogHelper.Info("任务号:" + resSignalInfo.TaskId1, "堆垛机");
                var wcsTask = WCSHelper.GetWcsTask(resSignalInfo.TaskId1.ToString());
 
                if (wcsTask != null)
                {
                    WMSTask wmsTask = WMSHelper.GetWmsTask(wcsTask.S_OP_CODE);
                    if (resSignalInfo.Alarm_Code == 1)
                    {
                        if (wcsTask.N_B_STATE < 3 && wcsTask.S_SCHEDULE_TYPE.Equals("RB"))
                        {
                            // 任务状态
                            switch (resSignalInfo.TaskStatus1)
                            {
                                case 1:// 确认接收任务
                                    WCSHelper.Begin(wcsTask);
                                    break;
                                case 3: // 完成
                                case 5: // 强制完成
                                        // 1.开始货位解绑、终点货位绑定
                                    LocationHelper.UnBindingLoc(wcsTask.S_START_LOC, new List<string> { wcsTask.S_CNTR_CODE });
                                    LocationHelper.UnLockLoc(wcsTask.S_START_LOC);
 
                                    LocationHelper.BindingLoc(wcsTask.S_END_LOC, new List<string> { wcsTask.S_CNTR_CODE });
                                    LocationHelper.UnLockLoc(wcsTask.S_END_LOC);
                                    WCSHelper.End(wcsTask);
 
                                    LogHelper.Info("检测堆垛机任务完成,任务:" + JsonConvert.SerializeObject(wcsTask), "堆垛机");
 
                                    // 2.判断是一段任务,还是二段任务
                                    if (wmsTask != null && wmsTask.S_END_LOC.Equals(wcsTask.S_END_LOC))
                                    {
                                        wmsTask.N_B_STATE = 2;
                                        wmsTask.T_END_TIME = DateTime.Now;
                                        WMSHelper.UpdateTaskState(wmsTask);
 
                                        // 2.1更新出入库记录
                                        var cntrItemRels = ItemHelper.GetCntrItemByCntrCode(wcsTask.S_CNTR_CODE);
                                        if (cntrItemRels != null && cntrItemRels.Count > 0)
                                        {
                                            foreach (var cntrItemRel in cntrItemRels)
                                            {
                                                WMSHelper.AddStockRecord(wmsTask, cntrItemRel);
                                            }
                                        }
                                        else
                                        {
                                            WMSHelper.AddStockRecord(wmsTask, null);
                                        }
                                    }
                                    // 3.当收到PLC的完成时(TaskStatus1=3)上位写3确认,PLC收到确认,清除任务状态
                                    S7Helper.WriteInt(deviceNo, 550, 28, 3);
 
                                    // 4.任务完成,清除堆垛机数据
                                    clearingDDJData(deviceNo);
                                    break;
                                case 6:
                                    // 取消任务
                                    WCSHelper.Fail(wcsTask);
                                    LocationHelper.UnLockLoc(wcsTask.S_START_LOC);
                                    LocationHelper.UnLockLoc(wcsTask.S_END_LOC);
                                    /* clearingDDJData(deviceNo);*/
 
                                    // 取消作业
                                    wmsTask.N_B_STATE = 3;
                                    wmsTask.T_END_TIME = DateTime.Now;
                                    WMSHelper.UpdateTaskState(wmsTask);
                                    LocationHelper.UnLockLoc(wmsTask.S_END_LOC);
                                    LogHelper.Info("任务取消,任务号:" + wcsTask.S_CODE, "堆垛机");
                                    break;
                            }
                            WCSHelper.AddActionRecord(wcsTask.S_CODE, resSignalInfo.TaskStatus1, wcsTask.S_EQ_NO, null);
                        }
                    }
                    else
                    {
                        if (resSignalInfo.Busines1 == 1 || resSignalInfo.Busines1 == 3)
                        {
                            // 再清除wcs写入通道内任务数据,同时为该任务计算一个新的放货终点。
                            var cntrItemRels = ItemHelper.GetCntrItemByCntrCode(wcsTask.S_CNTR_CODE);
                            Location formerEndLoc = LocationHelper.GetLoc(wcsTask.S_END_LOC);
                            Location endLoc = WMSHelper.GetEndLocation(formerEndLoc.S_AREA_CODE, cntrItemRels[0].S_ITEM_CODE, formerEndLoc.N_ROADWAY, 1);
                            wcsTask.S_END_LOC = endLoc.S_CODE;
                            wcsTask.N_B_STATE = 0;
                            wcsTask.S_B_STATE = "等待";
                            wcsTask.N_PRIORITY = 10;
                            wcsTask.S_ERR = resSignalInfo.Busines1.ToString() + ":满入和放货阻塞报警;阻塞货位:" + formerEndLoc.S_CODE;
                            WCSHelper.UpdateWcsTask(wcsTask);
 
                            LocationHelper.UnLockLoc(formerEndLoc.S_CODE);
                            LocationHelper.LockLoc(formerEndLoc.S_CODE, 3);
                            LocationHelper.LockLoc(endLoc.S_CODE, 1);
 
                            if (formerEndLoc.S_CODE == wmsTask.S_END_LOC)
                            {
                                wmsTask.S_END_LOC = endLoc.S_CODE;
                                WMSHelper.UpdateTask(wmsTask);
                            }
 
                            // wcs在任务推送后需追加循环读取该地址信号。若为1.  需先写入地址4为12通知堆垛机清除其对应通道任务数据。
                            S7Helper.WriteInt(deviceNo, 550, 4, 12);
                            LogHelper.Info("满入和放货阻塞报警", "堆垛机");
                        }
                        if (resSignalInfo.Busines1 == 2 || resSignalInfo.Busines1 == 4)
                        {
                            /* S7Helper.WriteInt(deviceNo, 550, 4, 12);
                             var cntrItemRels = ItemHelper.GetCntrItemByCntrCode(wcsTask.S_CNTR_CODE);
                             LocationHelper.UnLockLoc(formerStartLoc.S_CODE);
                             LocationHelper.LockLoc(formerStartLoc.S_CODE, 3);*/
 
                            Location formerStartLoc = LocationHelper.GetLoc(wcsTask.S_START_LOC);
                            wcsTask.N_B_STATE = 4;
                            wcsTask.S_B_STATE = "错误";
                            wcsTask.S_ERR = resSignalInfo.Busines1.ToString() + ":空取和取货阻塞报警; 取货货位:" + formerStartLoc.S_CODE;
                            LogHelper.Info("空取和取货阻塞报警", "堆垛机");
                        }
                    }
                }
                else 
                {
                    LogHelper.Info($"任务号:{resSignalInfo.TaskId1},未查询到堆垛机任务", "堆垛机");
                }
            }
 
            // 堆垛机任务状态推送
            try
            {
                // FromWcs(DB550)下发任务数据
                short[] resSignal = S7Helper.ReadInt(deviceNo, 550, 8, 8);
 
                // 1.堆垛机状态推送数字孪生
                MQTTCore.MqttClientService mqttClientService = null;
                DDJStatusInfo info = new DDJStatusInfo()
                {
                    deviceNo = resSignalInfo.SRM_Num.ToString(),
                    auto = resSignalInfo.Auto.ToString(),
                    status = resSignalInfo.State.ToString(),
                    alertorInfo = resSignalInfo.Alarm_Code.ToString(),
                    Loaded1 = resSignalInfo.Loaded1.ToString(),
                    Busines1 = resSignalInfo.Busines1.ToString(),
                    Row = resSignalInfo.Row.ToString(),
                    Layer = resSignalInfo.Layer.ToString(),
                    b_Fork1Pos = resSignalInfo.b_Fork1Pos.ToString(),
                    TaskId1 = resSignalInfo.TaskId1.ToString(),
                    dataDate = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
                };
 
                if (resSignal != null) {
                    info.Source_Z_S1 = resSignal[0].ToString();
                    info.Source_X_S1 = resSignal[1].ToString();
                    info.Source_Y_S1 = resSignal[2].ToString();
                    info.Dest_Z_S1 = resSignal[4].ToString();
                    info.Dest_X_S1 = resSignal[5].ToString();
                    info.Dest_Y_S1 = resSignal[6].ToString();
                }
                MQTTCore.mqqtClients.TryGetValue("数字孪生", out mqttClientService);
                mqttClientService.Publish("stackerStatus", JsonConvert.SerializeObject(info));
 
                // 2.向数字孪生推送堆垛机任务数据
                MQTTCore.mqqtClients.TryGetValue("数字孪生", out mqttClientService);
                mqttClientService.Publish("unfinishedTask", JsonConvert.SerializeObject(WCSHelper.getDDJProceedTaskData()));
            }
            catch (Exception ex)
            {
                LogHelper.Error("推送堆垛机任务数据错误", ex, "数字孪生");
            }
        }
 
        public static void clearingDDJData(string plc) {
            short HandShake = 0;
            short[] handShakeValues = S7Helper.ReadInt(plc, 551, 0, 1);
            if (handShakeValues != null)
            {
                if (handShakeValues[0] == 0)
                {
                    HandShake = 1;
                }
                else if (handShakeValues[0] == 1)
                {
                    HandShake = 0;
                }
            }
 
            // TODO s7 wcs -> plc 写入任务
            RBWriteTaskInfo rBWriteTaskInfo = new RBWriteTaskInfo()
            {
                HandShake = HandShake,
                SRM_Num = 0,
                CommandType = 0,
                Source_Z_S1 = 0,
                Source_X_S1 = 0,
                Source_Y_S1 = 0,
                Source_L_S1 = 0,
                Dest_Z_S1 = 0,
                Dest_X_S1 = 0,
                Dest_Y_S1 = 0,
                Dest_L_S1 = 0,
                Task_S1 = 0,                    // TODO 任务号待确定
                ConfirmSignal1 = 0,
            };
            RBWriteTask(plc, rBWriteTaskInfo);
           
            LogHelper.Info("堆垛机任务取消或完成,清除数据:" + JsonConvert.SerializeObject(rBWriteTaskInfo), "堆垛机");
        }
 
 
        [SugarTable("TN_Test_StackerCrane")]
        public class ResSignalInfo : BaseModel
        {
            public short HandShake { get; set; } // 心跳
            public short SRM_Num { get; set; } // 堆垛机号
            public short Auto { get; set; } // 堆垛机当前模式指示  1 联机自动;2手动;3半自动;4维修;
            public short Alarm_Code { get; set; } // 堆垛机报警代码 0:默认;1:正常 ;其他数字故障信息
            public short State { get; set; } // 1:状态正常,允许任务下发(联机自动模式,不报警,叉在中位,无上条任务残留数据时才为1)其他:异常状态
            public short TaskStatus1 { get; set; } // 0:默认; 1:确认接收任务; 3:工位1正常任务完成; 5.工位1强制完成 ; 6.取消任务
            public short Loaded1 { get; set; } // 工位1载货台有无货 1:有货;0:无货
            public short Busines1 { get; set; } // 工位1特殊业务状态反馈 [1:满入,2.空取,3:放货阻塞,4:取货阻塞]
            public int TaskId1 { get; set; } // 工位1任务号
            public short Row { get; set; } // 堆垛机当前列(行走方向)
            public short Layer { get; set; } // 堆垛机当前层(升降方向)
            public short b_Fork1Pos { get; set; } // 堆垛机货叉1位置 [1:左远,2.左近,3:居中,4:右近,5:右远]
 
            
        }
 
    }
}