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
814
815
816
package com.mes.job;
 
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.json.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.github.yulichang.query.MPJQueryWrapper;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import com.mes.common.S7object;
import com.mes.common.config.Const;
import com.mes.damage.entity.Damage;
import com.mes.damage.service.DamageService;
import com.mes.device.PlcParameterObject;
import com.mes.edgglasstask.entity.EdgGlassTaskInfo;
import com.mes.edgglasstask.service.EdgGlassTaskInfoService;
import com.mes.edgstoragecage.entity.EdgStorageCage;
import com.mes.edgstoragecage.entity.EdgStorageCageDetails;
import com.mes.edgstoragecage.service.EdgStorageCageDetailsService;
import com.mes.edgstoragecage.service.EdgStorageCageService;
import com.mes.glassinfo.entity.GlassInfo;
import com.mes.glassinfo.service.GlassInfoService;
import com.mes.taskcache.entity.TaskCache;
import com.mes.taskcache.service.TaskCacheService;
import com.mes.tools.WebSocketServer;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
 
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
 
/**
 * @Author : zhoush
 * @Date: 2024/5/8 8:17
 * @Description:
 */
@Component
@Slf4j
public class CacheGlassTask {
 
    @Autowired
    TaskCacheService taskCacheService;
    @Autowired
    GlassInfoService glassInfoService;
    @Autowired
    EdgStorageCageService edgStorageCageService;
    @Autowired
    EdgStorageCageDetailsService edgStorageCageDetailsService;
 
    @Autowired
    EdgGlassTaskInfoService edgGlassTaskInfoService;
 
    @Autowired
    WebSocketServer webServerService;
    @Autowired
    DamageService damageService;
 
    @Value("${mes.threshold}")
    private int threshold;
 
    @Value("${mes.ratio}")
    private int ratio;
 
    @Value("${mes.sequence.order}")
    private boolean sequenceOrder;
 
    @Value("${mes.max.firstLength}")
    private String firstLength;
 
    @Value("${mes.max.secondLength}")
    private String secondLength;
 
    @Value("${mes.cache.cacheWidth}")
    private double cacheWidth;
 
    @Value("${mes.cache.cacheHeight}")
    private double cacheHeight;
 
    @Value("${mes.min.one.firstLength}")
    private String minOneFirstLength;
 
    @Value("${mes.min.one.secondLength}")
    private String minOneSecondLength;
 
    @Value("${mes.min.two.firstLength}")
    private String minTwoFirstLength;
 
    @Value("${mes.min.two.secondLength}")
    private String minTwoSecondLength;
 
    public static String engineerId = "";
 
    @Scheduled(fixedDelay = 1000)
    public void plcHomeEdgTask() {
        Date startDate = new Date();
        log.info("本次任务开始执行时间:{}", startDate);
        PlcParameterObject plcParameterObject = S7object.getinstance().PlcMesObject;
        String taskRequestTypeValue = plcParameterObject.getPlcParameter("A06_request_word").getValue();
        String glassIdeValue = plcParameterObject.getPlcParameter("A05_scanning_ID").getValue();
        String confirmationWrodValue = plcParameterObject.getPlcParameter("MES_confirmation_word").getValue();
        //A08  A09表示线路相同  可做等价  无数据转int异常
        String out08Glassstate = plcParameterObject.getPlcParameter("A08_glass_status").getValue();
        String out10Glassstate = plcParameterObject.getPlcParameter("A10_glass_status").getValue();
        String confirmationWrodAddress = plcParameterObject.getPlcParameter("MES_confirmation_word").getAddress();
        String currentSlot = plcParameterObject.getPlcParameter("Current_slot").getValue();
 
        log.info("1、获取到的请求字为:{},获取到的扫描ID为:{},获取到的确认字为:{},获取到的出片状态分别为:A09:{}、A10:{},当前格子号为:{}",
                taskRequestTypeValue, glassIdeValue, confirmationWrodValue, out08Glassstate, out10Glassstate, currentSlot);
 
        if ("0".equals(taskRequestTypeValue)) {
            if ("0".equals(confirmationWrodValue)) {
                log.info("2、获取到的请求字为0,且确认字为0,不执行任务");
                return;
            }
            log.info("2、获取到的请求字为0,将确认字改为0");
            S7object.getinstance().plccontrol.writeWord(confirmationWrodAddress, 0);
            return;
        }
        if (!"0".equals(confirmationWrodValue)) {
            log.info("2、获取到的请求字不为0,将确认字不为0,直接结束");
            return;
        }
        if ("1".equals(taskRequestTypeValue)) {
            log.info("2、进片请求,且确认字为0,执行进片任务");
            inTo(glassIdeValue, confirmationWrodAddress, currentSlot);
        } else if ("2".equals(taskRequestTypeValue)) {
            //09空闲 :1      10空闲 :2        都空闲:3    其他0
            log.info("2、出片请求,且确认字为0,执行出片任务");
            outTo(Integer.parseInt(out08Glassstate),
                    Integer.parseInt(out10Glassstate), confirmationWrodAddress, "", 0);
        } else if ("3".equals(taskRequestTypeValue)) {
            log.info("2、进片和出片都空闲,执行出片任务");
            //加笼子里面是否有玻璃,有先出,无玻璃先进
            boolean outFlase = outTo(Integer.parseInt(out08Glassstate),
                    Integer.parseInt(out10Glassstate), confirmationWrodAddress, glassIdeValue, Integer.parseInt(currentSlot));
            log.info("出片任务是否完成:{},且玻璃id:{}不为空则执行进片任务", outFlase, glassIdeValue);
            if (!outFlase && StringUtils.isNotBlank(glassIdeValue)) {
                inTo(glassIdeValue, confirmationWrodAddress, currentSlot);
            }
        }
        Date endDate = new Date();
        log.info("本次任务结束时间:{},共耗时:{}ms", endDate, endDate.getTime() - startDate.getTime());
    }
 
 
    @Scheduled(fixedDelay = 1000)
    public void dealDamageTask() {
        Date startDate = new Date();
        log.info("卧式理片破损玻璃清除任务开始执行时间:{}", startDate);
        List<TaskCache> taskCacheList = taskCacheService.list(new LambdaQueryWrapper<TaskCache>().in(TaskCache::getTaskStatus, Const.GLASS_STATE_DAMAGE_TAKE)
                .in(TaskCache::getTaskType, Const.GLASS_CACHE_TYPE_OUT_ALL));
        if (CollectionUtils.isNotEmpty(taskCacheList)) {
            //获取破损/拿走玻璃id
            List<String> glassList = taskCacheList.stream().map(TaskCache::getGlassId).collect(Collectors.toList());
            //将磨边队列的数据删除
            edgGlassTaskInfoService.remove(new LambdaQueryWrapper<EdgGlassTaskInfo>().in(EdgGlassTaskInfo::getGlassId, glassList));
            //将任务表中的数据删除
            taskCacheService.remove(new LambdaQueryWrapper<TaskCache>().in(TaskCache::getGlassId, glassList).in(TaskCache::getTaskType, Const.GLASS_CACHE_TYPE_OUT_ALL));
            List<Damage> damageList = taskCacheList.stream().map(e -> {
                Damage damage = new Damage();
                damage.setGlassId(e.getGlassId());
                damage.setLine(e.getEndCell());
                damage.setWorkingProcedure("磨边");
                damage.setRemark("磨边前卧式理片");
                damage.setStatus(1);
                damage.setType(e.getTaskStatus());
                return damage;
            }).collect(Collectors.toList());
            damageService.batchInsertDamage(damageList);
        }
        Date endDate = new Date();
        log.info("本次任务结束时间:{},共耗时:{}ms", endDate, endDate.getTime() - startDate.getTime());
    }
 
 
    /**
     * 进片任务
     *
     * @param glassId
     * @param confirmationWrodAddress
     * @param currentSlot
     */
    public void inTo(String glassId, String confirmationWrodAddress, String currentSlot) {
        log.info("1、按照玻璃id:{}获取玻璃小片信息,当前格子为:{}", glassId, currentSlot);
 
        GlassInfo feedGlassInfo = glassInfoService.getOne(
                new LambdaQueryWrapper<GlassInfo>()
                .eq(GlassInfo::getGlassId,glassId)
        );
        if(Math.max(feedGlassInfo.getWidth(),feedGlassInfo.getHeight())>cacheWidth||Math.min(feedGlassInfo.getWidth(),feedGlassInfo.getHeight())>cacheHeight){
            log.info("1.1、玻璃超过卧式理片最大尺寸:{}", feedGlassInfo);
            return;
        }
 
        //添加进片任务  查找空格
        EdgStorageCage nearestEmpty = edgStorageCageService.selectNearestEmpty(Integer.parseInt(currentSlot), Boolean.FALSE);
        Assert.isTrue(null != nearestEmpty, "格子已满");
        log.info("2、查询卧式理片笼里面的空格:{}", nearestEmpty);
 
        //查询玻璃并进行交换
        GlassInfo glassInfo = queryAndChangeGlass(glassId);
        if (glassInfo == null) {
            return;
        }
        log.info("4、将玻璃信息插入卧式理片笼,当前玻璃信息:{}", glassInfo);
        EdgStorageCageDetails details = new EdgStorageCageDetails();
        BeanUtils.copyProperties(glassInfo, details);
        details.setState(Const.GLASS_STATE_IN);
        details.setSlot(nearestEmpty.getSlot());
        details.setDeviceId(nearestEmpty.getDeviceId());
        edgStorageCageDetailsService.save(details);
        log.info("5、玻璃信息已存入理片笼详情表,玻璃信息为{}", details);
        //添加进片任务
        boolean taskCache = saveTaskCache(details.getGlassId(), 0, nearestEmpty.getSlot(), Const.GLASS_CACHE_TYPE_IN);
        log.info("6、生成进片任务信息存入任务表是否完成:{}", taskCache);
 
        S7object.getinstance().plccontrol.writeWord(confirmationWrodAddress, 1);
        log.info("7、发送确认字完成");
 
    }
 
    /**
     * 查询玻璃并进行交换
     *
     * @param glassId
     * @return
     */
    public GlassInfo queryAndChangeGlass(String glassId) {
        GlassInfo glassInfo = glassInfoService.getOne(new LambdaQueryWrapper<GlassInfo>().eq(GlassInfo::getGlassId, glassId));
//                .inSql(GlassInfo::getEngineerId, "select engineer_id from engineering where state = 1"));
        Assert.isFalse(null == glassInfo, "玻璃信息不存在");
        if ((glassInfo.getWidth() < Double.parseDouble(minTwoFirstLength) && glassInfo.getHeight() < Double.parseDouble(minTwoSecondLength)) || (glassInfo.getWidth() < Double.parseDouble(minTwoSecondLength) && glassInfo.getHeight() < Double.parseDouble(minTwoFirstLength))) {
            log.info("3、玻璃信息不符合进入理片笼最小尺寸,玻璃信息为{}", glassInfo);
            return null;
        }
        //按照玻璃尺寸
        LambdaQueryWrapper<GlassInfo> queryWrapper = new LambdaQueryWrapper<GlassInfo>()
                .eq(GlassInfo::getWidth, glassInfo.getWidth())
                .eq(GlassInfo::getHeight, glassInfo.getHeight())
                .eq(GlassInfo::getThickness, glassInfo.getThickness())
                .eq(GlassInfo::getFilmsid, glassInfo.getFilmsid())
                .eq(GlassInfo::getTotalLayer, glassInfo.getTotalLayer())
                .eq(GlassInfo::getLayer, glassInfo.getLayer())
                .eq(GlassInfo::getEngineerId, glassInfo.getEngineerId())
                .notInSql(GlassInfo::getGlassId, "select distinct glass_id from edg_storage_cage_details " +
                        "where engineer_id = '" + glassInfo.getEngineerId() + "' and width = " + glassInfo.getWidth() + " and height = " + glassInfo.getHeight())
                .orderByAsc(GlassInfo::getTemperingLayoutId)
                .orderBy(Boolean.TRUE, sequenceOrder, GlassInfo::getTemperingFeedSequence)
                .last("Limit 1");
        GlassInfo swapGlassInfo = glassInfoService.getOne(queryWrapper);
        if (swapGlassInfo != null && !glassInfo.getGlassId().equals(swapGlassInfo.getGlassId())) {
            String swapGlassId = swapGlassInfo.getGlassId();
            log.info("将玻璃{}和玻璃{},信息互换,进玻璃 {}", glassInfo, swapGlassInfo, swapGlassInfo);
            swapGlassInfo.setGlassId(glassId);
            glassInfo.setGlassId(swapGlassId);
            glassInfoService.updateById(swapGlassInfo);
            glassInfoService.updateById(glassInfo);
            return swapGlassInfo;
        }
        return glassInfo;
    }
 
    /**
     * 出片任务
     *
     * @param out08Glassstate
     * @param out10Glassstate
     * @param confirmationWrodAddress
     * @param glassId
     * @param currentSlot
     */
    public boolean outTo(int out08Glassstate, int out10Glassstate, String confirmationWrodAddress, String glassId, int currentSlot) {
        //逻辑步骤:
//        0、A09、A10是否空闲,是否可以执行出片任务
//        1、获取钢化版图是否超过阈值
//        1.1、超过阈值:获取当前最小版图需要出片的玻璃信息
//        1.1.1、获取两条线最后一次出片的任务信息
//        1.1.2、按照出片信息去详情表查询格子在笼子里面剩余相同尺寸的玻璃数据
//        1.1.3、判断哪条线玻璃数量在理片笼内的数据最少,决定最小版图走该条线(问题:如果这条线非空闲,直接结束)
//        1.2、未超过阈值:
//        1.2.1、获取程序那条线空闲
//        1.2.2、获取该条线最后一次出片的任务信息
//        1.2.3、按照出片信息去详情表查询格子在笼子里面剩余相同尺寸的玻璃数据且以版图id、版序升序排序  取第一块玻璃出片
//        2、如果没有历史出片任务
//        2.1、出当前版图id最小版序最小的玻璃(问题:两条线都没有历史任务,出片时两条线的玻璃尺寸相同,是否找尺寸不同的)
        if ((out08Glassstate == 2 && out10Glassstate == 2) || (out08Glassstate == 0 && out10Glassstate == 0)) {
            log.info("A09、A10为{},{}非自动状态,无法出片", out08Glassstate, out10Glassstate);
            return Boolean.FALSE;
        }
        log.info("0、出片任务出的状态:A09:【{}】;A10:【{}】)", out08Glassstate, out10Glassstate);
        //定义出片玻璃信息
        int endcell = 0;
        EdgStorageCageDetails glassInfo = null;
        boolean flag = queryMaxMinDiff(threshold);
        log.info("1、获取钢化版图是否超过阈值:{}", flag);
        if (flag) {
            //获取当前最小版图需要出片的玻璃信息
            glassInfo = queryMinGlass(0.0, 0.0, glassId);
            log.info("1.1、超过阈值:获取当前最小版图需要出片的玻璃信息:{}", glassInfo);
            if (glassInfo == null) {
                log.info("笼子内和待进片没有玻璃");
                return Boolean.FALSE;
            }
            EdgStorageCageDetails a09EdgGlass = queryGlassByTaskCache(Const.A09_OUT_TARGET_POSITION, Const.GLASS_CACHE_TYPE_OUT_ALL);
            EdgStorageCageDetails a10EdgGlass = queryGlassByTaskCache(Const.A10_OUT_TARGET_POSITION, Const.GLASS_CACHE_TYPE_OUT_ALL);
            endcell = queryLineByGlassInfo(a09EdgGlass, a10EdgGlass, glassInfo, out08Glassstate, out10Glassstate);
        } else {
            //按照状态判断两条线走那条线
            endcell = computerLineByState(out08Glassstate, out10Glassstate);
 
            if (out08Glassstate == 1 && out10Glassstate == 1) {
                EdgStorageCageDetails a09EdgGlass = queryGlassByTaskCache(Const.A09_OUT_TARGET_POSITION, Const.GLASS_CACHE_TYPE_OUT_ALL);
                EdgStorageCageDetails a10EdgGlass = queryGlassByTaskCache(Const.A10_OUT_TARGET_POSITION, Const.GLASS_CACHE_TYPE_OUT_ALL);
                if (a09EdgGlass == null && a10EdgGlass == null) {
                    MPJQueryWrapper<EdgStorageCageDetails> wrapper = new MPJQueryWrapper<>();
                    wrapper.select("count(t.glass_id), t.width, t.height")
                            .eq("t.state", Const.GLASS_STATE_IN)
                            .inSql("t.slot", "select slot from edg_storage_cage where enable_state = " + Const.SLOT_ON)
                            .groupBy("t.width, t.height");
                    if (endcell == Const.A10_OUT_TARGET_POSITION) {
                        wrapper.innerJoin("(select glass_id, case when height <= width then width else height end as first_length, " +
                                "case when width < height then width else height end as second_length from edg_storage_cage_details) t1 " +
                                "on t.glass_id = t1.glass_id and (t1.first_length between " + minTwoFirstLength + " and " + firstLength
                                + " and t1.second_length between " + minTwoSecondLength + " and " + secondLength + ") ");
                    } else {
                        wrapper.innerJoin("(select glass_id, case when height <= width then width else height end as first_length, " +
                                "case when width < height then width else height end as second_length from edg_storage_cage_details) t1 " +
                                "on t.glass_id = t1.glass_id and (t1.first_length >=" + minOneFirstLength + " and t1.second_length>=" + minOneSecondLength + ") ");
                    }
                    wrapper.last("order by count(t.glass_id) desc  limit 2");
                    List<EdgStorageCageDetails> list = edgStorageCageDetailsService.list(wrapper);
                    if (CollectionUtil.isEmpty(list)) {
                        MPJQueryWrapper<GlassInfo> queryWrapper = new MPJQueryWrapper<GlassInfo>()
                                .selectAll(GlassInfo.class).eq("t.glass_id", glassId);
                        if (endcell == Const.A10_OUT_TARGET_POSITION) {
                            queryWrapper.innerJoin("(select glass_id, case when height <= width then width else height end as first_length, " +
                                    "case when width < height then width else height end as second_length from edg_storage_cage_details) t1 " +
                                    "on t.glass_id = t1.glass_id and (t1.first_length between " + minTwoFirstLength + " and " + firstLength
                                    + " and t1.second_length between " + minTwoSecondLength + " and " + secondLength + ") ");
                        } else {
                            queryWrapper.innerJoin("(select glass_id, case when height <= width then width else height end as first_length, " +
                                    "case when width < height then width else height end as second_length from edg_storage_cage_details) t1 " +
                                    "on t.glass_id = t1.glass_id and (t1.first_length >=" + minOneFirstLength + " and t1.second_length>=" + minOneSecondLength + ") ");
                        }
                        GlassInfo one = glassInfoService.getOne(queryWrapper);
                        if (one != null) {
                            EdgStorageCageDetails resultDetails = new EdgStorageCageDetails();
                            BeanUtils.copyProperties(one, resultDetails);
                            glassInfo = resultDetails;
                        } else {
                            log.info("获取笼内玻璃和待进片玻璃为空或者不符合磨边尺寸");
                        }
                    } else {
                        glassInfo = queryMinGlass(list.get(0).getWidth(), list.get(0).getHeight(), glassId);
                    }
                } else if (a09EdgGlass != null && a10EdgGlass != null) {
                    List<EdgStorageCageDetails> a09Count = queryGlassByTaskLine(Const.A09_OUT_TARGET_POSITION);
                    List<EdgStorageCageDetails> a10Count = queryGlassByTaskLine(Const.A10_OUT_TARGET_POSITION);
                    endcell = a09Count.size() > a10Count.size() ? Const.A09_OUT_TARGET_POSITION : Const.A10_OUT_TARGET_POSITION;
                    glassInfo = a09Count.size() > a10Count.size() ? queryMinGlass(a09EdgGlass.getWidth(), a09EdgGlass.getHeight(), glassId)
                            : queryMinGlass(a10EdgGlass.getWidth(), a10EdgGlass.getHeight(), glassId);
                } else {
                    //按照历史任务获取需要执行的路线,如果该条线笼内无玻璃 走其他逻辑
                    endcell = a09EdgGlass == null ? Const.A09_OUT_TARGET_POSITION : Const.A10_OUT_TARGET_POSITION;
                    List<EdgStorageCageDetails> details = queryGlassByTaskLine(endcell);
                    if (CollectionUtil.isEmpty(details)) {
                        int othercell = endcell == Const.A10_OUT_TARGET_POSITION ? Const.A09_OUT_TARGET_POSITION : Const.A10_OUT_TARGET_POSITION;
                        glassInfo = queryChangeGlassInfo(othercell, glassId);
                    } else {
                        glassInfo = a09EdgGlass != null ? queryMinGlass(a09EdgGlass.getWidth(), a09EdgGlass.getHeight(), glassId)
                                : queryMinGlass(a10EdgGlass.getWidth(), a10EdgGlass.getHeight(), glassId);
                    }
                }
            } else {
                List<EdgStorageCageDetails> edgStorageCageDetails = queryGlassByTaskLine(endcell);
                if (CollectionUtil.isNotEmpty(edgStorageCageDetails)) {
                    glassInfo = edgStorageCageDetails.get(0);
                } else {
                    //获取笼内所有玻璃信息,包括待进片的
                    List<EdgStorageCageDetails> glassList = queryEdgAllGlass(glassId);
                    if (CollectionUtil.isEmpty(glassList)) {
                        log.info("笼内和待进片都没有玻璃");
                        return Boolean.FALSE;
                    }
                    if (glassList.size() == 1 && glassId.equals(glassList.get(0).getGlassId())) {
                        glassInfo = glassList.get(0);
                    } else {
                        int othercell = endcell == Const.A10_OUT_TARGET_POSITION ? Const.A09_OUT_TARGET_POSITION : Const.A10_OUT_TARGET_POSITION;
                        glassInfo = queryChangeGlassInfo(othercell, glassId);
                    }
                }
            }
        }
        return saveOutGlassMessage(glassInfo, endcell, confirmationWrodAddress, glassId, currentSlot);
    }
 
    /**
     * 获取笼子内所有玻璃信息,包括待进片的
     *
     * @param glassId
     */
    private List<EdgStorageCageDetails> queryEdgAllGlass(String glassId) {
        List<EdgStorageCageDetails> glassList = new ArrayList<>();
        //获取待进片玻璃
        //todo: 获取正在执行的工程信息
        if (StringUtils.isNotBlank(glassId)) {
            GlassInfo one = glassInfoService.getOne(new LambdaQueryWrapper<GlassInfo>()
                    .eq(GlassInfo::getGlassId, glassId));
//                    .inSql(GlassInfo::getEngineerId, "select engineer_id from engineering where state = 1"));
            EdgStorageCageDetails resultDetails = new EdgStorageCageDetails();
            BeanUtils.copyProperties(one, resultDetails);
            glassList.add(resultDetails);
        }
        //获取笼内玻璃
        List<EdgStorageCageDetails> details = edgStorageCageDetailsService.selectJoinList(EdgStorageCageDetails.class, new MPJLambdaWrapper<EdgStorageCageDetails>().selectAll(EdgStorageCageDetails.class)
                .leftJoin(EdgStorageCage.class, on -> on.eq(EdgStorageCage::getSlot, EdgStorageCageDetails::getSlot).eq(EdgStorageCageDetails::getState, Const.GLASS_STATE_IN))
                .eq(EdgStorageCage::getEnableState, Const.SLOT_ON));
        glassList.addAll(details);
        return glassList;
 
    }
 
    /**
     * 获取详情表内最大最小版图id的差值,判断是否出最小版图玻璃
     *
     * @return
     */
    public boolean queryMaxMinDiff(int threshold) {
        //获取笼子内最大版图id和最小版图id插值,判断是否大于阈值,大于阈值直接出最小版图玻璃
        QueryWrapper<EdgStorageCageDetails> queryWrapper = new QueryWrapper<>();
        queryWrapper.select("max(tempering_layout_id)-min(tempering_layout_id) as diff")
                .eq("state", Const.GLASS_STATE_IN)
                .inSql("slot", "select slot from edg_storage_cage where enable_state = " + Const.SLOT_ON);
        List<Object> list = edgStorageCageDetailsService.listObjs(queryWrapper);
        //获取笼内玻璃版图差值是否大于阈值
        if (CollectionUtil.isNotEmpty(list)) {
            Long diff = (Long) list.get(0);
            return diff > threshold;
        } else {
            return Boolean.FALSE;
        }
    }
 
    /**
     * 获取当前最小版图需要出片的玻璃信息
     *
     * @param width
     * @param height
     */
    private EdgStorageCageDetails queryMinGlass(double width, double height, String glassId) {
        //获取表内版图id最小的玻璃信息
        EdgStorageCageDetails glassDetails = edgStorageCageDetailsService.getOne(new LambdaQueryWrapper<EdgStorageCageDetails>()
                .eq(EdgStorageCageDetails::getState, 100)
                .eq(width != 0, EdgStorageCageDetails::getWidth, width)
                .eq(height != 0, EdgStorageCageDetails::getHeight, height)
                .inSql(EdgStorageCageDetails::getSlot, "select slot from edg_storage_cage where enable_state = " + Const.SLOT_ON)
                .orderByAsc(EdgStorageCageDetails::getTemperingLayoutId)
                .orderBy(Boolean.TRUE, sequenceOrder, EdgStorageCageDetails::getTemperingFeedSequence)
                .last("limit 1"));
        if (StringUtils.isBlank(glassId)) {
            return glassDetails;
        }
        GlassInfo one = glassInfoService.getOne(new LambdaQueryWrapper<GlassInfo>()
                        .eq(GlassInfo::getGlassId, glassId)
//                .inSql(GlassInfo::getEngineerId, "select engineer_id from engineering where state = 1")
        );
        EdgStorageCageDetails resultDetails = new EdgStorageCageDetails();
        BeanUtils.copyProperties(one, resultDetails);
        if (null == glassDetails) {
            return resultDetails;
        }
        return resultDetails.getTemperingLayoutId() <= glassDetails.getTemperingLayoutId() && resultDetails.getTemperingFeedSequence() > glassDetails.getTemperingFeedSequence() ?
                resultDetails : glassDetails;
    }
 
    /**
     * 获取任务表中指定线路笼子内还剩余的玻璃信息
     */
    private List<EdgStorageCageDetails> queryGlassByTaskLine(int line) {
        //获取任务表中最后一次出片的玻璃id
        TaskCache taskCache = taskCacheService.queryGlassByTaskCache(line, Const.GLASS_CACHE_TYPE_OUT_ALL);
        if (null == taskCache) {
            log.info("没有找到{}线任务信息", line);
            return new ArrayList<>();
        }
        MPJQueryWrapper<EdgStorageCageDetails> mpjLambdaWrapper = new MPJQueryWrapper<>();
        mpjLambdaWrapper.select("t1.*")
                .innerJoin("edg_storage_cage_details t1 on t.width = t1.width and t.height = t1.height")
                .eq("t.glass_id", taskCache.getGlassId())
                .ne("t1.glass_id", taskCache.getGlassId())
                .eq("t1.state", Const.GLASS_STATE_IN)
                .inSql("t1.slot", "select slot from edg_storage_cage where enable_state = " + Const.SLOT_ON)
                .orderByAsc("t1.tempering_layout_id")
                .orderBy(Boolean.TRUE, sequenceOrder, "t1.tempering_feed_sequence");
        List<EdgStorageCageDetails> details = edgStorageCageDetailsService.selectJoinList(EdgStorageCageDetails.class, mpjLambdaWrapper);
        if (CollectionUtil.isEmpty(details)) {
            return new ArrayList<>();
        }
        return details;
    }
 
    /**
     * 按照任务类型、线号获取任务信息
     *
     * @param line
     * @param taskTypes
     * @return
     */
    private EdgStorageCageDetails queryGlassByTaskCache(int line, List<Integer> taskTypes) {
        TaskCache taskCache = taskCacheService.queryGlassByTaskCache(line, taskTypes);
        if (null == taskCache) {
            log.info("没有找到{}线任务信息", line);
            return null;
        }
        return edgStorageCageDetailsService.getOne(new LambdaQueryWrapper<EdgStorageCageDetails>().eq(EdgStorageCageDetails::getGlassId, taskCache.getGlassId())
                .inSql(EdgStorageCageDetails::getSlot, "select slot from edg_storage_cage where enable_state = " + Const.SLOT_ON)
                .last(" limit 1 "));
    }
 
    /**
     * 按照任务玻璃信息和待出片玻璃信息获取出片路径
     *
     * @param a08EdgStorageCageDetails 09号线出片玻璃信息
     * @param a10EdgStorageCageDetails 10号线出片玻璃信息
     * @param glassInfo                带出片玻璃信息
     * @param out08Glassstate          09号线空闲状态
     * @param out10Glassstate          10号线空闲状态
     * @return
     */
    private Integer queryLineByGlassInfo(EdgStorageCageDetails a08EdgStorageCageDetails, EdgStorageCageDetails a10EdgStorageCageDetails,
                                         EdgStorageCageDetails glassInfo, int out08Glassstate, int out10Glassstate) {
        //存在出片任务 07为空
        if (out08Glassstate == 1 && out10Glassstate == 1) {
            boolean b08 = a08EdgStorageCageDetails != null && a08EdgStorageCageDetails.getHeight() == glassInfo.getHeight()
                    && a08EdgStorageCageDetails.getWidth() == glassInfo.getWidth();
            boolean b10 = a10EdgStorageCageDetails != null && a10EdgStorageCageDetails.getHeight() == glassInfo.getHeight()
                    && a10EdgStorageCageDetails.getWidth() == glassInfo.getWidth();
            if (b08) {
                return Const.A09_OUT_TARGET_POSITION;
            }
            if (b10) {
                return Const.A10_OUT_TARGET_POSITION;
            }
        }
        return computerLineByState(out08Glassstate, out10Glassstate);
    }
 
    /**
     * 按照尺寸出玻璃
     *
     * @param endcell endcell = out08Glassstate == 1 ? Const.A09_OUT_TARGET_POSITION : Const.A10_OUT_TARGET_POSITION;
     * @param glassId
     * @return
     */
    public EdgStorageCageDetails queryChangeGlassInfo(int endcell, String glassId) {
        //获取笼子内数量前二的玻璃数量
        MPJQueryWrapper<EdgStorageCageDetails> wrapper = new MPJQueryWrapper<>();
        wrapper.select("count(t.glass_id) as count, t.width, t.height")
                .eq("t.state", Const.GLASS_STATE_IN)
                .inSql("t.slot", "select slot from edg_storage_cage where enable_state = " + Const.SLOT_ON)
                .groupBy("t.width, t.height");
        if (endcell == Const.A10_OUT_TARGET_POSITION) {
            wrapper.innerJoin("(select glass_id, case when height <= width then width else height end as first_length, " +
                    "case when width < height then width else height end as second_length from edg_storage_cage_details) t1 " +
                    "on t.glass_id = t1.glass_id and (t1.first_length >= " + minOneFirstLength + " and t1.second_length >= " + minOneSecondLength + ")");
        } else {
            wrapper.innerJoin("(select glass_id, case when height <= width then width else height end as first_length, " +
                    "case when width < height then width else height end as second_length from edg_storage_cage_details) t1 " +
                    "on t.glass_id = t1.glass_id and (t1.first_length between " + minTwoFirstLength + " and " + firstLength
                    + " and t1.second_length between " + minTwoSecondLength + " and " + secondLength + ") ");
        }
        wrapper.last("order by count(t.glass_id) desc  limit 2");
        List<EdgStorageCageDetails> list = edgStorageCageDetailsService.list(wrapper);
        if (CollectionUtil.isEmpty(list)) {
            return null;
        }
        log.info("获取笼子内数量前二的玻璃数量:{}", list);
        //获取宽高拍第一的玻璃信息
        EdgStorageCageDetails firstSize = list.get(0);
        Integer firstCount = firstSize.getCount();
        double firstWidth = firstSize.getWidth();
        double firstHeight = firstSize.getHeight();
        if (list.size() == 1) {
            return queryMinGlass(firstWidth, firstHeight, glassId);
        }
        //获取宽高拍第二的玻璃信息
        EdgStorageCageDetails secondSize = list.get(1);
        Integer secondCount = secondSize.getCount();
        double secondWidth = secondSize.getWidth();
        double secondHeight = secondSize.getHeight();
        //获取数量前2的玻璃数量比例
        Integer mix = firstCount / secondCount;
        log.info("获取玻璃数量前2的玻璃占比为:{}", mix);
 
        if (mix >= 2) {
            log.info("获取玻璃数量前2的玻璃占比为{},大于2,直接出玻璃数据的最多的,宽:{},高:{}", mix, firstWidth, firstHeight);
            return queryMinGlass(firstWidth, firstHeight, glassId);
        } else {
            log.info("获取玻璃数量前2的玻璃占比为{},小于2", mix);
            //获取任务表中最后一次出片的玻璃id
            TaskCache taskCache = taskCacheService.queryGlassByTaskCache(endcell, Const.GLASS_CACHE_TYPE_OUT_ALL);
            log.info("获取任务表中{}线最后一次出片的玻璃任务信息:{}", endcell, taskCache);
            if (null == taskCache) {
                log.info("{}线没有出片任务信息,直接出片", endcell);
                return queryMinGlass(firstSize.getWidth(), firstSize.getHeight(), glassId);
            }
            EdgStorageCageDetails outGlassInfo = edgStorageCageDetailsService.getOne(new LambdaQueryWrapper<EdgStorageCageDetails>()
                    .eq(EdgStorageCageDetails::getGlassId, taskCache.getGlassId()).last("limit 1"));
            log.info("{}线有出片任务信息,任务信息为{},玻璃信息为{}", endcell, taskCache, outGlassInfo);
            if (outGlassInfo.getWidth() == firstWidth && outGlassInfo.getHeight() == firstHeight) {
                log.info("数量最多的宽{}高{}和{}线任务的宽{}高{}相同,出数量排第二的玻璃,宽{}高{}",
                        firstWidth, firstHeight, endcell, outGlassInfo.getWidth(), outGlassInfo.getHeight(), secondWidth, secondHeight);
                return queryMinGlass(secondWidth, secondHeight, glassId);
            } else {
                log.info("数量第二多的宽{}高{}和{}线任务的宽{}高{}相同,出数量排第二的玻璃,宽{}高{}",
                        secondWidth, secondHeight, endcell, outGlassInfo.getWidth(), outGlassInfo.getHeight(), firstWidth, firstHeight);
                return queryMinGlass(firstWidth, firstHeight, glassId);
            }
        }
    }
 
    /**
     * 保存出片任务相关信息
     *
     * @param glassInfo
     * @param endcell
     * @param confirmationWrodAddress
     * @param glassId
     * @return
     */
    private boolean saveOutGlassMessage(EdgStorageCageDetails glassInfo, int endcell, String confirmationWrodAddress, String glassId, Integer currentSlot) {
        if (glassInfo != null) {
            log.info("4、添加出片任务,玻璃id:{},任务类型:{},起始位置:{},结束位置:{}", glassInfo.getGlassId(),
                    2, glassInfo.getSlot(), endcell);
            if (glassInfo.getGlassId().equals(glassId)) {
                log.info("5、直通任务,将玻璃信息插入卧式理片笼,当前玻璃信息:{}", glassInfo);
                EdgStorageCageDetails details = new EdgStorageCageDetails();
                BeanUtils.copyProperties(glassInfo, details);
                EdgStorageCage nearestEmpty = edgStorageCageService.selectNearestEmpty(currentSlot, Boolean.TRUE);
                Assert.isTrue(null != nearestEmpty, "格子已满,无法执行直通任务");
                log.info("3、查询卧式理片笼里面的空格:{}", nearestEmpty);
                details.setSlot(nearestEmpty.getSlot());
                details.setState(Const.GLASS_STATE_OUT);
                edgStorageCageDetailsService.save(details);
 
                boolean taskCacheStatus = saveTaskCache(glassInfo.getGlassId(), nearestEmpty.getSlot(), endcell, Const.GLASS_CACHE_TYPE_THROUGH);
                log.info("6、添加出片任务是否完成:{}", taskCacheStatus);
            } else {
                log.info("5、非直通任务,将玻璃信息插入卧式理片笼,当前玻璃信息:{}", glassInfo);
                LambdaUpdateWrapper<EdgStorageCageDetails> wrapper = new LambdaUpdateWrapper<>();
                wrapper.eq(EdgStorageCageDetails::getGlassId, glassInfo.getGlassId()).set(EdgStorageCageDetails::getState, Const.GLASS_STATE_OUT);
                edgStorageCageDetailsService.update(wrapper);
                log.info("5、更新出片玻璃的状态为{}", Const.GLASS_STATE_OUT);
 
                boolean taskCacheStatus = saveTaskCache(glassInfo.getGlassId(), glassInfo.getSlot(), endcell, Const.GLASS_CACHE_TYPE_OUT);
                log.info("6、添加出片任务是否完成:{}", taskCacheStatus);
            }
            boolean glassSizeStatus = saveGlassSize(glassInfo, endcell);
            log.info("7、添加出片玻璃尺寸信息到磨边前玻璃表是否完成:{}", glassSizeStatus);
            S7object.getinstance().plccontrol.writeWord(confirmationWrodAddress, 1);
            log.info("8、发送确认字已完成");
            Damage damage=new Damage();
            damage.setGlassId(glassInfo.getGlassId());
            damage.setWorkingProcedure("切割");
            damage.setLine(1001);
            damage.setType(1);
            damage.setRemark("过卧式理片");
            damageService.insertDamage(damage);
            return Boolean.TRUE;
        }
        return Boolean.FALSE;
    }
 
    /**
     * 添加理片笼任务
     *
     * @param glassId
     * @param startcell
     * @param endcell
     * @param taskType
     * @return
     */
    private boolean saveTaskCache(String glassId, int startcell, int endcell, int taskType) {
        TaskCache taskCache = new TaskCache();
        taskCache.setGlassId(glassId);
        taskCache.setTaskStatus(0);
        taskCache.setStartCell(startcell);
        taskCache.setEndCell(endcell);
        taskCache.setTaskType(taskType);
        taskCache.setCreateTime(new Date());
        return taskCacheService.save(taskCache);
    }
 
    /**
     * 添加磨边队列信息
     *
     * @param glassInfo
     * @param endcell
     * @return
     */
    private boolean saveGlassSize(EdgStorageCageDetails glassInfo, int endcell) {
        EdgGlassTaskInfo edgGlassTaskInfo = new EdgGlassTaskInfo();
        BeanUtils.copyProperties(glassInfo, edgGlassTaskInfo);
        edgGlassTaskInfo.setHeight((int) (glassInfo.getHeight() * ratio));
        edgGlassTaskInfo.setWidth((int) (glassInfo.getWidth() * ratio));
        edgGlassTaskInfo.setThickness((int) (glassInfo.getThickness() * ratio));
        edgGlassTaskInfo.setStatus(Const.EDG_GLASS_BEFORE);
        edgGlassTaskInfo.setLine(endcell);
        edgGlassTaskInfo.setTime(new Date());
        //先将历史对列表中本玻璃的数据删除,重新新增一份最新的数据
        edgGlassTaskInfoService.remove(new LambdaQueryWrapper<EdgGlassTaskInfo>().eq(EdgGlassTaskInfo::getGlassId, glassInfo.getGlassId()));
        return edgGlassTaskInfoService.save(edgGlassTaskInfo);
    }
 
    /**
     * 计算出片线路
     * 已排除都为2  都为0 的情况
     *
     * @param out08Glassstate
     * @param out10Glassstate
     * @return
     */
    private int computerLineByState(int out08Glassstate, int out10Glassstate) {
        if (out08Glassstate == 0) {
            if (out10Glassstate == 2) {
                return Const.A09_OUT_TARGET_POSITION;
            } else {
                return Const.A10_OUT_TARGET_POSITION;
            }
        } else if (out08Glassstate == 1) {
            return Const.A09_OUT_TARGET_POSITION;
        } else {
            return Const.A10_OUT_TARGET_POSITION;
        }
    }
 
    @Scheduled(fixedDelay = 1000)
    public void CacheGlassTasks() {
        JSONObject jsonObject = new JSONObject();
        List<Map<String, Object>> currentCutTerritorys = edgStorageCageDetailsService.selectCutTerritory();
        jsonObject.append("currentCutTerritory", currentCutTerritorys);
        //log.info("本次识别版图{},++++{}",engineerId,currentCutTerritorys);
        //卧室缓存笼内信息
        List<Map<String, Object>> EdgStorageCageinfos = edgStorageCageService.selectEdgStorageCages();
        jsonObject.append("EdgStorageCageinfos", EdgStorageCageinfos);
 
        ArrayList<WebSocketServer> sendwServer = WebSocketServer.sessionMap.get("cacheGlass");
        if (sendwServer != null) {
            for (WebSocketServer webserver : sendwServer) {
                if (webserver != null) {
                    webserver.sendMessage(jsonObject.toString());
                    List<String> messages = webserver.getMessages();
                    if (!messages.isEmpty()) {
                        // // 将最后一个消息转换为整数类型的列表
                        webserver.clearMessages();
                    }
                } else {
                    log.info("Home is closed");
                }
            }
        }
    }
 
    @Scheduled(fixedDelay = 1000)
    public void cacheGlassIsRun() {
        JSONObject jsonObject = new JSONObject();
        //正在进行的任务
        List<EdgGlassTaskInfo> taskCaches = edgGlassTaskInfoService.selectTaskCacheIsRun();
        jsonObject.append("taskCaches", taskCaches);
        ArrayList<WebSocketServer> sendwServer = WebSocketServer.sessionMap.get("cacheGlassIsRun");
        if (sendwServer != null) {
            for (WebSocketServer webserver : sendwServer) {
                if (webserver != null) {
                    webserver.sendMessage(jsonObject.toString());
                } else {
                    log.info("Home is closed");
                }
            }
        }
    }
 
    @Scheduled(fixedDelay = 1000)
    public void CacheGlassTaskss() {
        JSONObject jsonObject = new JSONObject();
        //log.info("本次识别版图{},++++{}",engineerId,currentCutTerritorys);
        //磨边信息
        List<EdgGlassTaskInfo> edgTasks = edgGlassTaskInfoService.selectEdgInfo();
        jsonObject.append("edgTasks", edgTasks);
 
        ArrayList<WebSocketServer> sendwServer = WebSocketServer.sessionMap.get("edgTasks");
        if (sendwServer != null) {
            for (WebSocketServer webserver : sendwServer) {
                if (webserver != null) {
                    webserver.sendMessage(jsonObject.toString());
                    List<String> messages = webserver.getMessages();
                    if (!messages.isEmpty()) {
                        // // 将最后一个消息转换为整数类型的列表
                        webserver.clearMessages();
                    }
                } else {
                    log.info("edgTasks is closed");
                }
            }
        }
    }
}